summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/_inc/lib')
-rw-r--r--plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php352
-rw-r--r--plugins/jetpack/_inc/lib/admin-pages/class.jetpack-landing-page.php3
-rw-r--r--plugins/jetpack/_inc/lib/admin-pages/class.jetpack-react-page.php422
-rw-r--r--plugins/jetpack/_inc/lib/admin-pages/class.jetpack-settings-page.php154
-rw-r--r--plugins/jetpack/_inc/lib/class.color.php755
-rw-r--r--plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php3327
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-automatic-install-skin.php111
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-iframe-embed.php84
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-keyring-service-helper.php204
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-password-checker.php1288
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-photon-image-sizes.php182
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-photon-image.php243
-rw-r--r--plugins/jetpack/_inc/lib/class.jetpack-search-performance-logger.php83
-rw-r--r--plugins/jetpack/_inc/lib/class.media-extractor.php436
-rw-r--r--plugins/jetpack/_inc/lib/class.media-summary.php354
-rw-r--r--plugins/jetpack/_inc/lib/class.media.php505
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php330
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php1769
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php60
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php56
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php39
-rw-r--r--plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php40
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/business-hours.php49
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php79
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php71
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php22
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php121
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php194
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php167
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/service-api-keys.php281
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php37
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php62
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-fields/attachment-fields-videopress.php171
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php353
-rw-r--r--plugins/jetpack/_inc/lib/debugger/0-load.php13
-rw-r--r--plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-test-base.php302
-rw-r--r--plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-tests.php338
-rw-r--r--plugins/jetpack/_inc/lib/debugger/class-jetpack-debugger.php530
-rw-r--r--plugins/jetpack/_inc/lib/functions.wp-notify.php353
-rw-r--r--plugins/jetpack/_inc/lib/icalendar-reader.php913
-rw-r--r--plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-builder.php341
-rw-r--r--plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-parser.php683
-rw-r--r--plugins/jetpack/_inc/lib/markdown/0-load.php6
-rw-r--r--plugins/jetpack/_inc/lib/markdown/README.md19
-rw-r--r--plugins/jetpack/_inc/lib/markdown/extra.php3207
-rw-r--r--plugins/jetpack/_inc/lib/markdown/gfm.php400
-rw-r--r--plugins/jetpack/_inc/lib/plugins.php132
-rw-r--r--plugins/jetpack/_inc/lib/tonesque.php237
-rw-r--r--plugins/jetpack/_inc/lib/tracks/class.tracks-client.php191
-rw-r--r--plugins/jetpack/_inc/lib/tracks/class.tracks-event.php149
-rw-r--r--plugins/jetpack/_inc/lib/tracks/client.php130
-rw-r--r--plugins/jetpack/_inc/lib/tracks/tracks-ajax.js63
-rw-r--r--plugins/jetpack/_inc/lib/tracks/tracks-callables.js72
-rw-r--r--plugins/jetpack/_inc/lib/widgets.php776
54 files changed, 0 insertions, 21259 deletions
diff --git a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php b/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php
deleted file mode 100644
index 0c50f380..00000000
--- a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php
+++ /dev/null
@@ -1,352 +0,0 @@
-<?php
-
-// Shared logic between Jetpack admin pages
-abstract class Jetpack_Admin_Page {
- // Add page specific actions given the page hook
- abstract function add_page_actions( $hook );
-
- // Create a menu item for the page and returns the hook
- abstract function get_page_hook();
-
- // Enqueue and localize page specific scripts
- abstract function page_admin_scripts();
-
- // Render page specific HTML
- abstract function page_render();
-
- /**
- * Should we block the page rendering because the site is in IDC?
- * @var bool
- */
- static $block_page_rendering_for_idc;
-
- /**
- * Function called after admin_styles to load any additional needed styles.
- *
- * @since 4.3.0
- */
- function additional_styles() {}
-
- function __construct() {
- $this->jetpack = Jetpack::init();
- self::$block_page_rendering_for_idc = (
- Jetpack::validate_sync_error_idc_option() && ! Jetpack_Options::get_option( 'safe_mode_confirmed' )
- );
- }
-
- function add_actions() {
- global $pagenow;
-
- // If user is not an admin and site is in Dev Mode, don't do anything
- if ( ! current_user_can( 'manage_options' ) && Jetpack::is_development_mode() ) {
- return;
- }
-
- // Don't add in the modules page unless modules are available!
- if ( $this->dont_show_if_not_active && ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
- return;
- }
-
- // Initialize menu item for the page in the admin
- $hook = $this->get_page_hook();
-
- // Attach hooks common to all Jetpack admin pages based on the created
- // hook
- add_action( "load-$hook", array( $this, 'admin_help' ) );
- add_action( "load-$hook", array( $this, 'admin_page_load' ) );
- add_action( "admin_head-$hook", array( $this, 'admin_head' ) );
-
- add_action( "admin_print_styles-$hook", array( $this, 'admin_styles' ) );
- add_action( "admin_print_scripts-$hook", array( $this, 'admin_scripts' ) );
-
- if ( ! self::$block_page_rendering_for_idc ) {
- add_action( "admin_print_styles-$hook", array( $this, 'additional_styles' ) );
- }
- // If someone just activated Jetpack, let's show them a fullscreen connection banner.
- if (
- ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] )
- && ! Jetpack::is_active()
- && current_user_can( 'jetpack_connect' )
- && ! Jetpack::is_development_mode()
- ) {
- add_action( 'admin_enqueue_scripts', array( 'Jetpack_Connection_Banner', 'enqueue_banner_scripts' ) );
- add_action( 'admin_print_styles', array( Jetpack::init(), 'admin_banner_styles' ) );
- add_action( 'admin_notices', array( 'Jetpack_Connection_Banner', 'render_connect_prompt_full_screen' ) );
- delete_transient( 'activated_jetpack' );
- }
-
- // Check if the site plan changed and deactivate modules accordingly.
- add_action( 'current_screen', array( $this, 'check_plan_deactivate_modules' ) );
-
- // Attach page specific actions in addition to the above
- $this->add_page_actions( $hook );
- }
-
- function admin_head() {
- if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
- /**
- * Fires in the <head> of a particular Jetpack configuration page.
- *
- * The dynamic portion of the hook name, `$_GET['configure']`,
- * refers to the slug of module, such as 'stats', 'sso', etc.
- * A complete hook for the latter would be
- * 'jetpack_module_configuration_head_sso'.
- *
- * @since 3.0.0
- */
- do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
- }
- }
-
- // Render the page with a common top and bottom part, and page specific content
- function render() {
- // We're in an IDC: we need a decision made before we show the UI again.
- if ( self::$block_page_rendering_for_idc ) {
- return;
- }
-
- // Check if we are looking at the main dashboard
- if (
- isset( $_GET['page'] ) &&
- 'jetpack' === $_GET['page'] &&
- empty( $_GET['configure'] )
- )
- {
- $this->page_render();
- return;
- }
- Jetpack_Admin_Page::wrap_ui( array( $this, 'page_render' ) );
- }
-
- function admin_help() {
- $this->jetpack->admin_help();
- }
-
- function admin_page_load() {
- // This is big. For the moment, just call the existing one.
- $this->jetpack->admin_page_load();
- }
-
- // Add page specific scripts and jetpack stats for all menu pages
- function admin_scripts() {
- $this->page_admin_scripts(); // Delegate to inheriting class
- add_action( 'admin_footer', array( $this->jetpack, 'do_stats' ) );
- }
-
- // Enqueue the Jetpack admin stylesheet
- function admin_styles() {
- $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
-
- wp_enqueue_style( 'jetpack-admin', plugins_url( "css/jetpack-admin{$min}.css", JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION . '-20121016' );
- wp_style_add_data( 'jetpack-admin', 'rtl', 'replace' );
- wp_style_add_data( 'jetpack-admin', 'suffix', $min );
- }
-
- /**
- * Checks if REST API is enabled.
- *
- * @since 4.4.2
- *
- * @return bool
- */
- function is_rest_api_enabled() {
- return
- /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
- apply_filters( 'rest_enabled', true ) &&
- /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
- apply_filters( 'rest_jsonp_enabled', true ) &&
- /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
- apply_filters( 'rest_authentication_errors', true );
- }
-
- /**
- * Checks the site plan and deactivates modules that were active but are no longer included in the plan.
- *
- * @since 4.4.0
- *
- * @param $page
- *
- * @return array
- */
- function check_plan_deactivate_modules( $page ) {
- if (
- Jetpack::is_development_mode()
- || ! in_array(
- $page->base,
- array(
- 'toplevel_page_jetpack',
- 'admin_page_jetpack_modules',
- 'jetpack_page_vaultpress',
- 'jetpack_page_stats',
- 'jetpack_page_akismet-key-config'
- )
- )
- ) {
- return false;
- }
-
- $current = Jetpack_Plan::get();
-
- $to_deactivate = array();
- if ( isset( $current['product_slug'] ) ) {
- $active = Jetpack::get_active_modules();
- switch ( $current['product_slug'] ) {
- case 'jetpack_free':
- $to_deactivate = array( 'seo-tools', 'videopress', 'google-analytics', 'wordads', 'search' );
- break;
- case 'jetpack_personal':
- case 'jetpack_personal_monthly':
- $to_deactivate = array( 'seo-tools', 'videopress', 'google-analytics', 'wordads', 'search' );
- break;
- case 'jetpack_premium':
- case 'jetpack_premium_monthly':
- $to_deactivate = array( 'seo-tools', 'google-analytics', 'search' );
- break;
- }
- $to_deactivate = array_intersect( $active, $to_deactivate );
-
- $to_leave_enabled = array();
- foreach ( $to_deactivate as $feature ) {
- if ( Jetpack_Plan::supports( $feature ) ) {
- $to_leave_enabled []= $feature;
- }
- }
- $to_deactivate = array_diff( $to_deactivate, $to_leave_enabled );
-
- if ( ! empty( $to_deactivate ) ) {
- Jetpack::update_active_modules( array_filter( array_diff( $active, $to_deactivate ) ) );
- }
- }
- return array(
- 'current' => $current,
- 'deactivate' => $to_deactivate
- );
- }
-
- static function load_wrapper_styles( ) {
- $rtl = is_rtl() ? '.rtl' : '';
- wp_enqueue_style( 'dops-css', plugins_url( "_inc/build/admin.dops-style{$rtl}.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
- wp_enqueue_style( 'components-css', plugins_url( "_inc/build/style.min{$rtl}.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
- $custom_css = '
- #wpcontent {
- padding-left: 0 !important;
- }
- #wpbody-content {
- background-color: #f6f6f6;
- }
-
- #jp-plugin-container .wrap {
- margin: 0 auto;
- max-width:45rem;
- padding: 0 1.5rem;
- }
- #jp-plugin-container.is-wide .wrap {
- max-width: 1040px;
- }
- #jp-plugin-container .wrap .jetpack-wrap-container {
- margin-top: 1em;
- }
- .wp-admin #dolly {
- float: none;
- position: relative;
- right: 0;
- left: 0;
- top: 0;
- padding: .625rem;
- text-align: right;
- background: #fff;
- font-size: .75rem;
- font-style: italic;
- color: #87a6bc;
- border-bottom: 1px #e9eff3 solid;
- }
- ';
- wp_add_inline_style( 'dops-css', $custom_css );
- }
-
- static function wrap_ui( $callback, $args = array() ) {
- $defaults = array(
- 'is-wide' => false,
- );
- $args = wp_parse_args( $args, $defaults );
- $jetpack_admin_url = admin_url( 'admin.php?page=jetpack' );
-
- ?>
- <div id="jp-plugin-container" class="<?php if ( $args['is-wide'] ) { echo "is-wide"; } ?>">
-
- <div class="jp-masthead">
- <div class="jp-masthead__inside-container">
- <div class="jp-masthead__logo-container">
- <a class="jp-masthead__logo-link" href="<?php echo esc_url( $jetpack_admin_url ); ?>">
- <svg class="jetpack-logo__masthead" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" height="32" viewBox="0 0 118 32"><path fill="#00BE28" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M15,19H7l8-16V19z M17,29V13h8L17,29z"></path><path d="M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z"></path><path d="M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z"></path><path d="M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z"></path><path d="M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z"></path><path d="M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z"></path><path d="M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z"></path><path d="M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z"></path></svg>
- </a>
- </div>
- <div class="jp-masthead__nav">
- <?php if ( is_network_admin() ) {
- $current_screen = get_current_screen();
-
- $highlight_current_sites = ( 'toplevel_page_jetpack-network' === $current_screen->id ? 'is-primary' : '' );
- $highlight_current_settings = ( 'jetpack_page_jetpack-settings-network' === $current_screen->id ? 'is-primary' : '' );
- ?>
- <span class="dops-button-group">
- <?php
- if ( current_user_can( 'jetpack_network_sites_page' ) ) {
- ?><a href="<?php echo esc_url( network_admin_url( 'admin.php?page=jetpack' ) ); ?>" type="button" class="<?php echo esc_attr( $highlight_current_sites ); ?> dops-button is-compact" title="<?php esc_html_e( "Manage your network's Jetpack Sites.", 'jetpack' ); ?>"><?php echo esc_html_x( 'Sites', 'Navigation item', 'jetpack' ); ?></a><?php
- } if ( current_user_can( 'jetpack_network_settings_page' ) ) {
- ?><a href="<?php echo esc_url( network_admin_url( 'admin.php?page=jetpack-settings' ) ); ?>" type="button" class="<?php echo esc_attr( $highlight_current_settings ); ?> dops-button is-compact" title="<?php esc_html_e( "Manage your network's Jetpack Sites.", 'jetpack' ); ?>"><?php echo esc_html_x( 'Network Settings', 'Navigation item', 'jetpack' ); ?></a><?php
- } ?>
- </span>
- <?php } else { ?>
- <span class="dops-button-group">
- <a href="<?php echo esc_url( $jetpack_admin_url ); ?>" type="button" class="dops-button is-compact"><?php esc_html_e( 'Dashboard', 'jetpack' ); ?></a><?php
- if ( current_user_can( 'jetpack_manage_modules' ) ) {
- ?><a href="<?php echo esc_url( $jetpack_admin_url . '#/settings' ); ?>" type="button" class="dops-button is-compact"><?php esc_html_e( 'Settings', 'jetpack' ); ?></a><?php
- } ?>
- </span>
- <?php } ?>
- </div>
- </div>
- </div>
- <div class="wrap"><div id="jp-admin-notices" aria-live="polite"></div></div>
- <!-- START OF CALLBACK -->
- <?php
- ob_start();
- call_user_func( $callback );
- $callback_ui = ob_get_contents();
- ob_end_clean();
- echo $callback_ui;
- ?>
- <!-- END OF CALLBACK -->
- <div class="jp-footer">
- <div class="jp-footer__a8c-attr-container"><a href="https://automattic.com" target="_blank" rel="noopener noreferrer"><svg role="img" class="jp-footer__a8c-attr" x="0" y="0" viewBox="0 0 935 38.2" enable-background="new 0 0 935 38.2" aria-labelledby="a8c-svg-title"><title id="a8c-svg-title">An Automattic Airline</title><path d="M317.1 38.2c-12.6 0-20.7-9.1-20.7-18.5v-1.2c0-9.6 8.2-18.5 20.7-18.5 12.6 0 20.8 8.9 20.8 18.5v1.2C337.9 29.1 329.7 38.2 317.1 38.2zM331.2 18.6c0-6.9-5-13-14.1-13s-14 6.1-14 13v0.9c0 6.9 5 13.1 14 13.1s14.1-6.2 14.1-13.1V18.6zM175 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7L157 1.3h5.5L182 36.8H175zM159.7 8.2L152 23.1h15.7L159.7 8.2zM212.4 38.2c-12.7 0-18.7-6.9-18.7-16.2V1.3h6.6v20.9c0 6.6 4.3 10.5 12.5 10.5 8.4 0 11.9-3.9 11.9-10.5V1.3h6.7V22C231.4 30.8 225.8 38.2 212.4 38.2zM268.6 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H268.6zM397.3 36.8V8.7l-1.8 3.1 -14.9 25h-3.3l-14.7-25 -1.8-3.1v28.1h-6.5V1.3h9.2l14 24.4 1.7 3 1.7-3 13.9-24.4h9.1v35.5H397.3zM454.4 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7l19.2-35.5h5.5l19.5 35.5H454.4zM439.1 8.2l-7.7 14.9h15.7L439.1 8.2zM488.4 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H488.4zM537.3 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H537.3zM569.3 36.8V4.6c2.7 0 3.7-1.4 3.7-3.4h2.8v35.5L569.3 36.8 569.3 36.8zM628 11.3c-3.2-2.9-7.9-5.7-14.2-5.7 -9.5 0-14.8 6.5-14.8 13.3v0.7c0 6.7 5.4 13 15.3 13 5.9 0 10.8-2.8 13.9-5.7l4 4.2c-3.9 3.8-10.5 7.1-18.3 7.1 -13.4 0-21.6-8.7-21.6-18.3v-1.2c0-9.6 8.9-18.7 21.9-18.7 7.5 0 14.3 3.1 18 7.1L628 11.3zM321.5 12.4c1.2 0.8 1.5 2.4 0.8 3.6l-6.1 9.4c-0.8 1.2-2.4 1.6-3.6 0.8l0 0c-1.2-0.8-1.5-2.4-0.8-3.6l6.1-9.4C318.7 11.9 320.3 11.6 321.5 12.4L321.5 12.4z"></path><path d="M37.5 36.7l-4.7-8.9H11.7l-4.6 8.9H0L19.4 0.8H25l19.7 35.9H37.5zM22 7.8l-7.8 15.1h15.9L22 7.8zM82.8 36.7l-23.3-24 -2.3-2.5v26.6h-6.7v-36H57l22.6 24 2.3 2.6V0.8h6.7v35.9H82.8z"></path><path d="M719.9 37l-4.8-8.9H694l-4.6 8.9h-7.1l19.5-36h5.6l19.8 36H719.9zM704.4 8l-7.8 15.1h15.9L704.4 8zM733 37V1h6.8v36H733zM781 37c-1.8 0-2.6-2.5-2.9-5.8l-0.2-3.7c-0.2-3.6-1.7-5.1-8.4-5.1h-12.8V37H750V1h19.6c10.8 0 15.7 4.3 15.7 9.9 0 3.9-2 7.7-9 9 7 0.5 8.5 3.7 8.6 7.9l0.1 3c0.1 2.5 0.5 4.3 2.2 6.1V37H781zM778.5 11.8c0-2.6-2.1-5.1-7.9-5.1h-13.8v10.8h14.4c5 0 7.3-2.4 7.3-5.2V11.8zM794.8 37V1h6.8v30.4h28.2V37H794.8zM836.7 37V1h6.8v36H836.7zM886.2 37l-23.4-24.1 -2.3-2.5V37h-6.8V1h6.5l22.7 24.1 2.3 2.6V1h6.8v36H886.2zM902.3 37V1H935v5.6h-26v9.2h20v5.5h-20v10.1h26V37H902.3z"></path></svg></a></div>
- <ul class="jp-footer__links">
- <li class="jp-footer__link-item">
- <a href="https://jetpack.com" target="_blank" rel="noopener noreferrer" class="jp-footer__link" title="<?php esc_html_e( 'Jetpack version', 'jetpack' ); ?>">Jetpack <?php echo JETPACK__VERSION; ?></a>
- </li>
- <li class="jp-footer__link-item">
- <a href="https://wordpress.com/tos/" target="_blank" rel="noopener noreferrer" title="<?php esc_html__( 'WordPress.com Terms of Service', 'jetpack' ); ?>" class="jp-footer__link"><?php echo esc_html_x( 'Terms', 'Navigation item', 'jetpack' ); ?></a>
- </li>
- <li class="jp-footer__link-item">
- <a href="<?php echo esc_url( $jetpack_admin_url . '#/privacy' ); ?>" rel="noopener noreferrer" title="<?php esc_html_e( "Automattic's Privacy Policy", 'jetpack' ); ?>" class="jp-footer__link"><?php echo esc_html_x( 'Privacy', 'Navigation item', 'jetpack' ); ?></a>
- </li>
- <?php if ( is_multisite() && current_user_can( 'jetpack_network_sites_page' ) ) { ?>
- <li class="jp-footer__link-item">
- <a href="<?php echo esc_url( network_admin_url( 'admin.php?page=jetpack' ) ); ?>" title="<?php esc_html_e( "Manage your network's Jetpack Sites.", 'jetpack' ); ?>" class="jp-footer__link"><?php echo esc_html_x( 'Network Sites', 'Navigation item', 'jetpack' ); ?></a>
- </li>
- <?php } ?>
- <?php if ( is_multisite() && current_user_can( 'jetpack_network_settings_page' ) ) { ?>
- <li class="jp-footer__link-item">
- <a href="<?php echo esc_url( network_admin_url( 'admin.php?page=jetpack-settings' ) ); ?>" title="<?php esc_html_e( "Manage your network's Jetpack Sites.", 'jetpack' ); ?>" class="jp-footer__link"><?php echo esc_html_x( 'Network Settings', 'Navigation item', 'jetpack' ); ?></a>
- </li>
- <?php } ?>
- <?php if ( current_user_can( 'manage_options' ) ) { ?>
- <li class="jp-footer__link-item">
- <a href="<?php echo esc_url( admin_url() . 'admin.php?page=jetpack-debugger' ); ?>" title="<?php esc_html_e( "Test your site's compatibility with Jetpack.", 'jetpack' ); ?>" class="jp-footer__link"><?php echo esc_html_x( 'Debug', 'Navigation item', 'jetpack' ); ?></a>
- </li>
- <?php } ?>
- </ul>
- </div>
- </div>
-<?php return;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-landing-page.php b/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-landing-page.php
deleted file mode 100644
index 5c06c284..00000000
--- a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-landing-page.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-// This is intentionally left empty as a stub because some sites were caching the require()
-// @see https://github.com/Automattic/jetpack/issues/5091
diff --git a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-react-page.php b/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-react-page.php
deleted file mode 100644
index f84b62c3..00000000
--- a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-react-page.php
+++ /dev/null
@@ -1,422 +0,0 @@
-<?php
-include_once( 'class.jetpack-admin-page.php' );
-
-// Builds the landing page and its menu
-class Jetpack_React_Page extends Jetpack_Admin_Page {
-
- protected $dont_show_if_not_active = false;
-
- protected $is_redirecting = false;
-
- function get_page_hook() {
- // Add the main admin Jetpack menu
- return add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div' );
- }
-
- function add_page_actions( $hook ) {
- /** This action is documented in class.jetpack.php */
- do_action( 'jetpack_admin_menu', $hook );
-
- // Place the Jetpack menu item on top and others in the order they appear
- add_filter( 'custom_menu_order', '__return_true' );
- add_filter( 'menu_order', array( $this, 'jetpack_menu_order' ) );
-
- if ( ! isset( $_GET['page'] ) || 'jetpack' !== $_GET['page'] || ! empty( $_GET['configure'] ) ) {
- return; // No need to handle the fallback redirection if we are not on the Jetpack page
- }
-
- // Adding a redirect meta tag if the REST API is disabled
- if ( ! $this->is_rest_api_enabled() ) {
- $this->is_redirecting = true;
- add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) );
- }
-
- // Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled
- add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) );
-
- // Adding a redirect tag wrapped in browser conditional comments
- add_action( 'admin_head', array( $this, 'add_legacy_browsers_head_script' ) );
- }
-
- /**
- * Add Jetpack Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active.
- *
- * Works in Dev Mode or when user is connected.
- *
- * @since 4.3.0
- */
- function jetpack_add_dashboard_sub_nav_item() {
- if ( Jetpack::is_development_mode() || Jetpack::is_active() ) {
- global $submenu;
- if ( current_user_can( 'jetpack_admin_page' ) ) {
- $submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', 'admin.php?page=jetpack#/dashboard' );
- }
- }
- }
-
- /**
- * If user is allowed to see the Jetpack Admin, add Settings sub-link.
- *
- * @since 4.3.0
- */
- function jetpack_add_settings_sub_nav_item() {
- if ( ( Jetpack::is_development_mode() || Jetpack::is_active() ) && current_user_can( 'jetpack_admin_page' ) && current_user_can( 'edit_posts' ) ) {
- global $submenu;
- $submenu['jetpack'][] = array( __( 'Settings', 'jetpack' ), 'jetpack_admin_page', 'admin.php?page=jetpack#/settings' );
- }
- }
-
- function add_fallback_head_meta() {
- echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
- }
-
- function add_noscript_head_meta() {
- echo '<noscript>';
- $this->add_fallback_head_meta();
- echo '</noscript>';
- }
-
- function add_legacy_browsers_head_script() {
- echo
- "<script type=\"text/javascript\">\n"
- . "/*@cc_on\n"
- . "if ( @_jscript_version <= 10) {\n"
- . "window.location.href = '?page=jetpack_modules';\n"
- . "}\n"
- . "@*/\n"
- . "</script>";
- }
-
- function jetpack_menu_order( $menu_order ) {
- $jp_menu_order = array();
-
- foreach ( $menu_order as $index => $item ) {
- if ( $item != 'jetpack' )
- $jp_menu_order[] = $item;
-
- if ( $index == 0 )
- $jp_menu_order[] = 'jetpack';
- }
-
- return $jp_menu_order;
- }
-
- // Render the configuration page for the module if it exists and an error
- // screen if the module is not configurable
- // @todo remove when real settings are in place
- function render_nojs_configurable( $module_name ) {
- $module_name = preg_replace( '/[^\da-z\-]+/', '', $_GET['configure'] );
-
- echo '<div class="wrap configure-module">';
-
- if ( Jetpack::is_module( $module_name ) && current_user_can( 'jetpack_configure_modules' ) ) {
- Jetpack::admin_screen_configure_module( $module_name );
- } else {
- echo '<h2>' . esc_html__( 'Error, bad module.', 'jetpack' ) . '</h2>';
- }
-
- echo '</div><!-- /wrap -->';
- }
-
- function page_render() {
- // Handle redirects to configuration pages
- if ( ! empty( $_GET['configure'] ) ) {
- return $this->render_nojs_configurable( $_GET['configure'] );
- }
-
- /** This action is already documented in views/admin/admin-page.php */
- do_action( 'jetpack_notices' );
-
- // Try fetching by patch
- $static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' );
-
- if ( false === $static_html ) {
-
- // If we still have nothing, display an error
- echo '<p>';
- esc_html_e( 'Error fetching static.html. Try running: ', 'jetpack' );
- echo '<code>yarn distclean && yarn build</code>';
- echo '</p>';
- } else {
-
- // We got the static.html so let's display it
- echo $static_html;
- }
- }
-
- /**
- * Gets array of any Jetpack notices that have been dismissed.
- *
- * @since 4.0.1
- * @return mixed|void
- */
- function get_dismissed_jetpack_notices() {
- $jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
- /**
- * Array of notices that have been dismissed.
- *
- * @since 4.0.1
- *
- * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
- */
- $dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
- return $dismissed_notices;
- }
-
- function additional_styles() {
- Jetpack_Admin_Page::load_wrapper_styles();
- }
-
- function page_admin_scripts() {
- if ( $this->is_redirecting || isset( $_GET['configure'] ) ) {
- return; // No need for scripts on a fallback page.
- }
-
- wp_enqueue_script(
- 'react-plugin',
- plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ),
- array( 'wp-i18n' ),
- JETPACK__VERSION,
- true
- );
-
- wp_set_script_translations( 'react-plugin', 'jetpack', JETPACK__PLUGIN_DIR . 'languages/json' );
-
- if ( ! Jetpack::is_development_mode() && Jetpack::is_active() ) {
- // Required for Analytics.
- wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
- }
-
- // Add objects to be passed to the initial state of the app.
- wp_localize_script( 'react-plugin', 'Initial_State', $this->get_initial_state() );
- }
-
- function get_initial_state() {
- // Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
- $moduleListEndpoint = new Jetpack_Core_API_Module_List_Endpoint();
- $modules = $moduleListEndpoint->get_modules();
-
- // Preparing translated fields for JSON encoding by transforming all HTML entities to
- // respective characters.
- foreach( $modules as $slug => $data ) {
- $modules[ $slug ]['name'] = html_entity_decode( $data['name'] );
- $modules[ $slug ]['description'] = html_entity_decode( $data['description'] );
- $modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
- $modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] );
- }
-
- // Collecting roles that can view site stats.
- $stats_roles = array();
- $enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
-
- if ( ! function_exists( 'get_editable_roles' ) ) {
- require_once ABSPATH . 'wp-admin/includes/user.php';
- }
- foreach ( get_editable_roles() as $slug => $role ) {
- $stats_roles[ $slug ] = array(
- 'name' => translate_user_role( $role['name'] ),
- 'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
- );
- }
-
- // Get information about current theme.
- $current_theme = wp_get_theme();
-
- // Get all themes that Infinite Scroll provides support for natively.
- $inf_scr_support_themes = array();
- foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
- if ( is_readable( $path ) ) {
- $inf_scr_support_themes[] = basename( $path, '.php' );
- }
- }
-
- // Get last post, to build the link to Customizer in the Related Posts module.
- $last_post = get_posts( array( 'posts_per_page' => 1 ) );
- $last_post = isset( $last_post[0] ) && $last_post[0] instanceof WP_Post
- ? get_permalink( $last_post[0]->ID )
- : get_home_url();
-
- // Ensure that class to get the affiliate code is loaded
- if ( ! class_exists( 'Jetpack_Affiliate' ) ) {
- require_once JETPACK__PLUGIN_DIR . 'class.jetpack-affiliate.php';
- }
-
- return array(
- 'WP_API_root' => esc_url_raw( rest_url() ),
- 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
- 'pluginBaseUrl' => plugins_url( '', JETPACK__PLUGIN_FILE ),
- 'connectionStatus' => array(
- 'isActive' => Jetpack::is_active(),
- 'isStaging' => Jetpack::is_staging_site(),
- 'devMode' => array(
- 'isActive' => Jetpack::is_development_mode(),
- 'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
- 'url' => site_url() && false === strpos( site_url(), '.' ),
- 'filter' => apply_filters( 'jetpack_development_mode', false ),
- ),
- 'isPublic' => '1' == get_option( 'blog_public' ),
- 'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
- 'sandboxDomain' => JETPACK__SANDBOX_DOMAIN,
- ),
- 'connectUrl' => Jetpack::init()->build_connect_url( true, false, false ),
- 'dismissedNotices' => $this->get_dismissed_jetpack_notices(),
- 'isDevVersion' => Jetpack::is_development_version(),
- 'currentVersion' => JETPACK__VERSION,
- 'is_gutenberg_available' => true,
- 'getModules' => $modules,
- 'showJumpstart' => jetpack_show_jumpstart(),
- 'rawUrl' => Jetpack::build_raw_urls( get_home_url() ),
- 'adminUrl' => esc_url( admin_url() ),
- 'stats' => array(
- // data is populated asynchronously on page load
- 'data' => array(
- 'general' => false,
- 'day' => false,
- 'week' => false,
- 'month' => false,
- ),
- 'roles' => $stats_roles,
- ),
- 'aff' => Jetpack_Affiliate::init()->get_affiliate_code(),
- 'settings' => $this->get_flattened_settings( $modules ),
- 'userData' => array(
-// 'othersLinked' => Jetpack::get_other_linked_admins(),
- 'currentUser' => jetpack_current_user_data(),
- ),
- 'siteData' => array(
- 'icon' => has_site_icon()
- ? apply_filters( 'jetpack_photon_url', get_site_icon_url(), array( 'w' => 64 ) )
- : '',
- 'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ),
- /**
- * Whether promotions are visible or not.
- *
- * @since 4.8.0
- *
- * @param bool $are_promotions_active Status of promotions visibility. True by default.
- */
- 'showPromotions' => apply_filters( 'jetpack_show_promotions', true ),
- 'isAtomicSite' => jetpack_is_atomic_site(),
- 'plan' => Jetpack_Plan::get(),
- 'showBackups' => Jetpack::show_backups_ui(),
- ),
- 'themeData' => array(
- 'name' => $current_theme->get( 'Name' ),
- 'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
- 'support' => array(
- 'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes ),
- ),
- ),
- 'locale' => Jetpack::get_i18n_data_json(),
- 'localeSlug' => join( '-', explode( '_', get_user_locale() ) ),
- 'jetpackStateNotices' => array(
- 'messageCode' => Jetpack::state( 'message' ),
- 'errorCode' => Jetpack::state( 'error' ),
- 'errorDescription' => Jetpack::state( 'error_description' ),
- ),
- 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
- 'currentIp' => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
- 'lastPostUrl' => esc_url( $last_post ),
- 'externalServicesConnectUrls' => $this->get_external_services_connect_urls()
- );
- }
-
- function get_external_services_connect_urls() {
- $connect_urls = array();
- jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
- foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
- $connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info[ 'for' ] );
- }
- return $connect_urls;
- }
-
- /**
- * Returns an array of modules and settings both as first class members of the object.
- *
- * @param array $modules the result of an API request to get all modules.
- *
- * @return array flattened settings with modules.
- */
- function get_flattened_settings( $modules ) {
- $core_api_endpoint = new Jetpack_Core_API_Data();
- $settings = $core_api_endpoint->get_all_options();
- return $settings->data;
- }
-}
-
-/*
- * Only show Jump Start on first activation.
- * Any option 'jumpstart' other than 'new connection' will hide it.
- *
- * The option can be of 4 things, and will be stored as such:
- * new_connection : Brand new connection - Show
- * jumpstart_activated : Jump Start has been activated - dismiss
- * jetpack_action_taken: Manual activation of a module already happened - dismiss
- * jumpstart_dismissed : Manual dismissal of Jump Start - dismiss
- *
- * @todo move to functions.global.php when available
- * @since 3.6
- * @return bool | show or hide
- */
-function jetpack_show_jumpstart() {
- if ( ! Jetpack::is_active() ) {
- return false;
- }
- $jumpstart_option = Jetpack_Options::get_option( 'jumpstart' );
-
- $hide_options = array(
- 'jumpstart_activated',
- 'jetpack_action_taken',
- 'jumpstart_dismissed'
- );
-
- if ( ! $jumpstart_option || in_array( $jumpstart_option, $hide_options ) ) {
- return false;
- }
-
- return true;
-}
-
-/**
- * Gather data about the current user.
- *
- * @since 4.1.0
- *
- * @return array
- */
-function jetpack_current_user_data() {
- $current_user = wp_get_current_user();
- $is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
- $dotcom_data = Jetpack::get_connected_user_data();
- // Add connected user gravatar to the returned dotcom_data.
- $dotcom_data['avatar'] = get_avatar_url( $dotcom_data['email'], array( 'size' => 64, 'default' => 'mysteryman' ) );
-
- $current_user_data = array(
- 'isConnected' => Jetpack::is_user_connected( $current_user->ID ),
- 'isMaster' => $is_master_user,
- 'username' => $current_user->user_login,
- 'id' => $current_user->ID,
- 'wpcomUser' => $dotcom_data,
- 'gravatar' => get_avatar( $current_user->ID, 40, 'mm', '', array( 'force_display' => true ) ),
- 'permissions' => array(
- 'admin_page' => current_user_can( 'jetpack_admin_page' ),
- 'connect' => current_user_can( 'jetpack_connect' ),
- 'disconnect' => current_user_can( 'jetpack_disconnect' ),
- 'manage_modules' => current_user_can( 'jetpack_manage_modules' ),
- 'network_admin' => current_user_can( 'jetpack_network_admin_page' ),
- 'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
- 'edit_posts' => current_user_can( 'edit_posts' ),
- 'publish_posts' => current_user_can( 'publish_posts' ),
- 'manage_options' => current_user_can( 'manage_options' ),
- 'view_stats' => current_user_can( 'view_stats' ),
- 'manage_plugins' => current_user_can( 'install_plugins' )
- && current_user_can( 'activate_plugins' )
- && current_user_can( 'update_plugins' )
- && current_user_can( 'delete_plugins' ),
- ),
- );
-
- return $current_user_data;
-}
diff --git a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-settings-page.php b/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-settings-page.php
deleted file mode 100644
index 35369ada..00000000
--- a/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-settings-page.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-include_once( 'class.jetpack-admin-page.php' );
-include_once( JETPACK__PLUGIN_DIR . 'class.jetpack-modules-list-table.php' );
-
-// Builds the settings page and its menu
-class Jetpack_Settings_Page extends Jetpack_Admin_Page {
-
- // Show the settings page only when Jetpack is connected or in dev mode
- protected $dont_show_if_not_active = true;
-
- function add_page_actions( $hook ) {}
-
- // Adds the Settings sub menu
- function get_page_hook() {
- return add_submenu_page(
- null,
- __( 'Jetpack Settings', 'jetpack' ),
- __( 'Settings', 'jetpack' ),
- 'jetpack_manage_modules',
- 'jetpack_modules',
- array( $this, 'render' )
- );
- }
-
- // Renders the module list table where you can use bulk action or row
- // actions to activate/deactivate and configure modules
- function page_render() {
- $list_table = new Jetpack_Modules_List_Table;
-
- // We have static.html so let's continue trying to fetch the others
- $noscript_notice = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static-noscript-notice.html' );
- $rest_api_notice = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static-version-notice.html' );
- $ie_notice = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static-ie-notice.html' );
-
- $noscript_notice = str_replace(
- '#HEADER_TEXT#',
- esc_html__( 'You have JavaScript disabled', 'jetpack' ),
- $noscript_notice
- );
- $noscript_notice = str_replace(
- '#TEXT#',
- esc_html__( "Turn on JavaScript to unlock Jetpack's full potential!", 'jetpack' ),
- $noscript_notice
- );
-
- $rest_api_notice = str_replace(
- '#HEADER_TEXT#',
- esc_html( __( 'WordPress REST API is disabled', 'jetpack' ) ),
- $rest_api_notice
- );
- $rest_api_notice = str_replace(
- '#TEXT#',
- esc_html( __( "Enable WordPress REST API to unlock Jetpack's full potential!", 'jetpack' ) ),
- $rest_api_notice
- );
-
- $ie_notice = str_replace(
- '#HEADER_TEXT#',
- esc_html__( 'You are using an unsupported browser version.', 'jetpack' ),
- $ie_notice
- );
- $ie_notice = str_replace(
- '#TEXT#',
- esc_html__( "Update your browser to unlock Jetpack's full potential!", 'jetpack' ),
- $ie_notice
- );
-
- if ( ! $this->is_rest_api_enabled() ) {
- echo $rest_api_notice;
- }
- echo $noscript_notice;
- echo $ie_notice;
- ?>
-
- <div class="page-content configure">
- <div class="frame top hide-if-no-js">
- <div class="wrap">
- <div class="manage-left">
- <table class="table table-bordered fixed-top">
- <thead>
- <tr>
- <th class="check-column"><input type="checkbox" class="checkall"></th>
- <th colspan="2">
- <?php $list_table->unprotected_display_tablenav( 'top' ); ?>
- <span class="filter-search">
- <button type="button" class="button">Filter</button>
- </span>
- </th>
- </tr>
- </thead>
- </table>
- </div>
- </div><!-- /.wrap -->
- </div><!-- /.frame -->
- <div class="frame bottom">
- <div class="wrap">
- <div class="manage-right" style="display: none;">
- <div class="bumper">
- <form class="navbar-form" role="search">
- <input type="hidden" name="page" value="jetpack_modules" />
- <?php $list_table->search_box( __( 'Search', 'jetpack' ), 'srch-term' ); ?>
- <p><?php esc_html_e( 'View:', 'jetpack' ); ?></p>
- <div class="button-group filter-active">
- <button type="button" class="button <?php if ( empty( $_GET['activated'] ) ) echo 'active'; ?>"><?php esc_html_e( 'All', 'jetpack' ); ?></button>
- <button type="button" class="button <?php if ( ! empty( $_GET['activated'] ) && 'true' == $_GET['activated'] ) echo 'active'; ?>" data-filter-by="activated" data-filter-value="true"><?php esc_html_e( 'Active', 'jetpack' ); ?></button>
- <button type="button" class="button <?php if ( ! empty( $_GET['activated'] ) && 'false' == $_GET['activated'] ) echo 'active'; ?>" data-filter-by="activated" data-filter-value="false"><?php esc_html_e( 'Inactive', 'jetpack' ); ?></button>
- </div>
- <p><?php esc_html_e( 'Sort by:', 'jetpack' ); ?></p>
- <div class="button-group sort">
- <button type="button" class="button <?php if ( empty( $_GET['sort_by'] ) ) echo 'active'; ?>" data-sort-by="name"><?php esc_html_e( 'Alphabetical', 'jetpack' ); ?></button>
- <button type="button" class="button <?php if ( ! empty( $_GET['sort_by'] ) && 'introduced' == $_GET['sort_by'] ) echo 'active'; ?>" data-sort-by="introduced" data-sort-order="reverse"><?php esc_html_e( 'Newest', 'jetpack' ); ?></button>
- <button type="button" class="button <?php if ( ! empty( $_GET['sort_by'] ) && 'sort' == $_GET['sort_by'] ) echo 'active'; ?>" data-sort-by="sort"><?php esc_html_e( 'Popular', 'jetpack' ); ?></button>
- </div>
- <p><?php esc_html_e( 'Show:', 'jetpack' ); ?></p>
- <?php $list_table->views(); ?>
- </form>
- </div>
- </div>
- <div class="manage-left" style="width: 100%;">
- <form class="jetpack-modules-list-table-form" onsubmit="return false;">
- <table class="<?php echo implode( ' ', $list_table->get_table_classes() ); ?>">
- <tbody id="the-list">
- <?php $list_table->display_rows_or_placeholder(); ?>
- </tbody>
- </table>
- </form>
- </div>
- </div><!-- /.wrap -->
- </div><!-- /.frame -->
- </div><!-- /.content -->
- <?php
-
- JetpackTracking::record_user_event( 'wpa_page_view', array( 'path' => 'old_settings' ) );
- }
-
- /**
- * Load styles for static page.
- *
- * @since 4.3.0
- */
- function additional_styles() {
- Jetpack_Admin_Page::load_wrapper_styles();
- }
-
- // Javascript logic specific to the list table
- function page_admin_scripts() {
- wp_enqueue_script(
- 'jetpack-admin-js',
- Jetpack::get_file_url_for_environment( '_inc/build/jetpack-admin.min.js', '_inc/jetpack-admin.js' ),
- array( 'jquery' ),
- JETPACK__VERSION
- );
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.color.php b/plugins/jetpack/_inc/lib/class.color.php
deleted file mode 100644
index a57f2009..00000000
--- a/plugins/jetpack/_inc/lib/class.color.php
+++ /dev/null
@@ -1,755 +0,0 @@
-<?php
-/**
- * Color utility and conversion
- *
- * Represents a color value, and converts between RGB/HSV/XYZ/Lab/HSL
- *
- * Example:
- * $color = new Jetpack_Color(0xFFFFFF);
- *
- * @author Harold Asbridge <hasbridge@gmail.com>
- * @author Matt Wiebe <wiebe@automattic.com>
- * @license http://www.opensource.org/licenses/MIT
- */
-
-class Jetpack_Color {
- /**
- * @var int
- */
- protected $color = 0;
-
- /**
- * Initialize object
- *
- * @param string|array $color A color of the type $type
- * @param string $type The type of color we will construct from.
- * One of hex (default), rgb, hsl, int
- */
- public function __construct( $color = null, $type = 'hex' ) {
- if ( $color ) {
- switch ( $type ) {
- case 'hex':
- $this->fromHex( $color );
- break;
- case 'rgb':
- if ( is_array( $color ) && count( $color ) == 3 ) {
- list( $r, $g, $b ) = array_values( $color );
- $this->fromRgbInt( $r, $g, $b );
- }
- break;
- case 'hsl':
- if ( is_array( $color ) && count( $color ) == 3 ) {
- list( $h, $s, $l ) = array_values( $color );
- $this->fromHsl( $h, $s, $l );
- }
- break;
- case 'int':
- $this->fromInt( $color );
- break;
- default:
- // there is no default.
- break;
- }
- }
- }
-
- /**
- * Init color from hex value
- *
- * @param string $hexValue
- *
- * @return Jetpack_Color
- */
- public function fromHex($hexValue) {
- $hexValue = str_replace( '#', '', $hexValue );
- // handle short hex codes like #fff
- if ( 3 === strlen( $hexValue ) ) {
- $short = $hexValue;
- $i = 0;
- $hexValue = '';
- while ( $i < 3 ) {
- $chunk = substr($short, $i, 1 );
- $hexValue .= $chunk . $chunk;
- $i++;
- }
- }
- $intValue = hexdec( $hexValue );
-
- if ( $intValue < 0 || $intValue > 16777215 ) {
- throw new RangeException( $hexValue . " out of valid color code range" );
- }
-
- $this->color = $intValue;
-
- return $this;
- }
-
- /**
- * Init color from integer RGB values
- *
- * @param int $red
- * @param int $green
- * @param int $blue
- *
- * @return Jetpack_Color
- */
- public function fromRgbInt($red, $green, $blue)
- {
- if ( $red < 0 || $red > 255 )
- throw new RangeException( "Red value " . $red . " out of valid color code range" );
-
- if ( $green < 0 || $green > 255 )
- throw new RangeException( "Green value " . $green . " out of valid color code range" );
-
- if ( $blue < 0 || $blue > 255 )
- throw new RangeException( "Blue value " . $blue . " out of valid color code range" );
-
- $this->color = (int)(($red << 16) + ($green << 8) + $blue);
-
- return $this;
- }
-
- /**
- * Init color from hex RGB values
- *
- * @param string $red
- * @param string $green
- * @param string $blue
- *
- * @return Jetpack_Color
- */
- public function fromRgbHex($red, $green, $blue)
- {
- return $this->fromRgbInt(hexdec($red), hexdec($green), hexdec($blue));
- }
-
- /**
- * Converts an HSL color value to RGB. Conversion formula
- * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
- * @param int $h Hue. [0-360]
- * @param in $s Saturation [0, 100]
- * @param int $l Lightness [0, 100]
- */
- public function fromHsl( $h, $s, $l ) {
- $h /= 360; $s /= 100; $l /= 100;
-
- if ( $s == 0 ) {
- $r = $g = $b = $l; // achromatic
- }
- else {
- $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
- $p = 2 * $l - $q;
- $r = $this->hue2rgb( $p, $q, $h + 1/3 );
- $g = $this->hue2rgb( $p, $q, $h );
- $b = $this->hue2rgb( $p, $q, $h - 1/3 );
- }
-
- return $this->fromRgbInt( $r * 255, $g * 255, $b * 255 );
- }
-
- /**
- * Helper function for Jetpack_Color::fromHsl()
- */
- private function hue2rgb( $p, $q, $t ) {
- if ( $t < 0 ) $t += 1;
- if ( $t > 1 ) $t -= 1;
- if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t;
- if ( $t < 1/2 ) return $q;
- if ( $t < 2/3 ) return $p + ( $q - $p ) * ( 2/3 - $t ) * 6;
- return $p;
- }
-
- /**
- * Init color from integer value
- *
- * @param int $intValue
- *
- * @return Jetpack_Color
- */
- public function fromInt($intValue)
- {
- if ( $intValue < 0 || $intValue > 16777215 )
- throw new RangeException( $intValue . " out of valid color code range" );
-
- $this->color = $intValue;
-
- return $this;
- }
-
- /**
- * Convert color to hex
- *
- * @return string
- */
- public function toHex()
- {
- return str_pad(dechex($this->color), 6, '0', STR_PAD_LEFT);
- }
-
- /**
- * Convert color to RGB array (integer values)
- *
- * @return array
- */
- public function toRgbInt()
- {
- return array(
- 'red' => (int)(255 & ($this->color >> 16)),
- 'green' => (int)(255 & ($this->color >> 8)),
- 'blue' => (int)(255 & ($this->color))
- );
- }
-
- /**
- * Convert color to RGB array (hex values)
- *
- * @return array
- */
- public function toRgbHex()
- {
- $r = array();
- foreach ($this->toRgbInt() as $item) {
- $r[] = dechex($item);
- }
- return $r;
- }
-
- /**
- * Get Hue/Saturation/Value for the current color
- * (float values, slow but accurate)
- *
- * @return array
- */
- public function toHsvFloat()
- {
- $rgb = $this->toRgbInt();
-
- $rgbMin = min($rgb);
- $rgbMax = max($rgb);
-
- $hsv = array(
- 'hue' => 0,
- 'sat' => 0,
- 'val' => $rgbMax
- );
-
- // If v is 0, color is black
- if ($hsv['val'] == 0) {
- return $hsv;
- }
-
- // Normalize RGB values to 1
- $rgb['red'] /= $hsv['val'];
- $rgb['green'] /= $hsv['val'];
- $rgb['blue'] /= $hsv['val'];
- $rgbMin = min($rgb);
- $rgbMax = max($rgb);
-
- // Calculate saturation
- $hsv['sat'] = $rgbMax - $rgbMin;
- if ($hsv['sat'] == 0) {
- $hsv['hue'] = 0;
- return $hsv;
- }
-
- // Normalize saturation to 1
- $rgb['red'] = ($rgb['red'] - $rgbMin) / ($rgbMax - $rgbMin);
- $rgb['green'] = ($rgb['green'] - $rgbMin) / ($rgbMax - $rgbMin);
- $rgb['blue'] = ($rgb['blue'] - $rgbMin) / ($rgbMax - $rgbMin);
- $rgbMin = min($rgb);
- $rgbMax = max($rgb);
-
- // Calculate hue
- if ($rgbMax == $rgb['red']) {
- $hsv['hue'] = 0.0 + 60 * ($rgb['green'] - $rgb['blue']);
- if ($hsv['hue'] < 0) {
- $hsv['hue'] += 360;
- }
- } else if ($rgbMax == $rgb['green']) {
- $hsv['hue'] = 120 + (60 * ($rgb['blue'] - $rgb['red']));
- } else {
- $hsv['hue'] = 240 + (60 * ($rgb['red'] - $rgb['green']));
- }
-
- return $hsv;
- }
-
- /**
- * Get HSV values for color
- * (integer values from 0-255, fast but less accurate)
- *
- * @return int
- */
- public function toHsvInt()
- {
- $rgb = $this->toRgbInt();
-
- $rgbMin = min($rgb);
- $rgbMax = max($rgb);
-
- $hsv = array(
- 'hue' => 0,
- 'sat' => 0,
- 'val' => $rgbMax
- );
-
- // If value is 0, color is black
- if ($hsv['val'] == 0) {
- return $hsv;
- }
-
- // Calculate saturation
- $hsv['sat'] = round(255 * ($rgbMax - $rgbMin) / $hsv['val']);
- if ($hsv['sat'] == 0) {
- $hsv['hue'] = 0;
- return $hsv;
- }
-
- // Calculate hue
- if ($rgbMax == $rgb['red']) {
- $hsv['hue'] = round(0 + 43 * ($rgb['green'] - $rgb['blue']) / ($rgbMax - $rgbMin));
- } else if ($rgbMax == $rgb['green']) {
- $hsv['hue'] = round(85 + 43 * ($rgb['blue'] - $rgb['red']) / ($rgbMax - $rgbMin));
- } else {
- $hsv['hue'] = round(171 + 43 * ($rgb['red'] - $rgb['green']) / ($rgbMax - $rgbMin));
- }
- if ($hsv['hue'] < 0) {
- $hsv['hue'] += 255;
- }
-
- return $hsv;
- }
-
- /**
- * Converts an RGB color value to HSL. Conversion formula
- * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
- * Assumes r, g, and b are contained in the set [0, 255] and
- * returns h in [0, 360], s in [0, 100], l in [0, 100]
- *
- * @return Array The HSL representation
- */
- public function toHsl() {
- list( $r, $g, $b ) = array_values( $this->toRgbInt() );
- $r /= 255; $g /= 255; $b /= 255;
- $max = max( $r, $g, $b );
- $min = min( $r, $g, $b );
- $h = $s = $l = ( $max + $min ) / 2;
- #var_dump( array( compact('max', 'min', 'r', 'g', 'b')) );
- if ( $max == $min ) {
- $h = $s = 0; // achromatic
- }
- else {
- $d = $max - $min;
- $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );
- switch ( $max ) {
- case $r:
- $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
- break;
- case $g:
- $h = ( $b - $r ) / $d + 2;
- break;
- case $b:
- $h = ( $r - $g ) / $d + 4;
- break;
- }
- $h /= 6;
- }
- $h = (int) round( $h * 360 );
- $s = (int) round( $s * 100 );
- $l = (int) round( $l * 100 );
- return compact( 'h', 's', 'l' );
- }
-
- public function toCSS( $type = 'hex', $alpha = 1 ) {
- switch ( $type ) {
- case 'hex':
- return $this->toString();
- break;
- case 'rgb':
- case 'rgba':
- list( $r, $g, $b ) = array_values( $this->toRgbInt() );
- if ( is_numeric( $alpha ) && $alpha < 1 ) {
- return "rgba( {$r}, {$g}, {$b}, $alpha )";
- }
- else {
- return "rgb( {$r}, {$g}, {$b} )";
- }
- break;
- case 'hsl':
- case 'hsla':
- list( $h, $s, $l ) = array_values( $this->toHsl() );
- if ( is_numeric( $alpha ) && $alpha < 1 ) {
- return "hsla( {$h}, {$s}, {$l}, $alpha )";
- }
- else {
- return "hsl( {$h}, {$s}, {$l} )";
- }
- break;
- default:
- return $this->toString();
- break;
- }
- }
-
- /**
- * Get current color in XYZ format
- *
- * @return array
- */
- public function toXyz()
- {
- $rgb = $this->toRgbInt();
-
- // Normalize RGB values to 1
-
- $rgb_new = array();
- foreach ($rgb as $item) {
- $rgb_new[] = $item / 255;
- }
- $rgb = $rgb_new;
-
- $rgb_new = array();
- foreach ($rgb as $item) {
- if ($item > 0.04045) {
- $item = pow((($item + 0.055) / 1.055), 2.4);
- } else {
- $item = $item / 12.92;
- }
- $rgb_new[] = $item * 100;
- }
- $rgb = $rgb_new;
-
- // Observer. = 2°, Illuminant = D65
- $xyz = array(
- 'x' => ($rgb['red'] * 0.4124) + ($rgb['green'] * 0.3576) + ($rgb['blue'] * 0.1805),
- 'y' => ($rgb['red'] * 0.2126) + ($rgb['green'] * 0.7152) + ($rgb['blue'] * 0.0722),
- 'z' => ($rgb['red'] * 0.0193) + ($rgb['green'] * 0.1192) + ($rgb['blue'] * 0.9505)
- );
-
- return $xyz;
- }
-
- /**
- * Get color CIE-Lab values
- *
- * @return array
- */
- public function toLabCie()
- {
- $xyz = $this->toXyz();
-
- //Ovserver = 2*, Iluminant=D65
- $xyz['x'] /= 95.047;
- $xyz['y'] /= 100;
- $xyz['z'] /= 108.883;
-
- $xyz_new = array();
- foreach ($xyz as $item) {
- if ($item > 0.008856) {
- $xyz_new[] = pow($item, 1/3);
- } else {
- $xyz_new[] = (7.787 * $item) + (16 / 116);
- }
- }
- $xyz = $xyz_new;
-
- $lab = array(
- 'l' => (116 * $xyz['y']) - 16,
- 'a' => 500 * ($xyz['x'] - $xyz['y']),
- 'b' => 200 * ($xyz['y'] - $xyz['z'])
- );
-
- return $lab;
- }
-
- /**
- * Convert color to integer
- *
- * @return int
- */
- public function toInt()
- {
- return $this->color;
- }
-
- /**
- * Alias of toString()
- *
- * @return string
- */
- public function __toString()
- {
- return $this->toString();
- }
-
- /**
- * Get color as string
- *
- * @return string
- */
- public function toString()
- {
- $str = $this->toHex();
- return strtoupper("#{$str}");
- }
-
- /**
- * Get the distance between this color and the given color
- *
- * @param Jetpack_Color $color
- *
- * @return int
- */
- public function getDistanceRgbFrom(Jetpack_Color $color)
- {
- $rgb1 = $this->toRgbInt();
- $rgb2 = $color->toRgbInt();
-
- $rDiff = abs($rgb1['red'] - $rgb2['red']);
- $gDiff = abs($rgb1['green'] - $rgb2['green']);
- $bDiff = abs($rgb1['blue'] - $rgb2['blue']);
-
- // Sum of RGB differences
- $diff = $rDiff + $gDiff + $bDiff;
- return $diff;
- }
-
- /**
- * Get distance from the given color using the Delta E method
- *
- * @param Jetpack_Color $color
- *
- * @return float
- */
- public function getDistanceLabFrom(Jetpack_Color $color)
- {
- $lab1 = $this->toLabCie();
- $lab2 = $color->toLabCie();
-
- $lDiff = abs($lab2['l'] - $lab1['l']);
- $aDiff = abs($lab2['a'] - $lab1['a']);
- $bDiff = abs($lab2['b'] - $lab1['b']);
-
- $delta = sqrt($lDiff + $aDiff + $bDiff);
-
- return $delta;
- }
-
- public function toLuminosity() {
- $lum = array();
- foreach( $this->toRgbInt() as $slot => $value ) {
- $chan = $value / 255;
- $lum[ $slot ] = ( $chan <= 0.03928 ) ? $chan / 12.92 : pow( ( ( $chan + 0.055 ) / 1.055 ), 2.4 );
- }
- return 0.2126 * $lum['red'] + 0.7152 * $lum['green'] + 0.0722 * $lum['blue'];
- }
-
- /**
- * Get distance between colors using luminance.
- * Should be more than 5 for readable contrast
- *
- * @param Jetpack_Color $color Another color
- * @return float
- */
- public function getDistanceLuminosityFrom( Jetpack_Color $color ) {
- $L1 = $this->toLuminosity();
- $L2 = $color->toLuminosity();
- if ( $L1 > $L2 ) {
- return ( $L1 + 0.05 ) / ( $L2 + 0.05 );
- }
- else{
- return ( $L2 + 0.05 ) / ( $L1 + 0.05 );
- }
- }
-
- public function getMaxContrastColor() {
- $withBlack = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#000') );
- $withWhite = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#fff') );
- $color = new Jetpack_Color;
- $hex = ( $withBlack >= $withWhite ) ? '#000000' : '#ffffff';
- return $color->fromHex( $hex );
- }
-
- public function getGrayscaleContrastingColor( $contrast = false ) {
- if ( ! $contrast ) {
- return $this->getMaxContrastColor();
- }
- // don't allow less than 5
- $target_contrast = ( $contrast < 5 ) ? 5 : $contrast;
- $color = $this->getMaxContrastColor();
- $contrast = $color->getDistanceLuminosityFrom( $this );
-
- // if current max contrast is less than the target contrast, we had wishful thinking.
- if ( $contrast <= $target_contrast ) {
- return $color;
- }
-
- $incr = ( '#000000' === $color->toString() ) ? 1 : -1;
- while ( $contrast > $target_contrast ) {
- $color = $color->incrementLightness( $incr );
- $contrast = $color->getDistanceLuminosityFrom( $this );
- }
-
- return $color;
- }
-
- /**
- * Gets a readable contrasting color. $this is assumed to be the text and $color the background color.
- * @param object $bg_color A Color object that will be compared against $this
- * @param integer $min_contrast The minimum contrast to achieve, if possible.
- * @return object A Color object, an increased contrast $this compared against $bg_color
- */
- public function getReadableContrastingColor( $bg_color = false, $min_contrast = 5 ) {
- if ( ! $bg_color || ! is_a( $bg_color, 'Jetpack_Color' ) ) {
- return $this;
- }
- // you shouldn't use less than 5, but you might want to.
- $target_contrast = $min_contrast;
- // working things
- $contrast = $bg_color->getDistanceLuminosityFrom( $this );
- $max_contrast_color = $bg_color->getMaxContrastColor();
- $max_contrast = $max_contrast_color->getDistanceLuminosityFrom( $bg_color );
-
- // if current max contrast is less than the target contrast, we had wishful thinking.
- // still, go max
- if ( $max_contrast <= $target_contrast ) {
- return $max_contrast_color;
- }
- // or, we might already have sufficient contrast
- if ( $contrast >= $target_contrast ) {
- return $this;
- }
-
- $incr = ( 0 === $max_contrast_color->toInt() ) ? -1 : 1;
- while ( $contrast < $target_contrast ) {
- $this->incrementLightness( $incr );
- $contrast = $bg_color->getDistanceLuminosityFrom( $this );
- // infininite loop prevention: you never know.
- if ( $this->color === 0 || $this->color === 16777215 ) {
- break;
- }
- }
-
- return $this;
- }
-
- /**
- * Detect if color is grayscale
- *
- * @param int @threshold
- *
- * @return bool
- */
- public function isGrayscale($threshold = 16)
- {
- $rgb = $this->toRgbInt();
-
- // Get min and max rgb values, then difference between them
- $rgbMin = min($rgb);
- $rgbMax = max($rgb);
- $diff = $rgbMax - $rgbMin;
-
- return $diff < $threshold;
- }
-
- /**
- * Get the closest matching color from the given array of colors
- *
- * @param array $colors array of integers or Jetpack_Color objects
- *
- * @return mixed the array key of the matched color
- */
- public function getClosestMatch(array $colors)
- {
- $matchDist = 10000;
- $matchKey = null;
- foreach($colors as $key => $color) {
- if (false === ($color instanceof Jetpack_Color)) {
- $c = new Jetpack_Color($color);
- }
- $dist = $this->getDistanceLabFrom($c);
- if ($dist < $matchDist) {
- $matchDist = $dist;
- $matchKey = $key;
- }
- }
-
- return $matchKey;
- }
-
- /* TRANSFORMS */
-
- public function darken( $amount = 5 ) {
- return $this->incrementLightness( - $amount );
- }
-
- public function lighten( $amount = 5 ) {
- return $this->incrementLightness( $amount );
- }
-
- public function incrementLightness( $amount ) {
- $hsl = $this->toHsl();
- extract( $hsl );
- $l += $amount;
- if ( $l < 0 ) $l = 0;
- if ( $l > 100 ) $l = 100;
- return $this->fromHsl( $h, $s, $l );
- }
-
- public function saturate( $amount = 15 ) {
- return $this->incrementSaturation( $amount );
- }
-
- public function desaturate( $amount = 15 ) {
- return $this->incrementSaturation( - $amount );
- }
-
- public function incrementSaturation( $amount ) {
- $hsl = $this->toHsl();
- extract( $hsl );
- $s += $amount;
- if ( $s < 0 ) $s = 0;
- if ( $s > 100 ) $s = 100;
- return $this->fromHsl( $h, $s, $l );
- }
-
- public function toGrayscale() {
- $hsl = $this->toHsl();
- extract( $hsl );
- $s = 0;
- return $this->fromHsl( $h, $s, $l );
- }
-
- public function getComplement() {
- return $this->incrementHue( 180 );
- }
-
- public function getSplitComplement( $step = 1 ) {
- $incr = 180 + ( $step * 30 );
- return $this->incrementHue( $incr );
- }
-
- public function getAnalog( $step = 1 ) {
- $incr = $step * 30;
- return $this->incrementHue( $incr );
- }
-
- public function getTetrad( $step = 1 ) {
- $incr = $step * 60;
- return $this->incrementHue( $incr );
- }
-
- public function getTriad( $step = 1 ) {
- $incr = $step * 120;
- return $this->incrementHue( $incr );
- }
-
- public function incrementHue( $amount ) {
- $hsl = $this->toHsl();
- extract( $hsl );
- $h = ( $h + $amount ) % 360;
- if ( $h < 0 ) $h = 360 - $h;
- return $this->fromHsl( $h, $s, $l );
- }
-
-} // class Jetpack_Color
diff --git a/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php b/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php
deleted file mode 100644
index ba57e923..00000000
--- a/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php
+++ /dev/null
@@ -1,3327 +0,0 @@
-<?php
-/**
- * Register WP REST API endpoints for Jetpack.
- *
- * @author Automattic
- */
-
-/**
- * Disable direct access.
- */
-if ( ! defined( 'ABSPATH' ) ) {
- exit;
-}
-
-// Load WP_Error for error messages.
-require_once ABSPATH . '/wp-includes/class-wp-error.php';
-
-// Register endpoints when WP REST API is initialized.
-add_action( 'rest_api_init', array( 'Jetpack_Core_Json_Api_Endpoints', 'register_endpoints' ) );
-// Load API endpoints that are synced with WP.com
-// Each of these is a class that will register its own routes on 'rest_api_init'.
-require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/load-wpcom-endpoints.php';
-
-/**
- * Class Jetpack_Core_Json_Api_Endpoints
- *
- * @since 4.3.0
- */
-class Jetpack_Core_Json_Api_Endpoints {
-
- /**
- * @var string Generic error message when user is not allowed to perform an action.
- */
- public static $user_permissions_error_msg;
-
- /**
- * @var array Roles that can access Stats once they're granted access.
- */
- public static $stats_roles;
-
- /**
- * Declare the Jetpack REST API endpoints.
- *
- * @since 4.3.0
- */
- public static function register_endpoints() {
-
- // Load API endpoint base classes
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
-
- // Load API endpoints
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php';
- require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php';
-
- self::$user_permissions_error_msg = esc_html__(
- 'You do not have the correct user permissions to perform this action.
- Please contact your site admin if you think this is a mistake.',
- 'jetpack'
- );
-
- self::$stats_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
-
- Jetpack::load_xml_rpc_client();
- $ixr_client = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id() ) );
- $core_api_endpoint = new Jetpack_Core_API_Data( $ixr_client );
- $module_list_endpoint = new Jetpack_Core_API_Module_List_Endpoint();
- $module_data_endpoint = new Jetpack_Core_API_Module_Data_Endpoint();
- $module_toggle_endpoint = new Jetpack_Core_API_Module_Toggle_Endpoint( new Jetpack_IXR_Client() );
- $site_endpoint = new Jetpack_Core_API_Site_Endpoint();
- $widget_endpoint = new Jetpack_Core_API_Widget_Endpoint();
-
- register_rest_route( 'jetpack/v4', 'plans', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_plans',
- 'permission_callback' => __CLASS__ . '::connect_url_permission_callback',
-
- ) );
-
- register_rest_route( 'jetpack/v4', '/jitm', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_jitm_message',
- ) );
-
- register_rest_route( 'jetpack/v4', '/jitm', array(
- 'methods' => WP_REST_Server::CREATABLE,
- 'callback' => __CLASS__ . '::delete_jitm_message'
- ) );
-
- // Register a site
- register_rest_route( 'jetpack/v4', '/verify_registration', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::verify_registration',
- ) );
-
- // Authorize a remote user
- register_rest_route( 'jetpack/v4', '/remote_authorize', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::remote_authorize',
- ) );
-
- // Get current connection status of Jetpack
- register_rest_route( 'jetpack/v4', '/connection', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::jetpack_connection_status',
- ) );
-
- // Test current connection status of Jetpack
- register_rest_route( 'jetpack/v4', '/connection/test', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::jetpack_connection_test',
- 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
- ) );
-
- // Endpoint specific for privileged servers to request detailed debug information.
- register_rest_route( 'jetpack/v4', '/connection/test-wpcom/', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::jetpack_connection_test_for_external',
- 'permission_callback' => __CLASS__ . '::view_jetpack_connection_test_check',
- ) );
-
- register_rest_route( 'jetpack/v4', '/rewind', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_rewind_data',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- ) );
-
- // Fetches a fresh connect URL
- register_rest_route( 'jetpack/v4', '/connection/url', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::build_connect_url',
- 'permission_callback' => __CLASS__ . '::connect_url_permission_callback',
- ) );
-
- // Get current user connection data
- register_rest_route( 'jetpack/v4', '/connection/data', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_user_connection_data',
- 'permission_callback' => __CLASS__ . '::get_user_connection_data_permission_callback',
- ) );
-
- // Set the connection owner
- register_rest_route( 'jetpack/v4', '/connection/owner', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::set_connection_owner',
- 'permission_callback' => __CLASS__ . '::set_connection_owner_permission_callback',
- ) );
-
- // Current user: get or set tracking settings.
- register_rest_route( 'jetpack/v4', '/tracking/settings', array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_user_tracking_settings',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::update_user_tracking_settings',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- 'args' => array(
- 'tracks_opt_out' => array( 'type' => 'boolean' ),
- ),
- ),
- ) );
-
- // Disconnect site from WordPress.com servers
- register_rest_route( 'jetpack/v4', '/connection', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::disconnect_site',
- 'permission_callback' => __CLASS__ . '::disconnect_site_permission_callback',
- ) );
-
- // Disconnect/unlink user from WordPress.com servers
- register_rest_route( 'jetpack/v4', '/connection/user', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::unlink_user',
- 'permission_callback' => __CLASS__ . '::unlink_user_permission_callback',
- ) );
-
- // Get current site data
- register_rest_route( 'jetpack/v4', '/site', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_site_data',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- ) );
-
- // Get current site data
- register_rest_route( 'jetpack/v4', '/site/features', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $site_endpoint, 'get_features' ),
- 'permission_callback' => array( $site_endpoint , 'can_request' ),
- ) );
-
- // Confirm that a site in identity crisis should be in staging mode
- register_rest_route( 'jetpack/v4', '/identity-crisis/confirm-safe-mode', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::confirm_safe_mode',
- 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
- ) );
-
- // IDC resolve: create an entirely new shadow site for this URL.
- register_rest_route( 'jetpack/v4', '/identity-crisis/start-fresh', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::start_fresh_connection',
- 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
- ) );
-
- // Handles the request to migrate stats and subscribers during an identity crisis.
- register_rest_route( 'jetpack/v4', 'identity-crisis/migrate', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::migrate_stats_and_subscribers',
- 'permissison_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
- ) );
-
- // Return all modules
- register_rest_route( 'jetpack/v4', '/module/all', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $module_list_endpoint, 'process' ),
- 'permission_callback' => array( $module_list_endpoint, 'can_request' ),
- ) );
-
- // Activate many modules
- register_rest_route( 'jetpack/v4', '/module/all/active', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $module_list_endpoint, 'process' ),
- 'permission_callback' => array( $module_list_endpoint, 'can_request' ),
- 'args' => array(
- 'modules' => array(
- 'default' => '',
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string',
- ),
- 'required' => true,
- 'validate_callback' => __CLASS__ . '::validate_module_list',
- ),
- 'active' => array(
- 'default' => true,
- 'type' => 'boolean',
- 'required' => false,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- ),
- )
- ) );
-
- // Return a single module and update it when needed
- register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $core_api_endpoint, 'process' ),
- 'permission_callback' => array( $core_api_endpoint, 'can_request' ),
- ) );
-
- // Activate and deactivate a module
- register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/active', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $module_toggle_endpoint, 'process' ),
- 'permission_callback' => array( $module_toggle_endpoint, 'can_request' ),
- 'args' => array(
- 'active' => array(
- 'default' => true,
- 'type' => 'boolean',
- 'required' => true,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- ),
- )
- ) );
-
- // Update a module
- register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $core_api_endpoint, 'process' ),
- 'permission_callback' => array( $core_api_endpoint, 'can_request' ),
- 'args' => self::get_updateable_parameters( 'any' )
- ) );
-
- // Get data for a specific module, i.e. Protect block count, WPCOM stats,
- // Akismet spam count, etc.
- register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/data', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $module_data_endpoint, 'process' ),
- 'permission_callback' => array( $module_data_endpoint, 'can_request' ),
- 'args' => array(
- 'range' => array(
- 'default' => 'day',
- 'type' => 'string',
- 'required' => false,
- 'validate_callback' => __CLASS__ . '::validate_string',
- ),
- )
- ) );
-
- // Check if the API key for a specific service is valid or not
- register_rest_route( 'jetpack/v4', '/module/(?P<service>[a-z\-]+)/key/check', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $module_data_endpoint, 'key_check' ),
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- 'sanitize_callback' => 'sanitize_text_field',
- ) );
-
- register_rest_route( 'jetpack/v4', '/module/(?P<service>[a-z\-]+)/key/check', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $module_data_endpoint, 'key_check' ),
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- 'sanitize_callback' => 'sanitize_text_field',
- 'args' => array(
- 'api_key' => array(
- 'default' => '',
- 'type' => 'string',
- 'validate_callback' => __CLASS__ . '::validate_alphanum',
- ),
- )
- ) );
-
- // Update any Jetpack module option or setting
- register_rest_route( 'jetpack/v4', '/settings', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $core_api_endpoint, 'process' ),
- 'permission_callback' => array( $core_api_endpoint, 'can_request' ),
- 'args' => self::get_updateable_parameters( 'any' )
- ) );
-
- // Update a module
- register_rest_route( 'jetpack/v4', '/settings/(?P<slug>[a-z\-]+)', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( $core_api_endpoint, 'process' ),
- 'permission_callback' => array( $core_api_endpoint, 'can_request' ),
- 'args' => self::get_updateable_parameters()
- ) );
-
- // Return all module settings
- register_rest_route( 'jetpack/v4', '/settings/', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $core_api_endpoint, 'process' ),
- 'permission_callback' => array( $core_api_endpoint, 'can_request' ),
- ) );
-
- // Reset all Jetpack options
- register_rest_route( 'jetpack/v4', '/options/(?P<options>[a-z\-]+)', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::reset_jetpack_options',
- 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
- ) );
-
- // Return current Jumpstart status
- register_rest_route( 'jetpack/v4', '/jumpstart', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::jumpstart_status',
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- ) );
-
- // Update Jumpstart
- register_rest_route( 'jetpack/v4', '/jumpstart', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::jumpstart_toggle',
- 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
- 'args' => array(
- 'active' => array(
- 'required' => true,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- ),
- ),
- ) );
-
- // Updates: get number of plugin updates available
- register_rest_route( 'jetpack/v4', '/updates/plugins', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_plugin_update_count',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- ) );
-
- // Dismiss Jetpack Notices
- register_rest_route( 'jetpack/v4', '/notice/(?P<notice>[a-z\-_]+)', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::dismiss_notice',
- 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
- ) );
-
- // Plugins: get list of all plugins.
- register_rest_route( 'jetpack/v4', '/plugins', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_plugins',
- 'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',
- ) );
-
- // Plugins: check if the plugin is active.
- register_rest_route( 'jetpack/v4', '/plugin/(?P<plugin>[a-z\/\.\-_]+)', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_plugin',
- 'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',
- ) );
-
- // Widgets: get information about a widget that supports it.
- register_rest_route( 'jetpack/v4', '/widgets/(?P<id>[0-9a-z\-_]+)', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $widget_endpoint, 'process' ),
- 'permission_callback' => array( $widget_endpoint, 'can_request' ),
- ) );
-
- // Site Verify: check if the site is verified, and a get verification token if not
- register_rest_route( 'jetpack/v4', '/verify-site/(?P<service>[a-z\-_]+)', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::is_site_verified_and_token',
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- ) );
-
- register_rest_route( 'jetpack/v4', '/verify-site/(?P<service>[a-z\-_]+)/(?<keyring_id>[0-9]+)', array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::is_site_verified_and_token',
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- ) );
-
- // Site Verify: tell a service to verify the site
- register_rest_route( 'jetpack/v4', '/verify-site/(?P<service>[a-z\-_]+)', array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::verify_site',
- 'permission_callback' => __CLASS__ . '::update_settings_permission_check',
- 'args' => array(
- 'keyring_id' => array(
- 'required' => true,
- 'type' => 'integer',
- 'validate_callback' => __CLASS__ . '::validate_posint',
- ),
- )
- ) );
-
- // Get and set API keys.
- // Note: permission_callback intentionally omitted from the GET method.
- // Map block requires open access to API keys on the front end.
- register_rest_route(
- 'jetpack/v4',
- '/service-api-keys/(?P<service>[a-z\-_]+)',
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => __CLASS__ . '::get_service_api_key',
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => __CLASS__ . '::update_service_api_key',
- 'permission_callback' => array( 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys','edit_others_posts_check' ),
- 'args' => array(
- 'service_api_key' => array(
- 'required' => true,
- 'type' => 'text',
- ),
- ),
- ),
- array(
- 'methods' => WP_REST_Server::DELETABLE,
- 'callback' => __CLASS__ . '::delete_service_api_key',
- 'permission_callback' => array( 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys','edit_others_posts_check' ),
- ),
- )
- );
- }
-
- public static function get_plans( $request ) {
- $request = Jetpack_Client::wpcom_json_api_request_as_user(
- '/plans?_locale=' . get_user_locale(),
- '2',
- array(
- 'method' => 'GET',
- 'headers' => array(
- 'X-Forwarded-For' => Jetpack::current_user_ip( true ),
- ),
- )
- );
-
- $body = wp_remote_retrieve_body( $request );
- if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
- $data = $body;
- } else {
- // something went wrong so we'll just return the response without caching
- return $body;
- }
-
- return $data;
- }
-
- /**
- * Asks for a jitm, unless they've been disabled, in which case it returns an empty array
- *
- * @param $request WP_REST_Request
- *
- * @return array An array of jitms
- */
- public static function get_jitm_message( $request ) {
- require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-jitm.php' );
-
- $jitm = Jetpack_JITM::init();
-
- if ( ! $jitm ) {
- return array();
- }
-
- return $jitm->get_messages( $request['message_path'], urldecode_deep( $request['query'] ) );
- }
-
- /**
- * Dismisses a jitm
- * @param $request WP_REST_Request The request
- *
- * @return bool Always True
- */
- public static function delete_jitm_message( $request ) {
- require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-jitm.php' );
-
- $jitm = Jetpack_JITM::init();
-
- if ( ! $jitm ) {
- return true;
- }
-
- return $jitm->dismiss( $request['id'], $request['feature_class'] );
- }
-
- /**
- * Handles verification that a site is registered
- *
- * @since 5.4.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return array|wp-error
- */
- public static function verify_registration( $request ) {
- require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
- $xmlrpc_server = new Jetpack_XMLRPC_Server();
- $result = $xmlrpc_server->verify_registration( array( $request['secret_1'], $request['state'] ) );
-
- if ( is_a( $result, 'IXR_Error' ) ) {
- $result = new WP_Error( $result->code, $result->message );
- }
-
- return $result;
- }
-
-
- /**
- * Checks if this site has been verified using a service - only 'google' supported at present - and a specfic
- * keyring to use to get the token if it is not
- *
- * Returns 'verified' = true/false, and a token if 'verified' is false and site is ready for verification
- *
- * @since 6.6.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return array|wp-error
- */
- public static function is_site_verified_and_token( $request ) {
- /**
- * Return an error if the site uses a Maintenance / Coming Soon plugin
- * and if the plugin is configured to make the site private.
- *
- * We currently handle the following plugins:
- * - https://github.com/mojoness/mojo-marketplace-wp-plugin (used by bluehost)
- * - https://wordpress.org/plugins/mojo-under-construction
- * - https://wordpress.org/plugins/under-construction-page
- * - https://wordpress.org/plugins/ultimate-under-construction
- * - https://wordpress.org/plugins/coming-soon
- *
- * You can handle this in your own plugin thanks to the `jetpack_is_under_construction_plugin` filter.
- * If the filter returns true, we will consider the site as under construction.
- */
- $mm_coming_soon = get_option( 'mm_coming_soon', null );
- $under_construction_activation_status = get_option( 'underConstructionActivationStatus', null );
- $ucp_options = get_option( 'ucp_options', array() );
- $uuc_settings = get_option( 'uuc_settings', array() );
- $csp4 = get_option( 'seed_csp4_settings_content', array() );
- if (
- ( Jetpack::is_plugin_active( 'mojo-marketplace-wp-plugin/mojo-marketplace.php' ) && 'true' === $mm_coming_soon )
- || Jetpack::is_plugin_active( 'mojo-under-construction/mojo-contruction.php' ) && 1 == $under_construction_activation_status // WPCS: loose comparison ok.
- || ( Jetpack::is_plugin_active( 'under-construction-page/under-construction.php' ) && isset( $ucp_options['status'] ) && 1 == $ucp_options['status'] ) // WPCS: loose comparison ok.
- || ( Jetpack::is_plugin_active( 'ultimate-under-construction/ultimate-under-construction.php' ) && isset( $uuc_settings['enable'] ) && 1 == $uuc_settings['enable'] ) // WPCS: loose comparison ok.
- || ( Jetpack::is_plugin_active( 'coming-soon/coming-soon.php' ) && isset( $csp4['status'] ) && ( 1 == $csp4['status'] || 2 == $csp4['status'] ) ) // WPCS: loose comparison ok.
- /**
- * Allow plugins to mark a site as "under construction".
- *
- * @since 6.7.0
- *
- * @param false bool Is the site under construction? Default to false.
- */
- || true === apply_filters( 'jetpack_is_under_construction_plugin', false )
- ) {
- return new WP_Error( 'forbidden', __( 'Site is under construction and cannot be verified', 'jetpack' ) );
- }
-
- Jetpack::load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client( array(
- 'user_id' => get_current_user_id(),
- ) );
-
- $args = array(
- 'user_id' => get_current_user_id(),
- 'service' => $request[ 'service' ],
- );
-
- if ( isset( $request[ 'keyring_id' ] ) ) {
- $args[ 'keyring_id' ] = $request[ 'keyring_id' ];
- }
-
- $xml->query( 'jetpack.isSiteVerified', $args );
-
- if ( $xml->isError() ) {
- return new WP_Error( 'error_checking_if_site_verified_google', sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
- } else {
- return $xml->getResponse();
- }
- }
-
-
-
- public static function verify_site( $request ) {
- Jetpack::load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client( array(
- 'user_id' => get_current_user_id(),
- ) );
-
- $params = $request->get_json_params();
-
- $xml->query( 'jetpack.verifySite', array(
- 'user_id' => get_current_user_id(),
- 'service' => $request[ 'service' ],
- 'keyring_id' => $params[ 'keyring_id' ],
- )
- );
-
- if ( $xml->isError() ) {
- return new WP_Error( 'error_verifying_site_google', sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
- } else {
- $response = $xml->getResponse();
-
- if ( ! empty( $response['errors'] ) ) {
- $error = new WP_Error;
- $error->errors = $response['errors'];
- return $error;
- }
-
- return $response;
- }
- }
-
- /**
- * Handles verification that a site is registered
- *
- * @since 5.4.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return array|wp-error
- */
- public static function remote_authorize( $request ) {
- require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
- $xmlrpc_server = new Jetpack_XMLRPC_Server();
- $result = $xmlrpc_server->remote_authorize( $request );
-
- if ( is_a( $result, 'IXR_Error' ) ) {
- $result = new WP_Error( $result->code, $result->message );
- }
-
- return $result;
- }
-
- /**
- * Handles dismissing of Jetpack Notices
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return array|wp-error
- */
- public static function dismiss_notice( $request ) {
- $notice = $request['notice'];
-
- if ( ! isset( $request['dismissed'] ) || $request['dismissed'] !== true ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid parameter "dismissed".', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- if ( isset( $notice ) && ! empty( $notice ) ) {
- switch( $notice ) {
- case 'feedback_dash_request':
- case 'welcome':
- $notices = get_option( 'jetpack_dismissed_notices', array() );
- $notices[ $notice ] = true;
- update_option( 'jetpack_dismissed_notices', $notices );
- return rest_ensure_response( get_option( 'jetpack_dismissed_notices', array() ) );
-
- default:
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid parameter "notice".', 'jetpack' ), array( 'status' => 404 ) );
- }
- }
-
- return new WP_Error( 'required_param', esc_html__( 'Missing parameter "notice".', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- /**
- * Verify that the user can disconnect the site.
- *
- * @since 4.3.0
- *
- * @return bool|WP_Error True if user is able to disconnect the site.
- */
- public static function disconnect_site_permission_callback() {
- if ( current_user_can( 'jetpack_disconnect' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
-
- }
-
- /**
- * Verify that the user can get a connect/link URL
- *
- * @since 4.3.0
- *
- * @return bool|WP_Error True if user is able to disconnect the site.
- */
- public static function connect_url_permission_callback() {
- if ( current_user_can( 'jetpack_connect_user' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
-
- }
-
- /**
- * Verify that a user can get the data about the current user.
- * Only those who can connect.
- *
- * @since 4.3.0
- *
- * @uses Jetpack::is_user_connected();
- *
- * @return bool|WP_Error True if user is able to unlink.
- */
- public static function get_user_connection_data_permission_callback() {
- if ( current_user_can( 'jetpack_connect_user' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_user_connection_data', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Check that user has permission to change the master user.
- *
- * @since 6.2.0
- *
- * @return bool|WP_Error True if user is able to change master user.
- */
- public static function set_connection_owner_permission_callback() {
- if ( get_current_user_id() === Jetpack_Options::get_option( 'master_user' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_set_connection_owner', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that a user can use the /connection/user endpoint. Has to be a registered user and be currently linked.
- *
- * @since 4.3.0
- *
- * @uses Jetpack::is_user_connected();
- *
- * @return bool|WP_Error True if user is able to unlink.
- */
- public static function unlink_user_permission_callback() {
- if ( current_user_can( 'jetpack_connect_user' ) && Jetpack::is_user_connected( get_current_user_id() ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_unlink_user', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can manage Jetpack modules.
- *
- * @since 4.3.0
- *
- * @return bool Whether user has the capability 'jetpack_manage_modules'.
- */
- public static function manage_modules_permission_check() {
- if ( current_user_can( 'jetpack_manage_modules' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_manage_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can update Jetpack modules.
- *
- * @since 4.3.0
- *
- * @return bool Whether user has the capability 'jetpack_configure_modules'.
- */
- public static function configure_modules_permission_check() {
- if ( current_user_can( 'jetpack_configure_modules' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_configure_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can view Jetpack admin page.
- *
- * @since 4.3.0
- *
- * @return bool Whether user has the capability 'jetpack_admin_page'.
- */
- public static function view_admin_page_permission_check() {
- if ( current_user_can( 'jetpack_admin_page' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_view_admin', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can mitigate an identity crisis.
- *
- * @since 4.4.0
- *
- * @return bool Whether user has capability 'jetpack_disconnect'.
- */
- public static function identity_crisis_mitigation_permission_check() {
- if ( current_user_can( 'jetpack_disconnect' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_identity_crisis', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can update Jetpack general settings.
- *
- * @since 4.3.0
- *
- * @return bool Whether user has the capability 'update_settings_permission_check'.
- */
- public static function update_settings_permission_check() {
- if ( current_user_can( 'jetpack_configure_modules' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_manage_settings', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can view Jetpack admin page and can activate plugins.
- *
- * @since 4.3.0
- *
- * @return bool Whether user has the capability 'jetpack_admin_page' and 'activate_plugins'.
- */
- public static function activate_plugins_permission_check() {
- if ( current_user_can( 'jetpack_admin_page' ) && current_user_can( 'activate_plugins' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_activate_plugins', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Verify that user can edit other's posts (Editors and Administrators).
- *
- * @return bool Whether user has the capability 'edit_others_posts'.
- */
- public static function edit_others_posts_check() {
- if ( current_user_can( 'edit_others_posts' ) ) {
- return true;
- }
-
- return new WP_Error( 'invalid_user_permission_edit_others_posts', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
- }
-
- /**
- * Contextual HTTP error code for authorization failure.
- *
- * Taken from rest_authorization_required_code() in WP-API plugin until is added to core.
- * @see https://github.com/WP-API/WP-API/commit/7ba0ae6fe4f605d5ffe4ee85b1cd5f9fb46900a6
- *
- * @since 4.3.0
- *
- * @return int
- */
- public static function rest_authorization_required_code() {
- return is_user_logged_in() ? 403 : 401;
- }
-
- /**
- * Get connection status for this Jetpack site.
- *
- * @since 4.3.0
- *
- * @return bool True if site is connected
- */
- public static function jetpack_connection_status() {
- return rest_ensure_response( array(
- 'isActive' => Jetpack::is_active(),
- 'isStaging' => Jetpack::is_staging_site(),
- 'devMode' => array(
- 'isActive' => Jetpack::is_development_mode(),
- 'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
- 'url' => site_url() && false === strpos( site_url(), '.' ),
- 'filter' => apply_filters( 'jetpack_development_mode', false ),
- ),
- )
- );
- }
-
- /**
- * Test connection status for this Jetpack site.
- *
- * @since 6.8.0
- *
- * @return array|WP_Error WP_Error returned if connection test does not succeed.
- */
- public static function jetpack_connection_test() {
- jetpack_require_lib( 'debugger' );
- $cxntests = new Jetpack_Cxn_Tests();
-
- if ( $cxntests->pass() ) {
- return rest_ensure_response(
- array(
- 'code' => 'success',
- 'message' => __( 'All connection tests passed.', 'jetpack' ),
- )
- );
- } else {
- return $cxntests->output_fails_as_wp_error();
- }
- }
-
- /**
- * Test connection permission check method.
- *
- * @since 7.1.0
- *
- * @return bool
- */
- public static function view_jetpack_connection_test_check() {
- if ( ! isset( $_GET['signature'], $_GET['timestamp'], $_GET['url'] ) ) {
- return false;
- }
- $signature = base64_decode( $_GET['signature'] );
-
- $signature_data = wp_json_encode(
- array(
- 'rest_route' => $_GET['rest_route'],
- 'timestamp' => intval( $_GET['timestamp'] ),
- 'url' => wp_unslash( $_GET['url'] ),
- )
- );
-
- if (
- ! function_exists( 'openssl_verify' )
- || ! openssl_verify(
- $signature_data,
- $signature,
- JETPACK__DEBUGGER_PUBLIC_KEY
- )
- ) {
- return false;
- }
-
- // signature timestamp must be within 5min of current time
- if ( abs( time() - intval( $_GET['timestamp'] ) ) > 300 ) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Test connection status for this Jetpack site, encrypt the results for decryption by a third-party.
- *
- * @since 7.1.0
- *
- * @return array|mixed|object|WP_Error
- */
- public static function jetpack_connection_test_for_external() {
- // Since we are running this test for inclusion in the WP.com testing suite, let's not try to run them as part of these results.
- add_filter( 'jetpack_debugger_run_self_test', '__return_false' );
- jetpack_require_lib( 'debugger' );
- $cxntests = new Jetpack_Cxn_Tests();
-
- if ( $cxntests->pass() ) {
- $result = array(
- 'code' => 'success',
- 'message' => __( 'All connection tests passed.', 'jetpack' ),
- );
- } else {
- $error = $cxntests->output_fails_as_wp_error(); // Using this so the output is similar both ways.
- $errors = array();
-
- // Borrowed from WP_REST_Server::error_to_response().
- foreach ( (array) $error->errors as $code => $messages ) {
- foreach ( (array) $messages as $message ) {
- $errors[] = array(
- 'code' => $code,
- 'message' => $message,
- 'data' => $error->get_error_data( $code ),
- );
- }
- }
-
- $result = $errors[0];
- if ( count( $errors ) > 1 ) {
- // Remove the primary error.
- array_shift( $errors );
- $result['additional_errors'] = $errors;
- }
- }
-
- $result = wp_json_encode( $result );
-
- $encrypted = $cxntests->encrypt_string_for_wpcom( $result );
-
- if ( ! $encrypted || ! is_array( $encrypted ) ) {
- return rest_ensure_response(
- array(
- 'code' => 'action_required',
- 'message' => 'Please request results from the in-plugin debugger',
- )
- );
- }
-
- return rest_ensure_response(
- array(
- 'code' => 'response',
- 'debug' => array(
- 'data' => $encrypted['data'],
- 'key' => $encrypted['key'],
- ),
- )
- );
- }
-
- public static function rewind_data() {
- $site_id = Jetpack_Options::get_option( 'id' );
-
- if ( ! $site_id ) {
- return new WP_Error( 'site_id_missing' );
- }
-
- $response = Jetpack_Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d/rewind', $site_id ) .'?force=wpcom', '2', array(), null, 'wpcom' );
-
- if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
- return new WP_Error( 'rewind_data_fetch_failed' );
- }
-
- $body = wp_remote_retrieve_body( $response );
-
- return json_decode( $body );
- }
-
- /**
- * Get rewind data
- *
- * @since 5.7.0
- *
- * @return array Array of rewind properties.
- */
- public static function get_rewind_data() {
- $rewind_data = self::rewind_data();
-
- if ( ! is_wp_error( $rewind_data ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Rewind data correctly received.', 'jetpack' ),
- 'data' => wp_json_encode( $rewind_data ),
- )
- );
- }
-
- if ( $rewind_data->get_error_code() === 'rewind_data_fetch_failed' ) {
- return new WP_Error( 'rewind_data_fetch_failed', esc_html__( 'Failed fetching rewind data. Try again later.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- if ( $rewind_data->get_error_code() === 'site_id_missing' ) {
- return new WP_Error( 'site_id_missing', esc_html__( 'The ID of this site does not exist.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- return new WP_Error(
- 'error_get_rewind_data',
- esc_html__( 'Could not retrieve Rewind data.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- /**
- * Disconnects Jetpack from the WordPress.com Servers
- *
- * @uses Jetpack::disconnect();
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if Jetpack successfully disconnected.
- */
- public static function disconnect_site( $request ) {
-
- if ( ! isset( $request['isActive'] ) || $request['isActive'] !== false ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- if ( Jetpack::is_active() ) {
- Jetpack::disconnect();
- return rest_ensure_response( array( 'code' => 'success' ) );
- }
-
- return new WP_Error( 'disconnect_failed', esc_html__( 'Was not able to disconnect the site. Please try again.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- /**
- * Gets a new connect raw URL with fresh nonce.
- *
- * @uses Jetpack::disconnect();
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return string|WP_Error A raw URL if the connection URL could be built; error message otherwise.
- */
- public static function build_connect_url() {
- $url = Jetpack::init()->build_connect_url( true, false, false );
- if ( $url ) {
- return rest_ensure_response( $url );
- }
-
- return new WP_Error( 'build_connect_url_failed', esc_html__( 'Unable to build the connect URL. Please reload the page and try again.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- /**
- * Get miscellaneous user data related to the connection. Similar data available in old "My Jetpack".
- * Information about the master/primary user.
- * Information about the current user.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return object
- */
- public static function get_user_connection_data() {
- require_once( JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class.jetpack-react-page.php' );
-
- $response = array(
-// 'othersLinked' => Jetpack::get_other_linked_admins(),
- 'currentUser' => jetpack_current_user_data(),
- );
- return rest_ensure_response( $response );
- }
-
- /**
- * Change the master user.
- *
- * @since 6.2.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if owner successfully changed.
- */
- public static function set_connection_owner( $request ) {
- if ( ! isset( $request['owner'] ) ) {
- return new WP_Error(
- 'invalid_param',
- esc_html__( 'Invalid Parameter', 'jetpack' ),
- array( 'status' => 400 )
- );
- }
-
- $new_owner_id = $request['owner'];
- if ( ! user_can( $new_owner_id, 'administrator' ) ) {
- return new WP_Error(
- 'new_owner_not_admin',
- esc_html__( 'New owner is not admin', 'jetpack' ),
- array( 'status' => 400 )
- );
- }
-
- if ( $new_owner_id === get_current_user_id() ) {
- return new WP_Error(
- 'new_owner_is_current_user',
- esc_html__( 'New owner is same as current user', 'jetpack' ),
- array( 'status' => 400 )
- );
- }
-
- if ( ! Jetpack::is_user_connected( $new_owner_id ) ) {
- return new WP_Error(
- 'new_owner_not_connected',
- esc_html__( 'New owner is not connected', 'jetpack' ),
- array( 'status' => 400 )
- );
- }
-
- // Update the master user in Jetpack
- $updated = Jetpack_Options::update_option( 'master_user', $new_owner_id );
-
- // Notify WPCOM about the master user change
- Jetpack::load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client( array(
- 'user_id' => get_current_user_id(),
- ) );
- $xml->query( 'jetpack.switchBlogOwner', array(
- 'new_blog_owner' => $new_owner_id,
- ) );
-
- if ( $updated && ! $xml->isError() ) {
- return rest_ensure_response(
- array(
- 'code' => 'success',
- )
- );
- }
- return new WP_Error(
- 'error_setting_new_owner',
- esc_html__( 'Could not confirm new owner.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- /**
- * Unlinks current user from the WordPress.com Servers.
- *
- * @since 4.3.0
- * @uses Jetpack::unlink_user
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if user successfully unlinked.
- */
- public static function unlink_user( $request ) {
-
- if ( ! isset( $request['linked'] ) || $request['linked'] !== false ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- if ( Jetpack::unlink_user() ) {
- return rest_ensure_response(
- array(
- 'code' => 'success'
- )
- );
- }
-
- return new WP_Error( 'unlink_user_failed', esc_html__( 'Was not able to unlink the user. Please try again.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- /**
- * Gets current user's tracking settings.
- *
- * @since 6.0.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return WP_REST_Response|WP_Error Response, else error.
- */
- public static function get_user_tracking_settings( $request ) {
- if ( ! Jetpack::is_user_connected() ) {
- $response = array(
- 'tracks_opt_out' => true, // Default to opt-out if not connected to wp.com.
- );
- } else {
- $response = Jetpack_Client::wpcom_json_api_request_as_user(
- '/jetpack-user-tracking',
- 'v2',
- array(
- 'method' => 'GET',
- 'headers' => array(
- 'X-Forwarded-For' => Jetpack::current_user_ip( true ),
- ),
- )
- );
- if ( ! is_wp_error( $response ) ) {
- $response = json_decode( wp_remote_retrieve_body( $response ), true );
- }
- }
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Updates current user's tracking settings.
- *
- * @since 6.0.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return WP_REST_Response|WP_Error Response, else error.
- */
- public static function update_user_tracking_settings( $request ) {
- if ( ! Jetpack::is_user_connected() ) {
- $response = array(
- 'tracks_opt_out' => true, // Default to opt-out if not connected to wp.com.
- );
- } else {
- $response = Jetpack_Client::wpcom_json_api_request_as_user(
- '/jetpack-user-tracking',
- 'v2',
- array(
- 'method' => 'PUT',
- 'headers' => array(
- 'Content-Type' => 'application/json',
- 'X-Forwarded-For' => Jetpack::current_user_ip( true ),
- ),
- ),
- wp_json_encode( $request->get_params() )
- );
- if ( ! is_wp_error( $response ) ) {
- $response = json_decode( wp_remote_retrieve_body( $response ), true );
- }
- }
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Fetch site data from .com including the site's current plan.
- *
- * @since 5.5.0
- *
- * @return array Array of site properties.
- */
- public static function site_data() {
- $site_id = Jetpack_Options::get_option( 'id' );
-
- if ( ! $site_id ) {
- new WP_Error( 'site_id_missing' );
- }
-
- $response = Jetpack_Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d', $site_id ) .'?force=wpcom', '1.1' );
-
- if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
- return new WP_Error( 'site_data_fetch_failed' );
- }
-
- Jetpack_Plan::update_from_sites_response( $response );
-
- $body = wp_remote_retrieve_body( $response );
-
- return json_decode( $body );
- }
- /**
- * Get site data, including for example, the site's current plan.
- *
- * @since 4.3.0
- *
- * @return array Array of site properties.
- */
- public static function get_site_data() {
- $site_data = self::site_data();
-
- if ( ! is_wp_error( $site_data ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Site data correctly received.', 'jetpack' ),
- 'data' => json_encode( $site_data ),
- )
- );
- }
- if ( $site_data->get_error_code() === 'site_data_fetch_failed' ) {
- return new WP_Error( 'site_data_fetch_failed', esc_html__( 'Failed fetching site data. Try again later.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- if ( $site_data->get_error_code() === 'site_id_missing' ) {
- return new WP_Error( 'site_id_missing', esc_html__( 'The ID of this site does not exist.', 'jetpack' ), array( 'status' => 404 ) );
- }
- }
-
- /**
- * Handles identity crisis mitigation, confirming safe mode for this site.
- *
- * @since 4.4.0
- *
- * @return bool | WP_Error True if option is properly set.
- */
- public static function confirm_safe_mode() {
- $updated = Jetpack_Options::update_option( 'safe_mode_confirmed', true );
- if ( $updated ) {
- return rest_ensure_response(
- array(
- 'code' => 'success'
- )
- );
- }
- return new WP_Error(
- 'error_setting_jetpack_safe_mode',
- esc_html__( 'Could not confirm safe mode.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- /**
- * Handles identity crisis mitigation, migrating stats and subscribers from old url to this, new url.
- *
- * @since 4.4.0
- *
- * @return bool | WP_Error True if option is properly set.
- */
- public static function migrate_stats_and_subscribers() {
- if ( Jetpack_Options::get_option( 'sync_error_idc' ) && ! Jetpack_Options::delete_option( 'sync_error_idc' ) ) {
- return new WP_Error(
- 'error_deleting_sync_error_idc',
- esc_html__( 'Could not delete sync error option.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- if ( Jetpack_Options::get_option( 'migrate_for_idc' ) || Jetpack_Options::update_option( 'migrate_for_idc', true ) ) {
- return rest_ensure_response(
- array(
- 'code' => 'success'
- )
- );
- }
- return new WP_Error(
- 'error_setting_jetpack_migrate',
- esc_html__( 'Could not confirm migration.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- /**
- * This IDC resolution will disconnect the site and re-connect to a completely new
- * and separate shadow site than the original.
- *
- * It will first will disconnect the site without phoning home as to not disturb the production site.
- * It then builds a fresh connection URL and sends it back along with the response.
- *
- * @since 4.4.0
- * @return bool|WP_Error
- */
- public static function start_fresh_connection() {
- // First clear the options / disconnect.
- Jetpack::disconnect();
- return self::build_connect_url();
- }
-
- /**
- * Reset Jetpack options
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $options Available options to reset are options|modules
- * }
- *
- * @return bool|WP_Error True if options were reset. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function reset_jetpack_options( $request ) {
-
- if ( ! isset( $request['reset'] ) || $request['reset'] !== true ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- if ( isset( $request['options'] ) ) {
- $data = $request['options'];
-
- switch( $data ) {
- case ( 'options' ) :
- $options_to_reset = Jetpack::get_jetpack_options_for_reset();
-
- // Reset the Jetpack options
- foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
- Jetpack_Options::delete_option( $option_to_reset );
- }
-
- foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
- delete_option( $option_to_reset );
- }
-
- // Reset to default modules
- $default_modules = Jetpack::get_default_modules();
- Jetpack::update_active_modules( $default_modules );
-
- // Jumpstart option is special
- Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Jetpack options reset.', 'jetpack' ),
- ) );
- break;
-
- case 'modules':
- $default_modules = Jetpack::get_default_modules();
- Jetpack::update_active_modules( $default_modules );
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Modules reset to default.', 'jetpack' ),
- ) );
- break;
-
- default:
- return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
- }
- }
-
- return new WP_Error( 'required_param', esc_html__( 'Missing parameter "type".', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- /**
- * Retrieves the current status of Jumpstart.
- *
- * @since 4.5.0
- *
- * @return bool
- */
- public static function jumpstart_status() {
- return array(
- 'status' => Jetpack_Options::get_option( 'jumpstart' )
- );
- }
-
- /**
- * Toggles activation or deactivation of the JumpStart
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if toggling Jumpstart succeeded. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function jumpstart_toggle( $request ) {
-
- if ( $request[ 'active' ] ) {
- return self::jumpstart_activate( $request );
- } else {
- return self::jumpstart_deactivate( $request );
- }
- }
-
- /**
- * Activates a series of valid Jetpack modules and initializes some options.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if Jumpstart succeeded. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function jumpstart_activate( $request ) {
- $modules = Jetpack::get_available_modules();
- $activate_modules = array();
- foreach ( $modules as $module ) {
- $module_info = Jetpack::get_module( $module );
- if ( isset( $module_info['feature'] ) && is_array( $module_info['feature'] ) && in_array( 'Jumpstart', $module_info['feature'] ) ) {
- $activate_modules[] = $module;
- }
- }
-
- // Collect success/error messages like modules that are properly activated.
- $result = array(
- 'activated_modules' => array(),
- 'failed_modules' => array(),
- );
-
- // Update the jumpstart option
- if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
- $result['jumpstart_activated'] = Jetpack_Options::update_option( 'jumpstart', 'jumpstart_activated' );
- }
-
- // Check for possible conflicting plugins
- $module_slugs_filtered = Jetpack::init()->filter_default_modules( $activate_modules );
-
- foreach ( $module_slugs_filtered as $module_slug ) {
- Jetpack::log( 'activate', $module_slug );
- if ( Jetpack::activate_module( $module_slug, false, false ) ) {
- $result['activated_modules'][] = $module_slug;
- } else {
- $result['failed_modules'][] = $module_slug;
- }
- }
-
- // Set the default sharing buttons and set to display on posts if none have been set.
- $sharing_services = get_option( 'sharing-services' );
- $sharing_options = get_option( 'sharing-options' );
- if ( empty( $sharing_services['visible'] ) ) {
- // Default buttons to set
- $visible = array(
- 'twitter',
- 'facebook',
- );
- $hidden = array();
-
- // Set some sharing settings
- if ( class_exists( 'Sharing_Service' ) ) {
- $sharing = new Sharing_Service();
- $sharing_options['global'] = array(
- 'button_style' => 'icon',
- 'sharing_label' => $sharing->default_sharing_label,
- 'open_links' => 'same',
- 'show' => array( 'post' ),
- 'custom' => isset( $sharing_options['global']['custom'] ) ? $sharing_options['global']['custom'] : array()
- );
-
- $result['sharing_options'] = update_option( 'sharing-options', $sharing_options );
- $result['sharing_services'] = update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) );
- }
- }
-
- // If all Jumpstart modules were activated
- if ( empty( $result['failed_modules'] ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Jumpstart done.', 'jetpack' ),
- 'data' => $result,
- ) );
- }
-
- return new WP_Error( 'jumpstart_failed', esc_html( sprintf( _n( 'Jumpstart failed activating this module: %s.', 'Jumpstart failed activating these modules: %s.', count( $result['failed_modules'] ), 'jetpack' ), join( ', ', $result['failed_modules'] ) ) ), array( 'status' => 400 ) );
- }
-
- /**
- * Dismisses Jumpstart so user is not prompted to go through it again.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool|WP_Error True if Jumpstart was disabled or was nothing to dismiss. Otherwise, a WP_Error instance with a message.
- */
- public static function jumpstart_deactivate( $request ) {
-
- // If dismissed, flag the jumpstart option as such.
- if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
- if ( Jetpack_Options::update_option( 'jumpstart', 'jumpstart_dismissed' ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Jumpstart dismissed.', 'jetpack' ),
- ) );
- } else {
- return new WP_Error( 'jumpstart_failed_dismiss', esc_html__( 'Jumpstart could not be dismissed.', 'jetpack' ), array( 'status' => 400 ) );
- }
- }
-
- // If this was not a new connection and there was nothing to dismiss, don't fail.
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Nothing to dismiss. This was not a new connection.', 'jetpack' ),
- ) );
- }
-
- /**
- * Get the query parameters to update module options or general settings.
- *
- * @since 4.3.0
- * @since 4.4.0 Accepts a $selector parameter.
- *
- * @param string $selector Selects a set of options to update, Can be empty, a module slug or 'any'.
- *
- * @return array
- */
- public static function get_updateable_parameters( $selector = '' ) {
- $parameters = array(
- 'context' => array(
- 'default' => 'edit',
- ),
- );
-
- return array_merge( $parameters, self::get_updateable_data_list( $selector ) );
- }
-
- /**
- * Returns a list of module options or general settings that can be updated.
- *
- * @since 4.3.0
- * @since 4.4.0 Accepts 'any' as a parameter which will make it return the entire list.
- *
- * @param string|array $selector Module slug, 'any', or an array of parameters.
- * If empty, it's assumed we're updating a module and we'll try to get its slug.
- * If 'any' the full list is returned.
- * If it's an array of parameters, includes the elements by matching keys.
- *
- * @return array
- */
- public static function get_updateable_data_list( $selector = '' ) {
-
- $options = array(
-
- // Carousel
- 'carousel_background_color' => array(
- 'description' => esc_html__( 'Color scheme.', 'jetpack' ),
- 'type' => 'string',
- 'default' => 'black',
- 'enum' => array(
- 'black',
- 'white',
- ),
- 'enum_labels' => array(
- 'black' => esc_html__( 'Black', 'jetpack' ),
- 'white' => esc_html__( 'White', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'carousel',
- ),
- 'carousel_display_exif' => array(
- 'description' => wp_kses( sprintf( __( 'Show photo metadata (<a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format" target="_blank">Exif</a>) in carousel, when available.', 'jetpack' ) ), array( 'a' => array( 'href' => true, 'target' => true ) ) ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'carousel',
- ),
-
- // Comments
- 'highlander_comment_form_prompt' => array(
- 'description' => esc_html__( 'Greeting Text', 'jetpack' ),
- 'type' => 'string',
- 'default' => esc_html__( 'Leave a Reply', 'jetpack' ),
- 'sanitize_callback' => 'sanitize_text_field',
- 'jp_group' => 'comments',
- ),
- 'jetpack_comment_form_color_scheme' => array(
- 'description' => esc_html__( "Color scheme", 'jetpack' ),
- 'type' => 'string',
- 'default' => 'light',
- 'enum' => array(
- 'light',
- 'dark',
- 'transparent',
- ),
- 'enum_labels' => array(
- 'light' => esc_html__( 'Light', 'jetpack' ),
- 'dark' => esc_html__( 'Dark', 'jetpack' ),
- 'transparent' => esc_html__( 'Transparent', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'comments',
- ),
-
- // Custom Content Types
- 'jetpack_portfolio' => array(
- 'description' => esc_html__( 'Enable or disable Jetpack portfolio post type.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'custom-content-types',
- ),
- 'jetpack_portfolio_posts_per_page' => array(
- 'description' => esc_html__( 'Number of entries to show at most in Portfolio pages.', 'jetpack' ),
- 'type' => 'integer',
- 'default' => 10,
- 'validate_callback' => __CLASS__ . '::validate_posint',
- 'jp_group' => 'custom-content-types',
- ),
- 'jetpack_testimonial' => array(
- 'description' => esc_html__( 'Enable or disable Jetpack testimonial post type.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'custom-content-types',
- ),
- 'jetpack_testimonial_posts_per_page' => array(
- 'description' => esc_html__( 'Number of entries to show at most in Testimonial pages.', 'jetpack' ),
- 'type' => 'integer',
- 'default' => 10,
- 'validate_callback' => __CLASS__ . '::validate_posint',
- 'jp_group' => 'custom-content-types',
- ),
-
- // Galleries
- 'tiled_galleries' => array(
- 'description' => esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'tiled-gallery',
- ),
-
- 'gravatar_disable_hovercards' => array(
- 'description' => esc_html__( "View people's profiles when you mouse over their Gravatars", 'jetpack' ),
- 'type' => 'string',
- 'default' => 'enabled',
- // Not visible. This is used as the checkbox value.
- 'enum' => array(
- 'enabled',
- 'disabled',
- ),
- 'enum_labels' => array(
- 'enabled' => esc_html__( 'Enabled', 'jetpack' ),
- 'disabled' => esc_html__( 'Disabled', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'gravatar-hovercards',
- ),
-
- // Infinite Scroll
- 'infinite_scroll' => array(
- 'description' => esc_html__( 'To infinity and beyond', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'infinite-scroll',
- ),
- 'infinite_scroll_google_analytics' => array(
- 'description' => esc_html__( 'Use Google Analytics with Infinite Scroll', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'infinite-scroll',
- ),
-
- // Likes
- 'wpl_default' => array(
- 'description' => esc_html__( 'WordPress.com Likes are', 'jetpack' ),
- 'type' => 'string',
- 'default' => 'on',
- 'enum' => array(
- 'on',
- 'off',
- ),
- 'enum_labels' => array(
- 'on' => esc_html__( 'On for all posts', 'jetpack' ),
- 'off' => esc_html__( 'Turned on per post', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'likes',
- ),
- 'social_notifications_like' => array(
- 'description' => esc_html__( 'Send email notification when someone likes a post', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'likes',
- ),
-
- // Markdown
- 'wpcom_publish_comments_with_markdown' => array(
- 'description' => esc_html__( 'Use Markdown for comments.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'markdown',
- ),
- 'wpcom_publish_posts_with_markdown' => array(
- 'description' => esc_html__( 'Use Markdown for posts.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'markdown',
- ),
-
- // Mobile Theme
- 'wp_mobile_excerpt' => array(
- 'description' => esc_html__( 'Excerpts', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'minileven',
- ),
- 'wp_mobile_featured_images' => array(
- 'description' => esc_html__( 'Featured Images', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'minileven',
- ),
- 'wp_mobile_app_promos' => array(
- 'description' => esc_html__( 'Show a promo for the WordPress mobile apps in the footer of the mobile theme.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'minileven',
- ),
-
- // Monitor
- 'monitor_receive_notifications' => array(
- 'description' => esc_html__( 'Receive Monitor Email Notifications.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'monitor',
- ),
-
- // Post by Email
- 'post_by_email_address' => array(
- 'description' => esc_html__( 'Email Address', 'jetpack' ),
- 'type' => 'string',
- 'default' => 'noop',
- 'enum' => array(
- 'noop',
- 'create',
- 'regenerate',
- 'delete',
- ),
- 'enum_labels' => array(
- 'noop' => '',
- 'create' => esc_html__( 'Create Post by Email address', 'jetpack' ),
- 'regenerate' => esc_html__( 'Regenerate Post by Email address', 'jetpack' ),
- 'delete' => esc_html__( 'Delete Post by Email address', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'post-by-email',
- ),
-
- // Protect
- 'jetpack_protect_key' => array(
- 'description' => esc_html__( 'Protect API key', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_alphanum',
- 'jp_group' => 'protect',
- ),
- 'jetpack_protect_global_whitelist' => array(
- 'description' => esc_html__( 'Protect global whitelist', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_string',
- 'sanitize_callback' => 'esc_textarea',
- 'jp_group' => 'protect',
- ),
-
- // Sharing
- 'sharing_services' => array(
- 'description' => esc_html__( 'Enabled Services and those hidden behind a button', 'jetpack' ),
- 'type' => 'object',
- 'default' => array(
- 'visible' => array( 'twitter', 'facebook', 'google-plus-1' ),
- 'hidden' => array(),
- ),
- 'validate_callback' => __CLASS__ . '::validate_services',
- 'jp_group' => 'sharedaddy',
- ),
- 'button_style' => array(
- 'description' => esc_html__( 'Button Style', 'jetpack' ),
- 'type' => 'string',
- 'default' => 'icon',
- 'enum' => array(
- 'icon-text',
- 'icon',
- 'text',
- 'official',
- ),
- 'enum_labels' => array(
- 'icon-text' => esc_html__( 'Icon + text', 'jetpack' ),
- 'icon' => esc_html__( 'Icon only', 'jetpack' ),
- 'text' => esc_html__( 'Text only', 'jetpack' ),
- 'official' => esc_html__( 'Official buttons', 'jetpack' ),
- ),
- 'validate_callback' => __CLASS__ . '::validate_list_item',
- 'jp_group' => 'sharedaddy',
- ),
- 'sharing_label' => array(
- 'description' => esc_html__( 'Sharing Label', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_string',
- 'sanitize_callback' => 'esc_html',
- 'jp_group' => 'sharedaddy',
- ),
- 'show' => array(
- 'description' => esc_html__( 'Views where buttons are shown', 'jetpack' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string'
- ),
- 'default' => array( 'post' ),
- 'validate_callback' => __CLASS__ . '::validate_sharing_show',
- 'jp_group' => 'sharedaddy',
- ),
- 'jetpack-twitter-cards-site-tag' => array(
- 'description' => esc_html__( "The Twitter username of the owner of this site's domain.", 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_twitter_username',
- 'sanitize_callback' => 'esc_html',
- 'jp_group' => 'sharedaddy',
- ),
- 'sharedaddy_disable_resources' => array(
- 'description' => esc_html__( 'Disable CSS and JS', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'sharedaddy',
- ),
- 'custom' => array(
- 'description' => esc_html__( 'Custom sharing services added by user.', 'jetpack' ),
- 'type' => 'object',
- 'default' => array(
- 'sharing_name' => '',
- 'sharing_url' => '',
- 'sharing_icon' => '',
- ),
- 'validate_callback' => __CLASS__ . '::validate_custom_service',
- 'jp_group' => 'sharedaddy',
- ),
- // Not an option, but an action that can be perfomed on the list of custom services passing the service ID.
- 'sharing_delete_service' => array(
- 'description' => esc_html__( 'Delete custom sharing service.', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_custom_service_id',
- 'jp_group' => 'sharedaddy',
- ),
-
- // SSO
- 'jetpack_sso_require_two_step' => array(
- 'description' => esc_html__( 'Require Two-Step Authentication', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'sso',
- ),
- 'jetpack_sso_match_by_email' => array(
- 'description' => esc_html__( 'Match by Email', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'sso',
- ),
-
- // Subscriptions
- 'stb_enabled' => array(
- 'description' => esc_html__( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'subscriptions',
- ),
- 'stc_enabled' => array(
- 'description' => esc_html__( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'subscriptions',
- ),
-
- // Related Posts
- 'show_headline' => array(
- 'description' => esc_html__( 'Highlight related content with a heading', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'related-posts',
- ),
- 'show_thumbnails' => array(
- 'description' => esc_html__( 'Show a thumbnail image where available', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'related-posts',
- ),
-
- // Spelling and Grammar - After the Deadline
- 'onpublish' => array(
- 'description' => esc_html__( 'Proofread when a post or page is first published.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'onupdate' => array(
- 'description' => esc_html__( 'Proofread when a post or page is updated.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Bias Language' => array(
- 'description' => esc_html__( 'Bias Language', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Cliches' => array(
- 'description' => esc_html__( 'Clichés', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Complex Expression' => array(
- 'description' => esc_html__( 'Complex Phrases', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Diacritical Marks' => array(
- 'description' => esc_html__( 'Diacritical Marks', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Double Negative' => array(
- 'description' => esc_html__( 'Double Negatives', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Hidden Verbs' => array(
- 'description' => esc_html__( 'Hidden Verbs', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Jargon Language' => array(
- 'description' => esc_html__( 'Jargon', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Passive voice' => array(
- 'description' => esc_html__( 'Passive Voice', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Phrases to Avoid' => array(
- 'description' => esc_html__( 'Phrases to Avoid', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'Redundant Expression' => array(
- 'description' => esc_html__( 'Redundant Phrases', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'guess_lang' => array(
- 'description' => esc_html__( 'Use automatically detected language to proofread posts and pages', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'after-the-deadline',
- ),
- 'ignored_phrases' => array(
- 'description' => esc_html__( 'Add Phrase to be ignored', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'sanitize_callback' => 'esc_html',
- 'jp_group' => 'after-the-deadline',
- ),
- 'unignore_phrase' => array(
- 'description' => esc_html__( 'Remove Phrase from being ignored', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'sanitize_callback' => 'esc_html',
- 'jp_group' => 'after-the-deadline',
- ),
-
- // Verification Tools
- 'google' => array(
- 'description' => esc_html__( 'Google Search Console', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_verification_service',
- 'jp_group' => 'verification-tools',
- ),
- 'bing' => array(
- 'description' => esc_html__( 'Bing Webmaster Center', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_verification_service',
- 'jp_group' => 'verification-tools',
- ),
- 'pinterest' => array(
- 'description' => esc_html__( 'Pinterest Site Verification', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_verification_service',
- 'jp_group' => 'verification-tools',
- ),
- 'yandex' => array(
- 'description' => esc_html__( 'Yandex Site Verification', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_verification_service',
- 'jp_group' => 'verification-tools',
- ),
- 'enable_header_ad' => array(
- 'description' => esc_html__( 'Display an ad unit at the top of each page.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_approved' => array(
- 'description' => esc_html__( 'Is site approved for WordAds?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_second_belowpost' => array(
- 'description' => esc_html__( 'Display second ad below post?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_display_front_page' => array(
- 'description' => esc_html__( 'Display ads on the front page?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_display_post' => array(
- 'description' => esc_html__( 'Display ads on posts?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_display_page' => array(
- 'description' => esc_html__( 'Display ads on pages?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_display_archive' => array(
- 'description' => esc_html__( 'Display ads on archive pages?', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'wordads',
- ),
- 'wordads_custom_adstxt' => array(
- 'description' => esc_html__( 'Custom ads.txt entries', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_string',
- 'sanitize_callback' => 'sanitize_textarea_field',
- 'jp_group' => 'wordads',
- ),
-
- // Google Analytics
- 'google_analytics_tracking_id' => array(
- 'description' => esc_html__( 'Google Analytics', 'jetpack' ),
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_alphanum',
- 'jp_group' => 'google-analytics',
- ),
-
- // Stats
- 'admin_bar' => array(
- 'description' => esc_html__( 'Put a chart showing 48 hours of views in the admin bar.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'stats',
- ),
- 'roles' => array(
- 'description' => esc_html__( 'Select the roles that will be able to view stats reports.', 'jetpack' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string'
- ),
- 'default' => array( 'administrator' ),
- 'validate_callback' => __CLASS__ . '::validate_stats_roles',
- 'sanitize_callback' => __CLASS__ . '::sanitize_stats_allowed_roles',
- 'jp_group' => 'stats',
- ),
- 'count_roles' => array(
- 'description' => esc_html__( 'Count the page views of registered users who are logged in.', 'jetpack' ),
- 'type' => 'array',
- 'items' => array(
- 'type' => 'string'
- ),
- 'default' => array( 'administrator' ),
- 'validate_callback' => __CLASS__ . '::validate_stats_roles',
- 'jp_group' => 'stats',
- ),
- 'blog_id' => array(
- 'description' => esc_html__( 'Blog ID.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'stats',
- ),
- 'do_not_track' => array(
- 'description' => esc_html__( 'Do not track.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'stats',
- ),
- 'hide_smile' => array(
- 'description' => esc_html__( 'Hide the stats smiley face image.', 'jetpack' ),
- 'type' => 'boolean',
- 'default' => 1,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'stats',
- ),
- 'version' => array(
- 'description' => esc_html__( 'Version.', 'jetpack' ),
- 'type' => 'integer',
- 'default' => 9,
- 'validate_callback' => __CLASS__ . '::validate_posint',
- 'jp_group' => 'stats',
- ),
-
- // Akismet - Not a module, but a plugin. The options can be passed and handled differently.
- 'akismet_show_user_comments_approved' => array(
- 'description' => '',
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'settings',
- ),
-
- 'wordpress_api_key' => array(
- 'description' => '',
- 'type' => 'string',
- 'default' => '',
- 'validate_callback' => __CLASS__ . '::validate_alphanum',
- 'jp_group' => 'settings',
- ),
-
- // Apps card on dashboard
- 'dismiss_dash_app_card' => array(
- 'description' => '',
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'settings',
- ),
-
- // Empty stats card dismiss
- 'dismiss_empty_stats_card' => array(
- 'description' => '',
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'settings',
- ),
-
- 'lang_id' => array(
- 'description' => esc_html__( 'Primary language for the site.', 'jetpack' ),
- 'type' => 'string',
- 'default' => 'en_US',
- 'jp_group' => 'settings',
- ),
-
- 'onboarding' => array(
- 'description' => '',
- 'type' => 'object',
- 'default' => array(
- 'siteTitle' => '',
- 'siteDescription' => '',
- 'siteType' => 'personal',
- 'homepageFormat' => 'posts',
- 'addContactForm' => 0,
- 'businessAddress' => array(
- 'name' => '',
- 'street' => '',
- 'city' => '',
- 'state' => '',
- 'zip' => '',
- ),
- 'installWooCommerce' => false,
- ),
- 'validate_callback' => __CLASS__ . '::validate_onboarding',
- 'jp_group' => 'settings',
- ),
-
- // Show welcome for newly purchased plan
- 'show_welcome_for_new_plan' => array(
- 'description' => '',
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'settings',
- ),
-
- );
-
- // Add modules to list so they can be toggled
- $modules = Jetpack::get_available_modules();
- if ( is_array( $modules ) && ! empty( $modules ) ) {
- $module_args = array(
- 'description' => '',
- 'type' => 'boolean',
- 'default' => 0,
- 'validate_callback' => __CLASS__ . '::validate_boolean',
- 'jp_group' => 'modules',
- );
- foreach( $modules as $module ) {
- $options[ $module ] = $module_args;
- }
- }
-
- if ( is_array( $selector ) ) {
-
- // Return only those options whose keys match $selector keys
- return array_intersect_key( $options, $selector );
- }
-
- if ( 'any' === $selector ) {
-
- // Toggle module or update any module option or any general setting
- return $options;
- }
-
- // We're updating the options for a single module.
- if ( empty( $selector ) ) {
- $selector = self::get_module_requested();
- }
- $selected = array();
- foreach ( $options as $option => $attributes ) {
-
- // Not adding an isset( $attributes['jp_group'] ) because if it's not set, it must be fixed, otherwise options will fail.
- if ( $selector === $attributes['jp_group'] ) {
- $selected[ $option ] = $attributes;
- }
- }
- return $selected;
- }
-
- /**
- * Validates that the parameters are proper values that can be set during Jetpack onboarding.
- *
- * @since 5.4.0
- *
- * @param array $onboarding_data Values to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_onboarding( $onboarding_data, $request, $param ) {
- if ( ! is_array( $onboarding_data ) ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Not valid onboarding data.', 'jetpack' ) );
- }
- foreach ( $onboarding_data as $value ) {
- if ( is_string( $value ) ) {
- $onboarding_choice = self::validate_string( $value, $request, $param );
- } elseif ( is_array( $value ) ) {
- $onboarding_choice = self::validate_onboarding( $value, $request, $param );
- } else {
- $onboarding_choice = self::validate_boolean( $value, $request, $param );
- }
- if ( is_wp_error( $onboarding_choice ) ) {
- return $onboarding_choice;
- }
- }
- return true;
- }
-
- /**
- * Validates that the parameter is either a pure boolean or a numeric string that can be mapped to a boolean.
- *
- * @since 4.3.0
- *
- * @param string|bool $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_boolean( $value, $request, $param ) {
- if ( ! is_bool( $value ) && ! ( ( ctype_digit( $value ) || is_numeric( $value ) ) && in_array( $value, array( 0, 1 ) ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be true, false, 0 or 1.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is a positive integer.
- *
- * @since 4.3.0
- *
- * @param int $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_posint( $value = 0, $request, $param ) {
- if ( ! is_numeric( $value ) || $value <= 0 ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a positive integer.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter belongs to a list of admitted values.
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_list_item( $value = '', $request, $param ) {
- $attributes = $request->get_attributes();
- if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s not recognized', 'jetpack' ), $param ) );
- }
- $args = $attributes['args'][ $param ];
- if ( ! empty( $args['enum'] ) ) {
-
- // If it's an associative array, use the keys to check that the value is among those admitted.
- $enum = ( count( array_filter( array_keys( $args['enum'] ), 'is_string' ) ) > 0 ) ? array_keys( $args['enum'] ) : $args['enum'];
- if ( ! in_array( $value, $enum ) ) {
- return new WP_Error( 'invalid_param_value', sprintf(
- /* Translators: first variable is the parameter passed to endpoint that holds the list item, the second is a list of admitted values. */
- esc_html__( '%1$s must be one of %2$s', 'jetpack' ), $param, implode( ', ', $enum )
- ) );
- }
- }
- return true;
- }
-
- /**
- * Validates that the parameter belongs to a list of admitted values.
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_module_list( $value = '', $request, $param ) {
- if ( ! is_array( $value ) ) {
- return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be an array', 'jetpack' ), $param ) );
- }
-
- $modules = Jetpack::get_available_modules();
-
- if ( count( array_intersect( $value, $modules ) ) != count( $value ) ) {
- return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be a list of valid modules', 'jetpack' ), $param ) );
- }
-
- return true;
- }
-
- /**
- * Validates that the parameter is an alphanumeric or empty string (to be able to clear the field).
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_alphanum( $value = '', $request, $param ) {
- if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^[a-z0-9]+$/i', $value ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is a tag or id for a verification service, or an empty string (to be able to clear the field).
- *
- * @since 4.6.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_verification_service( $value = '', $request, $param ) {
- if ( ! empty( $value ) && ! ( is_string( $value ) && ( preg_match( '/^[a-z0-9_-]+$/i', $value ) || jetpack_verification_get_code( $value ) !== false ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string or a verification tag.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is among the roles allowed for Stats.
- *
- * @since 4.3.0
- *
- * @param string|bool $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_stats_roles( $value, $request, $param ) {
- if ( ! empty( $value ) && ! array_intersect( self::$stats_roles, $value ) ) {
- return new WP_Error( 'invalid_param', sprintf(
- /* Translators: first variable is the name of a parameter passed to endpoint holding the role that will be checked, the second is a list of roles allowed to see stats. The parameter is checked against this list. */
- esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', self::$stats_roles )
- ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is among the views where the Sharing can be displayed.
- *
- * @since 4.3.0
- *
- * @param string|bool $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_sharing_show( $value, $request, $param ) {
- $views = array( 'index', 'post', 'page', 'attachment', 'jetpack-portfolio' );
- if ( ! is_array( $value ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array of post types.', 'jetpack' ), $param ) );
- }
- if ( ! array_intersect( $views, $value ) ) {
- return new WP_Error( 'invalid_param', sprintf(
- /* Translators: first variable is the name of a parameter passed to endpoint holding the post type where Sharing will be displayed, the second is a list of post types where Sharing can be displayed */
- esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', $views )
- ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is among the views where the Sharing can be displayed.
- *
- * @since 4.3.0
- *
- * @param string|bool $value {
- * Value to check received by request.
- *
- * @type array $visible List of slug of services to share to that are displayed directly in the page.
- * @type array $hidden List of slug of services to share to that are concealed in a folding menu.
- * }
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_services( $value, $request, $param ) {
- if ( ! is_array( $value ) || ! isset( $value['visible'] ) || ! isset( $value['hidden'] ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with visible and hidden items.', 'jetpack' ), $param ) );
- }
-
- // Allow to clear everything.
- if ( empty( $value['visible'] ) && empty( $value['hidden'] ) ) {
- return true;
- }
-
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
- }
- $sharer = new Sharing_Service();
- $services = array_keys( $sharer->get_all_services() );
-
- if (
- ( ! empty( $value['visible'] ) && ! array_intersect( $value['visible'], $services ) )
- ||
- ( ! empty( $value['hidden'] ) && ! array_intersect( $value['hidden'], $services ) ) )
- {
- return new WP_Error( 'invalid_param', sprintf(
- /* Translators: placeholder 1 is a parameter holding the services passed to endpoint, placeholder 2 is a list of all Jetpack Sharing services */
- esc_html__( '%1$s visible and hidden items must be a list of %2$s.', 'jetpack' ), $param, join( ', ', $services )
- ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter has enough information to build a custom sharing button.
- *
- * @since 4.3.0
- *
- * @param string|bool $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_custom_service( $value, $request, $param ) {
- if ( ! is_array( $value ) || ! isset( $value['sharing_name'] ) || ! isset( $value['sharing_url'] ) || ! isset( $value['sharing_icon'] ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with sharing name, url and icon.', 'jetpack' ), $param ) );
- }
-
- // Allow to clear everything.
- if ( empty( $value['sharing_name'] ) && empty( $value['sharing_url'] ) && empty( $value['sharing_icon'] ) ) {
- return true;
- }
-
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
- }
-
- if ( ( ! empty( $value['sharing_name'] ) && ! is_string( $value['sharing_name'] ) )
- || ( ! empty( $value['sharing_url'] ) && ! is_string( $value['sharing_url'] ) )
- || ( ! empty( $value['sharing_icon'] ) && ! is_string( $value['sharing_icon'] ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s needs sharing name, url and icon.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is a custom sharing service ID like 'custom-1461976264'.
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_custom_service_id( $value = '', $request, $param ) {
- if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/custom\-[0-1]+/i', $value ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( "%s must be a string prefixed with 'custom-' and followed by a numeric ID.", 'jetpack' ), $param ) );
- }
-
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
- }
- $sharer = new Sharing_Service();
- $services = array_keys( $sharer->get_all_services() );
-
- if ( ! empty( $value ) && ! in_array( $value, $services ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s is not a registered custom sharing service.', 'jetpack' ), $param ) );
- }
-
- return true;
- }
-
- /**
- * Validates that the parameter is a Twitter username or empty string (to be able to clear the field).
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_twitter_username( $value = '', $request, $param ) {
- if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^@?\w{1,15}$/i', $value ) ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a Twitter username.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * Validates that the parameter is a string.
- *
- * @since 4.3.0
- *
- * @param string $value Value to check.
- * @param WP_REST_Request $request The request sent to the WP REST API.
- * @param string $param Name of the parameter passed to endpoint holding $value.
- *
- * @return bool|WP_Error
- */
- public static function validate_string( $value = '', $request, $param ) {
- if ( ! is_string( $value ) ) {
- return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a string.', 'jetpack' ), $param ) );
- }
- return true;
- }
-
- /**
- * If for some reason the roles allowed to see Stats are empty (for example, user tampering with checkboxes),
- * return an array with only 'administrator' as the allowed role and save it for 'roles' option.
- *
- * @since 4.3.0
- *
- * @param string|bool $value Value to check.
- *
- * @return bool|array
- */
- public static function sanitize_stats_allowed_roles( $value ) {
- if ( empty( $value ) ) {
- return array( 'administrator' );
- }
- return $value;
- }
-
- /**
- * Get the currently accessed route and return the module slug in it.
- *
- * @since 4.3.0
- *
- * @param string $route Regular expression for the endpoint with the module slug to return.
- *
- * @return array|string
- */
- public static function get_module_requested( $route = '/module/(?P<slug>[a-z\-]+)' ) {
-
- if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
- return '';
- }
-
- preg_match( "#$route#", $GLOBALS['wp']->query_vars['rest_route'], $module );
-
- if ( empty( $module['slug'] ) ) {
- return '';
- }
-
- return $module['slug'];
- }
-
- /**
- * Adds extra information for modules.
- *
- * @since 4.3.0
- *
- * @param string|array $modules Can be a single module or a list of modules.
- * @param null|string $slug Slug of the module in the first parameter.
- *
- * @return array|string
- */
- public static function prepare_modules_for_response( $modules = '', $slug = null ) {
- global $wp_rewrite;
-
- /** This filter is documented in modules/sitemaps/sitemaps.php */
- $location = apply_filters( 'jetpack_sitemap_location', '' );
-
- if ( $wp_rewrite->using_index_permalinks() ) {
- $sitemap_url = home_url( '/index.php' . $location . '/sitemap.xml' );
- $news_sitemap_url = home_url( '/index.php' . $location . '/news-sitemap.xml' );
- } else if ( $wp_rewrite->using_permalinks() ) {
- $sitemap_url = home_url( $location . '/sitemap.xml' );
- $news_sitemap_url = home_url( $location . '/news-sitemap.xml' );
- } else {
- $sitemap_url = home_url( $location . '/?jetpack-sitemap=sitemap.xml' );
- $news_sitemap_url = home_url( $location . '/?jetpack-sitemap=news-sitemap.xml' );
- }
-
- if ( is_null( $slug ) && isset( $modules['sitemaps'] ) ) {
- // Is a list of modules
- $modules['sitemaps']['extra']['sitemap_url'] = $sitemap_url;
- $modules['sitemaps']['extra']['news_sitemap_url'] = $news_sitemap_url;
- } elseif ( 'sitemaps' == $slug ) {
- // It's a single module
- $modules['extra']['sitemap_url'] = $sitemap_url;
- $modules['extra']['news_sitemap_url'] = $news_sitemap_url;
- }
- return $modules;
- }
-
- /**
- * Remove 'validate_callback' item from options available for module.
- * Fetch current option value and add to array of module options.
- * Prepare values of module options that need special handling, like those saved in wpcom.
- *
- * @since 4.3.0
- *
- * @param string $module Module slug.
- * @return array
- */
- public static function prepare_options_for_response( $module = '' ) {
- $options = self::get_updateable_data_list( $module );
-
- if ( ! is_array( $options ) || empty( $options ) ) {
- return $options;
- }
-
- // Some modules need special treatment.
- switch ( $module ) {
-
- case 'monitor':
- // Status of user notifications
- $options['monitor_receive_notifications']['current_value'] = self::cast_value( self::get_remote_value( 'monitor', 'monitor_receive_notifications' ), $options['monitor_receive_notifications'] );
- break;
-
- case 'post-by-email':
- // Email address
- $options['post_by_email_address']['current_value'] = self::cast_value( self::get_remote_value( 'post-by-email', 'post_by_email_address' ), $options['post_by_email_address'] );
- break;
-
- case 'protect':
- // Protect
- $options['jetpack_protect_key']['current_value'] = get_site_option( 'jetpack_protect_key', false );
- if ( ! function_exists( 'jetpack_protect_format_whitelist' ) ) {
- include_once( JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php' );
- }
- $options['jetpack_protect_global_whitelist']['current_value'] = jetpack_protect_format_whitelist();
- break;
-
- case 'related-posts':
- // It's local, but it must be broken apart since it's saved as an array.
- $options = self::split_options( $options, Jetpack_Options::get_option( 'relatedposts' ) );
- break;
-
- case 'verification-tools':
- // It's local, but it must be broken apart since it's saved as an array.
- $options = self::split_options( $options, get_option( 'verification_services_codes' ) );
- break;
-
- case 'google-analytics':
- $wga = get_option( 'jetpack_wga' );
- $code = '';
- if ( is_array( $wga ) && array_key_exists( 'code', $wga ) ) {
- $code = $wga[ 'code' ];
- }
- $options[ 'google_analytics_tracking_id' ][ 'current_value' ] = $code;
- break;
-
- case 'sharedaddy':
- // It's local, but it must be broken apart since it's saved as an array.
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- break;
- }
- $sharer = new Sharing_Service();
- $options = self::split_options( $options, $sharer->get_global_options() );
- $options['sharing_services']['current_value'] = $sharer->get_blog_services();
- $other_sharedaddy_options = array( 'jetpack-twitter-cards-site-tag', 'sharedaddy_disable_resources', 'sharing_delete_service' );
- foreach ( $other_sharedaddy_options as $key ) {
- $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
- $current_value = get_option( $key, $default_value );
- $options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] );
- }
- break;
-
- case 'after-the-deadline':
- if ( ! function_exists( 'AtD_get_options' ) ) {
- include_once( JETPACK__PLUGIN_DIR . 'modules/after-the-deadline.php' );
- }
- $atd_options = array_merge( AtD_get_options( get_current_user_id(), 'AtD_options' ), AtD_get_options( get_current_user_id(), 'AtD_check_when' ) );
- unset( $atd_options['name'] );
- foreach ( $atd_options as $key => $value ) {
- $options[ $key ]['current_value'] = self::cast_value( $value, $options[ $key ] );
- }
- $atd_options = AtD_get_options( get_current_user_id(), 'AtD_guess_lang' );
- $options['guess_lang']['current_value'] = self::cast_value( isset( $atd_options['true'] ), $options[ 'guess_lang' ] );
- $options['ignored_phrases']['current_value'] = AtD_get_setting( get_current_user_id(), 'AtD_ignored_phrases' );
- unset( $options['unignore_phrase'] );
- break;
-
- case 'stats':
- // It's local, but it must be broken apart since it's saved as an array.
- if ( ! function_exists( 'stats_get_options' ) ) {
- include_once( JETPACK__PLUGIN_DIR . 'modules/stats.php' );
- }
- $options = self::split_options( $options, stats_get_options() );
- break;
- default:
- // These option are just stored as plain WordPress options.
- foreach ( $options as $key => $value ) {
- $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
- $current_value = get_option( $key, $default_value );
- $options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] );
- }
- }
- // At this point some options have current_value not set because they're options
- // that only get written on update, so we set current_value to the default one.
- foreach ( $options as $key => $value ) {
- // We don't need validate_callback in the response
- if ( isset( $options[ $key ]['validate_callback'] ) ) {
- unset( $options[ $key ]['validate_callback'] );
- }
- $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
- if ( ! array_key_exists( 'current_value', $options[ $key ] ) ) {
- $options[ $key ]['current_value'] = self::cast_value( $default_value, $options[ $key ] );
- }
- }
- return $options;
- }
-
- /**
- * Splits module options saved as arrays like relatedposts or verification_services_codes into separate options to be returned in the response.
- *
- * @since 4.3.0
- *
- * @param array $separate_options Array of options admitted by the module.
- * @param array $grouped_options Option saved as array to be splitted.
- * @param string $prefix Optional prefix for the separate option keys.
- *
- * @return array
- */
- public static function split_options( $separate_options, $grouped_options, $prefix = '' ) {
- if ( is_array( $grouped_options ) ) {
- foreach ( $grouped_options as $key => $value ) {
- $option_key = $prefix . $key;
- if ( isset( $separate_options[ $option_key ] ) ) {
- $separate_options[ $option_key ]['current_value'] = self::cast_value( $grouped_options[ $key ], $separate_options[ $option_key ] );
- }
- }
- }
- return $separate_options;
- }
-
- /**
- * Perform a casting to the value specified in the option definition.
- *
- * @since 4.3.0
- *
- * @param mixed $value Value to cast to the proper type.
- * @param array $definition Type to cast the value to.
- *
- * @return bool|float|int|string
- */
- public static function cast_value( $value, $definition ) {
- if ( $value === 'NULL' ) {
- return null;
- }
-
- if ( isset( $definition['type'] ) ) {
- switch ( $definition['type'] ) {
- case 'boolean':
- if ( 'true' === $value ) {
- return true;
- } elseif ( 'false' === $value ) {
- return false;
- }
- return (bool) $value;
- break;
-
- case 'integer':
- return (int) $value;
- break;
-
- case 'float':
- return (float) $value;
- break;
-
- case 'string':
- return (string) $value;
- break;
- }
- }
- return $value;
- }
-
- /**
- * Get a value not saved locally.
- *
- * @since 4.3.0
- *
- * @param string $module Module slug.
- * @param string $option Option name.
- *
- * @return bool Whether user is receiving notifications or not.
- */
- public static function get_remote_value( $module, $option ) {
-
- if ( in_array( $module, array( 'post-by-email' ), true ) ) {
- $option .= get_current_user_id();
- }
-
- // If option doesn't exist, 'does_not_exist' will be returned.
- $value = get_option( $option, 'does_not_exist' );
-
- // If option exists, just return it.
- if ( 'does_not_exist' !== $value ) {
- return $value;
- }
-
- // Only check a remote option if Jetpack is connected.
- if ( ! Jetpack::is_active() ) {
- return false;
- }
-
- // Do what is necessary for each module.
- switch ( $module ) {
- case 'monitor':
- // Load the class to use the method. If class can't be found, do nothing.
- if ( ! class_exists( 'Jetpack_Monitor' ) && ! include_once( Jetpack::get_module_path( $module ) ) ) {
- return false;
- }
- $value = Jetpack_Monitor::user_receives_notifications( false );
- break;
-
- case 'post-by-email':
- // Load the class to use the method. If class can't be found, do nothing.
- if ( ! class_exists( 'Jetpack_Post_By_Email' ) && ! include_once( Jetpack::get_module_path( $module ) ) ) {
- return false;
- }
- $post_by_email = new Jetpack_Post_By_Email();
- $value = $post_by_email->get_post_by_email_address();
- if ( $value === null ) {
- $value = 'NULL'; // sentinel value so it actually gets set
- }
- break;
- }
-
- // Normalize value to boolean.
- if ( is_wp_error( $value ) || is_null( $value ) ) {
- $value = false;
- }
-
- // Save option to use it next time.
- update_option( $option, $value );
-
- return $value;
- }
-
- /**
- * Get number of plugin updates available.
- *
- * @since 4.3.0
- *
- * @return mixed|WP_Error Number of plugin updates available. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function get_plugin_update_count() {
- $updates = wp_get_update_data();
- if ( isset( $updates['counts'] ) && isset( $updates['counts']['plugins'] ) ) {
- $count = $updates['counts']['plugins'];
- if ( 0 == $count ) {
- $response = array(
- 'code' => 'success',
- 'message' => esc_html__( 'All plugins are up-to-date. Keep up the good work!', 'jetpack' ),
- 'count' => 0,
- );
- } else {
- $response = array(
- 'code' => 'updates-available',
- 'message' => esc_html( sprintf( _n( '%s plugin need updating.', '%s plugins need updating.', $count, 'jetpack' ), $count ) ),
- 'count' => $count,
- );
- }
- return rest_ensure_response( $response );
- }
-
- return new WP_Error( 'not_found', esc_html__( 'Could not check updates for plugins on this site.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
-
- /**
- * Returns a list of all plugins in the site.
- *
- * @since 4.2.0
- * @uses get_plugins()
- *
- * @return array
- */
- private static function core_get_plugins() {
- if ( ! function_exists( 'get_plugins' ) ) {
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
- }
- /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
- $plugins = apply_filters( 'all_plugins', get_plugins() );
-
- if ( is_array( $plugins ) && ! empty( $plugins ) ) {
- foreach ( $plugins as $plugin_slug => $plugin_data ) {
- $plugins[ $plugin_slug ]['active'] = self::core_is_plugin_active( $plugin_slug );
- }
- return $plugins;
- }
-
- return array();
- }
-
- /**
- * Deprecated - Get third party plugin API keys.
- * @deprecated
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function get_service_api_key( $request ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::get_service_api_key' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::get_service_api_key( $request );
- }
-
- /**
- * Deprecated - Update third party plugin API keys.
- * @deprecated
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function update_service_api_key( $request ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::update_service_api_key' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::update_service_api_key( $request ) ;
- }
-
- /**
- * Deprecated - Delete a third party plugin API key.
- * @deprecated
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function delete_service_api_key( $request ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::delete_service_api_key' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::delete_service_api_key( $request );
- }
-
- /**
- * Deprecated - Validate the service provided in /service-api-keys/ endpoints.
- * To add a service to these endpoints, add the service name to $valid_services
- * and add '{service name}_api_key' to the non-compact return array in get_option_names(),
- * in class-jetpack-options.php
- * @deprecated
- *
- * @param string $service The service the API key is for.
- * @return string Returns the service name if valid, null if invalid.
- */
- public static function validate_service_api_service( $service = null ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_service' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_service( $service );
- }
-
- /**
- * Error response for invalid service API key requests with an invalid service.
- */
- public static function service_api_invalid_service_response() {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::service_api_invalid_service_response' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::service_api_invalid_service_response();
- }
-
- /**
- * Deprecated - Validate API Key
- * @deprecated
- *
- * @param string $key The API key to be validated.
- * @param string $service The service the API key is for.
- *
- */
- public static function validate_service_api_key( $key = null, $service = null ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_key' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_key( $key , $service );
- }
-
- /**
- * Deprecated - Validate Mapbox API key
- * Based loosely on https://github.com/mapbox/geocoding-example/blob/master/php/MapboxTest.php
- * @deprecated
- *
- * @param string $key The API key to be validated.
- */
- public static function validate_service_api_key_mapbox( $key ) {
- _deprecated_function( __METHOD__, 'jetpack-6.9.0', 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_key' );
- return WPCOM_REST_API_V2_Endpoint_Service_API_Keys::validate_service_api_key_mapbox( $key );
-
- }
-
- /**
- * Checks if the queried plugin is active.
- *
- * @since 4.2.0
- * @uses is_plugin_active()
- *
- * @return bool
- */
- private static function core_is_plugin_active( $plugin ) {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
- }
-
- return is_plugin_active( $plugin );
- }
-
- /**
- * Get plugins data in site.
- *
- * @since 4.2.0
- *
- * @return WP_REST_Response|WP_Error List of plugins in the site. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function get_plugins() {
- $plugins = self::core_get_plugins();
-
- if ( ! empty( $plugins ) ) {
- return rest_ensure_response( $plugins );
- }
-
- return new WP_Error( 'not_found', esc_html__( 'Unable to list plugins.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- /**
- * Get data about the queried plugin. Currently it only returns whether the plugin is active or not.
- *
- * @since 4.2.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- *
- * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function get_plugin( $request ) {
-
- $plugins = self::core_get_plugins();
-
- if ( empty( $plugins ) ) {
- return new WP_Error( 'no_plugins_found', esc_html__( 'This site has no plugins.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- $plugin = stripslashes( $request['plugin'] );
-
- if ( ! in_array( $plugin, array_keys( $plugins ) ) ) {
- return new WP_Error( 'plugin_not_found', esc_html( sprintf( __( 'Plugin %s is not installed.', 'jetpack' ), $plugin ) ), array( 'status' => 404 ) );
- }
-
- $plugin_data = $plugins[ $plugin ];
-
- $plugin_data['active'] = self::core_is_plugin_active( $plugin );
-
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Plugin found.', 'jetpack' ),
- 'data' => $plugin_data
- ) );
- }
-
-} // class end
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-automatic-install-skin.php b/plugins/jetpack/_inc/lib/class.jetpack-automatic-install-skin.php
deleted file mode 100644
index 228c6b2c..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-automatic-install-skin.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
-include_once ABSPATH . 'wp-admin/includes/file.php';
-
-/**
- * Allows us to capture that the site doesn't have proper file system access.
- * In order to update the plugin.
- */
-class Jetpack_Automatic_Install_Skin extends Automatic_Upgrader_Skin {
- /**
- * Stores the last error key;
- **/
- protected $main_error_code = 'install_error';
-
- /**
- * Stores the last error message.
- **/
- protected $main_error_message = 'An unknown error occurred during installation';
-
- /**
- * Overwrites the set_upgrader to be able to tell if we e ven have the ability to write to the files.
- *
- * @param WP_Upgrader $upgrader
- *
- */
- public function set_upgrader( &$upgrader ) {
- parent::set_upgrader( $upgrader );
-
- // Check if we even have permission to.
- $result = $upgrader->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
- if ( ! $result ) {
- // set the string here since they are not available just yet
- $upgrader->generic_strings();
- $this->feedback( 'fs_unavailable' );
- }
- }
-
- /**
- * Overwrites the error function
- */
- public function error( $error ) {
- if ( is_wp_error( $error ) ) {
- $this->feedback( $error );
- }
- }
-
- private function set_main_error_code( $code ) {
- // Don't set the process_failed as code since it is not that helpful unless we don't have one already set.
- $this->main_error_code = ( $code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $code );
- }
-
- private function set_main_error_message( $message, $code ) {
- // Don't set the process_failed as message since it is not that helpful unless we don't have one already set.
- $this->main_error_message = ( $code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $message );
- }
-
- public function get_main_error_code() {
- return $this->main_error_code;
- }
-
- public function get_main_error_message() {
- return $this->main_error_message;
- }
-
- /**
- * Overwrites the feedback function
- */
- public function feedback( $data ) {
-
- $current_error = null;
- if ( is_wp_error( $data ) ) {
- $this->set_main_error_code( $data->get_error_code() );
- $string = $data->get_error_message();
- } elseif ( is_array( $data ) ) {
- return;
- } else {
- $string = $data;
- }
-
- if ( ! empty( $this->upgrader->strings[$string] ) ) {
- $this->set_main_error_code( $string );
-
- $current_error = $string;
- $string = $this->upgrader->strings[$string];
- }
-
- if ( strpos( $string, '%' ) !== false ) {
- $args = func_get_args();
- $args = array_splice( $args, 1 );
- if ( ! empty( $args ) ) {
- $string = vsprintf( $string, $args );
- }
- }
-
- $string = trim( $string );
- $string = wp_kses(
- $string, array(
- 'a' => array(
- 'href' => true
- ),
- 'br' => true,
- 'em' => true,
- 'strong' => true,
- )
- );
-
- $this->set_main_error_message( $string, $current_error );
- $this->messages[] = $string;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-iframe-embed.php b/plugins/jetpack/_inc/lib/class.jetpack-iframe-embed.php
deleted file mode 100644
index 4445cb65..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-iframe-embed.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * Tweak the preview when rendered in an iframe
- */
-
-class Jetpack_Iframe_Embed {
- static function init() {
- if ( ! self::is_embedding_in_iframe() ) {
- return;
- }
-
- // Disable the admin bar
- if ( ! defined( 'IFRAME_REQUEST' ) ) {
- define( 'IFRAME_REQUEST', true );
- }
-
- // Prevent canonical redirects
- remove_filter( 'template_redirect', 'redirect_canonical' );
-
- add_action( 'wp_head', array( 'Jetpack_Iframe_Embed', 'noindex' ), 1 );
- add_action( 'wp_head', array( 'Jetpack_Iframe_Embed', 'base_target_blank' ), 1 );
-
- add_filter( 'shortcode_atts_video', array( 'Jetpack_Iframe_Embed', 'disable_autoplay' ) );
- add_filter( 'shortcode_atts_audio', array( 'Jetpack_Iframe_Embed', 'disable_autoplay' ) );
-
- if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
- wp_enqueue_script( 'jetpack-iframe-embed', WPMU_PLUGIN_URL . '/jetpack-iframe-embed/jetpack-iframe-embed.js', array( 'jquery' ) );
- } else {
- $ver = sprintf( '%s-%s', gmdate( 'oW' ), defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : '' );
- wp_enqueue_script( 'jetpack-iframe-embed', '//s0.wp.com/wp-content/mu-plugins/jetpack-iframe-embed/jetpack-iframe-embed.js', array( 'jquery' ), $ver );
- }
- wp_localize_script( 'jetpack-iframe-embed', '_previewSite', array( 'siteURL' => get_site_url() ) );
- }
-
- static function is_embedding_in_iframe() {
- return (
- self::has_iframe_get_param() && (
- self::has_preview_get_param() ||
- self::has_preview_theme_preview_param()
- )
- );
- }
-
- private static function has_iframe_get_param() {
- return isset( $_GET['iframe'] ) && $_GET['iframe'] === 'true';
- }
-
- private static function has_preview_get_param() {
- return isset( $_GET['preview'] ) && $_GET['preview'] === 'true';
- }
-
- private static function has_preview_theme_preview_param() {
- return isset( $_GET['theme_preview'] ) && $_GET['theme_preview'] === 'true';
- }
-
- /**
- * Disable `autoplay` shortcode attribute in context of an iframe
- * Added via `shortcode_atts_video` & `shortcode_atts_audio` in `init`
- *
- * @param array $atts The output array of shortcode attributes.
- *
- * @return array The output array of shortcode attributes.
- */
- static function disable_autoplay( $atts ) {
- return array_merge( $atts, array( 'autoplay' => false ) );
- }
-
- /**
- * We don't want search engines to index iframe previews
- * Added via `wp_head` action in `init`
- */
- static function noindex() {
- echo '<meta name="robots" content="noindex,nofollow" />';
- }
-
- /**
- * Make sure all links and forms open in a new window by default
- * (unless overridden on client-side by JS)
- * Added via `wp_head` action in `init`
- */
- static function base_target_blank() {
- echo '<base target="_blank" />';
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-keyring-service-helper.php b/plugins/jetpack/_inc/lib/class.jetpack-keyring-service-helper.php
deleted file mode 100644
index c8005ea1..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-keyring-service-helper.php
+++ /dev/null
@@ -1,204 +0,0 @@
-<?php
-
-class Jetpack_Keyring_Service_Helper {
- /**
- * @var Jetpack_Keyring_Service_Helper
- **/
- private static $instance = null;
-
- static function init() {
- if ( is_null( self::$instance ) ) {
- self::$instance = new Jetpack_Keyring_Service_Helper;
- }
-
- return self::$instance;
- }
-
- public static $SERVICES = array(
- 'facebook' => array(
- 'for' => 'publicize'
- ),
- 'twitter' => array(
- 'for' => 'publicize'
- ),
- 'linkedin' => array(
- 'for' => 'publicize'
- ),
- 'tumblr' => array(
- 'for' => 'publicize'
- ),
- 'path' => array(
- 'for' => 'publicize'
- ),
- 'google_plus' => array(
- 'for' => 'publicize'
- ),
- 'google_site_verification' => array(
- 'for' => 'other'
- )
- );
-
- private function __construct() {
- add_action( 'load-settings_page_sharing', array( __CLASS__, 'admin_page_load' ), 9 );
- }
-
- function get_services( $filter = 'all' ) {
- $services = array(
-
- );
-
- if ( 'all' == $filter ) {
- return $services;
- } else {
- $connected_services = array();
- foreach ( $services as $service => $empty ) {
- $connections = $this->get_connections( $service );
- if ( $connections ) {
- $connected_services[ $service ] = $connections;
- }
- }
- return $connected_services;
- }
- }
-
- /**
- * Gets a URL to the public-api actions. Works like WP's admin_url
- *
- * @param string $service Shortname of a specific service.
- *
- * @return URL to specific public-api process
- */
- // on WordPress.com this is/calls Keyring::admin_url
- static function api_url( $service = false, $params = array() ) {
- /**
- * Filters the API URL used to interact with WordPress.com.
- *
- * @since 2.0.0
- *
- * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL.
- */
- $url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
-
- if ( $service ) {
- $url = add_query_arg( array( 'service' => $service ), $url );
- }
-
- if ( count( $params ) ) {
- $url = add_query_arg( $params, $url );
- }
-
- return $url;
- }
-
- static function connect_url( $service_name, $for ) {
- return add_query_arg( array(
- 'action' => 'request',
- 'service' => $service_name,
- 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
- 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
- 'for' => $for,
- ), menu_page_url( 'sharing', false ) );
- }
-
- static function refresh_url( $service_name, $for ) {
- return add_query_arg( array(
- 'action' => 'request',
- 'service' => $service_name,
- 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
- 'refresh' => 1,
- 'for' => $for,
- 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
- ), admin_url( 'options-general.php?page=sharing' ) );
- }
-
- static function disconnect_url( $service_name, $id ) {
- return add_query_arg( array(
- 'action' => 'delete',
- 'service' => $service_name,
- 'id' => $id,
- 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
- 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
- ), menu_page_url( 'sharing', false ) );
- }
-
- static function admin_page_load() {
- if ( isset( $_GET['action'] ) ) {
- if ( isset( $_GET['service'] ) ) {
- $service_name = $_GET['service'];
- }
-
- switch ( $_GET['action'] ) {
-
- case 'request':
- check_admin_referer( 'keyring-request', 'kr_nonce' );
- check_admin_referer( "keyring-request-$service_name", 'nonce' );
-
- $verification = Jetpack::generate_secrets( 'publicize' );
- if ( ! $verification ) {
- $url = Jetpack::admin_url( 'jetpack#/settings' );
- wp_die( sprintf( __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack' ), $url ) );
-
- }
- $stats_options = get_option( 'stats_options' );
- $wpcom_blog_id = Jetpack_Options::get_option( 'id' );
- $wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
-
- $user = wp_get_current_user();
- $redirect = Jetpack_Keyring_Service_Helper::api_url( $service_name, urlencode_deep( array(
- 'action' => 'request',
- 'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ),
- 'for' => 'publicize',
- // required flag that says this connection is intended for publicize
- 'siteurl' => site_url(),
- 'state' => $user->ID,
- 'blog_id' => $wpcom_blog_id,
- 'secret_1' => $verification['secret_1'],
- 'secret_2' => $verification['secret_2'],
- 'eol' => $verification['exp'],
- ) ) );
- wp_redirect( $redirect );
- exit;
- break;
-
- case 'completed':
- Jetpack::load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client();
- $xml->query( 'jetpack.fetchPublicizeConnections' );
-
- if ( ! $xml->isError() ) {
- $response = $xml->getResponse();
- Jetpack_Options::update_option( 'publicize_connections', $response );
- }
-
- break;
-
- case 'delete':
- $id = $_GET['id'];
-
- check_admin_referer( 'keyring-request', 'kr_nonce' );
- check_admin_referer( "keyring-request-$service_name", 'nonce' );
-
- Jetpack_Keyring_Service_Helper::disconnect( $service_name, $id );
-
- do_action( 'connection_disconnected', $service_name );
- break;
- }
- }
- }
-
- /**
- * Remove a Publicize connection
- */
- static function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) {
- Jetpack::load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client();
- $xml->query( 'jetpack.deletePublicizeConnection', $connection_id );
-
- if ( ! $xml->isError() ) {
- Jetpack_Options::update_option( 'publicize_connections', $xml->getResponse() );
- } else {
- return false;
- }
- }
-
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-password-checker.php b/plugins/jetpack/_inc/lib/class.jetpack-password-checker.php
deleted file mode 100644
index 14de6053..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-password-checker.php
+++ /dev/null
@@ -1,1288 +0,0 @@
-<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
-/**
- * The password strength checker.
- *
- * @package jetpack
- */
-
-/**
- * Checks passwords strength.
- */
-class Jetpack_Password_Checker {
-
- /**
- * Minimum entropy bits a password should contain. 36 bits of entropy is considered
- * to be a reasonable password, 28 stands for a weak one.
- *
- * @const Integer
- */
- const MINIMUM_BITS = 28;
-
- /**
- * Currently tested password.
- *
- * @var String
- */
- public $password = '';
-
- /**
- * Test results array.
- *
- * @var Array
- */
- public $test_results = '';
-
- /**
- * Current password score.
- *
- * @var Integer
- */
- public $score = 0;
-
- /**
- * Current multiplier affecting the score.
- *
- * @var Integer
- */
- public $multiplier = 4;
-
- /**
- * A common password blacklist, which on match will immediately disqualify the password.
- *
- * @var Array
- */
- public $common_passwords = array();
-
- /**
- * Minimum password length setting.
- *
- * @var Integer
- */
- public $min_password_length = 6;
-
- /**
- * User defined strings that passwords need to be tested for a match against.
- *
- * @var Array
- */
- private $user_strings_to_test = array();
-
- /**
- * The user object for whom the password is being tested.
- *
- * @var WP_User
- */
- protected $user;
-
- /**
- * The user identifier for whom the password is being tested, used if there's no user object.
- *
- * @var WP_User
- */
- protected $user_id;
-
- /**
- * Creates an instance of the password checker class for the specified user, or
- * defaults to the currently logged in user.
- *
- * @param Mixed $user can be an integer ID, or a WP_User object.
- */
- public function __construct( $user = null ) {
-
- /**
- * Filters Jetpack's password strength enforcement settings. You can supply your own passwords
- * that should not be used for authenticating in addition to weak and easy to guess strings for
- * each user. For example, you can add passwords from known password databases to avoid compromised
- * password usage.
- *
- * @since 7.2.0
- *
- * @param Array $restricted_passwords strings that are forbidden for use as passwords.
- */
- $this->common_passwords = apply_filters( 'jetpack_password_checker_restricted_strings', array() );
-
- if ( is_null( $user ) ) {
- $this->user_id = get_current_user_id();
- } elseif ( is_object( $user ) && isset( $user->ID ) ) {
-
- // Existing user, using their ID.
- $this->user_id = $user->ID;
-
- } elseif ( is_object( $user ) ) {
-
- // Newly created user, using existing data.
- $this->user = $user;
- $this->user_id = 'new_user';
-
- } else {
- $this->user_id = $user;
- }
- $this->min_password_length = apply_filters( 'better_password_min_length', $this->min_password_length );
- }
-
- /**
- * Run tests against a password.
- *
- * @param String $password the tested string.
- * @param Boolean $required_only only test against required conditions, defaults to false.
- * @return Array $results an array containing failed and passed test results.
- */
- public function test( $password, $required_only = false ) {
-
- $this->password = $password;
- $results = $this->run_tests( $this->list_tests(), $required_only );
-
- // If we've failed on the required tests, return now.
- if ( ! empty( $results['failed'] ) ) {
- return array(
- 'passed' => false,
- 'test_results' => $results,
- );
- }
-
- /**
- * Filters Jetpack's password strength enforcement settings. You can modify the minimum
- * entropy bits requirement using this filter.
- *
- * @since 7.2.0
- *
- * @param Array $minimum_entropy_bits minimum entropy bits requirement.
- */
- $bits = apply_filters( 'jetpack_password_checker_minimum_entropy_bits', self::MINIMUM_BITS );
- $entropy_bits = $this->calculate_entropy_bits( $this->password );
-
- // If we have failed the entropy bits test, run the regex tests so we can suggest improvements.
- if ( $entropy_bits < $bits ) {
- $results['failed']['entropy_bits'] = $entropy_bits;
- $results = array_merge(
- $results,
- $this->run_tests( $this->list_tests( 'preg_match' ), false )
- );
- }
-
- return( array(
- 'passed' => empty( $results['failed'] ),
- 'test_results' => $results,
- ) );
- }
-
- /**
- * Run the tests using the currently set up object values.
- *
- * @param Array $tests tests to run.
- * @param Boolean $required_only whether to run only required tests.
- * @return Array test results.
- */
- protected function run_tests( $tests, $required_only = false ) {
-
- $results = array(
- 'passed' => array(),
- 'failed' => array(),
- );
-
- foreach ( $tests as $test_type => $section_tests ) {
- foreach ( $section_tests as $test_name => $test_data ) {
-
- // Skip non-required tests if required_only param is set.
- if ( $required_only && ! $test_data['required'] ) {
- continue;
- }
-
- $test_function = 'test_' . $test_type;
-
- $result = call_user_func( array( $this, $test_function ), $test_data );
-
- if ( $result ) {
- $results['passed'][] = array( 'test_name' => $test_name );
- } else {
- $results['failed'][] = array(
- 'test_name' => $test_name,
- 'explanation' => $test_data['error'],
- );
-
- if ( isset( $test_data['fail_immediately'] ) ) {
- return $results;
- }
- }
- }
- }
-
- return $results;
- }
-
- /**
- * Returns a list of tests that need to be run on password strings.
- *
- * @param Array $sections only return specific sections with the passed keys, defaults to all.
- * @return Array test descriptions.
- */
- protected function list_tests( $sections = false ) {
- // Note: these should be in order of priority.
- $tests = array(
- 'preg_match' => array(
- 'no_backslashes' => array(
- 'pattern' => '^[^\\\\]*$',
- 'error' => __( 'Passwords may not contain the character "\".', 'jetpack' ),
- 'required' => true,
- 'fail_immediately' => true,
- ),
- 'minimum_length' => array(
- 'pattern' => '^.{' . $this->min_password_length . ',}',
- /* translators: %d is a number of characters in the password. */
- 'error' => sprintf( __( 'Password must be at least %d characters.', 'jetpack' ), $this->min_password_length ),
- 'required' => true,
- 'fail_immediately' => true,
- ),
- 'has_mixed_case' => array(
- 'pattern' => '([a-z].*?[A-Z]|[A-Z].*?[a-z])',
- 'error' => __( 'This password is too easy to guess: you can improve it by adding additional uppercase letters, lowercase letters, or numbers.', 'jetpack' ),
- 'trim' => true,
- 'required' => false,
- ),
- 'has_digit' => array(
- 'pattern' => '\d',
- 'error' => __( 'This password is too easy to guess: you can improve it by mixing both letters and numbers.', 'jetpack' ),
- 'trim' => false,
- 'required' => false,
- ),
- 'has_special_char' => array(
- 'pattern' => '[^a-zA-Z\d]',
- 'error' => __( 'This password is too easy to guess: you can improve it by including special characters such as !#=?*&.', 'jetpack' ),
- 'required' => false,
- ),
- ),
- 'compare_to_list' => array(
- 'not_a_common_password' => array(
- 'list_callback' => 'get_common_passwords',
- 'compare_callback' => 'negative_in_array',
- 'error' => __( 'This is a very common password. Choose something that will be harder for others to guess.', 'jetpack' ),
- 'required' => true,
- ),
- 'not_same_as_other_user_data' => array(
- 'list_callback' => 'get_other_user_data',
- 'compare_callback' => 'test_not_same_as_other_user_data',
- 'error' => __( 'Your password is too weak: Looks like you\'re including easy to guess information about yourself. Try something a little more unique.', 'jetpack' ),
- 'required' => true,
- ),
- ),
- );
-
- /**
- * Filters Jetpack's password strength enforcement settings. You can determine the tests run
- * and their order based on whatever criteria you wish to specify.
- *
- * @since 7.2.0
- *
- * @param Array $minimum_entropy_bits minimum entropy bits requirement.
- */
- $tests = apply_filters( 'jetpack_password_checker_tests', $tests );
-
- if ( ! $sections ) {
- return $tests;
- }
-
- $sections = (array) $sections;
- return array_intersect_key( $tests, array_flip( $sections ) );
- }
-
- /**
- * Provides the regular expression tester functionality.
- *
- * @param Array $test_data the current test data.
- * @return Boolean does the test pass?
- */
- protected function test_preg_match( $test_data ) {
- $password = stripslashes( $this->password );
-
- if ( isset( $test_data['trim'] ) ) {
- $password = substr( $password, 1, -1 );
- }
-
- if ( ! preg_match( '/' . $test_data['pattern'] . '/u', $password ) ) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Provides the comparison tester functionality.
- *
- * @param Array $test_data the current test data.
- * @return Boolean does the test pass?
- */
- protected function test_compare_to_list( $test_data ) {
- $list_callback = $test_data['list_callback'];
- $compare_callback = $test_data['compare_callback'];
-
- if (
- ! is_callable( array( $this, $list_callback ) )
- || ! is_callable( array( $this, $compare_callback ) )
- ) {
- return false;
- }
-
- $list = call_user_func( array( $this, $list_callback ) );
- if ( empty( $list ) ) {
- return true;
- }
-
- return call_user_func( array( $this, $compare_callback ), $this->password, $list );
- }
-
- /**
- * Getter for the common password list.
- *
- * @return Array common passwords.
- */
- protected function get_common_passwords() {
- return $this->common_passwords;
- }
-
- /**
- * Returns the widely known user data that can not be used in the password to avoid
- * predictable strings.
- *
- * @return Array user data.
- */
- protected function get_other_user_data() {
-
- if ( ! isset( $this->user ) ) {
- $user_data = get_userdata( $this->user_id );
-
- $first_name = get_user_meta( $user_data->ID, 'first_name', true );
- $last_name = get_user_meta( $user_data->ID, 'last_name', true );
- $nickname = get_user_meta( $user_data->ID, 'nickname', true );
-
- $this->add_user_strings_to_test( $nickname );
- $this->add_user_strings_to_test( $user_data->user_nicename );
- $this->add_user_strings_to_test( $user_data->display_name );
- } else {
- $user_data = $this->user;
-
- $first_name = $user_data->first_name;
- $last_name = $user_data->last_name;
- }
- $email_username = substr( $user_data->user_email, 0, strpos( $user_data->user_email, '@' ) );
-
- $this->add_user_strings_to_test( $user_data->user_email );
- $this->add_user_strings_to_test( $email_username, '.' );
- $this->add_user_strings_to_test( $first_name );
- $this->add_user_strings_to_test( $last_name );
-
- return $this->user_strings_to_test;
- }
-
- /**
- * Compare the password for matches with known user data.
- *
- * @param String $password the string to be tested.
- * @param Array $strings_to_test known user data.
- * @return Boolean does the test pass?
- */
- protected function test_not_same_as_other_user_data( $password, $strings_to_test ) {
- $password_lowercase = strtolower( $password );
- foreach ( array_unique( $strings_to_test ) as $string ) {
- if ( empty( $string ) ) {
- continue;
- }
-
- $string = strtolower( $string );
- $string_reversed = strrev( $string );
-
- if ( $password_lowercase === $string || $password_lowercase === $string_reversed ) {
- return false;
- }
-
- // Also check for the string or reversed string with any numbers just stuck to the end to catch things like bob123 as passwords.
- if (
- preg_match( '/^' . preg_quote( $string, '/' ) . '\d+$/', $password_lowercase )
- || preg_match( '/^' . preg_quote( $string_reversed, '/' ) . '\d+$/', $password_lowercase )
- ) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * A shorthand for the not in array construct.
- *
- * @param Mixed $needle the needle.
- * @param Array $haystack the haystack.
- * @return is the needle not in the haystack?
- */
- protected function negative_in_array( $needle, $haystack ) {
- if ( in_array( $needle, $haystack, true ) ) {
- return false;
- }
-
- return true;
- }
-
- /**
- * A helper function used to break a single string into its constituents so
- * that both the full string and its constituents and any variants thereof
- * can be tested against the password.
- *
- * @param String $string the string to be broken down.
- * @param String $explode_delimiter delimiter.
- * @return NULL|Array array of fragments, or NULL on empty string.
- */
- protected function add_user_strings_to_test( $string, $explode_delimiter = ' ' ) {
-
- // Don't check against empty strings.
- if ( empty( $string ) ) {
- return;
- }
-
- $strings = explode( $explode_delimiter, $string );
-
- // Remove any non alpha numeric characters from the strings to check against.
- foreach ( $strings as $key => $_string ) {
- $strings[ $key ] = preg_replace( '/[^a-zA-Z0-9]/', '', $_string );
- }
-
- // Check the original too.
- $strings[] = $string;
-
- // Check the original minus non alpha numeric characters.
- $strings[] = preg_replace( '/[^a-zA-Z0-9]/', '', $string );
-
- // Remove any empty strings.
- $strings = array_filter( $strings );
- $this->user_strings_to_test = array_merge( $this->user_strings_to_test, $strings );
- }
-
- /**
- * Return a character set size that is used in the string.
- *
- * @param String $password the password.
- * @return Integer number of different character sets in use.
- */
- protected function get_charset_size( $password ) {
- $size = 0;
-
- // Lowercase a-z.
- if ( preg_match( '/[a-z]/', $password ) ) {
- $size += 26;
- }
-
- // Uppercase A-Z.
- if ( preg_match( '/[A-Z]/', substr( $password, 1, -1 ) ) ) {
- $size += 26;
- }
-
- // Digits.
- if ( preg_match( '/\d/', substr( $password, 1, -1 ) ) ) {
- $size += 10;
- }
-
- // Over digits symbols.
- if ( preg_match( '/[!|@|#|$|%|^|&|*|(|)]/', $password ) ) {
- $size += 10;
- }
-
- // Other symbols.
- if ( preg_match( '#[`|~|-|_|=|+|\[|{|\]|}|\\|\|;:\'",<\.>/\?]#', $password ) ) {
- $size += 20;
- }
-
- // Spaces.
- if ( strpos( $password, ' ' ) ) {
- $size++;
- }
-
- return $size;
- }
-
- /**
- * Shorthand for getting a character index.
- *
- * @param String $char character.
- * @return Integer the character code.
- */
- protected function get_char_index( $char ) {
- $char = strtolower( $char[0] );
- if ( $char < 'a' || $char > 'z' ) {
- return 0;
- } else {
- return ord( $char[0] ) - ord( 'a' ) + 1;
- }
- }
-
- /**
- * This is the password strength calculation algorithm, based on the formula H = L(logN/log2).
- *
- * H = Entropy
- * L = String length (the for iterator)
- * N = Our charset size, via get_charset_size()
- *
- * @see http://en.wikipedia.org/wiki/Password_strength#Random_passwords
- *
- * On top of the base formula, we're also multiplying the bits of entropy for every char
- * by 1 - (the probabily of it following the previous char)
- * i.e.: the probablity of U following Q is ~0.84. If our password contains this pair of characters,
- * the u char will only add ( 0.16^2 * charset_score ) to our total of entropy bits.
- *
- * @param String $password the password.
- */
- protected function calculate_entropy_bits( $password ) {
- $bits = 0;
- $charset_score = log( $this->get_charset_size( $password ) ) / log( 2 );
-
- $aidx = $this->get_char_index( $password[0] );
- $length = strlen( $password );
-
- for ( $b = 1; $b < $length; $b++ ) {
- $bidx = $this->get_char_index( $password[ $b ] );
-
- // 27 = number of chars in the index (a-z,' ').
- $c = 1.0 - $this->frequency_table[ $aidx * 27 + $bidx ];
- $bits += $charset_score * $c * $c;
-
- // Move on to next pair.
- $aidx = $bidx;
- }
-
- return $bits;
- }
-
- /**
- * A frequency table of character pairs, starting with ' ' then ' a', ' b' [...] , 'a ', 'aa' etc.
- *
- * @see http://rumkin.com/tools/password/passchk.php
- * @var Array
- */
- public $frequency_table = array(
- 0.23653710453418866,
- 0.04577693541332556,
- 0.03449832337075375,
- 0.042918209651552706,
- 0.037390873305146524,
- 0.028509112115468728,
- 0.02350896632162123,
- 0.022188657238664526,
- 0.028429800262428927,
- 0.04357019973757107,
- 0.00913602565971716,
- 0.03223093745443942,
- 0.02235311269864412,
- 0.04438081352966905,
- 0.04512377897652719,
- 0.020055401662049863,
- 0.055903192885260244,
- 0.0024388394809739026,
- 0.035207464644991984,
- 0.07355941099285611,
- 0.036905671380667734,
- 0.026134421927394666,
- 0.023787724158040528,
- 0.011352092141711621,
- 0.0032354570637119114,
- 0.005986878553725033,
- 0.008861933226417843,
- 0.11511532293337222,
- 0.027556203528211108,
- 0.024331243621519172,
- 0.039266365359381834,
- 0.031599941682461,
- 0.014403265782183991,
- 0.015480973902901297,
- 0.027770812071730572,
- 0.00942761335471643,
- 0.039872867764980315,
- 0.0078122175244204695,
- 0.02808456043154979,
- 0.08429100451960927,
- 0.04688963405744277,
- 0.13831170724595424,
- 0.002540311998833649,
- 0.025211838460416972,
- 0.001543082081936142,
- 0.09519638431258201,
- 0.061845750109345385,
- 0.08907071001603732,
- 0.02137571074500656,
- 0.027093162268552268,
- 0.005521504592506197,
- 0.003023181221752442,
- 0.007086747339262283,
- 0.010262720513194342,
- 0.08785070710016038,
- 0.14617757690625455,
- 0.03417291150313457,
- 0.0059635515381250915,
- 0.006146668610584633,
- 0.195202799241872,
- 0.002774748505613063,
- 0.004715556203528212,
- 0.0044776206444088066,
- 0.11205481848665985,
- 0.005654468581425864,
- 0.0028820527773727946,
- 0.07383000437381543,
- 0.005516839189386207,
- 0.006496573844583759,
- 0.09843067502551392,
- 0.0027140982650532145,
- 0.0006893133109782768,
- 0.08425368129464937,
- 0.021325557661466685,
- 0.006493074792243767,
- 0.07023414491908442,
- 0.002077270739174807,
- 0.0024633328473538415,
- 0.0007744569179180639,
- 0.015413325557661468,
- 0.0011990086018370024,
- 0.13162851727657093,
- 0.10115993585070711,
- 0.0026989357049132527,
- 0.03319317684793702,
- 0.002946202070272634,
- 0.0783216212275842,
- 0.0018358361277154103,
- 0.00258813238081353,
- 0.2141688292754046,
- 0.09853681294649366,
- 0.0032482869222918796,
- 0.04359352675317102,
- 0.01993526753171016,
- 0.0036880011663507797,
- 0.008011663507799971,
- 0.12014696019827964,
- 0.0029846916460125384,
- 0.0017553579238956116,
- 0.029470185158186325,
- 0.010413179763813967,
- 0.030699518880303252,
- 0.03508499781309229,
- 0.002021285901734947,
- 0.0010613792097973467,
- 0.0005295232541186761,
- 0.009677212421635807,
- 0.010585799679253535,
- 0.17101734946785244,
- 0.07968625164018078,
- 0.007839043592360402,
- 0.005438693687126403,
- 0.0183606939787141,
- 0.2732701559994168,
- 0.004953491762647616,
- 0.007259367254701851,
- 0.008104971570199739,
- 0.13274588132380813,
- 0.004210526315789474,
- 0.004997813092287506,
- 0.017006560723137484,
- 0.007442484327161393,
- 0.016789619478058026,
- 0.08477737279486806,
- 0.005106283714827234,
- 0.0005026971861787433,
- 0.04040355736987899,
- 0.037535500801866156,
- 0.00885960052485785,
- 0.0336410555474559,
- 0.007066919376002332,
- 0.005344219273946639,
- 0.0006333284735384167,
- 0.010684939495553289,
- 0.0063064586674442345,
- 0.15386849394955532,
- 0.015049424114302375,
- 0.012162705933809595,
- 0.020425134859308938,
- 0.037366379938766583,
- 0.02157165767604607,
- 0.009373961218836564,
- 0.0173214754337367,
- 0.009616562181075958,
- 0.029522670943286193,
- 0.010154249890654615,
- 0.018600962239393497,
- 0.06362210234728094,
- 0.03157078291296107,
- 0.151603440734801,
- 0.0062329785683044175,
- 0.014775331681003062,
- 0.0020854351946347867,
- 0.1826342032366234,
- 0.0878017203674005,
- 0.054190989940224525,
- 0.010329202507654177,
- 0.012763376585508092,
- 0.0064872430383437815,
- 0.006381105117364048,
- 0.005388540603586529,
- 0.0090800408222773,
- 0.09611196967487973,
- 0.09940691062837148,
- 0.01033969966467415,
- 0.004034407348009914,
- 0.008826942703017933,
- 0.11474675608689314,
- 0.07132584924916169,
- 0.012388977985129028,
- 0.005435194634786413,
- 0.1417174515235457,
- 0.0037066627788307337,
- 0.0045802595130485495,
- 0.060800699810468,
- 0.005341886572386646,
- 0.005683627350925791,
- 0.12434932205860913,
- 0.004596588423968508,
- 0.0007534626038781163,
- 0.07107041842834232,
- 0.022361277154104096,
- 0.04784720804782038,
- 0.06277533168100306,
- 0.003441901151771395,
- 0.005828254847645429,
- 0.0009669047966175828,
- 0.009470768333576322,
- 0.002077270739174807,
- 0.12797667298440007,
- 0.08797783933518005,
- 0.005388540603586529,
- 0.0024913252660737715,
- 0.007550954949701123,
- 0.2786866890217233,
- 0.002509986878553725,
- 0.029002478495407494,
- 0.0303204548768042,
- 0.07576614666861058,
- 0.00246799825047383,
- 0.00592389561160519,
- 0.039574281965301064,
- 0.00706808572678233,
- 0.03304505029887739,
- 0.05474150750838315,
- 0.0028633911648928414,
- 0.0005073625892987316,
- 0.07293541332555767,
- 0.053528502697186175,
- 0.022566554891383584,
- 0.038151334013704616,
- 0.002716430966613209,
- 0.005049132526607377,
- 0.0009902318122175246,
- 0.008997229916897508,
- 0.0011861787432570347,
- 0.1666377022889634,
- 0.14414462749671964,
- 0.003374252806531564,
- 0.005169266656947077,
- 0.008468873013558828,
- 0.16337541915731155,
- 0.002873888321912815,
- 0.004305000728969237,
- 0.0031141565825922144,
- 0.1241172182533897,
- 0.0052800699810468,
- 0.008969237498177577,
- 0.024094474413179766,
- 0.017029887738737422,
- 0.01722700102055693,
- 0.10618457501093455,
- 0.006147834961364631,
- 0.0008269427030179326,
- 0.03303571949263741,
- 0.024188948826359528,
- 0.05213937891820965,
- 0.04505846333284735,
- 0.0035270447587111824,
- 0.006799825047383001,
- 0.0008199445983379502,
- 0.02206735675754483,
- 0.001010059775477475,
- 0.11971191135734072,
- 0.04656538854060359,
- 0.011243621519171892,
- 0.06513019390581717,
- 0.032375564951159064,
- 0.06347047674588133,
- 0.013678961947805804,
- 0.03309870243475726,
- 0.006982942119842543,
- 0.009726199154395685,
- 0.010121592068814697,
- 0.032514360693978714,
- 0.04986032949409535,
- 0.039734072022160664,
- 0.15690683773144773,
- 0.03949963551538125,
- 0.014790494241143023,
- 0.002722262720513194,
- 0.02614375273363464,
- 0.10753637556495116,
- 0.06764834523983088,
- 0.006221315060504448,
- 0.021317393206006705,
- 0.0030826651115322934,
- 0.002399183554454002,
- 0.0019069835252952323,
- 0.015595276279341012,
- 0.0925126111678087,
- 0.18437906400349907,
- 0.006538562472663654,
- 0.008719638431258201,
- 0.02116693395538708,
- 0.18241376293920394,
- 0.007290858725761773,
- 0.005976381396705059,
- 0.005629975215045925,
- 0.09721300481119698,
- 0.004810030616707975,
- 0.024303251202799244,
- 0.012954658113427612,
- 0.011057005394372358,
- 0.02733459688001166,
- 0.10135121737862662,
- 0.012016912086309959,
- 0.001055547455897361,
- 0.009027555037177431,
- 0.07162326869806095,
- 0.01007143898527482,
- 0.07297623560285756,
- 0.006741507508383147,
- 0.0036891675171307776,
- 0.0008409389123778977,
- 0.011272780288671819,
- 0.007020265344802449,
- 0.1030389269572824,
- 0.15350809155853623,
- 0.004232686980609419,
- 0.004353987461729115,
- 0.0023385333138941536,
- 0.14450386353695874,
- 0.002546143752733635,
- 0.0024470039364338824,
- 0.01200758128006998,
- 0.0981227584195947,
- 0.003161976964572095,
- 0.040695145064878264,
- 0.03460446129173349,
- 0.003908441463770229,
- 0.01598483743986004,
- 0.13107216795451232,
- 0.003129319142732177,
- 0.00032307916605919226,
- 0.04050386353695874,
- 0.05452689896486368,
- 0.03589677795597026,
- 0.07087097244496282,
- 0.006143169558244642,
- 0.008684647907858289,
- 0.0004607085580988482,
- 0.022010205569324977,
- 0.0009097536083977258,
- 0.07328765126111678,
- 0.14751421490013122,
- 0.008015162560139961,
- 0.006601545414783497,
- 0.025279486805656802,
- 0.1682449336637994,
- 0.008313748359819215,
- 0.007010934538562473,
- 0.005886572386645284,
- 0.16889575739903775,
- 0.004123050007289692,
- 0.011925936725470185,
- 0.10007289692374982,
- 0.013380376148126549,
- 0.009021723283277445,
- 0.08650823735238372,
- 0.007756232686980609,
- 0.0007243038343781893,
- 0.0026791077416533026,
- 0.02797492345823006,
- 0.032384895757399036,
- 0.04187432570345531,
- 0.00882461000145794,
- 0.0032401224668318998,
- 0.00033357632307916605,
- 0.027878116343490307,
- 0.0022277299897944304,
- 0.14333518005540166,
- 0.1725534334451086,
- 0.02781629975215046,
- 0.006909462020702727,
- 0.005264907420906838,
- 0.16661437527336345,
- 0.004325995043009185,
- 0.003334596880011664,
- 0.005312727802886718,
- 0.14024668318996938,
- 0.0013261408368566844,
- 0.003504884093891238,
- 0.006375273363464061,
- 0.04964922000291588,
- 0.008290421344219274,
- 0.09536783787724158,
- 0.05394372357486515,
- 0.005505175681586237,
- 0.005339553870826651,
- 0.01782067356757545,
- 0.006710016037323225,
- 0.05105933809593235,
- 0.002983525295232541,
- 0.002940370316372649,
- 0.0004548768041988629,
- 0.01208456043154979,
- 0.000915585362297711,
- 0.20146260387811635,
- 0.067196967487972,
- 0.006158332118384605,
- 0.025438110511736407,
- 0.07753783350342616,
- 0.1273876658405015,
- 0.009337804344656656,
- 0.07683452398308792,
- 0.0070412596588423975,
- 0.08747164309666132,
- 0.0038827817466102928,
- 0.018116926665694706,
- 0.005017641055547455,
- 0.004567429654468581,
- 0.028277008310249308,
- 0.05271555620352821,
- 0.004394809739029013,
- 0.0013343052923166642,
- 0.00411605190260971,
- 0.059621519171890944,
- 0.09073859163143316,
- 0.01446858142586383,
- 0.006770666277883074,
- 0.003425572240851436,
- 0.0004455459979588861,
- 0.010401516256013998,
- 0.005825922146085436,
- 0.10833882490158916,
- 0.007584779122321038,
- 0.016903921854497742,
- 0.02719580113719201,
- 0.0304814112844438,
- 0.02206385770520484,
- 0.013064295086747339,
- 0.02696369733197259,
- 0.009581571657676046,
- 0.026761918647033093,
- 0.006510570053943724,
- 0.021941390873305145,
- 0.07042659279778393,
- 0.05437410701268406,
- 0.1425175681586237,
- 0.027802303542790494,
- 0.037690625455605774,
- 0.0019606356611750987,
- 0.1095623268698061,
- 0.06157748942994606,
- 0.044618749088788455,
- 0.04955124653739612,
- 0.03608689313310978,
- 0.018381688292754043,
- 0.003404577926811489,
- 0.015036594255722409,
- 0.009600233270156,
- 0.10794693103951014,
- 0.12447528794284882,
- 0.0031981338387520046,
- 0.0074716430966613205,
- 0.003202799241871993,
- 0.13437643971424407,
- 0.006655197550663361,
- 0.0036693395538708266,
- 0.049338970695436656,
- 0.09486863974340283,
- 0.0015990669193760023,
- 0.0026604461291733486,
- 0.051775477474850555,
- 0.0041347135150896636,
- 0.005450357194926374,
- 0.12030325120279925,
- 0.04581309228750547,
- 0.0004537104534188657,
- 0.12425601399620935,
- 0.025981629975215047,
- 0.023926519900860182,
- 0.04423385333138941,
- 0.0017950138504155123,
- 0.002661612479953346,
- 0.0006333284735384167,
- 0.008449045050298877,
- 0.000653156436798367,
- 0.04816678816153958,
- 0.008625164018078437,
- 0.0039037760606502403,
- 0.005228750546726928,
- 0.004531272780288672,
- 0.0056672984400058316,
- 0.00359585945473101,
- 0.0032179618020119548,
- 0.0038093016474704767,
- 0.011452398308791368,
- 0.002519317684793702,
- 0.00280390727511299,
- 0.005572824026826068,
- 0.004554599795888614,
- 0.004531272780288672,
- 0.0035841959469310393,
- 0.004400641492928998,
- 0.0036670068523108326,
- 0.004839189386207902,
- 0.006258638285464354,
- 0.004897506925207757,
- 0.840776789619478,
- 0.004968654322787578,
- 0.002886718180492783,
- 0.0019757982213150604,
- 0.0018568304417553576,
- 0.001691208630995772,
- 0.09009243329931477,
- 0.14030150167662925,
- 0.013242746756086894,
- 0.013746610293045632,
- 0.027342761335471644,
- 0.16938912377897652,
- 0.006607377168683481,
- 0.01661933226417845,
- 0.008173786266219566,
- 0.13297448607668758,
- 0.0034675608689313307,
- 0.016641492928998396,
- 0.011722991689750693,
- 0.021493512173786266,
- 0.03430820819361423,
- 0.10099548039072752,
- 0.00873596734217816,
- 0.0018323370753754193,
- 0.020103222044029742,
- 0.047197550663362,
- 0.040833940807697915,
- 0.03361189677795597,
- 0.010844729552412887,
- 0.005544831608106138,
- 0.0007522962530981193,
- 0.01525120279924187,
- 0.00815512465373961,
- 0.2109648636827526,
- 0.058258055110074355,
- 0.007181221752442048,
- 0.043560868931331105,
- 0.004058900714389853,
- 0.10618107595859454,
- 0.0062399766729844,
- 0.004835690333867911,
- 0.02679224376731302,
- 0.08414637702288964,
- 0.0030698352529523252,
- 0.03637498177576906,
- 0.01592885260242018,
- 0.017413617145356466,
- 0.008430383437818923,
- 0.037231083248286924,
- 0.03290275550371775,
- 0.007538125091121154,
- 0.004500947660008748,
- 0.05932409972299169,
- 0.16006764834523984,
- 0.03309636973319726,
- 0.007766729844000583,
- 0.005225251494386936,
- 0.0006321621227584196,
- 0.012989648636827526,
- 0.005274238227146815,
- 0.1254503571949264,
- 0.12852719055255868,
- 0.0035433736696311416,
- 0.005203090829566993,
- 0.0019314768916751715,
- 0.20520775623268697,
- 0.002509986878553725,
- 0.00343606939787141,
- 0.027138649948972155,
- 0.13926578218399185,
- 0.004565096952908587,
- 0.005614812654905963,
- 0.00874413179763814,
- 0.004109053797929727,
- 0.008300918501239247,
- 0.08270943286193323,
- 0.002912377897652719,
- 0.0037066627788307337,
- 0.06909578655780726,
- 0.03242805073625893,
- 0.05237614812654906,
- 0.04723487388832191,
- 0.0038991106575302524,
- 0.006299460562764251,
- 0.00043388249015891526,
- 0.020029741944889927,
- 0.005311561452106721,
- 0.09334072022160665,
- 0.022940953491762648,
- 0.024658988190698353,
- 0.02901297565242747,
- 0.03531593526753171,
- 0.0758023035427905,
- 0.013711619769645722,
- 0.021597317393206007,
- 0.009670214316955824,
- 0.044728386062108175,
- 0.010596296836273509,
- 0.03264382563055839,
- 0.0604822860475288,
- 0.05489546581134276,
- 0.11501851581863246,
- 0.01837585653885406,
- 0.026237060796034405,
- 0.0011255285026971862,
- 0.08704125965884241,
- 0.10156349322058608,
- 0.06660562764251349,
- 0.023434319871701415,
- 0.010777081207173057,
- 0.005409534917626476,
- 0.003123487388832191,
- 0.0028762210234728096,
- 0.0089995626184575,
- 0.07518297127861205,
- 0.2314868056568013,
- 0.002226563639014434,
- 0.003285610147251786,
- 0.0027455897361131363,
- 0.2724537104534189,
- 0.0016655489138358362,
- 0.0019209797346551977,
- 0.0022137337804344656,
- 0.17690392185449774,
- 0.0014532730718763668,
- 0.0024994897215337513,
- 0.015302522233561744,
- 0.003441901151771395,
- 0.015303688584341741,
- 0.09314593964134713,
- 0.0017833503426155418,
- 0.0005108616416387229,
- 0.017828838023035427,
- 0.010385187345094037,
- 0.003168975069252078,
- 0.01902901297565243,
- 0.005525003644846187,
- 0.0010088934246974776,
- 0.0009272488700976819,
- 0.036282840064149294,
- 0.0022977110365942554,
- 0.0766805656801283,
- 0.22270418428342326,
- 0.005283569033386791,
- 0.007155562035282111,
- 0.01173582154833066,
- 0.1715620352821111,
- 0.003925936725470185,
- 0.004425134859308937,
- 0.020040239101909902,
- 0.14243242455168392,
- 0.0016737133692958156,
- 0.0066808572678232975,
- 0.011980755212130047,
- 0.012638577052048404,
- 0.07206065024055984,
- 0.08115701997375711,
- 0.00710424260096224,
- 0.0007278028867181805,
- 0.02347630849978131,
- 0.04595538708266512,
- 0.01481965301064295,
- 0.013925061962385188,
- 0.0018125091121154687,
- 0.00529173348884677,
- 0.0016340574427759146,
- 0.03072401224668319,
- 0.0023746901880740633,
- 0.25174165330223064,
- 0.06673392622831317,
- 0.00878378772415804,
- 0.03956261845750109,
- 0.010077270739174807,
- 0.0844787869951888,
- 0.00985216503863537,
- 0.004973319725907567,
- 0.01893220586091267,
- 0.11200583175389998,
- 0.0028715556203528212,
- 0.004095057588569762,
- 0.01202391019098994,
- 0.01756757544831608,
- 0.014825484764542934,
- 0.05312961073042717,
- 0.06746872721971132,
- 0.003845458521650386,
- 0.0210806239976673,
- 0.019443067502551394,
- 0.08017028721387957,
- 0.01825572240851436,
- 0.005365213587986587,
- 0.01959702580551101,
- 0.026184575010934536,
- 0.02474879720075813,
- 0.002171745152354571,
- 0.25827321767021433,
- 0.048050153083539875,
- 0.01043184137629392,
- 0.03930485493512174,
- 0.027640180784370902,
- 0.03294007872867765,
- 0.006474413179763814,
- 0.018314039947514214,
- 0.015119405161102202,
- 0.014706516984983233,
- 0.005494678524566263,
- 0.03309870243475726,
- 0.043864120134130345,
- 0.058996355153812505,
- 0.06265986295378335,
- 0.04633328473538417,
- 0.03790756670068523,
- 0.0004642076104388394,
- 0.037849249161685375,
- 0.08369966467415076,
- 0.04999679253535501,
- 0.02392768625164018,
- 0.010998687855372504,
- 0.009881323808135296,
- 0.003867619186470331,
- 0.012434465665548913,
- 0.007253535500801866,
- 0.11106225397288234,
- 0.17624726636535937,
- 0.008209943140399476,
- 0.008390727511299025,
- 0.012682898381688294,
- 0.1825653885406036,
- 0.001538416678816154,
- 0.004590756670068524,
- 0.008710307625018223,
- 0.1299513048549351,
- 0.002677941390873305,
- 0.012309666132089225,
- 0.014087184720804781,
- 0.01199941682461,
- 0.031246537396121883,
- 0.07206648199445984,
- 0.008254264470039366,
- 0.0007033095203382417,
- 0.007034261554162415,
- 0.006599212713223502,
- 0.013906400349905234,
- 0.050098265053214755,
- 0.007133401370462167,
- 0.017750692520775622,
- 0.0008257763522379356,
- 0.03918821985712203,
- 0.06015454147834961,
- );
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-photon-image-sizes.php b/plugins/jetpack/_inc/lib/class.jetpack-photon-image-sizes.php
deleted file mode 100644
index 8dc22d19..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-photon-image-sizes.php
+++ /dev/null
@@ -1,182 +0,0 @@
-<?php
-/**
- * The Image Sizes library.
- *
- * @package jetpack
- */
-
-jetpack_require_lib( 'class.jetpack-photon-image' );
-
-/**
- * Class Jetpack_Photon_ImageSizes
- *
- * Manages image resizing via Jetpack CDN Service.
- */
-class Jetpack_Photon_ImageSizes {
-
- /**
- * @var array $data Attachment metadata.
- */
- public $data;
-
- /**
- * @var Image Image to be resized.
- */
- public $image;
-
- /**
- * @var null|array $sizes Intermediate sizes.
- */
- public static $sizes = null;
-
- /**
- * Construct new sizes meta
- *
- * @param int $attachment_id Attachment ID.
- * @param array $data Attachment metadata.
- */
- public function __construct( $attachment_id, $data ) {
- $this->data = $data;
- $this->image = new Jetpack_Photon_Image( $data, get_post_mime_type( $attachment_id ) );
- $this->generate_sizes();
- }
-
- /**
- * Generate sizes for attachment.
- *
- * @return array Array of sizes; empty array as failure fallback.
- */
- protected function generate_sizes() {
-
- // There is no need to generate the sizes a new for every single image.
- if ( null !== self::$sizes ) {
- return self::$sizes;
- }
-
- /*
- * The following logic is copied over from wp_generate_attachment_metadata
- */
- $_wp_additional_image_sizes = wp_get_additional_image_sizes();
-
- $sizes = array();
-
- $intermediate_image_sizes = get_intermediate_image_sizes();
-
- foreach ( $intermediate_image_sizes as $s ) {
- $sizes[ $s ] = array(
- 'width' => '',
- 'height' => '',
- 'crop' => false,
- );
- if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) {
- // For theme-added sizes.
- $sizes[ $s ]['width'] = intval( $_wp_additional_image_sizes[ $s ]['width'] );
- } else {
- // For default sizes set in options.
- $sizes[ $s ]['width'] = get_option( "{$s}_size_w" );
- }
-
- if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) {
- // For theme-added sizes.
- $sizes[ $s ]['height'] = intval( $_wp_additional_image_sizes[ $s ]['height'] );
- } else {
- // For default sizes set in options.
- $sizes[ $s ]['height'] = get_option( "{$s}_size_h" );
- }
-
- if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) {
- // For theme-added sizes.
- $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop'];
- } else {
- // For default sizes set in options.
- $sizes[ $s ]['crop'] = get_option( "{$s}_crop" );
- }
- }
-
- self::$sizes = $sizes;
-
- return $sizes;
- }
-
- /**
- * @return array
- */
- public function filtered_sizes() {
- // Remove filter preventing the creation of advanced sizes.
- remove_filter(
- 'intermediate_image_sizes_advanced',
- array( 'Jetpack_Photon', 'filter_photon_noresize_intermediate_sizes' )
- );
-
- /** This filter is documented in wp-admin/includes/image.php */
- $sizes = apply_filters( 'intermediate_image_sizes_advanced', self::$sizes, $this->data );
-
- // Re-add the filter removed above.
- add_filter(
- 'intermediate_image_sizes_advanced',
- array( 'Jetpack_Photon', 'filter_photon_noresize_intermediate_sizes' )
- );
-
- return (array) $sizes;
- }
-
- /**
- * Standardises and validates the size_data array.
- *
- * @param array $size_data Size data array - at least containing height or width key. Can contain crop as well.
- *
- * @return array Array with populated width, height and crop keys; empty array if no width and height are provided.
- */
- public function standardize_size_data( $size_data ) {
- $has_at_least_width_or_height = ( isset( $size_data['width'] ) || isset( $size_data['height'] ) );
- if ( ! $has_at_least_width_or_height ) {
- return array();
- }
-
- $defaults = array(
- 'width' => null,
- 'height' => null,
- 'crop' => false,
- );
-
- return array_merge( $defaults, $size_data );
- }
-
- /**
- * Get sizes for attachment post meta.
- *
- * @return array ImageSizes for attachment postmeta.
- */
- public function generate_sizes_meta() {
-
- $metadata = array();
-
- foreach ( $this->filtered_sizes() as $size => $size_data ) {
-
- $size_data = $this->standardize_size_data( $size_data );
-
- if ( true === empty( $size_data ) ) {
- continue;
- }
-
- $resized_image = $this->resize( $size_data );
-
- if ( true === is_array( $resized_image ) ) {
- $metadata[ $size ] = $resized_image;
- }
- }
-
- return $metadata;
- }
-
- /**
- * @param array $size_data
- *
- * @return array|\WP_Error Array for usage in $metadata['sizes']; WP_Error on failure.
- */
- protected function resize( $size_data ) {
-
- return $this->image->get_size( $size_data );
-
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-photon-image.php b/plugins/jetpack/_inc/lib/class.jetpack-photon-image.php
deleted file mode 100644
index d364f2a3..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-photon-image.php
+++ /dev/null
@@ -1,243 +0,0 @@
-<?php
-/**
- * The Image Class.
- *
- * @package Jetpack
- */
-
-/**
- * Represents a resizable image, exposing properties necessary for properly generating srcset.
- */
-class Jetpack_Photon_Image {
-
- /**
- * @var string $filename Attachment's Filename.
- */
- public $filename;
-
- /**
- * @var string/WP_Erorr $mime_type Attachment's mime-type, WP_Error on failure when recalculating the dimensions.
- */
- private $mime_type;
-
- /**
- * @var int $original_width Image original width.
- */
- private $original_width;
-
- /**
- * @var int $original_width Image original height.
- */
- private $original_height;
-
- /**
- * @var int $width Current attachment's width.
- */
- private $width;
-
- /**
- * @var int $height Current attachment's height.
- */
- private $height;
-
- /**
- * @var bool $is_resized Whether the attachment has been resized yet, or not.
- */
- private $is_resized = false;
-
- /**
- * Constructs the image object.
- *
- * The $data array should provide at least
- * file : string Image file path
- * width : int Image width
- * height : int Image height
- *
- * @param array $data Array of attachment metadata, typically value of _wp_attachment_metadata postmeta
- * @param string|\WP_Error $mime_type Typically value returned from get_post_mime_type function.
- */
- public function __construct( $data, $mime_type ) {
- $this->filename = $data['file'];
- $this->width = $this->original_width = $data['width'];
- $this->height = $this->original_height = $data['height'];
- $this->mime_type = $mime_type;
- }
-
- /**
- * Resizes the image to given size.
- *
- * @param array $size_data Array of width, height, and crop properties of a size.
- *
- * @return bool|\WP_Error True if resize was successful, WP_Error on failure.
- */
- public function resize( $size_data ) {
-
- $dimensions = $this->image_resize_dimensions( $size_data['width'], $size_data['height'], $size_data['crop'] );
-
- if ( true === is_wp_error( $dimensions ) ) {
- return $dimensions; // Returns \WP_Error.
- }
-
- if ( true === is_wp_error( $this->mime_type ) ) {
- return $this->mime_type; // Returns \WP_Error.
- }
-
- $this->set_width_height( $dimensions );
-
- return $this->is_resized = true;
- }
-
- /**
- * Generates size data for usage in $metadata['sizes'];.
- *
- * @param array $size_data Array of width, height, and crop properties of a size.
- *
- * @return array|\WP_Error An array containing file, width, height, and mime-type keys and it's values. WP_Error on failure.
- */
- public function get_size( $size_data ) {
-
- $is_resized = $this->resize( $size_data );
-
- if ( true === is_wp_error( $is_resized ) ) {
- return $is_resized;
- }
-
- return array(
- 'file' => $this->get_filename(),
- 'width' => $this->get_width(),
- 'height' => $this->get_height(),
- 'mime-type' => $this->get_mime_type(),
- );
- }
-
- /**
- * Resets the image to it's original dimensions.
- *
- * @return bool True on successful reset to original dimensions.
- */
- public function reset_to_original() {
- $this->width = $this->original_width;
- $this->height = $this->original_height;
- $this->is_resized = false;
-
- return true;
- }
-
- /**
- * Return the basename filename. If the image has been resized, including
- * the resizing params for Jetpack CDN.
- *
- * @return string Basename of the filename.
- */
- public function get_filename() {
-
- if ( true === $this->is_resized() ) {
- $filename = $this->get_resized_filename();
- } else {
- $filename = $this->filename;
- }
-
- return wp_basename( $filename );
- }
-
- /**
- * Returns current image width. Either original, or after resize.
- *
- * @return int
- */
- public function get_width() {
- return (int) $this->width;
- }
-
- /**
- * Returns current image height. Either original, or after resize.
- *
- * @return int
- */
- public function get_height() {
- return (int) $this->height;
- }
-
- /**
- * Returns image mime type.
- *
- * @return string|WP_Error Image's mime type or WP_Error if it was not determined.
- */
- public function get_mime_type() {
- return $this->mime_type;
- }
-
- /**
- * Checks the resize status of the image.
- *
- * @return bool If the image has been resized.
- */
- public function is_resized() {
- return ( true === $this->is_resized );
- }
-
- /**
- * Get filename with proper args for the Photon service.
- *
- * @return string Filename with query args for Photon service
- */
- protected function get_resized_filename() {
- $query_args = array(
- 'resize' => join(
- ',',
- array(
- $this->get_width(),
- $this->get_height(),
- )
- ),
- );
-
- return add_query_arg( $query_args, $this->filename );
- }
-
- /**
- * Get resize dimensions used for the Jetpack CDN service.
- *
- * Converts the list of values returned from `image_resize_dimensions()` to
- * associative array for the sake of more readable code no relying on index
- * nor `list`.
- *
- * @param int $max_width
- * @param int $max_height
- * @param bool|array $crop
- *
- * @return array|\WP_Error Array of dimensions matching the parameters to imagecopyresampled. WP_Error on failure.
- */
- protected function image_resize_dimensions( $max_width, $max_height, $crop ) {
- $dimensions = image_resize_dimensions( $this->original_width, $this->original_height, $max_width, $max_height, $crop );
- if ( ! $dimensions ) {
- return new WP_Error( 'error_getting_dimensions', __( 'Could not calculate resized image dimensions' ), $this->filename );
- }
-
- return array_combine(
- array(
- 'dst_x',
- 'dst_y',
- 'src_x',
- 'src_y',
- 'dst_w',
- 'dst_h',
- 'src_w',
- 'src_h',
- ),
- $dimensions
- );
- }
-
- /**
- * Sets proper width and height from dimensions.
- *
- * @param Array $dimensions an array of image dimensions.
- * @return void
- */
- protected function set_width_height( $dimensions ) {
- $this->width = (int) $dimensions['dst_w'];
- $this->height = (int) $dimensions['dst_h'];
- }
-
-}
diff --git a/plugins/jetpack/_inc/lib/class.jetpack-search-performance-logger.php b/plugins/jetpack/_inc/lib/class.jetpack-search-performance-logger.php
deleted file mode 100644
index f8de70ee..00000000
--- a/plugins/jetpack/_inc/lib/class.jetpack-search-performance-logger.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-class Jetpack_Search_Performance_Logger {
- /**
- * @var Jetpack_Search_Performance_Logger
- **/
- private static $instance = null;
-
- private $current_query = null;
- private $query_started = null;
- private $stats = null;
-
- static function init() {
- if ( is_null( self::$instance ) ) {
- self::$instance = new Jetpack_Search_Performance_Logger;
- }
-
- return self::$instance;
- }
-
- private function __construct() {
- $this->stats = array();
- add_action( 'pre_get_posts', array( $this, 'begin_log_query' ), 10, 1 );
- add_action( 'did_jetpack_search_query', array( $this, 'log_jetpack_search_query' ) );
- add_filter( 'found_posts', array( $this, 'log_mysql_query' ), 10, 2 );
- add_action( 'wp_footer', array( $this, 'print_stats' ) );
- }
-
- public function begin_log_query( $query ) {
- if ( $this->should_log_query( $query ) ) {
- $this->query_started = microtime( true );
- $this->current_query = $query;
- }
- }
-
- public function log_mysql_query( $found_posts, $query ) {
- if ( $this->current_query === $query ) {
- $duration = microtime( true ) - $this->query_started;
- if ( $duration < 60 ) { // eliminate outliers, likely tracking errors
- $this->record_query_time( $duration, false );
- }
- $this->reset_query_state();
- }
-
- return $found_posts;
- }
-
- public function log_jetpack_search_query() {
- $duration = microtime( true ) - $this->query_started;
- if ( $duration < 60 ) { // eliminate outliers, likely tracking errors
- $this->record_query_time( $duration, true );
- }
- $this->reset_query_state();
- }
-
- private function reset_query_state() {
- $this->query_started = null;
- $this->current_query = null;
- }
-
- private function should_log_query( $query ) {
- return $query->is_main_query() && $query->is_search();
- }
-
- private function record_query_time( $duration, $was_jetpack_search ) {
- $this->stats[] = array( $was_jetpack_search, intval( $duration * 1000 ) );
- }
-
- public function print_stats() {
- $beacons = array();
- if ( ! empty( $this->stats ) ) {
- foreach( $this->stats as $stat ) {
- $search_type = $stat[0] ? 'es' : 'mysql';
- $beacons[] = "%22jetpack.search.{$search_type}.duration:{$stat[1]}|ms%22";
- }
-
- $encoded_json = '{%22beacons%22:[' . implode(',', $beacons ) . ']}';
- $encoded_site_url = urlencode( site_url() );
- $url = "https://pixel.wp.com/boom.gif?v=0.9&u={$encoded_site_url}&json={$encoded_json}";
- echo '<img src="' . $url . '" width="1" height="1" style="display:none;" alt=":)"/>';
- }
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.media-extractor.php b/plugins/jetpack/_inc/lib/class.media-extractor.php
deleted file mode 100644
index 6acf34db..00000000
--- a/plugins/jetpack/_inc/lib/class.media-extractor.php
+++ /dev/null
@@ -1,436 +0,0 @@
-<?php
-/**
- * Class with methods to extract metadata from a post/page about videos, images, links, mentions embedded
- * in or attached to the post/page.
- *
- * @todo Additionally, have some filters on number of items in each field
- */
-class Jetpack_Media_Meta_Extractor {
-
- // Some consts for what to extract
- const ALL = 255;
- const LINKS = 1;
- const MENTIONS = 2;
- const IMAGES = 4;
- const SHORTCODES = 8; // Only the keeper shortcodes below
- const EMBEDS = 16;
- const HASHTAGS = 32;
-
- // For these, we try to extract some data from the shortcode, rather than just recording its presence (which we do for all)
- // There should be a function get_{shortcode}_id( $atts ) or static method SomethingShortcode::get_{shortcode}_id( $atts ) for these.
- private static $KEEPER_SHORTCODES = array(
- 'youtube',
- 'vimeo',
- 'hulu',
- 'ted',
- 'wpvideo',
- 'videopress',
- );
-
- /**
- * Gets the specified media and meta info from the given post.
- * NOTE: If you have the post's HTML content already and don't need image data, use extract_from_content() instead.
- *
- * @param $blog_id The ID of the blog
- * @param $post_id The ID of the post
- * @param $what_to_extract (int) A mask of things to extract, e.g. Jetpack_Media_Meta_Extractor::IMAGES | Jetpack_Media_Meta_Extractor::MENTIONS
- * @returns a structure containing metadata about the embedded things, or empty array if nothing found, or WP_Error on error
- */
- static public function extract( $blog_id, $post_id, $what_to_extract = self::ALL ) {
-
- // multisite?
- if ( function_exists( 'switch_to_blog') )
- switch_to_blog( $blog_id );
-
- $post = get_post( $post_id );
- $content = $post->post_title . "\n\n" . $post->post_content;
- $char_cnt = strlen( $content );
-
- //prevent running extraction on really huge amounts of content
- if ( $char_cnt > 100000 ) //about 20k English words
- $content = substr( $content, 0, 100000 );
-
- $extracted = array();
-
- // Get images first, we need the full post for that
- if ( self::IMAGES & $what_to_extract ) {
- $extracted = self::get_image_fields( $post );
-
- // Turn off images so we can safely call extract_from_content() below
- $what_to_extract = $what_to_extract - self::IMAGES;
- }
-
- if ( function_exists( 'switch_to_blog') )
- restore_current_blog();
-
- // All of the other things besides images can be extracted from just the content
- $extracted = self::extract_from_content( $content, $what_to_extract, $extracted );
-
- return $extracted;
- }
-
- /**
- * Gets the specified meta info from the given post content.
- * NOTE: If you want IMAGES, call extract( $blog_id, $post_id, ...) which will give you more/better image extraction
- * This method will give you an error if you ask for IMAGES.
- *
- * @param $content The HTML post_content of a post
- * @param $what_to_extract (int) A mask of things to extract, e.g. Jetpack_Media_Meta_Extractor::IMAGES | Jetpack_Media_Meta_Extractor::MENTIONS
- * @param $already_extracted (array) Previously extracted things, e.g. images from extract(), which can be used for x-referencing here
- * @returns a structure containing metadata about the embedded things, or empty array if nothing found, or WP_Error on error
- */
- static public function extract_from_content( $content, $what_to_extract = self::ALL, $already_extracted = array() ) {
- $stripped_content = self::get_stripped_content( $content );
-
- // Maybe start with some previously extracted things (e.g. images from extract()
- $extracted = $already_extracted;
-
- // Embedded media objects will have already been converted to shortcodes by pre_kses hooks on save.
-
- if ( self::IMAGES & $what_to_extract ) {
- $images = Jetpack_Media_Meta_Extractor::extract_images_from_content( $stripped_content, array() );
- $extracted = array_merge( $extracted, $images );
- }
-
- // ----------------------------------- MENTIONS ------------------------------
-
- if ( self::MENTIONS & $what_to_extract ) {
- if ( preg_match_all( '/(^|\s)@(\w+)/u', $stripped_content, $matches ) ) {
- $mentions = array_values( array_unique( $matches[2] ) ); //array_unique() retains the keys!
- $mentions = array_map( 'strtolower', $mentions );
- $extracted['mention'] = array( 'name' => $mentions );
- if ( !isset( $extracted['has'] ) )
- $extracted['has'] = array();
- $extracted['has']['mention'] = count( $mentions );
- }
- }
-
- // ----------------------------------- HASHTAGS ------------------------------
- /** Some hosts may not compile with --enable-unicode-properties and kick a warning:
- * Warning: preg_match_all() [function.preg-match-all]: Compilation failed: support for \P, \p, and \X has not been compiled
- * Therefore, we only run this code block on wpcom, not in Jetpack.
- */
- if ( ( defined( 'IS_WPCOM' ) && IS_WPCOM ) && ( self::HASHTAGS & $what_to_extract ) ) {
- //This regex does not exactly match Twitter's
- // if there are problems/complaints we should implement this:
- // https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java
- if ( preg_match_all( '/(?:^|\s)#(\w*\p{L}+\w*)/u', $stripped_content, $matches ) ) {
- $hashtags = array_values( array_unique( $matches[1] ) ); //array_unique() retains the keys!
- $hashtags = array_map( 'strtolower', $hashtags );
- $extracted['hashtag'] = array( 'name' => $hashtags );
- if ( !isset( $extracted['has'] ) )
- $extracted['has'] = array();
- $extracted['has']['hashtag'] = count( $hashtags );
- }
- }
-
- // ----------------------------------- SHORTCODES ------------------------------
-
- // Always look for shortcodes.
- // If we don't want them, we'll just remove them, so we don't grab them as links below
- $shortcode_pattern = '/' . get_shortcode_regex() . '/s';
- if ( preg_match_all( $shortcode_pattern, $content, $matches ) ) {
-
- $shortcode_total_count = 0;
- $shortcode_type_counts = array();
- $shortcode_types = array();
- $shortcode_details = array();
-
- if ( self::SHORTCODES & $what_to_extract ) {
-
- foreach( $matches[2] as $key => $shortcode ) {
- //Elasticsearch (and probably other things) doesn't deal well with some chars as key names
- $shortcode_name = preg_replace( '/[.,*"\'\/\\\\#+ ]/', '_', $shortcode );
-
- $attr = shortcode_parse_atts( $matches[3][ $key ] );
-
- $shortcode_total_count++;
- if ( ! isset( $shortcode_type_counts[$shortcode_name] ) )
- $shortcode_type_counts[$shortcode_name] = 0;
- $shortcode_type_counts[$shortcode_name]++;
-
- // Store (uniquely) presence of all shortcode regardless of whether it's a keeper (for those, get ID below)
- // @todo Store number of occurrences?
- if ( ! in_array( $shortcode_name, $shortcode_types ) )
- $shortcode_types[] = $shortcode_name;
-
- // For keeper shortcodes, also store the id/url of the object (e.g. youtube video, TED talk, etc.)
- if ( in_array( $shortcode, self::$KEEPER_SHORTCODES ) ) {
- unset( $id ); // Clear shortcode ID data left from the last shortcode
- // We'll try to get the salient ID from the function jetpack_shortcode_get_xyz_id()
- // If the shortcode is a class, we'll call XyzShortcode::get_xyz_id()
- $shortcode_get_id_func = "jetpack_shortcode_get_{$shortcode}_id";
- $shortcode_class_name = ucfirst( $shortcode ) . 'Shortcode';
- $shortcode_get_id_method = "get_{$shortcode}_id";
- if ( function_exists( $shortcode_get_id_func ) ) {
- $id = call_user_func( $shortcode_get_id_func, $attr );
- } else if ( method_exists( $shortcode_class_name, $shortcode_get_id_method ) ) {
- $id = call_user_func( array( $shortcode_class_name, $shortcode_get_id_method ), $attr );
- }
- if ( ! empty( $id )
- && ( ! isset( $shortcode_details[$shortcode_name] ) || ! in_array( $id, $shortcode_details[$shortcode_name] ) ) )
- $shortcode_details[$shortcode_name][] = $id;
- }
- }
-
- if ( $shortcode_total_count > 0 ) {
- // Add the shortcode info to the $extracted array
- if ( !isset( $extracted['has'] ) )
- $extracted['has'] = array();
- $extracted['has']['shortcode'] = $shortcode_total_count;
- $extracted['shortcode'] = array();
- foreach ( $shortcode_type_counts as $type => $count )
- $extracted['shortcode'][$type] = array( 'count' => $count );
- if ( ! empty( $shortcode_types ) )
- $extracted['shortcode_types'] = $shortcode_types;
- foreach ( $shortcode_details as $type => $id )
- $extracted['shortcode'][$type]['id'] = $id;
- }
- }
-
- // Remove the shortcodes form our copy of $content, so we don't count links in them as links below.
- $content = preg_replace( $shortcode_pattern, ' ', $content );
- }
-
- // ----------------------------------- LINKS ------------------------------
-
- if ( self::LINKS & $what_to_extract ) {
-
- // To hold the extracted stuff we find
- $links = array();
-
- // @todo Get the text inside the links?
-
- // Grab any links, whether in <a href="..." or not, but subtract those from shortcodes and images
- // (we treat embed links as just another link)
- if ( preg_match_all( '#(?:^|\s|"|\')(https?://([^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))))#', $content, $matches ) ) {
-
- foreach ( $matches[1] as $link_raw ) {
- $url = parse_url( $link_raw );
-
- // Data URI links
- if ( isset( $url['scheme'] ) && 'data' === $url['scheme'] )
- continue;
-
- // Remove large (and likely invalid) links
- if ( 4096 < strlen( $link_raw ) )
- continue;
-
- // Build a simple form of the URL so we can compare it to ones we found in IMAGES or SHORTCODES and exclude those
- $simple_url = $url['scheme'] . '://' . $url['host'] . ( ! empty( $url['path'] ) ? $url['path'] : '' );
- if ( isset( $extracted['image']['url'] ) ) {
- if ( in_array( $simple_url, (array) $extracted['image']['url'] ) )
- continue;
- }
-
- list( $proto, $link_all_but_proto ) = explode( '://', $link_raw );
-
- // Build a reversed hostname
- $host_parts = array_reverse( explode( '.', $url['host'] ) );
- $host_reversed = '';
- foreach ( $host_parts as $part ) {
- $host_reversed .= ( ! empty( $host_reversed ) ? '.' : '' ) . $part;
- }
-
- $link_analyzed = '';
- if ( !empty( $url['path'] ) ) {
- // The whole path (no query args or fragments)
- $path = substr( $url['path'], 1 ); // strip the leading '/'
- $link_analyzed .= ( ! empty( $link_analyzed ) ? ' ' : '' ) . $path;
-
- // The path split by /
- $path_split = explode( '/', $path );
- if ( count( $path_split ) > 1 ) {
- $link_analyzed .= ' ' . implode( ' ', $path_split );
- }
-
- // The fragment
- if ( ! empty( $url['fragment'] ) )
- $link_analyzed .= ( ! empty( $link_analyzed ) ? ' ' : '' ) . $url['fragment'];
- }
-
- // @todo Check unique before adding
- $links[] = array(
- 'url' => $link_all_but_proto,
- 'host_reversed' => $host_reversed,
- 'host' => $url['host'],
- );
- }
-
- }
-
- $link_count = count( $links );
- if ( $link_count ) {
- $extracted[ 'link' ] = $links;
- if ( !isset( $extracted['has'] ) )
- $extracted['has'] = array();
- $extracted['has']['link'] = $link_count;
- }
- }
-
- // ----------------------------------- EMBEDS ------------------------------
-
- //Embeds are just individual links on their own line
- if ( self::EMBEDS & $what_to_extract ) {
-
- if ( !function_exists( '_wp_oembed_get_object' ) )
- include( ABSPATH . WPINC . '/class-oembed.php' );
-
- // get an oembed object
- $oembed = _wp_oembed_get_object();
-
- // Grab any links on their own lines that may be embeds
- if ( preg_match_all( '|^\s*(https?://[^\s"]+)\s*$|im', $content, $matches ) ) {
-
- // To hold the extracted stuff we find
- $embeds = array();
-
- foreach ( $matches[1] as $link_raw ) {
- $url = parse_url( $link_raw );
-
- list( $proto, $link_all_but_proto ) = explode( '://', $link_raw );
-
- // Check whether this "link" is really an embed.
- foreach ( $oembed->providers as $matchmask => $data ) {
- list( $providerurl, $regex ) = $data;
-
- // Turn the asterisk-type provider URLs into regex
- if ( !$regex ) {
- $matchmask = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' ) ) . '#i';
- $matchmask = preg_replace( '|^#http\\\://|', '#https?\://', $matchmask );
- }
-
- if ( preg_match( $matchmask, $link_raw ) ) {
- $provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML
- $embeds[] = $link_all_but_proto; // @todo Check unique before adding
-
- // @todo Try to get ID's for the ones we care about (shortcode_keepers)
- break;
- }
- }
- }
-
- if ( ! empty( $embeds ) ) {
- if ( !isset( $extracted['has'] ) )
- $extracted['has'] = array();
- $extracted['has']['embed'] = count( $embeds );
- $extracted['embed'] = array( 'url' => array() );
- foreach ( $embeds as $e )
- $extracted['embed']['url'][] = $e;
- }
- }
- }
-
- return $extracted;
- }
-
- /**
- * @param $post A post object
- * @param $args (array) Optional args, see defaults list for details
- * @returns array Returns an array of all images meeting the specified criteria in $args
- *
- * Uses Jetpack Post Images
- */
- private static function get_image_fields( $post, $args = array() ) {
-
- $defaults = array(
- 'width' => 200, // Required minimum width (if possible to determine)
- 'height' => 200, // Required minimum height (if possible to determine)
- );
-
- $args = wp_parse_args( $args, $defaults );
-
- $image_list = array();
- $image_booleans = array();
- $image_booleans['gallery'] = 0;
-
- $from_featured_image = Jetpack_PostImages::from_thumbnail( $post->ID, $args['width'], $args['height'] );
- if ( !empty( $from_featured_image ) ) {
- $srcs = wp_list_pluck( $from_featured_image, 'src' );
- $image_list = array_merge( $image_list, $srcs );
- }
-
- $from_slideshow = Jetpack_PostImages::from_slideshow( $post->ID, $args['width'], $args['height'] );
- if ( !empty( $from_slideshow ) ) {
- $srcs = wp_list_pluck( $from_slideshow, 'src' );
- $image_list = array_merge( $image_list, $srcs );
- }
-
- $from_gallery = Jetpack_PostImages::from_gallery( $post->ID );
- if ( !empty( $from_gallery ) ) {
- $srcs = wp_list_pluck( $from_gallery, 'src' );
- $image_list = array_merge( $image_list, $srcs );
- $image_booleans['gallery']++; // @todo This count isn't correct, will only every count 1
- }
-
- // @todo Can we check width/height of these efficiently? Could maybe use query args at least, before we strip them out
- $image_list = Jetpack_Media_Meta_Extractor::get_images_from_html( $post->post_content, $image_list );
-
- return Jetpack_Media_Meta_Extractor::build_image_struct( $image_list, $image_booleans );
- }
-
- public static function extract_images_from_content( $content, $image_list ) {
- $image_list = Jetpack_Media_Meta_Extractor::get_images_from_html( $content, $image_list );
- return Jetpack_Media_Meta_Extractor::build_image_struct( $image_list, array() );
- }
-
- public static function build_image_struct( $image_list, $image_booleans ) {
- if ( ! empty( $image_list ) ) {
- $retval = array( 'image' => array() );
- $image_list = array_unique( $image_list );
- foreach ( $image_list as $img ) {
- $retval['image'][] = array( 'url' => $img );
- }
- $image_booleans['image'] = count( $retval['image'] );
- if ( ! empty( $image_booleans ) )
- $retval['has'] = $image_booleans;
- return $retval;
- } else {
- return array();
- }
- }
-
- /**
- *
- * @param string $html Some markup, possibly containing image tags
- * @param array $images_already_extracted (just an array of image URLs without query strings, no special structure), used for de-duplication
- * @return array Image URLs extracted from the HTML, stripped of query params and de-duped
- */
- public static function get_images_from_html( $html, $images_already_extracted ) {
- $image_list = $images_already_extracted;
- $from_html = Jetpack_PostImages::from_html( $html );
- if ( !empty( $from_html ) ) {
- $srcs = wp_list_pluck( $from_html, 'src' );
- foreach( $srcs as $image_url ) {
- if ( ( $src = parse_url( $image_url ) ) && isset( $src['scheme'], $src['host'], $src['path'] ) ) {
- // Rebuild the URL without the query string
- $queryless = $src['scheme'] . '://' . $src['host'] . $src['path'];
- } elseif ( $length = strpos( $image_url, '?' ) ) {
- // If parse_url() didn't work, strip off the query string the old fashioned way
- $queryless = substr( $image_url, 0, $length );
- } else {
- // Failing that, there was no spoon! Err ... query string!
- $queryless = $image_url;
- }
-
- // Discard URLs that are longer then 4KB, these are likely data URIs or malformed HTML.
- if ( 4096 < strlen( $queryless ) ) {
- continue;
- }
-
- if ( ! in_array( $queryless, $image_list ) ) {
- $image_list[] = $queryless;
- }
- }
- }
- return $image_list;
- }
-
- private static function get_stripped_content( $content ) {
- $clean_content = strip_tags( $content );
- $clean_content = html_entity_decode( $clean_content );
- //completely strip shortcodes and any content they enclose
- $clean_content = strip_shortcodes( $clean_content );
- return $clean_content;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.media-summary.php b/plugins/jetpack/_inc/lib/class.media-summary.php
deleted file mode 100644
index 1cce160b..00000000
--- a/plugins/jetpack/_inc/lib/class.media-summary.php
+++ /dev/null
@@ -1,354 +0,0 @@
-<?php
-/**
- * Class Jetpack_Media_Summary
- *
- * embed [video] > gallery > image > text
- */
-class Jetpack_Media_Summary {
-
- private static $cache = array();
-
- static function get( $post_id, $blog_id = 0, $args = array() ) {
-
- $defaults = array(
- 'max_words' => 16,
- 'max_chars' => 256,
- );
- $args = wp_parse_args( $args, $defaults );
-
- $switched = false;
- if ( !empty( $blog_id ) && $blog_id != get_current_blog_id() && function_exists( 'switch_to_blog' ) ) {
- switch_to_blog( $blog_id );
- $switched = true;
- } else {
- $blog_id = get_current_blog_id();
- }
-
- $cache_key = "{$blog_id}_{$post_id}_{$args['max_words']}_{$args['max_chars']}";
- if ( isset( self::$cache[ $cache_key ] ) ) {
- return self::$cache[ $cache_key ];
- }
-
- if ( ! class_exists( 'Jetpack_Media_Meta_Extractor' ) ) {
- if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
- jetpack_require_lib( 'class.wpcom-media-meta-extractor' );
- } else {
- jetpack_require_lib( 'class.media-extractor' );
- }
- }
-
- $post = get_post( $post_id );
- $permalink = get_permalink( $post_id );
-
- $return = array(
- 'type' => 'standard',
- 'permalink' => $permalink,
- 'image' => '',
- 'excerpt' => '',
- 'word_count' => 0,
- 'secure' => array(
- 'image' => '',
- ),
- 'count' => array(
- 'image' => 0,
- 'video' => 0,
- 'word' => 0,
- 'link' => 0,
- ),
- );
-
- if ( empty( $post->post_password ) ) {
- $return['excerpt'] = self::get_excerpt( $post->post_content, $post->post_excerpt, $args['max_words'], $args['max_chars'] , $post);
- $return['count']['word'] = self::get_word_count( $post->post_content );
- $return['count']['word_remaining'] = self::get_word_remaining_count( $post->post_content, $return['excerpt'] );
- $return['count']['link'] = self::get_link_count( $post->post_content );
- }
-
- $extract = Jetpack_Media_Meta_Extractor::extract( $blog_id, $post_id, Jetpack_Media_Meta_Extractor::ALL );
-
- if ( empty( $extract['has'] ) )
- return $return;
-
- // Prioritize [some] video embeds
- if ( !empty( $extract['has']['shortcode'] ) ) {
- foreach ( $extract['shortcode'] as $type => $data ) {
- switch ( $type ) {
- case 'videopress':
- case 'wpvideo':
- if ( 0 == $return['count']['video'] ) {
- // If there is no id on the video, then let's just skip this
- if ( ! isset ( $data['id'][0] ) ) {
- break;
- }
-
- $guid = $data['id'][0];
- $video_info = videopress_get_video_details( $guid );
-
- // Only add the video tags if the guid returns a valid videopress object.
- if ( $video_info instanceof stdClass ) {
- // Continue early if we can't find a Video slug.
- if ( empty( $video_info->files->std->mp4 ) ) {
- break;
- }
-
- $url = sprintf(
- 'https://videos.files.wordpress.com/%1$s/%2$s',
- $guid,
- $video_info->files->std->mp4
- );
-
- $thumbnail = $video_info->poster;
- if ( ! empty( $thumbnail ) ) {
- $return['image'] = $thumbnail;
- $return['secure']['image'] = $thumbnail;
- }
-
- $return['type'] = 'video';
- $return['video'] = esc_url_raw( $url );
- $return['video_type'] = 'video/mp4';
- $return['secure']['video'] = $return['video'];
- }
-
- }
- $return['count']['video']++;
- break;
- case 'youtube':
- if ( 0 == $return['count']['video'] ) {
- $return['type'] = 'video';
- $return['video'] = esc_url_raw( 'http://www.youtube.com/watch?feature=player_embedded&v=' . $extract['shortcode']['youtube']['id'][0] );
- $return['image'] = self::get_video_poster( 'youtube', $extract['shortcode']['youtube']['id'][0] );
- $return['secure']['video'] = self::https( $return['video'] );
- $return['secure']['image'] = self::https( $return['image'] );
- }
- $return['count']['video']++;
- break;
- case 'vimeo':
- if ( 0 == $return['count']['video'] ) {
- $return['type'] = 'video';
- $return['video'] = esc_url_raw( 'http://vimeo.com/' . $extract['shortcode']['vimeo']['id'][0] );
- $return['secure']['video'] = self::https( $return['video'] );
-
- $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
- if ( !empty( $poster_image ) ) {
- $return['image'] = $poster_image;
- $poster_url_parts = parse_url( $poster_image );
- $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
- }
- }
- $return['count']['video']++;
- break;
- }
- }
-
- }
-
- if ( !empty( $extract['has']['embed'] ) ) {
- foreach( $extract['embed']['url'] as $embed ) {
- if ( preg_match( '/((youtube|vimeo|dailymotion)\.com|youtu.be)/', $embed ) ) {
- if ( 0 == $return['count']['video'] ) {
- $return['type'] = 'video';
- $return['video'] = 'http://' . $embed;
- $return['secure']['video'] = self::https( $return['video'] );
- if ( false !== strpos( $embed, 'youtube' ) ) {
- $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
- $return['secure']['image'] = self::https( $return['image'] );
- } else if ( false !== strpos( $embed, 'youtu.be' ) ) {
- $youtube_id = jetpack_get_youtube_id( $return['video'] );
- $return['video'] = 'http://youtube.com/watch?v=' . $youtube_id . '&feature=youtu.be';
- $return['secure']['video'] = self::https( $return['video'] );
- $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
- $return['secure']['image'] = self::https( $return['image'] );
- } else if ( false !== strpos( $embed, 'vimeo' ) ) {
- $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
- if ( !empty( $poster_image ) ) {
- $return['image'] = $poster_image;
- $poster_url_parts = parse_url( $poster_image );
- $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
- }
- } else if ( false !== strpos( $embed, 'dailymotion' ) ) {
- $return['image'] = str_replace( 'dailymotion.com/video/','dailymotion.com/thumbnail/video/', $embed );
- $return['image'] = parse_url( $return['image'], PHP_URL_SCHEME ) === null ? 'http://' . $return['image'] : $return['image'];
- $return['secure']['image'] = self::https( $return['image'] );
- }
-
- }
- $return['count']['video']++;
- }
- }
- }
-
- // Do we really want to make the video the primary focus of the post?
- if ( 'video' == $return['type'] ) {
- $content = wpautop( strip_tags( $post->post_content ) );
- $paragraphs = explode( '</p>', $content );
- $number_of_paragraphs = 0;
-
- foreach ( $paragraphs as $i => $paragraph ) {
- // Don't include blank lines as a paragraph
- if ( '' == trim( $paragraph ) ) {
- unset( $paragraphs[$i] );
- continue;
- }
- $number_of_paragraphs++;
- }
-
- $number_of_paragraphs = $number_of_paragraphs - $return['count']['video']; // subtract amount for videos..
-
- // More than 2 paragraph? The video is not the primary focus so we can do some more analysis
- if ( $number_of_paragraphs > 2 )
- $return['type'] = 'standard';
- }
-
- // If we don't have any prioritized embed...
- if ( 'standard' == $return['type'] ) {
- if ( ( ! empty( $extract['has']['gallery'] ) || ! empty( $extract['shortcode']['gallery']['count'] ) ) && ! empty( $extract['image'] ) ) {
- //... Then we prioritize galleries first (multiple images returned)
- $return['type'] = 'gallery';
- $return['images'] = $extract['image'];
- foreach ( $return['images'] as $image ) {
- $return['secure']['images'][] = array( 'url' => self::ssl_img( $image['url'] ) );
- $return['count']['image']++;
- }
- } else if ( ! empty( $extract['has']['image'] ) ) {
- // ... Or we try and select a single image that would make sense
- $content = wpautop( strip_tags( $post->post_content ) );
- $paragraphs = explode( '</p>', $content );
- $number_of_paragraphs = 0;
-
- foreach ( $paragraphs as $i => $paragraph ) {
- // Don't include 'actual' captions as a paragraph
- if ( false !== strpos( $paragraph, '[caption' ) ) {
- unset( $paragraphs[$i] );
- continue;
- }
- // Don't include blank lines as a paragraph
- if ( '' == trim( $paragraph ) ) {
- unset( $paragraphs[$i] );
- continue;
- }
- $number_of_paragraphs++;
- }
-
- $return['image'] = $extract['image'][0]['url'];
- $return['secure']['image'] = self::ssl_img( $return['image'] );
- $return['count']['image']++;
-
- if ( $number_of_paragraphs <= 2 && 1 == count( $extract['image'] ) ) {
- // If we have lots of text or images, let's not treat it as an image post, but return its first image
- $return['type'] = 'image';
- }
- }
- }
-
- if ( $switched ) {
- restore_current_blog();
- }
-
- /**
- * Allow a theme or plugin to inspect and ultimately change the media summary.
- *
- * @since 4.4.0
- *
- * @param array $data The calculated media summary data.
- * @param int $post_id The id of the post this data applies to.
- */
- $return = apply_filters( 'jetpack_media_summary_output', $return, $post_id );
-
- self::$cache[ $cache_key ] = $return;
-
- return $return;
- }
-
- static function https( $str ) {
- return str_replace( 'http://', 'https://', $str );
- }
-
- static function ssl_img( $url ) {
- if ( false !== strpos( $url, 'files.wordpress.com' ) ) {
- return self::https( $url );
- } else {
- return self::https( jetpack_photon_url( $url ) );
- }
- }
-
- static function get_video_poster( $type, $id ) {
- if ( 'videopress' == $type ) {
- if ( function_exists( 'video_get_highest_resolution_image_url' ) ) {
- return video_get_highest_resolution_image_url( $id );
- } else if ( class_exists( 'VideoPress_Video' ) ) {
- $video = new VideoPress_Video( $id );
- return $video->poster_frame_uri;
- }
- } else if ( 'youtube' == $type ) {
- return 'http://img.youtube.com/vi/'.$id.'/0.jpg';
- }
- }
-
- static function clean_text( $text ) {
- return trim(
- preg_replace(
- '/[\s]+/',
- ' ',
- preg_replace(
- '@https?://[\S]+@',
- '',
- strip_shortcodes(
- strip_tags(
- $text
- )
- )
- )
- )
- );
- }
-
- /**
- * Retrieve an excerpt for the post summary.
- *
- * This function works around a suspected problem with Core. If resolved, this function should be simplified.
- * @link https://github.com/Automattic/jetpack/pull/8510
- * @link https://core.trac.wordpress.org/ticket/42814
- *
- * @param string $post_content The post's content.
- * @param string $post_excerpt The post's excerpt. Empty if none was explicitly set.
- * @param int $max_words Maximum number of words for the excerpt. Used on wp.com. Default 16.
- * @param int $max_chars Maximum characters in the excerpt. Used on wp.com. Default 256.
- * @param WP_Post $requested_post The post object.
- * @return string Post excerpt.
- **/
- static function get_excerpt( $post_content, $post_excerpt, $max_words = 16, $max_chars = 256, $requested_post = null ) {
- global $post;
- $original_post = $post; // Saving the global for later use.
- if ( function_exists( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
- return self::clean_text( wpcom_enhanced_excerpt_extract_excerpt( array(
- 'text' => $post_content,
- 'excerpt_only' => true,
- 'show_read_more' => false,
- 'max_words' => $max_words,
- 'max_chars' => $max_chars,
- 'read_more_threshold' => 25,
- ) ) );
- } elseif ( $requested_post instanceof WP_Post ) {
- $post = $requested_post; // setup_postdata does not set the global.
- setup_postdata( $post );
- /** This filter is documented in core/src/wp-includes/post-template.php */
- $post_excerpt = apply_filters( 'get_the_excerpt', $post_excerpt, $post );
- $post = $original_post; // wp_reset_postdata uses the $post global.
- wp_reset_postdata();
- return self::clean_text( $post_excerpt );
- }
- return '';
- }
-
- static function get_word_count( $post_content ) {
- return str_word_count( self::clean_text( $post_content ) );
- }
-
- static function get_word_remaining_count( $post_content, $excerpt_content ) {
- return str_word_count( self::clean_text( $post_content ) ) - str_word_count( self::clean_text( $excerpt_content ) );
- }
-
- static function get_link_count( $post_content ) {
- return preg_match_all( '/\<a[\> ]/', $post_content, $matches );
- }
-}
diff --git a/plugins/jetpack/_inc/lib/class.media.php b/plugins/jetpack/_inc/lib/class.media.php
deleted file mode 100644
index e48c4aad..00000000
--- a/plugins/jetpack/_inc/lib/class.media.php
+++ /dev/null
@@ -1,505 +0,0 @@
-<?php
-
-require_once( JETPACK__PLUGIN_DIR . 'sal/class.json-api-date.php' );
-
-/**
- * Class to handle different actions related to media.
- */
-class Jetpack_Media {
- public static $WP_ORIGINAL_MEDIA = '_wp_original_post_media';
- public static $WP_REVISION_HISTORY = '_wp_revision_history';
- public static $REVISION_HISTORY_MAXIMUM_AMOUNT = 0;
- public static $WP_ATTACHMENT_IMAGE_ALT = '_wp_attachment_image_alt';
-
- /**
- * Generate a filename in function of the original filename of the media.
- * The returned name has the `{basename}-{hash}-{random-number}.{ext}` shape.
- * The hash is built according to the filename trying to avoid name collisions
- * with other media files.
- *
- * @param number $media_id - media post ID
- * @param string $new_filename - the new filename
- * @return string A random filename.
- */
- public static function generate_new_filename( $media_id, $new_filename ) {
- // get the right filename extension
- $new_filename_paths = pathinfo( $new_filename );
- $new_file_ext = $new_filename_paths['extension'];
-
- // take out filename from the original file or from the current attachment
- $original_media = (array) self::get_original_media( $media_id );
-
- if ( ! empty( $original_media ) ) {
- $original_file_parts = pathinfo( $original_media['file'] );
- $filename_base = $original_file_parts['filename'];
- } else {
- $current_file = get_attached_file( $media_id );
- $current_file_parts = pathinfo( $current_file );
- $current_file_ext = $current_file_parts['filename'];
- $filename_base = $current_file_parts['filename'];
- }
-
- // add unique seed based on the filename
- $filename_base .= '-' . crc32( $filename_base ) . '-';
-
- $number_suffix = time() . rand( 100, 999 );
-
- do {
- $filename = $filename_base;
- $filename .= $number_suffix;
- $file_ext = $new_file_ext ? $new_file_ext : $current_file_ext;
-
- $new_filename = "{$filename}.{$file_ext}";
- $new_path = "{$current_file_parts['dirname']}/$new_filename";
- $number_suffix++;
- } while( file_exists( $new_path ) );
-
- return $new_filename;
- }
-
- /**
- * File urls use the post (image item) date to generate a folder path.
- * Post dates can change, so we use the original date used in the `guid`
- * url so edits can remain in the same folder. In the following function
- * we capture a string in the format of `YYYY/MM` from the guid.
- *
- * For example with a guid of
- * "http://test.files.wordpress.com/2016/10/test.png" the resulting string
- * would be: "2016/10"
- *
- * @param number $media_id
- * @return string
- */
- private function get_time_string_from_guid( $media_id ) {
- $time = date( "Y/m", strtotime( current_time( 'mysql' ) ) );
-
- if ( $media = get_post( $media_id ) ) {
- $pattern = '/\/(\d{4}\/\d{2})\//';
- preg_match( $pattern, $media->guid, $matches );
- if ( count( $matches ) > 1 ) {
- $time = $matches[1];
- }
- }
- return $time;
- }
-
- /**
- * Return an array of allowed mime_type items used to upload a media file.
- *
- * @return array mime_type array
- */
- static function get_allowed_mime_types( $default_mime_types ) {
- return array_unique( array_merge( $default_mime_types, array(
- 'application/msword', // .doc
- 'application/vnd.ms-powerpoint', // .ppt, .pps
- 'application/vnd.ms-excel', // .xls
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation', // .pptx
- 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', // .ppsx
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
- 'application/vnd.oasis.opendocument.text', // .odt
- 'application/pdf', // .pdf
- ) ) );
- }
-
- /**
- * Checks that the mime type of the file
- * is among those in a filterable list of mime types.
- *
- * @param string $file Path to file to get its mime type.
- * @return bool
- */
- protected static function is_file_supported_for_sideloading( $file ) {
- if ( class_exists( 'finfo' ) ) { // php 5.3+
- // phpcs:ignore PHPCompatibility.PHP.NewClasses.finfoFound
- $finfo = new finfo( FILEINFO_MIME );
- $mime = explode( '; ', $finfo->file( $file ) );
- $type = $mime[0];
-
- } elseif ( function_exists( 'mime_content_type' ) ) { // PHP 5.2
- $type = mime_content_type( $file );
-
- } else {
- return false;
- }
-
- /**
- * Filter the list of supported mime types for media sideloading.
- *
- * @since 4.0
- *
- * @module json-api
- *
- * @param array $supported_mime_types Array of the supported mime types for media sideloading.
- */
- $supported_mime_types = apply_filters( 'jetpack_supported_media_sideload_types', array(
- 'image/png',
- 'image/jpeg',
- 'image/gif',
- 'image/bmp',
- 'video/quicktime',
- 'video/mp4',
- 'video/mpeg',
- 'video/ogg',
- 'video/3gpp',
- 'video/3gpp2',
- 'video/h261',
- 'video/h262',
- 'video/h264',
- 'video/x-msvideo',
- 'video/x-ms-wmv',
- 'video/x-ms-asf',
- ) );
-
- // If the type returned was not an array as expected, then we know we don't have a match.
- if ( ! is_array( $supported_mime_types ) ) {
- return false;
- }
-
- return in_array( $type, $supported_mime_types );
- }
-
- /**
- * Try to remove the temporal file from the given file array.
- *
- * @param array $file_array Array with data about the temporal file
- * @return bool `true` if the file has been removed. `false` either the file doesn't exist or it couldn't be removed.
- */
- private static function remove_tmp_file( $file_array ) {
- if ( ! file_exists ( $file_array['tmp_name'] ) ) {
- return false;
- }
- return @unlink( $file_array['tmp_name'] );
- }
-
- /**
- * Save the given temporal file considering file type,
- * correct location according to the original file path, etc.
- * The file type control is done through of `jetpack_supported_media_sideload_types` filter,
- * which allows define to the users their own file types list.
- *
- * @param array $file_array file to save
- * @param number $media_id
- * @return array|WP_Error an array with information about the new file saved or a WP_Error is something went wrong.
- */
- public static function save_temporary_file( $file_array, $media_id ) {
- $tmp_filename = $file_array['tmp_name'];
-
- if ( ! file_exists( $tmp_filename ) ) {
- return new WP_Error( 'invalid_input', 'No media provided in input.' );
- }
-
- // add additional mime_types through of the `jetpack_supported_media_sideload_types` filter
- $mime_type_static_filter = array(
- 'Jetpack_Media',
- 'get_allowed_mime_types'
- );
-
- add_filter( 'jetpack_supported_media_sideload_types', $mime_type_static_filter );
- if (
- ! self::is_file_supported_for_sideloading( $tmp_filename ) &&
- ! file_is_displayable_image( $tmp_filename )
- ) {
- @unlink( $tmp_filename );
- return new WP_Error( 'invalid_input', 'Invalid file type.', 403 );
- }
- remove_filter( 'jetpack_supported_media_sideload_types', $mime_type_static_filter );
-
- // generate a new file name
- $tmp_new_filename = self::generate_new_filename( $media_id, $file_array[ 'name' ] );
-
- // start to create the parameters to move the temporal file
- $overrides = array( 'test_form' => false );
-
- // get time according to the original filaname
- $time = self::get_time_string_from_guid( $media_id );
-
- $file_array['name'] = $tmp_new_filename;
- $file = wp_handle_sideload( $file_array, $overrides, $time );
-
- self::remove_tmp_file( $file_array );
-
- if ( isset( $file['error'] ) ) {
- return new WP_Error( 'upload_error', $file['error'] );
- }
-
- return $file;
- }
-
- /**
- * Return an object with an snapshot of a revision item.
- *
- * @param object $media_item - media post object
- * @return object a revision item
- */
- public static function get_snapshot( $media_item ) {
- $current_file = get_attached_file( $media_item->ID );
- $file_paths = pathinfo( $current_file );
-
- $snapshot = array(
- 'date' => (string) WPCOM_JSON_API_Date::format_date( $media_item->post_modified_gmt, $media_item->post_modified ),
- 'URL' => (string) wp_get_attachment_url( $media_item->ID ),
- 'file' => (string) $file_paths['basename'],
- 'extension' => (string) $file_paths['extension'],
- 'mime_type' => (string) $media_item->post_mime_type,
- 'size' => (int) filesize( $current_file )
- );
-
- return (object) $snapshot;
- }
-
- /**
- * Add a new item into revision_history array.
- *
- * @param object $media_item - media post object
- * @param file $file - file recently added
- * @param bool $has_original_media - condition is the original media has been already added
- * @return bool `true` if the item has been added. Otherwise `false`.
- */
- public static function register_revision( $media_item, $file, $has_original_media ) {
- if ( is_wp_error( $file ) || ! $has_original_media ) {
- return false;
- }
-
- add_post_meta( $media_item->ID, self::$WP_REVISION_HISTORY, self::get_snapshot( $media_item ) );
- }
- /**
- * Return the `revision_history` of the given media.
- *
- * @param number $media_id - media post ID
- * @return array `revision_history` array
- */
- public static function get_revision_history( $media_id ) {
- return array_reverse( get_post_meta( $media_id, self::$WP_REVISION_HISTORY ) );
- }
-
- /**
- * Return the original media data
- */
- public static function get_original_media( $media_id ) {
- $original = get_post_meta( $media_id, self::$WP_ORIGINAL_MEDIA, true );
- $original = $original ? $original : array();
- return $original;
- }
-
- public static function delete_file( $pathname ) {
- if ( ! file_exists( $pathname ) || ! is_file( $pathname ) ) {
- // let's touch a fake file to try to `really` remove the media file
- touch( $pathname );
- }
-
- return wp_delete_file( $pathname );
- }
-
- /**
- * Try to delete a file according to the dirname of
- * the media attached file and the filename.
- *
- * @param number $media_id - media post ID
- * @param string $filename - basename of the file ( name-of-file.ext )
- * @return bool `true` is the file has been removed, `false` if not.
- */
- private static function delete_media_history_file( $media_id, $filename ) {
- $attached_path = get_attached_file( $media_id );
- $attached_parts = pathinfo( $attached_path );
- $dirname = $attached_parts['dirname'];
-
- $pathname = $dirname . '/' . $filename;
-
- // remove thumbnails
- $metadata = wp_generate_attachment_metadata( $media_id, $pathname );
-
- if ( isset( $metadata ) && isset( $metadata['sizes'] ) ) {
- foreach ( $metadata['sizes'] as $size => $properties ) {
- self::delete_file( $dirname . '/' . $properties['file'] );
- }
- }
-
- // remove primary file
- self::delete_file( $pathname );
- }
-
- /**
- * Remove specific items from the `revision history` array
- * depending on the given criteria: array(
- * 'from' => (int) <from>,
- * 'to' => (int) <to>,
- * )
- *
- * Also, it removes the file defined in each item.
- *
- * @param number $media_id - media post ID
- * @param object $criteria - criteria to remove the items
- * @param array [$revision_history] - revision history array
- * @return array `revision_history` array updated.
- */
- public static function remove_items_from_revision_history( $media_id, $criteria = array(), $revision_history ) {
- if ( ! isset ( $revision_history ) ) {
- $revision_history = self::get_revision_history( $media_id );
- }
-
- $from = $criteria['from'];
- $to = $criteria['to'] ? $criteria['to'] : ( $from + 1 );
-
- for ( $i = $from; $i < $to; $i++ ) {
- $removed_item = array_slice( $revision_history, $from, 1 );
- if ( ! $removed_item ) {
- break;
- }
-
- array_splice( $revision_history, $from, 1 );
- self::delete_media_history_file( $media_id, $removed_item[0]->file );
- }
-
- // override all history items
- delete_post_meta( $media_id, self::$WP_REVISION_HISTORY );
- $revision_history = array_reverse( $revision_history );
- foreach ( $revision_history as &$item ) {
- add_post_meta( $media_id, self::$WP_REVISION_HISTORY, $item );
- }
-
- return $revision_history;
- }
-
- /**
- * Limit the number of items of the `revision_history` array.
- * When the stack is overflowing the oldest item is remove from there (FIFO).
- *
- * @param number $media_id - media post ID
- * @param number [$limit] - maximun amount of items. 20 as default.
- * @return array items removed from `revision_history`
- */
- public static function limit_revision_history( $media_id, $limit = null) {
- if ( is_null( $limit ) ) {
- $limit = self::$REVISION_HISTORY_MAXIMUM_AMOUNT;
- }
-
- $revision_history = self::get_revision_history( $media_id );
-
- $total = count( $revision_history );
-
- if ( $total < $limit ) {
- return array();
- }
-
- self::remove_items_from_revision_history(
- $media_id,
- array( 'from' => $limit, 'to' => $total ),
- $revision_history
- );
-
- return self::get_revision_history( $media_id );
- }
-
- /**
- * Remove the original file and clean the post metadata.
- *
- * @param number $media_id - media post ID
- */
- public static function clean_original_media( $media_id ) {
- $original_file = self::get_original_media( $media_id );
-
- if ( ! $original_file ) {
- return null;
- }
-
- self::delete_media_history_file( $media_id, $original_file->file );
- return delete_post_meta( $media_id, self::$WP_ORIGINAL_MEDIA );
- }
-
- /**
- * Clean `revision_history` of the given $media_id. it means:
- * - remove all media files tied to the `revision_history` items.
- * - clean `revision_history` meta data.
- * - remove and clean the `original_media`
- *
- * @param number $media_id - media post ID
- * @return array results of removing these files
- */
- public static function clean_revision_history( $media_id ) {
- self::clean_original_media( $media_id );
-
- $revision_history = self::get_revision_history( $media_id );
- $total = count( $revision_history );
- $updated_history = array();
-
- if ( $total < 1 ) {
- return $updated_history;
- }
-
- $updated_history = self::remove_items_from_revision_history(
- $media_id,
- array( 'from' => 0, 'to' => $total ),
- $revision_history
- );
-
- return $updated_history;
- }
-
- /**
- * Edit media item process:
- *
- * - update attachment file
- * - preserve original media file
- * - trace revision history
- *
- * @param number $media_id - media post ID
- * @param array $file_array - temporal file
- * @return {Post|WP_Error} Updated media item or a WP_Error is something went wrong.
- */
- public static function edit_media_file( $media_id, $file_array ) {
- $media_item = get_post( $media_id );
- $has_original_media = self::get_original_media( $media_id );
-
- if ( ! $has_original_media ) {
- // The first time that the media is updated
- // the original media is stored into the revision_history
- $snapshot = self::get_snapshot( $media_item );
- add_post_meta( $media_id, self::$WP_ORIGINAL_MEDIA, $snapshot, true );
- }
-
- // save temporary file in the correct location
- $uploaded_file = self::save_temporary_file( $file_array, $media_id );
-
- if ( is_wp_error( $uploaded_file ) ) {
- self::remove_tmp_file( $file_array );
- return $uploaded_file;
- }
-
- // revision_history control
- self::register_revision( $media_item, $uploaded_file, $has_original_media );
-
- $uploaded_path = $uploaded_file['file'];
- $udpated_mime_type = $uploaded_file['type'];
- $was_updated = update_attached_file( $media_id, $uploaded_path );
-
- if ( ! $was_updated ) {
- return WP_Error( 'update_error', 'Media update error' );
- }
-
- $new_metadata = wp_generate_attachment_metadata( $media_id, $uploaded_path );
- wp_update_attachment_metadata( $media_id, $new_metadata );
-
- // check maximum amount of revision_history
- self::limit_revision_history( $media_id );
-
- $edited_action = wp_update_post( (object) array(
- 'ID' => $media_id,
- 'post_mime_type' => $udpated_mime_type
- ), true );
-
- if ( is_wp_error( $edited_action ) ) {
- return $edited_action;
- }
-
- return $media_item;
- }
-}
-
-// hook: clean revision history when the media item is deleted
-function clean_revision_history( $media_id ) {
- Jetpack_Media::clean_revision_history( $media_id );
-};
-
-add_action( 'delete_attachment', 'clean_revision_history' );
-
diff --git a/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php b/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php
deleted file mode 100644
index e599b275..00000000
--- a/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php
+++ /dev/null
@@ -1,330 +0,0 @@
-<?php
-
-// @todo - nicer API for array values?
-
-/**
- * `WP_REST_Controller` is basically a wrapper for `register_rest_route()`
- * `WPCOM_REST_API_V2_Field_Controller` is a mostly-analogous wrapper for `register_rest_field()`
- */
-abstract class WPCOM_REST_API_V2_Field_Controller {
- /**
- * @var string|string[] $object_type The REST Object Type(s) to which the field should be added.
- */
- protected $object_type;
-
- /**
- * @var string $field_name The name of the REST API field to add.
- */
- protected $field_name;
-
- public function __construct() {
- if ( ! $this->object_type ) {
- /* translators: %s: object_type */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::$object_type', sprintf( __( "Property '%s' must be overridden.", 'jetpack' ), 'object_type' ), 'Jetpack 6.8' );
- return;
- }
-
- if ( ! $this->field_name ) {
- /* translators: %s: field_name */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::$field_name', sprintf( __( "Property '%s' must be overridden.", 'jetpack' ), 'field_name' ), 'Jetpack 6.8' );
- return;
- }
-
- add_action( 'rest_api_init', array( $this, 'register_fields' ) );
- }
-
- /**
- * Registers the field with the appropriate schema and callbacks.
- */
- public function register_fields() {
- foreach ( (array) $this->object_type as $object_type ) {
- register_rest_field(
- $object_type,
- $this->field_name,
- array(
- 'get_callback' => array( $this, 'get_for_response' ),
- 'update_callback' => array( $this, 'update_from_request' ),
- 'schema' => $this->get_schema(),
- )
- );
- }
- }
-
- /**
- * Ensures the response matches the schema and request context.
- *
- * @param mixed $value
- * @param WP_REST_Request $request
- * @return mixed
- */
- private function prepare_for_response( $value, $request ) {
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $schema = $this->get_schema();
-
- $is_valid = rest_validate_value_from_schema( $value, $schema, $this->field_name );
- if ( is_wp_error( $is_valid ) ) {
- return $is_valid;
- }
-
- return $this->filter_response_by_context( $value, $schema, $context );
- }
-
- /**
- * Returns the schema's default value
- *
- * If there is no default, returns the type's falsey value.
- *
- * @param array $schema
- * @return mixed
- */
- final public function get_default_value( $schema ) {
- if ( isset( $schema['default'] ) ) {
- return $schema['default'];
- }
-
- // If you have something more complicated, use $schema['default'];
- switch ( isset( $schema['type'] ) ? $schema['type'] : 'null' ) {
- case 'string':
- return '';
- case 'integer':
- case 'number':
- return 0;
- case 'object':
- return (object) array();
- case 'array':
- return array();
- case 'boolean':
- return false;
- case 'null':
- default:
- return null;
- }
- }
-
- /**
- * The field's wrapped getter. Does permission checks and output preparation.
- *
- * This cannot be extended: implement `->get()` instead.
- *
- * @param mixed $object_data Probably an array. Whatever the endpoint returns.
- * @param string $field_name Should always match `->field_name`
- * @param WP_REST_Request $request
- * @param string $object_type Should always match `->object_type`
- * @return mixed
- */
- final public function get_for_response( $object_data, $field_name, $request, $object_type ) {
- $permission_check = $this->get_permission_check( $object_data, $request );
-
- if ( ! $permission_check ) {
- /* translators: %s: get_permission_check() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_permission_check', sprintf( __( "Method '%s' must return either true or WP_Error.", 'jetpack' ), 'get_permission_check' ), 'Jetpack 6.8' );
- return $this->get_default_value( $this->get_schema() );
- }
-
- if ( is_wp_error( $permission_check ) ) {
- return $this->get_default_value( $this->get_schema() );
- }
-
- $value = $this->get( $object_data, $request );
-
- return $this->prepare_for_response( $value, $request );
- }
-
- /**
- * The field's wrapped setter. Does permission checks.
- *
- * This cannot be extended: implement `->update()` instead.
- *
- * @param mixed $value The new value for the field.
- * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
- * @param string $field_name Should always match `->field_name`
- * @param WP_REST_Request $request
- * @param string $object_type Should always match `->object_type`
- * @return void|WP_Error
- */
- final public function update_from_request( $value, $object_data, $field_name, $request, $object_type ) {
- $permission_check = $this->update_permission_check( $value, $object_data, $request );
-
- if ( ! $permission_check ) {
- /* translators: %s: update_permission_check() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update_permission_check', sprintf( __( "Method '%s' must return either true or WP_Error.", 'jetpack' ), 'update_permission_check' ), 'Jetpack 6.8' );
- /* translators: %s: the name of an API response field */
- return new WP_Error( 'invalid_user_permission', sprintf( __( "You are not allowed to access the '%s' field.", 'jetpack' ), $this->field_name ) );
- }
-
- if ( is_wp_error( $permission_check ) ) {
- return $permission_check;
- }
-
- $updated = $this->update( $value, $object_data, $request );
-
- if ( is_wp_error( $updated ) ) {
- return $updated;
- }
- }
-
- /**
- * Permission Check for the field's getter. Must be implemented in the inheriting class.
- *
- * @param mixed $object_data Whatever the endpoint would return for its response.
- * @param WP_REST_Request $request
- * @return true|WP_Error
- */
- public function get_permission_check( $object_data, $request ) {
- /* translators: %s: get_permission_check() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_permission_check', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
- }
-
- /**
- * The field's "raw" getter. Must be implemented in the inheriting class.
- *
- * @param mixed $object_data Whatever the endpoint would return for its response.
- * @param WP_REST_Request $request
- * @return mixed
- */
- public function get( $object_data, $request ) {
- /* translators: %s: get() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
- }
-
- /**
- * Permission Check for the field's setter. Must be implemented in the inheriting class.
- *
- * @param mixed $value The new value for the field.
- * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
- * @param WP_REST_Request $request
- * @return true|WP_Error
- */
- public function update_permission_check( $value, $object_data, $request ) {
- /* translators: %s: update_permission_check() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update_permission_check', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
- }
-
- /**
- * The field's "raw" setter. Must be implemented in the inheriting class.
- *
- * @param mixed $value The new value for the field.
- * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
- * @param WP_REST_Request $request
- * @return mixed
- */
- public function update( $value, $object_data, $request ) {
- /* translators: %s: update() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
- }
-
- /**
- * The JSON Schema for the field
- *
- * @link https://json-schema.org/understanding-json-schema/
- * As of WordPress 5.0, Core currently understands:
- * * type
- * * string - not minLength, not maxLength, not pattern
- * * integer - minimum, maximum, exclusiveMinimum, exclusiveMaximum, not multipleOf
- * * number - minimum, maximum, exclusiveMinimum, exclusiveMaximum, not multipleOf
- * * boolean
- * * null
- * * object - properties, additionalProperties, not propertyNames, not dependencies, not patternProperties, not required
- * * array: only lists, not tuples - items, not minItems, not maxItems, not uniqueItems, not contains
- * * enum
- * * format
- * * date-time
- * * email
- * * ip
- * * uri
- * As of WordPress 5.0, Core does not support:
- * * Multiple type: `type: [ 'string', 'integer' ]`
- * * $ref, allOf, anyOf, oneOf, not, const
- *
- * @return array
- */
- public function get_schema() {
- /* translators: %s: get_schema() */
- _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_schema', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
- }
-
- /**
- * @param array $schema
- * @param string $context REST API Request context
- * @return bool
- */
- private function is_valid_for_context( $schema, $context ) {
- return empty( $schema['context'] ) || in_array( $context, $schema['context'], true );
- }
-
- /**
- * Removes properties that should not appear in the current
- * request's context
- *
- * $context is a Core REST API Framework request attribute that is
- * always one of:
- * * view (what you see on the blog)
- * * edit (what you see in an editor)
- * * embed (what you see in, e.g., an oembed)
- *
- * Fields (and sub-fields, and sub-sub-...) can be flagged for a
- * set of specific contexts via the field's schema.
- *
- * The Core API will filter out top-level fields with the wrong
- * context, but will not recurse deeply enough into arrays/objects
- * to remove all levels of sub-fields with the wrong context.
- *
- * This function handles that recursion.
- *
- * @param mixed $value
- * @param array $schema
- * @param string $context REST API Request context
- * @return mixed Filtered $value
- */
- final public function filter_response_by_context( $value, $schema, $context ) {
- if ( ! $this->is_valid_for_context( $schema, $context ) ) {
- // We use this intentionally odd looking WP_Error object
- // internally only in this recursive function (see below
- // in the `object` case). It will never be output by the REST API.
- // If we return this for the top level object, Core
- // correctly remove the top level object from the response
- // for us.
- return new WP_Error( '__wrong-context__' );
- }
-
- switch ( $schema['type'] ) {
- case 'array':
- if ( ! isset( $schema['items'] ) ) {
- return $value;
- }
-
- // Shortcircuit if we know none of the items are valid for this context.
- // This would only happen in a strangely written schema.
- if ( ! $this->is_valid_for_context( $schema['items'], $context ) ) {
- return array();
- }
-
- // Recurse to prune sub-properties of each item.
- foreach ( $value as $key => $item ) {
- $value[ $key ] = $this->filter_response_by_context( $item, $schema['items'], $context );
- }
-
- return $value;
- case 'object':
- if ( ! isset( $schema['properties'] ) ) {
- return $value;
- }
-
- foreach ( $value as $field_name => $field_value ) {
- if ( isset( $schema['properties'][ $field_name ] ) ) {
- $field_value = $this->filter_response_by_context( $field_value, $schema['properties'][ $field_name ], $context );
- if ( is_wp_error( $field_value ) && '__wrong-context__' === $field_value->get_error_code() ) {
- unset( $value[ $field_name ] );
- } else {
- // Respect recursion that pruned sub-properties of each property.
- $value[ $field_name ] = $field_value;
- }
- }
- }
-
- return (object) $value;
- }
-
- return $value;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php
deleted file mode 100644
index 368b381a..00000000
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php
+++ /dev/null
@@ -1,1769 +0,0 @@
-<?php
-/**
- * This is the base class for every Core API endpoint Jetpack uses.
- *
- */
-class Jetpack_Core_API_Module_Toggle_Endpoint
- extends Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
-
- /**
- * Check if the module requires the site to be publicly accessible from WPCOM.
- * If the site meets this requirement, the module is activated. Otherwise an error is returned.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * @type bool $active should module be activated.
- * }
- *
- * @return WP_REST_Response|WP_Error A REST response if the request was served successfully, otherwise an error.
- */
- public function process( $request ) {
- if ( $request['active'] ) {
- return $this->activate_module( $request );
- } else {
- return $this->deactivate_module( $request );
- }
- }
-
- /**
- * If it's a valid Jetpack module, activate it.
- *
- * @since 4.3.0
- *
- * @param string|WP_REST_Request $request It's a WP_REST_Request when called from endpoint /module/<slug>/*
- * and a string when called from Jetpack_Core_API_Data->update_data.
- * {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * }
- *
- * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function activate_module( $request ) {
- $module_slug = '';
-
- if (
- (
- is_array( $request )
- || is_object( $request )
- )
- && isset( $request['slug'] )
- ) {
- $module_slug = $request['slug'];
- } else {
- $module_slug = $request;
- }
-
- if ( ! Jetpack::is_module( $module_slug ) ) {
- return new WP_Error(
- 'not_found',
- esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- if ( ! Jetpack_Plan::supports( $module_slug ) ) {
- return new WP_Error(
- 'not_supported',
- esc_html__( 'The requested Jetpack module is not supported by your plan.', 'jetpack' ),
- array( 'status' => 424 )
- );
- }
-
- if ( Jetpack::activate_module( $module_slug, false, false ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'The requested Jetpack module was activated.', 'jetpack' ),
- ) );
- }
-
- return new WP_Error(
- 'activation_failed',
- esc_html__( 'The requested Jetpack module could not be activated.', 'jetpack' ),
- array( 'status' => 424 )
- );
- }
-
- /**
- * If it's a valid Jetpack module, deactivate it.
- *
- * @since 4.3.0
- *
- * @param string|WP_REST_Request $request It's a WP_REST_Request when called from endpoint /module/<slug>/*
- * and a string when called from Jetpack_Core_API_Data->update_data.
- * {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * }
- *
- * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function deactivate_module( $request ) {
- $module_slug = '';
-
- if (
- (
- is_array( $request )
- || is_object( $request )
- )
- && isset( $request['slug'] )
- ) {
- $module_slug = $request['slug'];
- } else {
- $module_slug = $request;
- }
-
- if ( ! Jetpack::is_module( $module_slug ) ) {
- return new WP_Error(
- 'not_found',
- esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- if ( ! Jetpack::is_module_active( $module_slug ) ) {
- return new WP_Error(
- 'already_inactive',
- esc_html__( 'The requested Jetpack module was already inactive.', 'jetpack' ),
- array( 'status' => 409 )
- );
- }
-
- if ( Jetpack::deactivate_module( $module_slug ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'The requested Jetpack module was deactivated.', 'jetpack' ),
- ) );
- }
- return new WP_Error(
- 'deactivation_failed',
- esc_html__( 'The requested Jetpack module could not be deactivated.', 'jetpack' ),
- array( 'status' => 400 )
- );
- }
-
- /**
- * Check that the current user has permissions to manage Jetpack modules.
- *
- * @since 4.3.0
- *
- * @return bool
- */
- public function can_request() {
- return current_user_can( 'jetpack_manage_modules' );
- }
-}
-
-class Jetpack_Core_API_Module_List_Endpoint {
-
- /**
- * A WordPress REST API callback method that accepts a request object and decides what to do with it.
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @since 4.3.0
- *
- * @return bool|Array|WP_Error a resulting value or object, or an error.
- */
- public function process( $request ) {
- if ( 'GET' === $request->get_method() ) {
- return $this->get_modules( $request );
- } else {
- return $this->activate_modules( $request );
- }
- }
-
- /**
- * Get a list of all Jetpack modules and their information.
- *
- * @since 4.3.0
- *
- * @return array Array of Jetpack modules.
- */
- public function get_modules() {
- require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php' );
-
- $modules = Jetpack_Admin::init()->get_modules();
- foreach ( $modules as $slug => $properties ) {
- $modules[ $slug ]['options'] =
- Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $slug );
- if (
- isset( $modules[ $slug ]['requires_connection'] )
- && $modules[ $slug ]['requires_connection']
- && Jetpack::is_development_mode()
- ) {
- $modules[ $slug ]['activated'] = false;
- }
- }
-
- $modules = Jetpack::get_translated_modules( $modules );
-
- return Jetpack_Core_Json_Api_Endpoints::prepare_modules_for_response( $modules );
- }
-
- /**
- * Activate a list of valid Jetpack modules.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * }
- *
- * @return bool|WP_Error True if modules were activated. Otherwise, a WP_Error instance with the corresponding error.
- */
- public static function activate_modules( $request ) {
-
- if (
- ! isset( $request['modules'] )
- || ! is_array( $request['modules'] )
- ) {
- return new WP_Error(
- 'not_found',
- esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- $activated = array();
- $failed = array();
-
- foreach ( $request['modules'] as $module ) {
- if ( Jetpack::activate_module( $module, false, false ) ) {
- $activated[] = $module;
- } else {
- $failed[] = $module;
- }
- }
-
- if ( empty( $failed ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'All modules activated.', 'jetpack' ),
- ) );
- }
-
- $error = '';
-
- $activated_count = count( $activated );
- if ( $activated_count > 0 ) {
- $activated_last = array_pop( $activated );
- $activated_text = $activated_count > 1 ? sprintf(
- /* Translators: first variable is a list followed by the last item, which is the second variable. Example: dog, cat and bird. */
- __( '%1$s and %2$s', 'jetpack' ),
- join( ', ', $activated ), $activated_last ) : $activated_last;
-
- $error = sprintf(
- /* Translators: the variable is a module name. */
- _n( 'The module %s was activated.', 'The modules %s were activated.', $activated_count, 'jetpack' ),
- $activated_text ) . ' ';
- }
-
- $failed_count = count( $failed );
- if ( count( $failed ) > 0 ) {
- $failed_last = array_pop( $failed );
- $failed_text = $failed_count > 1 ? sprintf(
- /* Translators: first variable is a list followed by the last item, which is the second variable. Example: dog, cat and bird. */
- __( '%1$s and %2$s', 'jetpack' ),
- join( ', ', $failed ), $failed_last ) : $failed_last;
-
- $error = sprintf(
- /* Translators: the variable is a module name. */
- _n( 'The module %s failed to be activated.', 'The modules %s failed to be activated.', $failed_count, 'jetpack' ),
- $failed_text ) . ' ';
- }
-
- return new WP_Error(
- 'activation_failed',
- esc_html( $error ),
- array( 'status' => 424 )
- );
- }
-
- /**
- * A WordPress REST API permission callback method that accepts a request object and decides
- * if the current user has enough privileges to act.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool does the current user have enough privilege.
- */
- public function can_request( $request ) {
- if ( 'GET' === $request->get_method() ) {
- return current_user_can( 'jetpack_admin_page' );
- } else {
- return current_user_can( 'jetpack_manage_modules' );
- }
- }
-}
-
-/**
- * Class that manages updating of Jetpack module options and general Jetpack settings or retrieving module data.
- * If no module is specified, all module settings are retrieved/updated.
- *
- * @since 4.3.0
- * @since 4.4.0 Renamed Jetpack_Core_API_Module_Endpoint from to Jetpack_Core_API_Data.
- *
- * @author Automattic
- */
-class Jetpack_Core_API_Data extends Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
-
- /**
- * Process request by returning the module or updating it.
- * If no module is specified, settings for all modules are assumed.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request
- *
- * @return bool|mixed|void|WP_Error
- */
- public function process( $request ) {
- if ( 'GET' === $request->get_method() ) {
- if ( isset( $request['slug'] ) ) {
- return $this->get_module( $request );
- }
-
- return $this->get_all_options();
- } else {
- return $this->update_data( $request );
- }
- }
-
- /**
- * Get information about a specific and valid Jetpack module.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * }
- *
- * @return mixed|void|WP_Error
- */
- public function get_module( $request ) {
- if ( Jetpack::is_module( $request['slug'] ) ) {
-
- $module = Jetpack::get_module( $request['slug'] );
-
- $module['options'] = Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $request['slug'] );
-
- if (
- isset( $module['requires_connection'] )
- && $module['requires_connection']
- && Jetpack::is_development_mode()
- ) {
- $module['activated'] = false;
- }
-
- $i18n = jetpack_get_module_i18n( $request['slug'] );
- if ( isset( $module['name'] ) ) {
- $module['name'] = $i18n['name'];
- }
- if ( isset( $module['description'] ) ) {
- $module['description'] = $i18n['description'];
- $module['short_description'] = $i18n['description'];
- }
-
- return Jetpack_Core_Json_Api_Endpoints::prepare_modules_for_response( $module );
- }
-
- return new WP_Error(
- 'not_found',
- esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- /**
- * Get information about all Jetpack module options and settings.
- *
- * @since 4.6.0
- *
- * @return WP_REST_Response $response
- */
- public function get_all_options() {
- $response = array();
-
- $modules = Jetpack::get_available_modules();
- if ( is_array( $modules ) && ! empty( $modules ) ) {
- foreach ( $modules as $module ) {
- // Add all module options
- $options = Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $module );
- foreach ( $options as $option_name => $option ) {
- $response[ $option_name ] = $option['current_value'];
- }
-
- // Add the module activation state
- $response[ $module ] = Jetpack::is_module_active( $module );
- }
- }
-
- $settings = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( 'settings' );
-
- if ( ! function_exists( 'is_plugin_active' ) ) {
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
- }
-
- foreach ( $settings as $setting => $properties ) {
- switch ( $setting ) {
- case 'lang_id':
- if ( defined( 'WPLANG' ) ) {
- // We can't affect this setting, so warn the client
- $response[ $setting ] = 'error_const';
- break;
- }
-
- if ( ! current_user_can( 'install_languages' ) ) {
- // The user doesn't have caps to install language packs, so warn the client
- $response[ $setting ] = 'error_cap';
- break;
- }
-
- $value = get_option( 'WPLANG' );
- $response[ $setting ] = empty( $value ) ? 'en_US' : $value;
- break;
-
- case 'wordpress_api_key':
- // When field is clear, return empty. Otherwise it would return "false".
- if ( '' === get_option( 'wordpress_api_key', '' ) ) {
- $response[ $setting ] = '';
- } else {
- if ( ! class_exists( 'Akismet' ) ) {
- if ( is_readable( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
- require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
- }
- }
- $response[ $setting ] = class_exists( 'Akismet' ) ? Akismet::get_api_key() : '';
- }
- break;
-
- case 'onboarding':
- $business_address = get_option( 'jpo_business_address' );
- $business_address = is_array( $business_address ) ? array_map( array( $this, 'decode_special_characters' ), $business_address ) : $business_address;
-
- $response[ $setting ] = array(
- 'siteTitle' => $this->decode_special_characters( get_option( 'blogname' ) ),
- 'siteDescription' => $this->decode_special_characters( get_option( 'blogdescription' ) ),
- 'siteType' => get_option( 'jpo_site_type' ),
- 'homepageFormat' => get_option( 'jpo_homepage_format' ),
- 'addContactForm' => intval( get_option( 'jpo_contact_page' ) ),
- 'businessAddress' => $business_address,
- 'installWooCommerce' => is_plugin_active( 'woocommerce/woocommerce.php' ),
- 'stats' => Jetpack::is_active() && Jetpack::is_module_active( 'stats' ),
- );
- break;
-
- default:
- $response[ $setting ] = Jetpack_Core_Json_Api_Endpoints::cast_value( get_option( $setting ), $settings[ $setting ] );
- break;
- }
- }
-
- $response['akismet'] = is_plugin_active( 'akismet/akismet.php' );
-
- return rest_ensure_response( $response );
- }
-
- /**
- * Decode the special HTML characters in a certain value.
- *
- * @since 5.8
- *
- * @param string $value Value to decode.
- *
- * @return string Value with decoded HTML characters.
- */
- private function decode_special_characters( $value ) {
- return (string) htmlspecialchars_decode( $value, ENT_QUOTES );
- }
-
- /**
- * If it's a valid Jetpack module and configuration parameters have been sent, update it.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Module slug.
- * }
- *
- * @return bool|WP_Error True if module was updated. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function update_data( $request ) {
-
- // If it's null, we're trying to update many module options from different modules.
- if ( is_null( $request['slug'] ) ) {
-
- // Value admitted by Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list that will make it return all module options.
- // It will not be passed. It's just checked in this method to pass that method a string or array.
- $request['slug'] = 'any';
- } else {
- if ( ! Jetpack::is_module( $request['slug'] ) ) {
- return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- if ( ! Jetpack::is_module_active( $request['slug'] ) ) {
- return new WP_Error( 'inactive', esc_html__( 'The requested Jetpack module is inactive.', 'jetpack' ), array( 'status' => 409 ) );
- }
- }
-
- // Get parameters to update the module. We can not simply use $request->get_params() because when we registered
- // this route, we are adding the entire output of Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list() to
- // the current request object's params. We are interested in body of the actual request.
- // This may be JSON:
- $params = $request->get_json_params();
- if ( ! is_array( $params ) ) {
- // Or it may be standard POST key-value pairs:
- $params = $request->get_body_params();
- }
-
- // Exit if no parameters were passed.
- if ( ! is_array( $params ) ) {
- return new WP_Error( 'missing_options', esc_html__( 'Missing options.', 'jetpack' ), array( 'status' => 404 ) );
- }
-
- // If $params was set via `get_body_params()` there may be some additional variables in the request that can
- // cause validation to fail. This method verifies that each param was in fact updated and will throw a `some_updated`
- // error if unused variables are included in the request.
- foreach ( array_keys( $params ) as $key ) {
- if ( is_int( $key ) || 'slug' === $key || 'context' === $key ) {
- unset( $params[ $key ] );
- }
- }
-
- // Get available module options.
- $options = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( 'any' === $request['slug']
- ? $params
- : $request['slug']
- );
-
- // Prepare to toggle module if needed
- $toggle_module = new Jetpack_Core_API_Module_Toggle_Endpoint( new Jetpack_IXR_Client() );
-
- // Options that are invalid or failed to update.
- $invalid = array_keys( array_diff_key( $params, $options ) );
- $not_updated = array();
-
- // Remove invalid options
- $params = array_intersect_key( $params, $options );
-
- // Used if response is successful. The message can be overwritten and additional data can be added here.
- $response = array(
- 'code' => 'success',
- 'message' => esc_html__( 'The requested Jetpack data updates were successful.', 'jetpack' ),
- );
-
- // If there are modules to activate, activate them first so they're ready when their options are set.
- foreach ( $params as $option => $value ) {
- if ( 'modules' === $options[ $option ]['jp_group'] ) {
-
- // Used if there was an error. Can be overwritten with specific error messages.
- $error = '';
-
- // Set to true if the module toggling was successful.
- $updated = false;
-
- // Check if user can toggle the module.
- if ( $toggle_module->can_request() ) {
-
- // Activate or deactivate the module according to the value passed.
- $toggle_result = $value
- ? $toggle_module->activate_module( $option )
- : $toggle_module->deactivate_module( $option );
-
- if (
- is_wp_error( $toggle_result )
- && 'already_inactive' === $toggle_result->get_error_code()
- ) {
-
- // If the module is already inactive, we don't fail
- $updated = true;
- } elseif ( is_wp_error( $toggle_result ) ) {
- $error = $toggle_result->get_error_message();
- } else {
- $updated = true;
- }
- } else {
- $error = Jetpack_Core_Json_Api_Endpoints::$user_permissions_error_msg;
- }
-
- // The module was not toggled.
- if ( ! $updated ) {
- $not_updated[ $option ] = $error;
- }
-
- // Remove module from list so we don't go through it again.
- unset( $params[ $option ] );
- }
- }
-
- foreach ( $params as $option => $value ) {
-
- // Used if there was an error. Can be overwritten with specific error messages.
- $error = '';
-
- // Set to true if the option update was successful.
- $updated = false;
-
- // Get option attributes, including the group it belongs to.
- $option_attrs = $options[ $option ];
-
- // If this is a module option and the related module isn't active for any reason, continue with the next one.
- if ( 'settings' !== $option_attrs['jp_group'] ) {
- if ( ! Jetpack::is_module( $option_attrs['jp_group'] ) ) {
- $not_updated[ $option ] = esc_html__( 'The requested Jetpack module was not found.', 'jetpack' );
- continue;
- }
-
- if (
- 'any' !== $request['slug']
- && ! Jetpack::is_module_active( $option_attrs['jp_group'] )
- ) {
-
- // We only take note of skipped options when updating one module
- $not_updated[ $option ] = esc_html__( 'The requested Jetpack module is inactive.', 'jetpack' );
- continue;
- }
- }
-
- // Properly cast value based on its type defined in endpoint accepted args.
- $value = Jetpack_Core_Json_Api_Endpoints::cast_value( $value, $option_attrs );
-
- switch ( $option ) {
- case 'lang_id':
- if ( defined( 'WPLANG' ) || ! current_user_can( 'install_languages' ) ) {
- // We can't affect this setting
- $updated = false;
- break;
- }
-
- if ( $value === 'en_US' || empty( $value ) ) {
- return delete_option( 'WPLANG' );
- }
-
- if ( ! function_exists( 'request_filesystem_credentials' ) ) {
- require_once( ABSPATH . 'wp-admin/includes/file.php' );
- }
-
- if ( ! function_exists( 'wp_download_language_pack' ) ) {
- require_once ABSPATH . 'wp-admin/includes/translation-install.php';
- }
-
- // `wp_download_language_pack` only tries to download packs if they're not already available
- $language = wp_download_language_pack( $value );
- if ( $language === false ) {
- // The language pack download failed.
- $updated = false;
- break;
- }
- $updated = get_option( 'WPLANG' ) === $language ? true : update_option( 'WPLANG', $language );
- break;
-
- case 'monitor_receive_notifications':
- $monitor = new Jetpack_Monitor();
-
- // If we got true as response, consider it done.
- $updated = true === $monitor->update_option_receive_jetpack_monitor_notification( $value );
- break;
-
- case 'post_by_email_address':
- if ( 'create' == $value ) {
- $result = $this->_process_post_by_email(
- 'jetpack.createPostByEmailAddress',
- esc_html__( 'Unable to create the Post by Email address. Please try again later.', 'jetpack' )
- );
- } elseif ( 'regenerate' == $value ) {
- $result = $this->_process_post_by_email(
- 'jetpack.regeneratePostByEmailAddress',
- esc_html__( 'Unable to regenerate the Post by Email address. Please try again later.', 'jetpack' )
- );
- } elseif ( 'delete' == $value ) {
- $result = $this->_process_post_by_email(
- 'jetpack.deletePostByEmailAddress',
- esc_html__( 'Unable to delete the Post by Email address. Please try again later.', 'jetpack' )
- );
- } else {
- $result = false;
- }
-
- // If we got an email address (create or regenerate) or 1 (delete), consider it done.
- if ( is_string( $result ) && preg_match( '/[a-z0-9]+@post.wordpress.com/', $result ) ) {
- $response[$option] = $result;
- $updated = true;
- } elseif ( 1 == $result ) {
- $updated = true;
- } elseif ( is_array( $result ) && isset( $result['message'] ) ) {
- $error = $result['message'];
- }
- break;
-
- case 'jetpack_protect_key':
- $protect = Jetpack_Protect_Module::instance();
- if ( 'create' == $value ) {
- $result = $protect->get_protect_key();
- } else {
- $result = false;
- }
-
- // If we got one of Protect keys, consider it done.
- if ( preg_match( '/[a-z0-9]{40,}/i', $result ) ) {
- $response[$option] = $result;
- $updated = true;
- }
- break;
-
- case 'jetpack_protect_global_whitelist':
- $updated = jetpack_protect_save_whitelist( explode( PHP_EOL, str_replace( array( ' ', ',' ), array( '', "\n" ), $value ) ) );
- if ( is_wp_error( $updated ) ) {
- $error = $updated->get_error_message();
- }
- break;
-
- case 'show_headline':
- case 'show_thumbnails':
- $grouped_options = $grouped_options_current = (array) Jetpack_Options::get_option( 'relatedposts' );
- $grouped_options[$option] = $value;
-
- // If option value was the same, consider it done.
- $updated = $grouped_options_current != $grouped_options ? Jetpack_Options::update_option( 'relatedposts', $grouped_options ) : true;
- break;
-
- case 'google':
- case 'bing':
- case 'pinterest':
- case 'yandex':
- $grouped_options = $grouped_options_current = (array) get_option( 'verification_services_codes' );
-
- // Extracts the content attribute from the HTML meta tag if needed
- if ( preg_match( '#.*<meta name="(?:[^"]+)" content="([^"]+)" />.*#i', $value, $matches ) ) {
- $grouped_options[ $option ] = $matches[1];
- } else {
- $grouped_options[ $option ] = $value;
- }
-
- // If option value was the same, consider it done.
- $updated = $grouped_options_current != $grouped_options ? update_option( 'verification_services_codes', $grouped_options ) : true;
- break;
-
- case 'sharing_services':
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- break;
- }
-
- $sharer = new Sharing_Service();
-
- // If option value was the same, consider it done.
- $updated = $value != $sharer->get_blog_services() ? $sharer->set_blog_services( $value['visible'], $value['hidden'] ) : true;
- break;
-
- case 'button_style':
- case 'sharing_label':
- case 'show':
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- break;
- }
-
- $sharer = new Sharing_Service();
- $grouped_options = $sharer->get_global_options();
- $grouped_options[ $option ] = $value;
- $updated = $sharer->set_global_options( $grouped_options );
- break;
-
- case 'custom':
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- break;
- }
-
- $sharer = new Sharing_Service();
- $updated = $sharer->new_service( stripslashes( $value['sharing_name'] ), stripslashes( $value['sharing_url'] ), stripslashes( $value['sharing_icon'] ) );
-
- // Return new custom service
- $response[$option] = $updated;
- break;
-
- case 'sharing_delete_service':
- if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
- break;
- }
-
- $sharer = new Sharing_Service();
- $updated = $sharer->delete_service( $value );
- break;
-
- case 'jetpack-twitter-cards-site-tag':
- $value = trim( ltrim( strip_tags( $value ), '@' ) );
- $updated = get_option( $option ) !== $value ? update_option( $option, $value ) : true;
- break;
-
- case 'onpublish':
- case 'onupdate':
- case 'Bias Language':
- case 'Cliches':
- case 'Complex Expression':
- case 'Diacritical Marks':
- case 'Double Negative':
- case 'Hidden Verbs':
- case 'Jargon Language':
- case 'Passive voice':
- case 'Phrases to Avoid':
- case 'Redundant Expression':
- case 'guess_lang':
- if ( in_array( $option, array( 'onpublish', 'onupdate' ) ) ) {
- $atd_option = 'AtD_check_when';
- } elseif ( 'guess_lang' == $option ) {
- $atd_option = 'AtD_guess_lang';
- $option = 'true';
- } else {
- $atd_option = 'AtD_options';
- }
- $user_id = get_current_user_id();
- if ( ! function_exists( 'AtD_get_options' ) ) {
- include_once( JETPACK__PLUGIN_DIR . 'modules/after-the-deadline.php' );
- }
- $grouped_options_current = AtD_get_options( $user_id, $atd_option );
- unset( $grouped_options_current['name'] );
- $grouped_options = $grouped_options_current;
- if ( $value && ! isset( $grouped_options [$option] ) ) {
- $grouped_options [$option] = $value;
- } elseif ( ! $value && isset( $grouped_options [$option] ) ) {
- unset( $grouped_options [$option] );
- }
- // If option value was the same, consider it done, otherwise try to update it.
- $options_to_save = implode( ',', array_keys( $grouped_options ) );
- $updated = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $options_to_save ) : true;
- break;
-
- case 'ignored_phrases':
- case 'unignore_phrase':
- $user_id = get_current_user_id();
- $atd_option = 'AtD_ignored_phrases';
- $grouped_options = $grouped_options_current = explode( ',', AtD_get_setting( $user_id, $atd_option ) );
- if ( 'ignored_phrases' == $option ) {
- $grouped_options = explode( ',', $value );
- } else {
- $index = array_search( $value, $grouped_options );
- if ( false !== $index ) {
- unset( $grouped_options[$index] );
- $grouped_options = array_values( $grouped_options );
- }
- }
- $ignored_phrases = implode( ',', array_filter( array_map( 'strip_tags', $grouped_options ) ) );
- $updated = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $ignored_phrases ) : true;
- break;
-
- case 'admin_bar':
- case 'roles':
- case 'count_roles':
- case 'blog_id':
- case 'do_not_track':
- case 'hide_smile':
- case 'version':
- $grouped_options = $grouped_options_current = (array) get_option( 'stats_options' );
- $grouped_options[$option] = $value;
-
- // If option value was the same, consider it done.
- $updated = $grouped_options_current != $grouped_options ? update_option( 'stats_options', $grouped_options ) : true;
- break;
-
- case 'akismet_show_user_comments_approved':
-
- // Save Akismet option '1' or '0' like it's done in akismet/class.akismet-admin.php
- $updated = get_option( $option ) != $value ? update_option( $option, (bool) $value ? '1' : '0' ) : true;
- break;
-
- case 'wordpress_api_key':
-
- if ( ! file_exists( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
- $error = esc_html__( 'Please install Akismet.', 'jetpack' );
- $updated = false;
- break;
- }
-
- if ( ! defined( 'AKISMET_VERSION' ) ) {
- $error = esc_html__( 'Please activate Akismet.', 'jetpack' );
- $updated = false;
- break;
- }
-
- // Allow to clear the API key field
- if ( '' === $value ) {
- $updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
- break;
- }
-
- require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
- require_once WP_PLUGIN_DIR . '/akismet/class.akismet-admin.php';
-
- if ( class_exists( 'Akismet_Admin' ) && method_exists( 'Akismet_Admin', 'save_key' ) ) {
- if ( Akismet::verify_key( $value ) === 'valid' ) {
- $akismet_user = Akismet_Admin::get_akismet_user( $value );
- if ( $akismet_user ) {
- if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) ) {
- $updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
- break;
- } else {
- $error = esc_html__( "Akismet user status doesn't allow to update the key", 'jetpack' );
- }
- } else {
- $error = esc_html__( 'Invalid Akismet user', 'jetpack' );
- }
- } else {
- $error = esc_html__( 'Invalid Akismet key', 'jetpack' );
- }
- } else {
- $error = esc_html__( 'Akismet is not installed or active', 'jetpack' );
- }
- $updated = false;
- break;
-
- case 'google_analytics_tracking_id':
- $grouped_options = $grouped_options_current = (array) get_option( 'jetpack_wga' );
- $grouped_options[ 'code' ] = $value;
-
- // If option value was the same, consider it done.
- $updated = $grouped_options_current != $grouped_options ? update_option( 'jetpack_wga', $grouped_options ) : true;
- break;
-
- case 'dismiss_dash_app_card':
- case 'dismiss_empty_stats_card':
- // If option value was the same, consider it done.
- $updated = get_option( $option ) != $value ? update_option( $option, (bool) $value ) : true;
- break;
-
- case 'onboarding':
- jetpack_require_lib( 'widgets' );
- // Break apart and set Jetpack onboarding options.
- $result = $this->_process_onboarding( (array) $value );
- if ( empty( $result ) ) {
- $updated = true;
- } else {
- $error = sprintf( esc_html__( 'Onboarding failed to process: %s', 'jetpack' ), $result );
- $updated = false;
- }
- break;
-
- case 'show_welcome_for_new_plan':
- // If option value was the same, consider it done.
- $updated = get_option( $option ) !== $value ? update_option( $option, (bool) $value ) : true;
- break;
-
- default:
- // If option value was the same, consider it done.
- $updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
- break;
- }
-
- // The option was not updated.
- if ( ! $updated ) {
- $not_updated[ $option ] = $error;
- }
- }
-
- if ( empty( $invalid ) && empty( $not_updated ) ) {
- // The option was updated.
- return rest_ensure_response( $response );
- } else {
- $invalid_count = count( $invalid );
- $not_updated_count = count( $not_updated );
- $error = '';
- if ( $invalid_count > 0 ) {
- $error = sprintf(
- /* Translators: the plural variable is a comma-separated list. Example: dog, cat, bird. */
- _n( 'Invalid option: %s.', 'Invalid options: %s.', $invalid_count, 'jetpack' ),
- join( ', ', $invalid )
- );
- }
- if ( $not_updated_count > 0 ) {
- $not_updated_messages = array();
- foreach ( $not_updated as $not_updated_option => $not_updated_message ) {
- if ( ! empty( $not_updated_message ) ) {
- $not_updated_messages[] = sprintf(
- /* Translators: the first variable is a module option or slug, or setting. The second is the error message . */
- __( '%1$s: %2$s', 'jetpack' ),
- $not_updated_option, $not_updated_message );
- }
- }
- if ( ! empty( $error ) ) {
- $error .= ' ';
- }
- if ( ! empty( $not_updated_messages ) ) {
- $error .= ' ' . join( '. ', $not_updated_messages );
- }
-
- }
- // There was an error because some options were updated but others were invalid or failed to update.
- return new WP_Error( 'some_updated', esc_html( $error ), array( 'status' => 400 ) );
- }
-
- }
-
- /**
- * Perform tasks in the site based on onboarding choices.
- *
- * @since 5.4.0
- *
- * @param array $data Onboarding choices made by user.
- *
- * @return string Result of onboarding processing and, if there is one, an error message.
- */
- private function _process_onboarding( $data ) {
- if ( isset( $data['end'] ) && $data['end'] ) {
- return Jetpack::invalidate_onboarding_token()
- ? ''
- : esc_html__( "The onboarding token couldn't be deleted.", 'jetpack' );
- }
-
- $error = array();
-
- if ( ! empty( $data['siteTitle'] ) ) {
- // If option value was the same, consider it done.
- if ( ! ( update_option( 'blogname', $data['siteTitle'] ) || get_option( 'blogname' ) == $data['siteTitle'] ) ) {
- $error[] = 'siteTitle';
- }
- }
-
- if ( isset( $data['siteDescription'] ) ) {
- // If option value was the same, consider it done.
- if ( ! ( update_option( 'blogdescription', $data['siteDescription'] ) || get_option( 'blogdescription' ) == $data['siteDescription'] ) ) {
- $error[] = 'siteDescription';
- }
- }
-
- $site_title = get_option( 'blogname' );
- $author = get_current_user_id() || 1;
-
- if ( ! empty( $data['siteType'] ) ) {
- if ( ! ( update_option( 'jpo_site_type', $data['siteType'] ) || get_option( 'jpo_site_type' ) == $data['siteType'] ) ) {
- $error[] = 'siteType';
- }
- }
-
- if ( isset( $data['homepageFormat'] ) ) {
- // If $data['homepageFormat'] is 'posts', we have nothing to do since it's WordPress' default
- // if it exists, just update
- $homepage_format = get_option( 'jpo_homepage_format' );
- if ( ! $homepage_format || $homepage_format !== $data['homepageFormat'] ) {
- if ( 'page' === $data['homepageFormat'] ) {
- if ( ! ( update_option( 'show_on_front', 'page' ) || get_option( 'show_on_front' ) == 'page' ) ) {
- $error[] = 'homepageFormat';
- }
-
- $home = wp_insert_post( array(
- 'post_type' => 'page',
- /* translators: this references the home page of a site, also called front page. */
- 'post_title' => esc_html_x( 'Home Page', 'The home page of a website.', 'jetpack' ),
- 'post_content' => sprintf( esc_html__( 'Welcome to %s.', 'jetpack' ), $site_title ),
- 'post_status' => 'publish',
- 'post_author' => $author,
- ) );
- if ( 0 == $home ) {
- $error[] = 'home insert: 0';
- } elseif ( is_wp_error( $home ) ) {
- $error[] = 'home creation: '. $home->get_error_message();
- }
- if ( ! ( update_option( 'page_on_front', $home ) || get_option( 'page_on_front' ) == $home ) ) {
-
- $error[] = 'home set';
- }
-
- $blog = wp_insert_post( array(
- 'post_type' => 'page',
- /* translators: this references the page where blog posts are listed. */
- 'post_title' => esc_html_x( 'Blog', 'The blog of a website.', 'jetpack' ),
- 'post_content' => sprintf( esc_html__( 'These are the latest posts in %s.', 'jetpack' ), $site_title ),
- 'post_status' => 'publish',
- 'post_author' => $author,
- ) );
- if ( 0 == $blog ) {
- $error[] = 'blog insert: 0';
- } elseif ( is_wp_error( $blog ) ) {
- $error[] = 'blog creation: '. $blog->get_error_message();
- }
- if ( ! ( update_option( 'page_for_posts', $blog ) || get_option( 'page_for_posts' ) == $blog ) ) {
- $error[] = 'blog set';
- }
- } else {
- $front_page = get_option( 'page_on_front' );
- $posts_page = get_option( 'page_for_posts' );
- if ( $posts_page && get_post( $posts_page ) ) {
- wp_delete_post( $posts_page );
- }
- if ( $front_page && get_post( $front_page ) ) {
- wp_delete_post( $front_page );
- }
- update_option( 'show_on_front', 'posts' );
- }
- }
- update_option( 'jpo_homepage_format', $data['homepageFormat'] );
- }
-
- // Setup contact page and add a form and/or business info
- $contact_page = '';
- if ( ! empty( $data['addContactForm'] ) && ! get_option( 'jpo_contact_page' ) ) {
- $contact_form_module_active = Jetpack::is_module_active( 'contact-form' );
- if ( ! $contact_form_module_active ) {
- $contact_form_module_active = Jetpack::activate_module( 'contact-form', false, false );
- }
-
- if ( $contact_form_module_active ) {
- $contact_page = '[contact-form][contact-field label="' . esc_html__( 'Name', 'jetpack' ) . '" type="name" required="true" /][contact-field label="' . esc_html__( 'Email', 'jetpack' ) . '" type="email" required="true" /][contact-field label="' . esc_html__( 'Website', 'jetpack' ) . '" type="url" /][contact-field label="' . esc_html__( 'Message', 'jetpack' ) . '" type="textarea" /][/contact-form]';
- } else {
- $error[] = 'contact-form activate';
- }
- }
-
- if ( isset( $data['businessPersonal'] ) && 'business' === $data['businessPersonal'] ) {
- $contact_page .= "\n" . join( "\n", $data['businessInfo'] );
- }
-
- if ( ! empty( $contact_page ) ) {
- $form = wp_insert_post( array(
- 'post_type' => 'page',
- /* translators: this references a page with contact details and possibly a form. */
- 'post_title' => esc_html_x( 'Contact us', 'Contact page for your website.', 'jetpack' ),
- 'post_content' => esc_html__( 'Send us a message!', 'jetpack' ) . "\n" . $contact_page,
- 'post_status' => 'publish',
- 'post_author' => $author,
- ) );
- if ( 0 == $form ) {
- $error[] = 'form insert: 0';
- } elseif ( is_wp_error( $form ) ) {
- $error[] = 'form creation: '. $form->get_error_message();
- } else {
- update_option( 'jpo_contact_page', $form );
- }
- }
-
- if ( isset( $data['businessAddress'] ) ) {
- $handled_business_address = self::handle_business_address( $data['businessAddress'] );
- if ( is_wp_error( $handled_business_address ) ) {
- $error[] = 'BusinessAddress';
- }
- }
-
- if ( ! empty( $data['installWooCommerce'] ) ) {
- jetpack_require_lib( 'plugins' );
- $wc_install_result = Jetpack_Plugins::install_and_activate_plugin( 'woocommerce' );
- delete_transient( '_wc_activation_redirect' ); // Redirecting to WC setup would kill our users' flow
- if ( is_wp_error( $wc_install_result ) ) {
- $error[] = 'woocommerce installation';
- }
- }
-
- if ( ! empty( $data['stats'] ) ) {
- if ( Jetpack::is_active() ) {
- $stats_module_active = Jetpack::is_module_active( 'stats' );
- if ( ! $stats_module_active ) {
- $stats_module_active = Jetpack::activate_module( 'stats', false, false );
- }
-
- if ( ! $stats_module_active ) {
- $error[] = 'stats activate';
- }
- } else {
- $error[] = 'stats not connected';
- }
- }
-
- return empty( $error )
- ? ''
- : join( ', ', $error );
- }
-
- /**
- * Add or update Business Address widget.
- *
- * @param array $address Array of business address fields.
- *
- * @return WP_Error|true True if the data was saved correctly.
- */
- static function handle_business_address( $address ) {
- $first_sidebar = Jetpack_Widgets::get_first_sidebar();
-
- $widgets_module_active = Jetpack::is_module_active( 'widgets' );
- if ( ! $widgets_module_active ) {
- $widgets_module_active = Jetpack::activate_module( 'widgets', false, false );
- }
- if ( ! $widgets_module_active ) {
- return new WP_Error( 'module_activation_failed', 'Failed to activate the widgets module.', 400 );
- }
-
- if ( $first_sidebar ) {
- $title = isset( $address['name'] ) ? sanitize_text_field( $address['name'] ) : '';
- $street = isset( $address['street'] ) ? sanitize_text_field( $address['street'] ) : '';
- $city = isset( $address['city'] ) ? sanitize_text_field( $address['city'] ) : '';
- $state = isset( $address['state'] ) ? sanitize_text_field( $address['state'] ) : '';
- $zip = isset( $address['zip'] ) ? sanitize_text_field( $address['zip'] ) : '';
- $country = isset( $address['country'] ) ? sanitize_text_field( $address['country'] ) : '';
-
- $full_address = implode( ' ', array_filter( array( $street, $city, $state, $zip, $country ) ) );
-
- $widget_options = array(
- 'title' => $title,
- 'address' => $full_address,
- 'phone' => '',
- 'hours' => '',
- 'showmap' => false,
- 'email' => ''
- );
-
- $widget_updated = '';
- if ( ! self::has_business_address_widget( $first_sidebar ) ) {
- $widget_updated = Jetpack_Widgets::insert_widget_in_sidebar( 'widget_contact_info', $widget_options, $first_sidebar );
- } else {
- $widget_updated = Jetpack_Widgets::update_widget_in_sidebar( 'widget_contact_info', $widget_options, $first_sidebar );
- }
- if ( is_wp_error( $widget_updated ) ) {
- return new WP_Error( 'widget_update_failed', 'Widget could not be updated.', 400 );
- }
-
- $address_save = array(
- 'name' => $title,
- 'street' => $street,
- 'city' => $city,
- 'state' => $state,
- 'zip' => $zip,
- 'country' => $country
- );
- update_option( 'jpo_business_address', $address_save );
- return true;
- }
-
- // No sidebar to place the widget
- return new WP_Error( 'sidebar_not_found', 'No sidebar.', 400 );
- }
-
- /**
- * Check whether "Contact Info & Map" widget is present in a given sidebar.
- *
- * @param string $sidebar ID of the sidebar to which the widget will be added.
- *
- * @return bool Whether the widget is present in a given sidebar.
- */
- static function has_business_address_widget( $sidebar ) {
- $sidebars_widgets = get_option( 'sidebars_widgets', array() );
- if ( ! isset( $sidebars_widgets[ $sidebar ] ) ) {
- return false;
- }
- foreach ( $sidebars_widgets[ $sidebar ] as $widget ) {
- if ( strpos( $widget, 'widget_contact_info' ) !== false ) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Calls WPCOM through authenticated request to create, regenerate or delete the Post by Email address.
- * @todo: When all settings are updated to use endpoints, move this to the Post by Email module and replace __process_ajax_proxy_request.
- *
- * @since 4.3.0
- *
- * @param string $endpoint Process to call on WPCOM to create, regenerate or delete the Post by Email address.
- * @param string $error Error message to return.
- *
- * @return array
- */
- private function _process_post_by_email( $endpoint, $error ) {
- if ( ! current_user_can( 'edit_posts' ) ) {
- return array( 'message' => $error );
- }
-
- $this->xmlrpc->query( $endpoint );
-
- if ( $this->xmlrpc->isError() ) {
- return array( 'message' => $error );
- }
-
- $response = $this->xmlrpc->getResponse();
- if ( empty( $response ) ) {
- return array( 'message' => $error );
- }
-
- // Used only in Jetpack_Core_Json_Api_Endpoints::get_remote_value.
- update_option( 'post_by_email_address' . get_current_user_id(), $response );
-
- return $response;
- }
-
- /**
- * Check if user is allowed to perform the update.
- *
- * @since 4.3.0
- *
- * @param WP_REST_Request $request The request sent to the WP REST API.
- *
- * @return bool
- */
- public function can_request( $request ) {
- $req_params = $request->get_params();
- if ( ! empty( $req_params['onboarding']['token'] ) && isset( $req_params['rest_route'] ) ) {
- return Jetpack::validate_onboarding_token_action( $req_params['onboarding']['token'], $req_params['rest_route'] );
- }
-
- if ( 'GET' === $request->get_method() ) {
- return current_user_can( 'jetpack_admin_page' );
- } else {
- $module = Jetpack_Core_Json_Api_Endpoints::get_module_requested();
- if ( empty( $module ) ) {
- $params = $request->get_json_params();
- if ( ! is_array( $params ) ) {
- $params = $request->get_body_params();
- }
- $options = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( $params );
- foreach ( $options as $option => $definition ) {
- if ( in_array( $options[ $option ]['jp_group'], array( 'after-the-deadline', 'post-by-email' ) ) ) {
- $module = $options[ $option ]['jp_group'];
- break;
- }
- }
- }
- // User is trying to create, regenerate or delete its PbE || ATD settings.
- if ( 'post-by-email' === $module || 'after-the-deadline' === $module ) {
- return current_user_can( 'edit_posts' ) && current_user_can( 'jetpack_admin_page' );
- }
- return current_user_can( 'jetpack_configure_modules' );
- }
- }
-}
-
-class Jetpack_Core_API_Module_Data_Endpoint {
-
- public function process( $request ) {
- switch( $request['slug'] ) {
- case 'protect':
- return $this->get_protect_data();
- case 'stats':
- return $this->get_stats_data( $request );
- case 'akismet':
- return $this->get_akismet_data();
- case 'monitor':
- return $this->get_monitor_data();
- case 'verification-tools':
- return $this->get_verification_tools_data();
- case 'vaultpress':
- return $this->get_vaultpress_data();
- }
- }
-
- /**
- * Decide against which service to check the key.
- *
- * @since 4.8.0
- *
- * @param WP_REST_Request $request
- *
- * @return bool
- */
- public function key_check( $request ) {
- switch( $request['service'] ) {
- case 'akismet':
- $params = $request->get_json_params();
- if ( isset( $params['api_key'] ) && ! empty( $params['api_key'] ) ) {
- return $this->check_akismet_key( $params['api_key'] );
- }
- return $this->check_akismet_key();
- }
- return false;
- }
-
- /**
- * Get number of blocked intrusion attempts.
- *
- * @since 4.3.0
- *
- * @return mixed|WP_Error Number of blocked attempts if protection is enabled. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function get_protect_data() {
- if ( Jetpack::is_module_active( 'protect' ) ) {
- return get_site_option( 'jetpack_protect_blocked_attempts' );
- }
-
- return new WP_Error(
- 'not_active',
- esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- /**
- * Get number of spam messages blocked by Akismet.
- *
- * @since 4.3.0
- *
- * @return int|string Number of spam blocked by Akismet. Otherwise, an error message.
- */
- public function get_akismet_data() {
- if ( ! is_wp_error( $status = $this->akismet_is_active_and_registered() ) ) {
- return rest_ensure_response( Akismet_Admin::get_stats( Akismet::get_api_key() ) );
- } else {
- return $status->get_error_code();
- }
- }
-
- /**
- * Verify the Akismet API key.
- *
- * @since 4.8.0
- *
- * @param string $api_key Optional API key to check.
- *
- * @return array Information about the key. 'validKey' is true if key is valid, false otherwise.
- */
- public function check_akismet_key( $api_key = '' ) {
- $akismet_status = $this->akismet_class_exists();
- if ( is_wp_error( $akismet_status ) ) {
- return rest_ensure_response( array(
- 'validKey' => false,
- 'invalidKeyCode' => $akismet_status->get_error_code(),
- 'invalidKeyMessage' => $akismet_status->get_error_message(),
- ) );
- }
-
- $key_status = Akismet::check_key_status( empty( $api_key ) ? Akismet::get_api_key() : $api_key );
-
- if ( ! $key_status || 'invalid' === $key_status || 'failed' === $key_status ) {
- return rest_ensure_response( array(
- 'validKey' => false,
- 'invalidKeyCode' => 'invalid_key',
- 'invalidKeyMessage' => esc_html__( 'Invalid Akismet key. Please contact support.', 'jetpack' ),
- ) );
- }
-
- return rest_ensure_response( array(
- 'validKey' => isset( $key_status[1] ) && 'valid' === $key_status[1]
- ) );
- }
-
- /**
- * Check if Akismet class file exists and if class is loaded.
- *
- * @since 4.8.0
- *
- * @return bool|WP_Error Returns true if class file exists and class is loaded, WP_Error otherwise.
- */
- private function akismet_class_exists() {
- if ( ! file_exists( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
- return new WP_Error( 'not_installed', esc_html__( 'Please install Akismet.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- if ( ! class_exists( 'Akismet' ) ) {
- return new WP_Error( 'not_active', esc_html__( 'Please activate Akismet.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- return true;
- }
-
- /**
- * Is Akismet registered and active?
- *
- * @since 4.3.0
- *
- * @return bool|WP_Error True if Akismet is active and registered. Otherwise, a WP_Error instance with the corresponding error.
- */
- private function akismet_is_active_and_registered() {
- if ( is_wp_error( $akismet_exists = $this->akismet_class_exists() ) ) {
- return $akismet_exists;
- }
-
- // What about if Akismet is put in a sub-directory or maybe in mu-plugins?
- require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
- require_once WP_PLUGIN_DIR . '/akismet/class.akismet-admin.php';
- $akismet_key = Akismet::verify_key( Akismet::get_api_key() );
-
- if ( ! $akismet_key || 'invalid' === $akismet_key || 'failed' === $akismet_key ) {
- return new WP_Error( 'invalid_key', esc_html__( 'Invalid Akismet key. Please contact support.', 'jetpack' ), array( 'status' => 400 ) );
- }
-
- return true;
- }
-
- /**
- * Get stats data for this site
- *
- * @since 4.1.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $date Date range to restrict results to.
- * }
- *
- * @return WP_Error|WP_HTTP_Response|WP_REST_Response Stats information relayed from WordPress.com.
- */
- public function get_stats_data( WP_REST_Request $request ) {
- // Get parameters to fetch Stats data.
- $range = $request->get_param( 'range' );
-
- // If no parameters were passed.
- if (
- empty ( $range )
- || ! in_array( $range, array( 'day', 'week', 'month' ), true )
- ) {
- $range = 'day';
- }
-
- if ( ! function_exists( 'stats_get_from_restapi' ) ) {
- require_once( JETPACK__PLUGIN_DIR . 'modules/stats.php' );
- }
-
- switch ( $range ) {
-
- // This is always called first on page load
- case 'day':
- $initial_stats = stats_get_from_restapi();
- return rest_ensure_response( array(
- 'general' => $initial_stats,
-
- // Build data for 'day' as if it was stats_get_from_restapi( array(), 'visits?unit=day&quantity=30' );
- 'day' => isset( $initial_stats->visits )
- ? $initial_stats->visits
- : array(),
- ) );
- case 'week':
- return rest_ensure_response( array(
- 'week' => stats_get_from_restapi( array(), 'visits?unit=week&quantity=14' ),
- ) );
- case 'month':
- return rest_ensure_response( array(
- 'month' => stats_get_from_restapi( array(), 'visits?unit=month&quantity=12&' ),
- ) );
- }
- }
-
- /**
- * Get date of last downtime.
- *
- * @since 4.3.0
- *
- * @return mixed|WP_Error Number of days since last downtime. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function get_monitor_data() {
- if ( ! Jetpack::is_module_active( 'monitor' ) ) {
- return new WP_Error(
- 'not_active',
- esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- $monitor = new Jetpack_Monitor();
- $last_downtime = $monitor->monitor_get_last_downtime();
- if ( is_wp_error( $last_downtime ) ) {
- return $last_downtime;
- } else if ( false === strtotime( $last_downtime ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'date' => null,
- ) );
- } else {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'date' => human_time_diff( strtotime( $last_downtime ), strtotime( 'now' ) ),
- ) );
- }
- }
-
- /**
- * Get services that this site is verified with.
- *
- * @since 4.3.0
- *
- * @return mixed|WP_Error List of services that verified this site. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function get_verification_tools_data() {
- if ( ! Jetpack::is_module_active( 'verification-tools' ) ) {
- return new WP_Error(
- 'not_active',
- esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- $verification_services_codes = get_option( 'verification_services_codes' );
- if (
- ! is_array( $verification_services_codes )
- || empty( $verification_services_codes )
- ) {
- return new WP_Error(
- 'empty',
- esc_html__( 'Site not verified with any service.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- $services = array();
- foreach ( jetpack_verification_services() as $name => $service ) {
- if ( is_array( $service ) && ! empty( $verification_services_codes[ $name ] ) ) {
- switch ( $name ) {
- case 'google':
- $services[] = 'Google';
- break;
- case 'bing':
- $services[] = 'Bing';
- break;
- case 'pinterest':
- $services[] = 'Pinterest';
- break;
- case 'yandex':
- $services[] = 'Yandex';
- break;
- }
- }
- }
-
- if ( empty( $services ) ) {
- return new WP_Error(
- 'empty',
- esc_html__( 'Site not verified with any service.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- if ( 2 > count( $services ) ) {
- $message = esc_html(
- sprintf(
- /* translators: %s is a service name like Google, Bing, Pinterest, etc. */
- __( 'Your site is verified with %s.', 'jetpack' ),
- $services[0]
- )
- );
- } else {
- $copy_services = $services;
- $last = count( $copy_services ) - 1;
- $last_service = $copy_services[ $last ];
- unset( $copy_services[ $last ] );
- $message = esc_html(
- sprintf(
- /* translators: %1$s is a comma separated list of services, and %2$s is a single service name like Google, Bing, Pinterest, etc. */
- __( 'Your site is verified with %1$s and %2$s.', 'jetpack' ),
- join( ', ', $copy_services ),
- $last_service
- )
- );
- }
-
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => $message,
- 'services' => $services,
- ) );
- }
-
- /**
- * Get VaultPress site data including, among other things, the date of the last backup if it was completed.
- *
- * @since 4.3.0
- *
- * @return mixed|WP_Error VaultPress site data. Otherwise, a WP_Error instance with the corresponding error.
- */
- public function get_vaultpress_data() {
- if ( ! class_exists( 'VaultPress' ) ) {
- return new WP_Error(
- 'not_active',
- esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- $vaultpress = new VaultPress();
- if ( ! $vaultpress->is_registered() ) {
- return rest_ensure_response( array(
- 'code' => 'not_registered',
- 'message' => esc_html__( 'You need to register for VaultPress.', 'jetpack' )
- ) );
- }
-
- $data = json_decode( base64_decode( $vaultpress->contact_service( 'plugin_data' ) ) );
- if ( false == $data ) {
- return rest_ensure_response( array(
- 'code' => 'not_registered',
- 'message' => esc_html__( 'Could not connect to VaultPress.', 'jetpack' )
- ) );
- } else if ( is_wp_error( $data ) || ! isset( $data->backups->last_backup ) ) {
- return $data;
- } else if ( empty( $data->backups->last_backup ) ) {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'VaultPress is active and will back up your site soon.', 'jetpack' ),
- 'data' => $data,
- ) );
- } else {
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html(
- sprintf(
- __( 'Your site was successfully backed-up %s ago.', 'jetpack' ),
- human_time_diff(
- $data->backups->last_backup,
- current_time( 'timestamp' )
- )
- )
- ),
- 'data' => $data,
- ) );
- }
- }
-
- /**
- * A WordPress REST API permission callback method that accepts a request object and
- * decides if the current user has enough privileges to act.
- *
- * @since 4.3.0
- *
- * @return bool does a current user have enough privileges.
- */
- public function can_request() {
- return current_user_can( 'jetpack_admin_page' );
- }
-}
-
-/**
- * Actions performed only when Gravatar Hovercards is activated through the endpoint call.
- *
- * @since 4.3.1
- */
-function jetpack_do_after_gravatar_hovercards_activation() {
-
- // When Gravatar Hovercards is activated, enable them automatically.
- update_option( 'gravatar_disable_hovercards', 'enabled' );
-}
-add_action( 'jetpack_activate_module_gravatar-hovercards', 'jetpack_do_after_gravatar_hovercards_activation' );
-
-/**
- * Actions performed only when Gravatar Hovercards is activated through the endpoint call.
- *
- * @since 4.3.1
- */
-function jetpack_do_after_gravatar_hovercards_deactivation() {
-
- // When Gravatar Hovercards is deactivated, disable them automatically.
- update_option( 'gravatar_disable_hovercards', 'disabled' );
-}
-add_action( 'jetpack_deactivate_module_gravatar-hovercards', 'jetpack_do_after_gravatar_hovercards_deactivation' );
-
-/**
- * Actions performed only when Markdown is activated through the endpoint call.
- *
- * @since 4.7.0
- */
-function jetpack_do_after_markdown_activation() {
-
- // When Markdown is activated, enable support for post editing automatically.
- update_option( 'wpcom_publish_posts_with_markdown', true );
-}
-add_action( 'jetpack_activate_module_markdown', 'jetpack_do_after_markdown_activation' );
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php
deleted file mode 100644
index 68327f51..00000000
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * This is the endpoint class for `/site` endpoints.
- *
- */
-class Jetpack_Core_API_Site_Endpoint {
-
- /**
- * Returns the result of `/sites/%s/features` endpoint call.
- * @return object $features has 'active' and 'available' properties each of which contain feature slugs.
- * 'active' is a simple array of slugs that are active on the current plan.
- * 'available' is an object with keys that represent feature slugs and values are arrays
- * of plan slugs that enable these features
- */
- public static function get_features() {
-
- // Make the API request
- $request = sprintf( '/sites/%d/features', Jetpack_Options::get_option( 'id' ) );
- $response = Jetpack_Client::wpcom_json_api_request_as_blog( $request, '1.1' );
-
- // Bail if there was an error or malformed response
- if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
- return new WP_Error(
- 'failed_to_fetch_data',
- esc_html__( 'Unable to fetch the requested data.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- // Decode the results
- $results = json_decode( $response['body'], true );
-
- // Bail if there were no results or plan details returned
- if ( ! is_array( $results ) ) {
- return new WP_Error(
- 'failed_to_fetch_data',
- esc_html__( 'Unable to fetch the requested data.', 'jetpack' ),
- array( 'status' => 500 )
- );
- }
-
- return rest_ensure_response( array(
- 'code' => 'success',
- 'message' => esc_html__( 'Site features correctly received.', 'jetpack' ),
- 'data' => wp_remote_retrieve_body( $response ),
- )
- );
- }
-
- /**
- * Check that the current user has permissions to request information about this site.
- *
- * @since 5.1.0
- *
- * @return bool
- */
- public static function can_request() {
- return current_user_can( 'jetpack_manage_modules' );
- }
-}
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php
deleted file mode 100644
index ffd62bb3..00000000
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-/**
- * Widget information getter endpoint.
- *
- */
-class Jetpack_Core_API_Widget_Endpoint {
-
- /**
- * @since 5.5.0
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $id Widget id.
- * }
- *
- * @return WP_REST_Response|WP_Error A REST response if the request was served successfully, otherwise an error.
- */
- public function process( $request ) {
- $widget_base = _get_widget_id_base( $request['id'] );
- $widget_id = (int) substr( $request['id'], strlen( $widget_base ) + 1 );
-
- switch( $widget_base ) {
- case 'milestone_widget':
- $instances = get_option( 'widget_milestone_widget', array() );
-
- if (
- class_exists( 'Milestone_Widget' )
- && is_active_widget( false, $widget_base . '-' . $widget_id, $widget_base )
- && isset( $instances[ $widget_id ] )
- ) {
- $instance = $instances[ $widget_id ];
- $widget = new Milestone_Widget();
- return $widget->get_widget_data( $instance );
- }
- }
-
- return new WP_Error(
- 'not_found',
- esc_html__( 'The requested widget was not found.', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- /**
- * Check that the current user has permissions to view widget information.
- * For the currently supported widget there are no permissions required.
- *
- * @since 5.5.0
- *
- * @return bool
- */
- public function can_request() {
- return true;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php
deleted file mode 100644
index abfc8627..00000000
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * This is the base class for every Core API endpoint that needs an XMLRPC client.
- *
- */
-abstract class Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
-
- /**
- * An instance of the Jetpack XMLRPC client to make WordPress.com requests
- *
- * @private
- * @var Jetpack_IXR_Client
- */
- protected $xmlrpc;
-
- /**
- *
- * @since 4.3.0
- *
- * @param Jetpack_IXR_Client $xmlrpc
- */
- public function __construct( $xmlrpc = null ) {
- $this->xmlrpc = $xmlrpc;
- }
-
- /**
- * Checks if the site is public and returns the result.
- *
- * @since 4.3.0
- *
- * @return Boolean $is_public
- */
- protected function is_site_public() {
- if ( $this->xmlrpc->query( 'jetpack.isSitePubliclyAccessible', home_url() ) ) {
- return $this->xmlrpc->getResponse();
- }
- return false;
- }
-} \ No newline at end of file
diff --git a/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php b/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php
deleted file mode 100644
index 2b26f78c..00000000
--- a/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/*
- * Loader for WP REST API endpoints that are synced with WP.com.
- *
- * On WP.com see:
- * - wp-content/mu-plugins/rest-api.php
- * - wp-content/rest-api-plugins/jetpack-endpoints/
- */
-
-function wpcom_rest_api_v2_load_plugin_files( $file_pattern ) {
- $plugins = glob( dirname( __FILE__ ) . '/' . $file_pattern );
-
- if ( ! is_array( $plugins ) ) {
- return;
- }
-
- foreach ( array_filter( $plugins, 'is_file' ) as $plugin ) {
- require_once $plugin;
- }
-}
-
-// API v2 plugins: define a class, then call this function.
-function wpcom_rest_api_v2_load_plugin( $class_name ) {
- global $wpcom_rest_api_v2_plugins;
-
- if ( ! isset( $wpcom_rest_api_v2_plugins ) ) {
- $_GLOBALS['wpcom_rest_api_v2_plugins'] = $wpcom_rest_api_v2_plugins = array();
- }
-
- if ( ! isset( $wpcom_rest_api_v2_plugins[ $class_name ] ) ) {
- $wpcom_rest_api_v2_plugins[ $class_name ] = new $class_name;
- }
-}
-
-require dirname( __FILE__ ) . '/class-wpcom-rest-field-controller.php';
-
-// Now load the endpoint files.
-wpcom_rest_api_v2_load_plugin_files( 'wpcom-endpoints/*.php' );
-wpcom_rest_api_v2_load_plugin_files( 'wpcom-fields/*.php' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/business-hours.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/business-hours.php
deleted file mode 100644
index 2bf80939..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/business-hours.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * Business Hours: Localized week
- *
- * @since 7.1
- */
-class WPCOM_REST_API_V2_Endpoint_Business_Hours extends WP_REST_Controller {
- function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'business-hours';
- // This endpoint *does not* need to connect directly to Jetpack sites.
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- public function register_routes() {
- // GET /sites/<blog_id>/business-hours/localized-week - Return the localized
- register_rest_route( $this->namespace, '/' . $this->rest_base . '/localized-week', array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_localized_week' ),
- )
- ) );
- }
-
- /**
- * Retreives localized business hours
- *
- * @return array data object containing information about business hours
- */
- public function get_localized_week() {
- global $wp_locale;
-
- return array(
- 'days' => array(
- 'Sun' => $wp_locale->get_weekday( 0 ),
- 'Mon' => $wp_locale->get_weekday( 1 ),
- 'Tue' => $wp_locale->get_weekday( 2 ),
- 'Wed' => $wp_locale->get_weekday( 3 ),
- 'Thu' => $wp_locale->get_weekday( 4 ),
- 'Fri' => $wp_locale->get_weekday( 5 ),
- 'Sat' => $wp_locale->get_weekday( 6 ),
- ),
- 'startOfWeek' => (int) get_option( 'start_of_week', 0 ),
- );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Business_Hours' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php
deleted file mode 100644
index 09ef9499..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-
-/**
- * Mailchimp: Get Mailchimp Status.
- * API to determine if current site has linked Mailchimp account and mailing list selected.
- * This API is meant to be used in Jetpack and on WPCOM.
- *
- * @since 7.1
- */
-class WPCOM_REST_API_V2_Endpoint_Mailchimp extends WP_REST_Controller {
- public function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'mailchimp';
- $this->wpcom_is_wpcom_only_endpoint = true;
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- /**
- * Called automatically on `rest_api_init()`.
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_mailchimp_status' ),
- ),
- )
- );
- }
-
- /**
- * Check if MailChimp is set up properly.
- *
- * @return bool
- */
- private function is_connected() {
- $option = get_option( 'jetpack_mailchimp' );
- if ( ! $option ) {
- return false;
- }
- $data = json_decode( $option, true );
- if ( ! $data ) {
- return false;
- }
- return isset( $data['follower_list_id'], $data['keyring_id'] );
- }
-
- /**
- * Get the status of current blog's Mailchimp connection
- *
- * @return mixed
- * code:string (connected|unconnected),
- * connect_url:string
- * site_id:int
- */
- public function get_mailchimp_status() {
- $is_wpcom = ( defined( 'IS_WPCOM' ) && IS_WPCOM );
- $site_id = $is_wpcom ? get_current_blog_id() : Jetpack_Options::get_option( 'id' );
- if ( ! $site_id ) {
- return new WP_Error(
- 'unavailable_site_id',
- __( 'Sorry, something is wrong with your Jetpack connection.', 'jetpack' ),
- 403
- );
- }
- $connect_url = sprintf( 'https://wordpress.com/sharing/%s', rawurlencode( $site_id ) );
- return array(
- 'code' => $this->is_connected() ? 'connected' : 'not_connected',
- 'connect_url' => $connect_url,
- 'site_id' => $site_id,
- );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Mailchimp' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php
deleted file mode 100644
index a10a4056..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/*
- * Gutenberg: List Available Gutenberg Extensions (Blocks and Plugins)
- *
- * [
- * { # Availabilty Object. See schema for more detail.
- * available: (boolean) Whether the extension is available
- * unavailable_reason: (string) Reason for the extension not being available
- * },
- * ...
- * ]
- *
- * @since 6.9
- */
-class WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions extends WP_REST_Controller {
- function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'gutenberg';
- $this->wpcom_is_site_specific_endpoint = true;
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- public function register_routes() {
- register_rest_route( $this->namespace, $this->rest_base . '/available-extensions', array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( 'Jetpack_Gutenberg', 'get_availability' ),
- 'permission_callback' => array( $this, 'get_items_permission_check' ),
- ),
- 'schema' => array( $this, 'get_item_schema' ),
- ) );
- }
-
- /**
- * Return the available Gutenberg extensions schema
- *
- * @return array Available Gutenberg extensions schema
- */
- public function get_public_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'gutenberg-available-extensions',
- 'type' => 'object',
- 'properties' => array(
- 'available' => array(
- 'description' => __( 'Whether the extension is available', 'jetpack' ),
- 'type' => 'boolean',
- ),
- 'unavailable_reason' => array(
- 'description' => __( 'Reason for the extension not being available', 'jetpack' ),
- 'type' => 'string',
- ),
- ),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Ensure the user has proper permissions
- *
- * @return boolean
- */
- public function get_items_permission_check() {
- return current_user_can( 'edit_posts' );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php
deleted file mode 100644
index a05769b2..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-class WPCOM_REST_API_V2_Endpoint_Hello {
- public function __construct() {
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- public function register_routes() {
- register_rest_route( 'wpcom/v2', '/hello', array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_data' ),
- ),
- ) );
- }
-
- public function get_data( $request ) {
- return array( 'hello' => 'world' );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Hello' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php
deleted file mode 100644
index 6e04a289..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-
-require_once dirname( __FILE__ ) . '/publicize-connections.php';
-
-/**
- * Publicize: List Connection Test Result Data
- *
- * All the same data as the Publicize Connections Endpoint, plus test results.
- *
- * @since 6.8
- */
-class WPCOM_REST_API_V2_Endpoint_List_Publicize_Connection_Test_Results extends WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections {
- public function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'publicize/connection-test-results';
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- /**
- * Called automatically on `rest_api_init()`.
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permission_check' ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * Adds the test results properties to the Connection schema.
- *
- * @return array
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'jetpack-publicize-connection-test-results',
- 'type' => 'object',
- 'properties' => $this->get_connection_schema_properties() + array(
- 'test_success' => array(
- 'description' => __( 'Did the Publicize Connection test pass?', 'jetpack' ),
- 'type' => 'boolean',
- ),
- 'test_message' => array(
- 'description' => __( 'Publicize Connection success or error message', 'jetpack' ),
- 'type' => 'string',
- ),
- 'can_refresh' => array(
- 'description' => __( 'Can the current user refresh the Publicize Connection?', 'jetpack' ),
- 'type' => 'boolean',
- ),
- 'refresh_text' => array(
- 'description' => __( 'Message instructing the user to refresh their Connection to the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- ),
- 'refresh_url' => array(
- 'description' => __( 'URL for refreshing the Connection to the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- 'format' => 'uri',
- ),
- ),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * @param WP_REST_Request
- * @see Publicize::get_publicize_conns_test_results()
- * @return WP_REST_Response suitable for 1-page collection
- */
- public function get_items( $request ) {
- global $publicize;
-
- $items = $this->get_connections();
-
- $test_results = $publicize->get_publicize_conns_test_results();
- $test_results_by_unique_id = array();
- foreach ( $test_results as $test_result ) {
- $test_results_by_unique_id[ $test_result['unique_id'] ] = $test_result;
- }
-
- $mapping = array(
- 'test_success' => 'connectionTestPassed',
- 'test_message' => 'connectionTestMessage',
- 'can_refresh' => 'userCanRefresh',
- 'refresh_text' => 'refreshText',
- 'refresh_url' => 'refreshURL',
- );
-
- foreach ( $items as &$item ) {
- $test_result = $test_results_by_unique_id[ $item['id'] ];
-
- foreach ( $mapping as $field => $test_result_field ) {
- $item[ $field ] = $test_result[ $test_result_field ];
- }
- }
-
- if ( 'linkedin' === $item['id'] && 'must_reauth' === $test_result['connectionTestPassed'] ) {
- $item['test_success'] = 'must_reauth';
- }
-
- $response = rest_ensure_response( $items );
-
- $response->header( 'X-WP-Total', count( $items ) );
- $response->header( 'X-WP-TotalPages', 1 );
-
- return $response;
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Connection_Test_Results' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php
deleted file mode 100644
index 34d6b2a6..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php
+++ /dev/null
@@ -1,194 +0,0 @@
-<?php
-
-/**
- * Publicize: List Connections
- *
- * [
- * { # Connnection Object. See schema for more detail.
- * id: (string) Connection unique_id
- * service_name: (string) Service slug
- * display_name: (string) User name/display name of user/connection on Service
- * global: (boolean) Is the Connection available to all users of the site?
- * },
- * ...
- * ]
- *
- * @since 6.8
- */
-class WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections extends WP_REST_Controller {
- /**
- * Flag to help WordPress.com decide where it should look for
- * Publicize data. Ignored for direct requests to Jetpack sites.
- *
- * @var bool $wpcom_is_wpcom_only_endpoint
- */
- public $wpcom_is_wpcom_only_endpoint = true;
-
- public function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'publicize/connections';
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- /**
- * Called automatically on `rest_api_init()`.
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permission_check' ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * Helper for generating schema. Used by this endpoint and by the
- * Connection Test Result endpoint.
- *
- * @internal
- * @return array
- */
- protected function get_connection_schema_properties() {
- return array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the Publicize Connection', 'jetpack' ),
- 'type' => 'string',
- ),
- 'service_name' => array(
- 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- ),
- 'display_name' => array(
- 'description' => __( 'Username of the connected account', 'jetpack' ),
- 'type' => 'string',
- ),
- 'global' => array(
- 'description' => __( 'Is this connection available to all users?', 'jetpack' ),
- 'type' => 'boolean',
- ),
- );
- }
-
- /**
- * @return array
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'jetpack-publicize-connection',
- 'type' => 'object',
- 'properties' => $this->get_connection_schema_properties(),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Helper for retrieving Connections. Used by this endpoint and by
- * the Connection Test Result endpoint.
- *
- * @internal
- * @return array
- */
- protected function get_connections() {
- global $publicize;
-
- $items = array();
-
- foreach ( (array) $publicize->get_services( 'connected' ) as $service_name => $connections ) {
- foreach ( $connections as $connection ) {
- $connection_meta = $publicize->get_connection_meta( $connection );
- $connection_data = $connection_meta['connection_data'];
-
- $items[] = array(
- 'id' => (string) $publicize->get_connection_unique_id( $connection ),
- 'service_name' => $service_name,
- 'display_name' => $publicize->get_display_name( $service_name, $connection ),
- // We expect an integer, but do loose comparison below in case some other type is stored
- 'global' => 0 == $connection_data['user_id'],
- );
- }
- }
-
- return $items;
- }
-
- /**
- * @param WP_REST_Request $request
- * @return WP_REST_Response suitable for 1-page collection
- */
- public function get_items( $request ) {
- $items = array();
-
- foreach ( $this->get_connections() as $item ) {
- $items[] = $this->prepare_item_for_response( $item, $request );
- }
-
- $response = rest_ensure_response( $items );
- $response->header( 'X-WP-Total', count( $items ) );
- $response->header( 'X-WP-TotalPages', 1 );
-
- return $response;
- }
-
- /**
- * Filters out data based on ?_fields= request parameter
- *
- * @param array $connection
- * @param WP_REST_Request $request
- * @return array filtered $connection
- */
- public function prepare_item_for_response( $connection, $request ) {
- if ( ! is_callable( array( $this, 'get_fields_for_response' ) ) ) {
- return $connection;
- }
-
- $fields = $this->get_fields_for_response( $request );
-
- $response_data = array();
- foreach ( $connection as $field => $value ) {
- if ( in_array( $field, $fields, true ) ) {
- $response_data[ $field ] = $value;
- }
- }
-
- return $response_data;
- }
-
- /**
- * Verify that user can access Publicize data
- *
- * @return true|WP_Error
- */
- public function get_items_permission_check() {
- global $publicize;
-
- if ( ! $publicize ) {
- return new WP_Error(
- 'publicize_not_available',
- __( 'Sorry, Publicize is not available on your site right now.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- if ( $publicize->current_user_can_access_publicize_data() ) {
- return true;
- }
-
- return new WP_Error(
- 'invalid_user_permission_publicize',
- __( 'Sorry, you are not allowed to access Publicize data on this site.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php
deleted file mode 100644
index 4641b218..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php
+++ /dev/null
@@ -1,167 +0,0 @@
-<?php
-
-/**
- * Publicize: List Publicize Services
- *
- * [
- * { # Service Object. See schema for more detail.
- * name: (string) Service slug
- * label: (string) Human readable label for the Service
- * url: (string) Connect URL
- * },
- * ...
- * ]
- *
- * @since 6.8
- */
-class WPCOM_REST_API_V2_Endpoint_List_Publicize_Services extends WP_REST_Controller {
- /**
- * Flag to help WordPress.com decide where it should look for
- * Publicize data. Ignored for direct requests to Jetpack sites.
- *
- * @var bool $wpcom_is_wpcom_only_endpoint
- */
- public $wpcom_is_wpcom_only_endpoint = true;
-
- public function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'publicize/services';
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- /**
- * Called automatically on `rest_api_init()`.
- */
- public function register_routes() {
- register_rest_route(
- $this->namespace,
- '/' . $this->rest_base,
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permission_check' ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- )
- );
- }
-
- /**
- * @return array
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'jetpack-publicize-service',
- 'type' => 'object',
- 'properties' => array(
- 'name' => array(
- 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- ),
- 'label' => array(
- 'description' => __( 'Human readable label for the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- ),
- 'url' => array(
- 'description' => __( 'The URL used to connect to the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- 'format' => 'uri',
- ),
- ),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Retrieves available Publicize Services.
- *
- * @see Publicize::get_available_service_data()
- *
- * @param WP_REST_Request $request
- * @return WP_REST_Response suitable for 1-page collection
- */
- public function get_items( $request ) {
- global $publicize;
- /**
- * We need this because Publicize::get_available_service_data() uses `Jetpack_Keyring_Service_Helper`
- * and `Jetpack_Keyring_Service_Helper` relies on `menu_page_url()`.
- *
- * We also need add_submenu_page(), as the URLs for connecting each service
- * rely on the `sharing` menu subpage being present.
- */
- include_once ABSPATH . 'wp-admin/includes/plugin.php';
-
- // The `sharing` submenu page must exist for service connect URLs to be correct.
- add_submenu_page( 'options-general.php', '', '', 'manage_options', 'sharing', '__return_empty_string' );
-
- $services_data = $publicize->get_available_service_data();
-
- $services = array();
- foreach ( $services_data as $service_data ) {
- $services[] = $this->prepare_item_for_response( $service_data, $request );
- }
-
- $response = rest_ensure_response( $services );
- $response->header( 'X-WP-Total', count( $services ) );
- $response->header( 'X-WP-TotalPages', 1 );
-
- return $response;
- }
-
- /**
- * Filters out data based on ?_fields= request parameter
- *
- * @param array $service
- * @param WP_REST_Request $request
- * @return array filtered $service
- */
- public function prepare_item_for_response( $service, $request ) {
- if ( ! is_callable( array( $this, 'get_fields_for_response' ) ) ) {
- return $service;
- }
-
- $fields = $this->get_fields_for_response( $request );
-
- $response_data = array();
- foreach ( $service as $field => $value ) {
- if ( in_array( $field, $fields, true ) ) {
- $response_data[ $field ] = $value;
- }
- }
-
- return $response_data;
- }
-
- /**
- * Verify that user can access Publicize data
- *
- * @return true|WP_Error
- */
- public function get_items_permission_check() {
- global $publicize;
-
- if ( ! $publicize ) {
- return new WP_Error(
- 'publicize_not_available',
- __( 'Sorry, Publicize is not available on your site right now.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- if ( $publicize->current_user_can_access_publicize_data() ) {
- return true;
- }
-
- return new WP_Error(
- 'invalid_user_permission_publicize',
- __( 'Sorry, you are not allowed to access Publicize data on this site.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Services' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/service-api-keys.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/service-api-keys.php
deleted file mode 100644
index 05d0ddd3..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/service-api-keys.php
+++ /dev/null
@@ -1,281 +0,0 @@
-<?php
-/*
- * Service API Keys: Exposes 3rd party api keys that are used on a site.
- *
- * [
- * { # Availabilty Object. See schema for more detail.
- * code: (string) Displays success if the operation was successfully executed and an error code if it was not
- * service: (string) The name of the service in question
- * service_api_key: (string) The API key used by the service empty if one is not set yet
- * message: (string) User friendly message
- * },
- * ...
- * ]
- *
- * @since 6.9
- */
-class WPCOM_REST_API_V2_Endpoint_Service_API_Keys extends WP_REST_Controller {
-
- function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'service-api-keys';
-
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- public function register_routes() {
- register_rest_route(
- 'wpcom/v2',
- '/service-api-keys/(?P<service>[a-z\-_]+)',
- array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( __CLASS__, 'get_service_api_key' ),
- ),
- array(
- 'methods' => WP_REST_Server::EDITABLE,
- 'callback' => array( __CLASS__, 'update_service_api_key' ),
- 'permission_callback' => array( __CLASS__, 'edit_others_posts_check' ),
- 'args' => array(
- 'service_api_key' => array(
- 'required' => true,
- 'type' => 'text',
- ),
- ),
- ),
- array(
- 'methods' => WP_REST_Server::DELETABLE,
- 'callback' => array( __CLASS__, 'delete_service_api_key' ),
- 'permission_callback' => array( __CLASS__, 'edit_others_posts_check' ),
- ),
- )
- );
- }
-
- public static function edit_others_posts_check() {
- if ( current_user_can( 'edit_others_posts' ) ) {
- return true;
- }
-
- $user_permissions_error_msg = esc_html__(
- 'You do not have the correct user permissions to perform this action.
- Please contact your site admin if you think this is a mistake.',
- 'jetpack'
- );
-
- return new WP_Error( 'invalid_user_permission_edit_others_posts', $user_permissions_error_msg, rest_authorization_required_code() );
- }
-
- /**
- * Return the available Gutenberg extensions schema
- *
- * @return array Service API Key schema
- */
- public function get_public_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'service-api-keys',
- 'type' => 'object',
- 'properties' => array(
- 'code' => array(
- 'description' => __( 'Displays success if the operation was successfully executed and an error code if it was not', 'jetpack' ),
- 'type' => 'string',
- ),
- 'service' => array(
- 'description' => __( 'The name of the service in question', 'jetpack' ),
- 'type' => 'string',
- ),
- 'service_api_key' => array(
- 'description' => __( 'The API key used by the service. Empty if none has been set yet', 'jetpack' ),
- 'type' => 'string',
- ),
- 'message' => array(
- 'description' => __( 'User friendly message', 'jetpack' ),
- 'type' => 'string',
- ),
- ),
- );
-
- return $this->add_additional_fields_schema( $schema );
- }
-
- /**
- * Get third party plugin API keys.
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function get_service_api_key( $request ) {
-
- $service = self::validate_service_api_service( $request['service'] );
- if ( ! $service ) {
- return self::service_api_invalid_service_response();
- }
- $option = self::key_for_api_service( $service );
- $message = esc_html__( 'API key retrieved successfully.', 'jetpack' );
- return array(
- 'code' => 'success',
- 'service' => $service,
- 'service_api_key' => Jetpack_Options::get_option( $option, '' ),
- 'message' => $message,
- );
- }
-
- /**
- * Update third party plugin API keys.
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function update_service_api_key( $request ) {
- $service = self::validate_service_api_service( $request['service'] );
- if ( ! $service ) {
- return self::service_api_invalid_service_response();
- }
- $json_params = $request->get_json_params();
- $params = ! empty( $json_params ) ? $json_params : $request->get_body_params();
- $service_api_key = trim( $params['service_api_key'] );
- $option = self::key_for_api_service( $service );
-
- $validation = self::validate_service_api_key( $service_api_key, $service, $params );
- if ( ! $validation['status'] ) {
- return new WP_Error( 'invalid_key', esc_html__( 'Invalid API Key', 'jetpack' ), array( 'status' => 404 ) );
- }
- $message = esc_html__( 'API key updated successfully.', 'jetpack' );
- Jetpack_Options::update_option( $option, $service_api_key );
- return array(
- 'code' => 'success',
- 'service' => $service,
- 'service_api_key' => Jetpack_Options::get_option( $option, '' ),
- 'message' => $message,
- );
- }
-
- /**
- * Delete a third party plugin API key.
- *
- * @param WP_REST_Request $request {
- * Array of parameters received by request.
- *
- * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
- * }
- */
- public static function delete_service_api_key( $request ) {
- $service = self::validate_service_api_service( $request['service'] );
- if ( ! $service ) {
- return self::service_api_invalid_service_response();
- }
- $option = self::key_for_api_service( $service );
- Jetpack_Options::delete_option( $option );
- $message = esc_html__( 'API key deleted successfully.', 'jetpack' );
- return array(
- 'code' => 'success',
- 'service' => $service,
- 'service_api_key' => Jetpack_Options::get_option( $option, '' ),
- 'message' => $message,
- );
- }
-
- /**
- * Validate the service provided in /service-api-keys/ endpoints.
- * To add a service to these endpoints, add the service name to $valid_services
- * and add '{service name}_api_key' to the non-compact return array in get_option_names(),
- * in class-jetpack-options.php
- *
- * @param string $service The service the API key is for.
- * @return string Returns the service name if valid, null if invalid.
- */
- public static function validate_service_api_service( $service = null ) {
- $valid_services = array(
- 'mapbox',
- );
- return in_array( $service, $valid_services, true ) ? $service : null;
- }
-
- /**
- * Error response for invalid service API key requests with an invalid service.
- */
- public static function service_api_invalid_service_response() {
- return new WP_Error(
- 'invalid_service',
- esc_html__( 'Invalid Service', 'jetpack' ),
- array( 'status' => 404 )
- );
- }
-
- /**
- * Validate API Key
- *
- * @param string $key The API key to be validated.
- * @param string $service The service the API key is for.
- */
- public static function validate_service_api_key( $key = null, $service = null ) {
- $validation = false;
- switch ( $service ) {
- case 'mapbox':
- $validation = self::validate_service_api_key_mapbox( $key );
- break;
- }
- return $validation;
- }
-
- /**
- * Validate Mapbox API key
- * Based loosely on https://github.com/mapbox/geocoding-example/blob/master/php/MapboxTest.php
- *
- * @param string $key The API key to be validated.
- */
- public static function validate_service_api_key_mapbox( $key ) {
- $status = true;
- $msg = null;
- $mapbox_url = sprintf(
- 'https://api.mapbox.com?%s',
- $key
- );
- $mapbox_response = wp_safe_remote_get( esc_url_raw( $mapbox_url ) );
- $mapbox_body = wp_remote_retrieve_body( $mapbox_response );
- if ( '{"api":"mapbox"}' !== $mapbox_body ) {
- $status = false;
- $msg = esc_html__( 'Can\'t connect to Mapbox', 'jetpack' );
- return array(
- 'status' => $status,
- 'error_message' => $msg,
- );
- }
- $mapbox_geocode_url = esc_url_raw(
- sprintf(
- 'https://api.mapbox.com/geocoding/v5/mapbox.places/%s.json?access_token=%s',
- '1+broadway+new+york+ny+usa',
- $key
- )
- );
- $mapbox_geocode_response = wp_safe_remote_get( esc_url_raw( $mapbox_geocode_url ) );
- $mapbox_geocode_body = wp_remote_retrieve_body( $mapbox_geocode_response );
- $mapbox_geocode_json = json_decode( $mapbox_geocode_body );
- if ( isset( $mapbox_geocode_json->message ) && ! isset( $mapbox_geocode_json->query ) ) {
- $status = false;
- $msg = $mapbox_geocode_json->message;
- }
- return array(
- 'status' => $status,
- 'error_message' => $msg,
- );
- }
-
- /**
- * Create site option key for service
- *
- * @param string $service The service to create key for.
- */
- private static function key_for_api_service( $service ) {
- return $service . '_api_key';
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php
deleted file mode 100644
index 4c34161c..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/*
- * Plugin Name: WPCOM Add Featured Media URL
- *
- * Adds `jetpack_featured_media_url` to post responses
- */
-
-class WPCOM_REST_API_V2_Sites_Posts_Add_Featured_Media_URL {
- function __construct() {
- add_action( 'rest_api_init', array( $this, 'add_featured_media_url' ) );
- }
-
- function add_featured_media_url() {
- register_rest_field( 'post', 'jetpack_featured_media_url',
- array(
- 'get_callback' => array( $this, 'get_featured_media_url' ),
- 'update_callback' => null,
- 'schema' => null,
- )
- );
- }
-
- function get_featured_media_url( $object, $field_name, $request ) {
- $featured_media_url = '';
- $image_attributes = wp_get_attachment_image_src(
- get_post_thumbnail_id( $object['id'] ),
- 'full'
- );
- if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
- $featured_media_url = (string) $image_attributes[0];
- }
- return $featured_media_url;
- }
-}
-
-wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Sites_Posts_Add_Featured_Media_URL' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php
deleted file mode 100644
index c1a712bd..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * Subscribers: Get subscriber count
- *
- * @since 6.9
- */
-class WPCOM_REST_API_V2_Endpoint_Subscribers extends WP_REST_Controller {
- function __construct() {
- $this->namespace = 'wpcom/v2';
- $this->rest_base = 'subscribers';
- // This endpoint *does not* need to connect directly to Jetpack sites.
- $this->wpcom_is_wpcom_only_endpoint = true;
- add_action( 'rest_api_init', array( $this, 'register_routes' ) );
- }
-
- public function register_routes() {
- // GET /sites/<blog_id>/subscribers/count - Return number of subscribers for this site.
- register_rest_route( $this->namespace, '/' . $this->rest_base . '/count', array(
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_subscriber_count' ),
- 'permission_callback' => array( $this, 'readable_permission_check' ),
- )
- ) );
- }
-
- public function readable_permission_check() {
- if ( ! current_user_can_for_blog( get_current_blog_id(), 'edit_posts' ) ) {
- return new WP_Error( 'authorization_required', 'Only users with the permission to edit posts can see the subscriber count.', array( 'status' => 401 ) );
- }
-
- return true;
- }
-
- /**
- * Retrieves subscriber count
- *
- * @param WP_REST_Request $request incoming API request info
- * @return array data object containing subscriber count
- */
- public function get_subscriber_count( $request ) {
- // Get the most up to date subscriber count when request is not a test
- if ( ! Jetpack_Constants::is_defined( 'TESTING_IN_JETPACK' ) ) {
- delete_transient( 'wpcom_subscribers_total' );
- }
-
- $subscriber_info = Jetpack_Subscriptions_Widget::fetch_subscriber_count();
- $subscriber_count = $subscriber_info['value'];
-
- return array(
- 'count' => $subscriber_count
- );
- }
-}
-
-if (
- Jetpack::is_module_active( 'subscriptions' ) ||
- ( Jetpack_Constants::is_defined( 'TESTING_IN_JETPACK' ) && Jetpack_Constants::get_constant( 'TESTING_IN_JETPACK' ) )
-) {
- wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Subscribers' );
-}
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-fields/attachment-fields-videopress.php b/plugins/jetpack/_inc/lib/core-api/wpcom-fields/attachment-fields-videopress.php
deleted file mode 100644
index b615c4e6..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-fields/attachment-fields-videopress.php
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-/**
- * Extend the REST API functionality for VideoPress users.
- *
- * @package Jetpack
- */
-
-/**
- * Add per-attachment VideoPress data.
- *
- * { # Attachment Object
- * ...
- * jetpack_videopress_guid: (string) VideoPress identifier
- * ...
- * }
- *
- * @since 7.1.0
- */
-class WPCOM_REST_API_V2_Attachment_VideoPress_Field extends WPCOM_REST_API_V2_Field_Controller {
- /**
- * The REST Object Type to which the jetpack_videopress_guid field will be added.
- *
- * @var string
- */
- protected $object_type = 'attachment';
-
- /**
- * The name of the REST API field to add.
- *
- * @var string $field_name
- */
- protected $field_name = 'jetpack_videopress_guid';
-
- /**
- * Registers the jetpack_videopress field and adds a filter to remove it for attachments that are not videos.
- */
- public function register_fields() {
- parent::register_fields();
-
- add_filter( 'rest_prepare_attachment', array( $this, 'remove_field_for_non_videos' ), 10, 2 );
- }
-
- /**
- * Defines data structure and what elements are visible in which contexts
- */
- public function get_schema() {
- return array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => $this->field_name,
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- 'description' => __( 'Unique VideoPress ID', 'jetpack' ),
- );
- }
-
- /**
- * Getter: Retrieve current VideoPress data for a given attachment.
- *
- * @param array $attachment Response from the attachment endpoint.
- * @param WP_REST_Request $request Request to the attachment endpoint.
- *
- * @return string
- */
- public function get( $attachment, $request ) {
- if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
- $blog_id = get_current_blog_id();
- } else {
- $blog_id = Jetpack_Options::get_option( 'id' );
- }
-
- $post_id = absint( $attachment['id'] );
-
- $videopress_guid = $this->get_videopress_guid( $post_id, $blog_id );
-
- if ( ! $videopress_guid ) {
- return '';
- }
-
- return $videopress_guid;
- }
-
- /**
- * Gets the VideoPress GUID for a given attachment.
- *
- * This is pulled out into a separate method to support unit test mocking.
- *
- * @param int $attachment_id Attachment ID.
- * @param int $blog_id Blog ID.
- *
- * @return string
- */
- public function get_videopress_guid( $attachment_id, $blog_id ) {
- return video_get_info_by_blogpostid( $blog_id, $attachment_id )->guid;
- }
-
- /**
- * Checks if the given attachment is a video.
- *
- * @param object $attachment The attachment object.
- *
- * @return false|int
- */
- public function is_video( $attachment ) {
- return wp_startswith( $attachment->post_mime_type, 'video/' );
- }
-
- /**
- * Removes the jetpack_videopress_guid field from the response if the
- * given attachment is not a video.
- *
- * @param WP_REST_Response $response Response from the attachment endpoint.
- * @param WP_Post $attachment The original attachment object.
- *
- * @return mixed
- */
- public function remove_field_for_non_videos( $response, $attachment ) {
- if ( ! $this->is_video( $attachment ) ) {
- unset( $response->data[ $this->field_name ] );
- }
-
- return $response;
- }
-
- /**
- * Setter: It does nothing since `jetpack_videopress` is a read-only field.
- *
- * @param mixed $value The new value for the field.
- * @param WP_Post $object The attachment object.
- * @param WP_REST_Request $request The request object.
- *
- * @return null
- */
- public function update( $value, $object, $request ) {
- return null;
- }
-
- /**
- * Permission Check for the field's getter. Delegate the responsibility to the
- * attachment endpoint, so it always returns true.
- *
- * @param mixed $object Response from the attachment endpoint.
- * @param WP_REST_Request $request Request to the attachment endpoint.
- *
- * @return true
- */
- public function get_permission_check( $object, $request ) {
- return true;
- }
-
- /**
- * Permission Check for the field's setter. Delegate the responsibility to the
- * attachment endpoint, so it always returns true.
- *
- * @param mixed $value The new value for the field.
- * @param WP_Post $object The attachment object.
- * @param WP_REST_Request $request Request to the attachment endpoint.
- *
- * @return true
- */
- public function update_permission_check( $value, $object, $request ) {
- return true;
- }
-}
-
-if (
- ( method_exists( 'Jetpack', 'is_active' ) && Jetpack::is_active() ) ||
- ( defined( 'IS_WPCOM' ) && IS_WPCOM )
-) {
- wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Attachment_VideoPress_Field' );
-}
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php b/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php
deleted file mode 100644
index c4254a9d..00000000
--- a/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php
+++ /dev/null
@@ -1,353 +0,0 @@
-<?php
-
-/**
- * Add per-post Publicize Connection data.
- *
- * { # Post Object
- * ...
- * jetpack_publicize_connections: { # Defined below in this file. See schema for more detail.
- * id: (string) Connection unique_id
- * service_name: (string) Service slug
- * display_name: (string) User name/display name of user/connection on Service
- * enabled: (boolean) Is this connection slated to be shared to? context=edit only
- * done: (boolean) Is this post (or connection) done sharing? context=edit only
- * toggleable: (boolean) Can the current user change the `enabled` setting for this Connection+Post? context=edit only
- * }
- * ...
- * meta: { # Not defined in this file. Handled in modules/publicize/publicize.php via `register_meta()`
- * jetpack_publicize_message: (string) The message to use instead of the post's title when sharing.
- * }
- * ...
- * }
- *
- * @since 6.8.0
- */
-class WPCOM_REST_API_V2_Post_Publicize_Connections_Field extends WPCOM_REST_API_V2_Field_Controller {
- protected $object_type = 'post';
- protected $field_name = 'jetpack_publicize_connections';
-
- public $memoized_updates = array();
-
- /**
- * Registers the jetpack_publicize_connections field. Called
- * automatically on `rest_api_init()`.
- */
- public function register_fields() {
- $this->object_type = get_post_types_by_support( 'publicize' );
-
- foreach ( $this->object_type as $post_type ) {
- // Adds meta support for those post types that don't already have it.
- // Only runs during REST API requests, so it doesn't impact UI.
- if ( ! post_type_supports( $post_type, 'custom-fields' ) ) {
- add_post_type_support( $post_type, 'custom-fields' );
- }
-
- add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'rest_pre_insert' ), 10, 2 );
- add_action( 'rest_insert_' . $post_type, array( $this, 'rest_insert' ), 10, 3 );
- }
-
- parent::register_fields();
- }
-
- /**
- * Defines data structure and what elements are visible in which contexts
- */
- public function get_schema() {
- return array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'jetpack-publicize-post-connections',
- 'type' => 'array',
- 'context' => array( 'view', 'edit' ),
- 'items' => $this->post_connection_schema(),
- 'default' => array(),
- );
- }
-
- private function post_connection_schema() {
- return array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => 'jetpack-publicize-post-connection',
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the Publicize Connection', 'jetpack' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'service_name' => array(
- 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'display_name' => array(
- 'description' => __( 'Username of the connected account', 'jetpack' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'enabled' => array(
- 'description' => __( 'Whether to share to this connection', 'jetpack' ),
- 'type' => 'boolean',
- 'context' => array( 'edit' ),
- ),
- 'done' => array(
- 'description' => __( 'Whether Publicize has already finished sharing for this post', 'jetpack' ),
- 'type' => 'boolean',
- 'context' => array( 'edit' ),
- 'readonly' => true,
- ),
- 'toggleable' => array(
- 'description' => __( 'Whether `enable` can be changed for this post/connection', 'jetpack' ),
- 'type' => 'boolean',
- 'context' => array( 'edit' ),
- 'readonly' => true,
- ),
- ),
- );
- }
-
- /**
- * @param int $post_id
- * @return true|WP_Error
- */
- function permission_check( $post_id ) {
- global $publicize;
-
- if ( ! $publicize ) {
- return new WP_Error(
- 'publicize_not_available',
- __( 'Sorry, Publicize is not available on your site right now.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- if ( $publicize->current_user_can_access_publicize_data( $post_id ) ) {
- return true;
- }
-
- return new WP_Error(
- 'invalid_user_permission_publicize',
- __( 'Sorry, you are not allowed to access Publicize data for this post.', 'jetpack' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
-
- /**
- * Getter permission check
- *
- * @param array $post_array Response data from Post Endpoint
- * @return true|WP_Error
- */
- function get_permission_check( $post_array, $request ) {
- return $this->permission_check( isset( $post_array['id'] ) ? $post_array['id'] : 0 );
-
- }
-
- /**
- * Setter permission check
- *
- * @param WP_Post $post
- * @return true|WP_Error
- */
- public function update_permission_check( $value, $post, $request ) {
- return $this->permission_check( isset( $post->ID ) ? $post->ID : 0 );
- }
-
- /**
- * Getter: Retrieve current list of connected social accounts for a given post.
- *
- * @see Publicize::get_filtered_connection_data()
- *
- * @param array $post_array Response from Post Endpoint
- * @param WP_REST_Request
- *
- * @return array List of connections
- */
- public function get( $post_array, $request ) {
- global $publicize;
-
- if ( ! $publicize ) {
- return array();
- }
-
- $schema = $this->post_connection_schema();
- $properties = array_keys( $schema['properties'] );
-
- $connections = $publicize->get_filtered_connection_data( $post_array['id'] );
-
- $output_connections = array();
- foreach ( $connections as $connection ) {
- $output_connection = array();
- foreach ( $properties as $property ) {
- if ( isset( $connection[ $property ] ) ) {
- $output_connection[ $property ] = $connection[ $property ];
- }
- }
-
- $output_connection['id'] = (string) $connection['unique_id'];
-
- $output_connections[] = $output_connection;
- }
-
- return $output_connections;
- }
-
- /**
- * Prior to updating the post, first calculate which Services to
- * Publicize to and which to skip.
- *
- * @param object $post Post data to insert/update.
- * @param WP_REST_Request $request
- * @return Filtered $post
- */
- public function rest_pre_insert( $post, $request ) {
- if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
- return $post;
- }
-
- $permission_check = $this->update_permission_check( $request['jetpack_publicize_connections'], $post, $request );
-
- if ( is_wp_error( $permission_check ) ) {
- return $permission_check;
- }
-
- // memoize
- $this->get_meta_to_update( $request['jetpack_publicize_connections'], isset( $post->ID ) ? $post->ID : 0 );
-
- return $post;
- }
-
- /**
- * After creating a new post, update our cached data to reflect
- * the new post ID.
- *
- * @param WP_Post $post
- * @param WP_REST_Request $request
- * @param bool $is_new
- */
- public function rest_insert( $post, $request, $is_new ) {
- if ( ! $is_new ) {
- // An existing post was edited - no need to update
- // our cache - we started out knowing the correct
- // post ID.
- return;
- }
-
- if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
- return;
- }
-
- if ( ! isset( $this->memoized_updates[0] ) ) {
- return;
- }
-
- $this->memoized_updates[ $post->ID ] = $this->memoized_updates[0];
- unset( $this->memoized_updates[0] );
- }
-
- protected function get_meta_to_update( $requested_connections, $post_id = 0 ) {
- global $publicize;
-
- if ( ! $publicize ) {
- return array();
- }
-
- if ( isset( $this->memoized_updates[$post_id] ) ) {
- return $this->memoized_updates[$post_id];
- }
-
- $available_connections = $publicize->get_filtered_connection_data( $post_id );
-
- $changed_connections = array();
-
- // Build lookup mappings
- $available_connections_by_unique_id = array();
- $available_connections_by_service_name = array();
- foreach ( $available_connections as $available_connection ) {
- $available_connections_by_unique_id[ $available_connection['unique_id'] ] = $available_connection;
-
- if ( ! isset( $available_connections_by_service_name[ $available_connection['service_name'] ] ) ) {
- $available_connections_by_service_name[ $available_connection['service_name'] ] = array();
- }
- $available_connections_by_service_name[ $available_connection['service_name'] ][] = $available_connection;
- }
-
- // Handle { service_name: $service_name, enabled: (bool) }
- foreach ( $requested_connections as $requested_connection ) {
- if ( ! isset( $requested_connection['service_name'] ) ) {
- continue;
- }
-
- if ( ! isset( $available_connections_by_service_name[ $requested_connection['service_name'] ] ) ) {
- continue;
- }
-
- foreach ( $available_connections_by_service_name[ $requested_connection['service_name'] ] as $available_connection ) {
- $changed_connections[ $available_connection['unique_id'] ] = $requested_connection['enabled'];
- }
- }
-
- // Handle { id: $id, enabled: (bool) }
- // These override the service_name settings
- foreach ( $requested_connections as $requested_connection ) {
- if ( ! isset( $requested_connection['id'] ) ) {
- continue;
- }
-
- if ( ! isset( $available_connections_by_unique_id[ $requested_connection['id'] ] ) ) {
- continue;
- }
-
- $changed_connections[ $requested_connection['id'] ] = $requested_connection['enabled'];
- }
-
- // Set all changed connections to their new value
- foreach ( $changed_connections as $unique_id => $enabled ) {
- $connection = $available_connections_by_unique_id[ $unique_id ];
-
- if ( $connection['done'] || ! $connection['toggleable'] ) {
- continue;
- }
-
- $available_connections_by_unique_id[ $unique_id ]['enabled'] = $enabled;
- }
-
- $meta_to_update = array();
- // For all connections, ensure correct post_meta
- foreach ( $available_connections_by_unique_id as $unique_id => $available_connection ) {
- if ( $available_connection['enabled'] ) {
- $meta_to_update[$publicize->POST_SKIP . $unique_id] = null;
- } else {
- $meta_to_update[$publicize->POST_SKIP . $unique_id] = 1;
- }
- }
-
- $this->memoized_updates[$post_id] = $meta_to_update;
-
- return $meta_to_update;
- }
-
- /**
- * Update the connections slated to be shared to.
- *
- * @param array $requested_connections
- * Items are either `{ id: (string) }` or `{ service_name: (string) }`
- * @param WP_Post $post
- * @param WP_REST_Request
- */
- public function update( $requested_connections, $post, $request ) {
- foreach ( $this->get_meta_to_update( $requested_connections, $post->ID ) as $meta_key => $meta_value ) {
- if ( is_null( $meta_value ) ) {
- delete_post_meta( $post->ID, $meta_key );
- } else {
- update_post_meta( $post->ID, $meta_key, $meta_value );
- }
- }
- }
-}
-
-if ( Jetpack::is_module_active( 'publicize' ) ) {
- wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Post_Publicize_Connections_Field' );
-}
diff --git a/plugins/jetpack/_inc/lib/debugger/0-load.php b/plugins/jetpack/_inc/lib/debugger/0-load.php
deleted file mode 100644
index b097dc50..00000000
--- a/plugins/jetpack/_inc/lib/debugger/0-load.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-/**
- * Loading the various functions used for Jetpack Debugging.
- *
- * @package Jetpack.
- */
-
-/* Jetpack Connection Testing Framework */
-require_once 'class-jetpack-cxn-test-base.php';
-/* Jetpack Connection Tests */
-require_once 'class-jetpack-cxn-tests.php';
-/* The "In-Plugin Debugger" admin page. */
-require_once 'class-jetpack-debugger.php';
diff --git a/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-test-base.php b/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-test-base.php
deleted file mode 100644
index 0cc861db..00000000
--- a/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-test-base.php
+++ /dev/null
@@ -1,302 +0,0 @@
-<?php
-/**
- * Jetpack Connection Testing
- *
- * Framework for various "unit tests" against the Jetpack connection.
- *
- * Individual tests should be added to the class-jetpack-cxn-tests.php file.
- *
- * @author Brandon Kraft
- * @package Jetpack
- */
-
-/**
- * "Unit Tests" for the Jetpack connection.
- */
-class Jetpack_Cxn_Test_Base {
-
- /**
- * Tests to run on the Jetpack connection.
- *
- * @var array $tests
- */
- protected $tests = array();
-
- /**
- * Results of the Jetpack connection tests.
- *
- * @var array $results
- */
- protected $results = array();
-
- /**
- * Status of the testing suite.
- *
- * Used internally to determine if a test should be skipped since the tests are already failing. Assume passing.
- *
- * @var bool $pass
- */
- protected $pass = true;
-
- /**
- * Jetpack_Cxn_Test constructor.
- */
- public function __construct() {
- $this->tests = array();
- $this->results = array();
- }
-
- /**
- * Adds a new test to the Jetpack Connection Testing suite.
- *
- * @param callable $callable Test to add to queue.
- * @param array $groups Testing groups to add test to.
- *
- * @return bool True if successfully added. False for a failure.
- */
- public function add_test( $callable, $groups = array( 'default' ) ) {
- if ( is_callable( $callable ) ) {
- $this->tests[] = array(
- 'test' => $callable,
- 'group' => $groups,
- );
- return true;
- }
-
- return false;
- }
-
- /**
- * Runs the Jetpack connection suite.
- */
- public function run_tests() {
- foreach ( $this->tests as $test ) {
- $result = call_user_func( $test['test'] );
- $result['group'] = $test['group'];
- $this->results[] = $result;
- if ( false === $result['pass'] ) {
- $this->pass = false;
- }
- }
- }
-
- /**
- * Returns the full results array.
- *
- * @param string $group Testing group whose results we want. Defaults to "default" group. Use "all" for all tests.
- * @return array Array of test results.
- */
- public function raw_results( $group = 'default' ) {
- if ( ! $this->results ) {
- $this->run_tests();
- }
-
- $results = $this->results;
-
- if ( 'all' === $group ) {
- return $results;
- }
-
- foreach ( $results as $test => $result ) {
- if ( ! in_array( $group, $result['group'], true ) ) {
- unset( $results[ $test ] );
- }
- }
-
- return $results;
- }
-
- /**
- * Returns the status of the connection suite.
- *
- * @param string $group Testing group to check status of. Optional, default all tests.
- *
- * @return true|array True if all tests pass. Array of failed tests.
- */
- public function pass( $group = 'default' ) {
- $results = $this->raw_results( $group );
-
- foreach ( $results as $result ) {
- // 'pass' could be true, false, or 'skipped'. We only want false.
- if ( isset( $result['pass'] ) && false === $result['pass'] ) {
- return false;
- }
- }
-
- return true;
-
- }
-
- /**
- * Return array of failed test messages.
- *
- * @param string $group Testing group whose failures we want. Defaults to "default". Use "all" for all tests.
- *
- * @return false|array False if no failed tests. Otherwise, array of failed tests.
- */
- public function list_fails( $group = 'default' ) {
- $results = $this->raw_results( $group );
-
- foreach ( $results as $test => $result ) {
- // We do not want tests that passed or ones that are misconfigured (no pass status or no failure message).
- if ( ! isset( $result['pass'] ) || false !== $result['pass'] || ! isset( $result['message'] ) ) {
- unset( $results[ $test ] );
- }
- }
-
- return $results;
- }
-
- /**
- * Helper function to return consistent responses for a passing test.
- *
- * @param string $name Test name.
- *
- * @return array Test results.
- */
- public static function passing_test( $name = 'Unnamed' ) {
- return array(
- 'name' => $name,
- 'pass' => true,
- 'message' => __( 'Test Passed!', 'jetpack' ),
- 'resolution' => false,
- );
- }
-
- /**
- * Helper function to return consistent responses for a skipped test.
- *
- * @param string $name Test name.
- * @param string $message Reason for skipping the test. Optional.
- *
- * @return array Test results.
- */
- public static function skipped_test( $name = 'Unnamed', $message = false ) {
- return array(
- 'name' => $name,
- 'pass' => 'skipped',
- 'message' => $message,
- 'resolution' => false,
- );
- }
-
- /**
- * Helper function to return consistent responses for a failing test.
- *
- * @param string $name Test name.
- * @param string $message Message detailing the failure.
- * @param string $resolution Steps to resolve.
- *
- * @return array Test results.
- */
- public static function failing_test( $name, $message, $resolution = false ) {
- // Provide standard resolutions steps, but allow pass-through of non-standard ones.
- switch ( $resolution ) {
- case 'cycle_connection':
- $resolution = __( 'Please disconnect and reconnect Jetpack.', 'jetpack' ); // @todo: Link.
- break;
- case 'outbound_requests':
- $resolution = __( 'Please ask your hosting provider to confirm your server can make outbound requests to jetpack.com.', 'jetpack' );
- break;
- case 'support':
- $resolution = __( 'Please contact support.', 'jetpack' ); // @todo: Link to support.
- break;
- }
-
- return array(
- 'name' => $name,
- 'pass' => false,
- 'message' => $message,
- 'resolution' => $resolution,
- );
- }
-
- /**
- * Provide WP_CLI friendly testing results.
- *
- * @param string $group Testing group whose results we are outputting. Default "default". Use "all" for all tests.
- */
- public function output_results_for_cli( $group = 'default' ) {
- if ( defined( 'WP_CLI' ) && WP_CLI ) {
- if ( Jetpack::is_development_mode() ) {
- WP_CLI::line( __( 'Jetpack is in Development Mode:', 'jetpack' ) );
- WP_CLI::line( Jetpack::development_mode_trigger_text() );
- }
- WP_CLI::line( __( 'TEST RESULTS:', 'jetpack' ) );
- foreach ( $this->raw_results( $group ) as $test ) {
- if ( true === $test['pass'] ) {
- WP_CLI::log( WP_CLI::colorize( '%gPassed:%n ' . $test['name'] ) );
- } elseif ( 'skipped' === $test['pass'] ) {
- WP_CLI::log( WP_CLI::colorize( '%ySkipped:%n ' . $test['name'] ) );
- if ( $test['message'] ) {
- WP_CLI::log( ' ' . $test['message'] ); // Number of spaces to "tab indent" the reason.
- }
- } else { // Failed.
- WP_CLI::log( WP_CLI::colorize( '%rFailed:%n ' . $test['name'] ) );
- WP_CLI::log( ' ' . $test['message'] ); // Number of spaces to "tab indent" the reason.
- }
- }
- }
- }
-
- /**
- * Provide single WP Error instance of all failures.
- *
- * @param string $group Testing group whose failures we want converted. Default "default". Use "all" for all tests.
- *
- * @return WP_Error|false WP_Error with all failed tests or false if there were no failures.
- */
- public function output_fails_as_wp_error( $group = 'default' ) {
- if ( $this->pass( $group ) ) {
- return false;
- }
- $fails = $this->list_fails( $group );
- $error = false;
-
- foreach ( $fails as $result ) {
- $code = 'failed_' . $result['name'];
- $message = $result['message'];
- $data = array(
- 'resolution' => $result['resolution'],
- );
- if ( ! $error ) {
- $error = new WP_Error( $code, $message, $data );
- } else {
- $error->add( $code, $message, $data );
- }
- }
-
- return $error;
- }
-
- /**
- * Encrypt data for sending to WordPress.com.
- *
- * @todo When PHP minimum is 5.3+, add cipher detection to use an agreed better cipher than RC4. RC4 should be the last resort.
- *
- * @param string $data Data to encrypt with the WP.com Public Key.
- *
- * @return false|array False if functionality not available. Array of encrypted data, encryption key.
- */
- public function encrypt_string_for_wpcom( $data ) {
- $return = false;
- if ( ! function_exists( 'openssl_get_publickey' ) || ! function_exists( 'openssl_seal' ) ) {
- return $return;
- }
-
- $public_key = openssl_get_publickey( JETPACK__DEBUGGER_PUBLIC_KEY );
-
- if ( $public_key && openssl_seal( $data, $encrypted_data, $env_key, array( $public_key ) ) ) {
- // We are returning base64-encoded values to ensure they're characters we can use in JSON responses without issue.
- $return = array(
- 'data' => base64_encode( $encrypted_data ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
- 'key' => base64_encode( $env_key[0] ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
- 'cipher' => 'RC4', // When Jetpack's minimum WP version is at PHP 5.3+, we will add in detecting and using a stronger one.
- );
- }
-
- openssl_free_key( $public_key );
-
- return $return;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-tests.php b/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-tests.php
deleted file mode 100644
index 6d4f00e6..00000000
--- a/plugins/jetpack/_inc/lib/debugger/class-jetpack-cxn-tests.php
+++ /dev/null
@@ -1,338 +0,0 @@
-<?php
-/**
- * Collection of tests to run on the Jetpack connection locally.
- *
- * @package Jetpack
- */
-
-/**
- * Class Jetpack_Cxn_Tests contains all of the actual tests.
- */
-class Jetpack_Cxn_Tests extends Jetpack_Cxn_Test_Base {
-
- /**
- * Jetpack_Cxn_Tests constructor.
- */
- public function __construct() {
- parent::__construct();
-
- $methods = get_class_methods( 'Jetpack_Cxn_Tests' );
-
- foreach ( $methods as $method ) {
- if ( false === strpos( $method, 'test__' ) ) {
- continue;
- }
- $this->add_test( array( $this, $method ) );
- }
-
- /**
- * Fires after loading default Jetpack Connection tests.
- *
- * @since 7.1.0
- */
- do_action( 'jetpack_connection_tests_loaded' );
-
- /**
- * Determines if the WP.com testing suite should be included.
- *
- * @since 7.1.0
- *
- * @param bool $run_test To run the WP.com testing suite. Default true.
- */
- if ( apply_filters( 'jetpack_debugger_run_self_test', true ) ) {
- /**
- * Intentionally added last as it checks for an existing failure state before attempting.
- * Generally, any failed location condition would result in the WP.com check to fail too, so
- * we will skip it to avoid confusing error messages.
- */
- $this->add_test( array( $this, 'last__wpcom_self_test' ) );
- }
- }
-
- /**
- * Helper function to look up the expected master user and return the local WP_User.
- *
- * @return WP_User Jetpack's expected master user.
- */
- protected function helper_retrieve_local_master_user() {
- $master_user = Jetpack_Options::get_option( 'master_user' );
- return new WP_User( $master_user );
- }
-
- /**
- * Is Jetpack even connected and supposed to be talking to WP.com?
- */
- protected function helper_is_jetpack_connected() {
- return ( Jetpack::is_active() && ! Jetpack::is_development_mode() );
- }
-
- /**
- * Test if Jetpack is connected.
- */
- protected function test__check_if_connected() {
- $name = __FUNCTION__;
- if ( $this->helper_is_jetpack_connected() ) {
- $result = self::passing_test( $name );
- } elseif ( Jetpack::is_development_mode() ) {
- $result = self::skipped_test( $name, __( 'Jetpack is in Development Mode:', 'jetpack' ) . ' ' . Jetpack::development_mode_trigger_text(), __( 'Disable development mode.', 'jetpack' ) );
- } else {
- $result = self::failing_test( $name, __( 'Jetpack is not connected.', 'jetpack' ), 'cycle_connection' );
- }
-
- return $result;
- }
-
- /**
- * Test that the master user still exists on this site.
- *
- * @return array Test results.
- */
- protected function test__master_user_exists_on_site() {
- $name = __FUNCTION__;
- if ( ! $this->helper_is_jetpack_connected() ) {
- return self::skipped_test( $name, __( 'Jetpack is not connected. No master user to check.', 'jetpack' ) ); // Skip test.
- }
- $local_user = $this->helper_retrieve_local_master_user();
-
- if ( $local_user->exists() ) {
- $result = self::passing_test( $name );
- } else {
- $result = self::failing_test( $name, __( 'The user who setup the Jetpack connection no longer exists on this site.', 'jetpack' ), 'cycle_connection' );
- }
-
- return $result;
- }
-
- /**
- * Test that the master user has the manage options capability (e.g. is an admin).
- *
- * Generic calls from WP.com execute on Jetpack as the master user. If it isn't an admin, random things will fail.
- *
- * @return array Test results.
- */
- protected function test__master_user_can_manage_options() {
- $name = __FUNCTION__;
- if ( ! $this->helper_is_jetpack_connected() ) {
- return self::skipped_test( $name, __( 'Jetpack is not connected.', 'jetpack' ) ); // Skip test.
- }
- $master_user = $this->helper_retrieve_local_master_user();
-
- if ( user_can( $master_user, 'manage_options' ) ) {
- $result = self::passing_test( $name );
- } else {
- /* translators: a WordPress username */
- $result = self::failing_test( $name, sprintf( __( 'The user (%s) who setup the Jetpack connection is not an administrator.', 'jetpack' ), $master_user->user_login ), __( 'Either upgrade the user or disconnect and reconnect Jetpack.', 'jetpack' ) ); // @todo: Link to the right places.
- }
-
- return $result;
- }
-
- /**
- * Test that the PHP's XML library is installed.
- *
- * While it should be installed by default, increasingly in PHP 7, some OSes require an additional php-xml package.
- *
- * @return array Test results.
- */
- protected function test__xml_parser_available() {
- $name = __FUNCTION__;
- if ( function_exists( 'xml_parser_create' ) ) {
- $result = self::passing_test( $name );
- } else {
- $result = self::failing_test( $name, __( 'PHP XML manipluation libraries are not available.', 'jetpack' ), __( "Please ask your hosting provider to refer to our server requirements at https://jetpack.com/support/server-requirements/ and enable PHP's XML module.", 'jetpack' ) );
- }
-
- return $result;
- }
-
- /**
- * Test that the server is able to send an outbound http communication.
- *
- * @return array Test results.
- */
- protected function test__outbound_http() {
- $name = __FUNCTION__;
- $request = wp_remote_get( preg_replace( '/^https:/', 'http:', JETPACK__API_BASE ) . 'test/1/' );
- $code = wp_remote_retrieve_response_code( $request );
-
- if ( 200 === intval( $code ) ) {
- $result = self::passing_test( $name );
- } else {
- $result = self::failing_test( $name, __( 'Your server did not successfully connect to the Jetpack server using HTTP', 'jetpack' ), 'outbound_requests' );
- }
-
- return $result;
- }
-
- /**
- * Test that the server is able to send an outbound https communication.
- *
- * @return array Test results.
- */
- protected function test__outbound_https() {
- $name = __FUNCTION__;
- $request = wp_remote_get( preg_replace( '/^http:/', 'https:', JETPACK__API_BASE ) . 'test/1/' );
- $code = wp_remote_retrieve_response_code( $request );
-
- if ( 200 === intval( $code ) ) {
- $result = self::passing_test( $name );
- } else {
- $result = self::failing_test( $name, __( 'Your server did not successfully connect to the Jetpack server using HTTPS', 'jetpack' ), 'outbound_requests' );
- }
-
- return $result;
- }
-
- /**
- * Check for an IDC.
- *
- * @return array Test results.
- */
- protected function test__identity_crisis() {
- $name = __FUNCTION__;
- if ( ! $this->helper_is_jetpack_connected() ) {
- return self::skipped_test( $name, __( 'Jetpack is not connected.', 'jetpack' ) ); // Skip test.
- }
- $identity_crisis = Jetpack::check_identity_crisis();
-
- if ( ! $identity_crisis ) {
- $result = self::passing_test( $name );
- } else {
- $message = sprintf(
- /* translators: Two URLs. The first is the locally-recorded value, the second is the value as recorded on WP.com. */
- __( 'Your url is set as `%1$s`, but your WordPress.com connection lists it as `%2$s`!', 'jetpack' ),
- $identity_crisis['home'],
- $identity_crisis['wpcom_home']
- );
- $result = self::failing_test( $name, $message, 'support' );
- }
- return $result;
- }
-
- /**
- * Tests connection status against wp.com's test-connection endpoint
- *
- * @todo: Compare with the wpcom_self_test. We only need one of these.
- *
- * @return array Test results.
- */
- protected function test__wpcom_connection_test() {
- $name = __FUNCTION__;
-
- if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || Jetpack::is_staging_site() || ! $this->pass ) {
- return self::skipped_test( $name );
- }
-
- $response = Jetpack_Client::wpcom_json_api_request_as_blog(
- sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
- Jetpack_Client::WPCOM_JSON_API_VERSION
- );
-
- if ( is_wp_error( $response ) ) {
- /* translators: %1$s is the error code, %2$s is the error message */
- $message = sprintf( __( 'Connection test failed (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() );
- return self::failing_test( $name, $message );
- }
-
- $body = wp_remote_retrieve_body( $response );
- if ( ! $body ) {
- $message = __( 'Connection test failed (empty response body)', 'jetpack' ) . wp_remote_retrieve_response_code( $response );
- return self::failing_test( $name, $message );
- }
-
- $result = json_decode( $body );
- $is_connected = (bool) $result->connected;
- $message = $result->message . wp_remote_retrieve_response_code( $response );
-
- if ( $is_connected ) {
- return self::passing_test( $name );
- } else {
- return self::failing_test( $name, $message );
- }
- }
-
- /**
- * Tests the port number to ensure it is an expected value.
- *
- * We expect that sites on be on one of:
- * port 80,
- * port 443 (https sites only),
- * the value of JETPACK_SIGNATURE__HTTP_PORT,
- * unless the site is intentionally on a different port (e.g. example.com:8080 is the site's URL).
- *
- * If the value isn't one of those and the site's URL doesn't include a port, then the signature verification will fail.
- *
- * This happens most commonly on sites with reverse proxies, so the edge (e.g. Varnish) is running on 80/443, but nginx
- * or Apache is responding internally on a different port (e.g. 81).
- *
- * @return array Test results
- */
- protected function test__server_port_value() {
- $name = __FUNCTION__;
- if ( ! isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) && ! isset( $_SERVER['SERVER_PORT'] ) ) {
- $message = 'The server port values are not defined. This is most common when running PHP via a CLI.';
- return self::skipped_test( $name, $message );
- }
- $site_port = wp_parse_url( home_url(), PHP_URL_PORT );
- $server_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? (int) $_SERVER['HTTP_X_FORWARDED_PORT'] : (int) $_SERVER['SERVER_PORT'];
- $http_ports = array( 80 );
- $https_ports = array( 80, 443 );
-
- if ( defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ) {
- $http_ports[] = JETPACK_SIGNATURE__HTTP_PORT;
- }
-
- if ( defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ) {
- $https_ports[] = JETPACK_SIGNATURE__HTTPS_PORT;
- }
-
- if ( $site_port ) {
- return self::skipped_test( $name ); // Not currently testing for this situation.
- }
-
- if ( is_ssl() && in_array( $server_port, $https_ports, true ) ) {
- return self::passing_test( $name );
- } elseif ( in_array( $server_port, $http_ports, true ) ) {
- return self::passing_test( $name );
- } else {
- if ( is_ssl() ) {
- $needed_constant = 'JETPACK_SIGNATURE__HTTPS_PORT';
- } else {
- $needed_constant = 'JETPACK_SIGNATURE__HTTP_PORT';
- }
- $message = __( 'The server port value is unexpected.', 'jetpack' );
- $resolution = __( 'Try adding the following to your wp-config.php file:', 'jetpack' ) . " define( '$needed_constant', $server_port );";
- return self::failing_test( $name, $message, $resolution );
- }
- }
-
- /**
- * Calls to WP.com to run the connection diagnostic testing suite.
- *
- * Intentionally added last as it will be skipped if any local failed conditions exist.
- *
- * @return array Test results.
- */
- protected function last__wpcom_self_test() {
- $name = 'test__wpcom_self_test';
- if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || Jetpack::is_staging_site() || ! $this->pass ) {
- return self::skipped_test( $name );
- }
-
- $self_xml_rpc_url = site_url( 'xmlrpc.php' );
-
- $testsite_url = Jetpack::fix_url_for_bad_hosts( JETPACK__API_BASE . 'testsite/1/?url=' );
-
- add_filter( 'http_request_timeout', array( 'Jetpack_Debugger', 'jetpack_increase_timeout' ) );
-
- $response = wp_remote_get( $testsite_url . $self_xml_rpc_url );
-
- remove_filter( 'http_request_timeout', array( 'Jetpack_Debugger', 'jetpack_increase_timeout' ) );
-
- if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
- return self::passing_test( $name );
- } else {
- return self::failing_test( $name, __( 'Jetpack.com detected an error.', 'jetpack' ), __( 'Visit the Jetpack.com debugging page for more information or contact support.', 'jetpack' ) ); // @todo direct links.
- }
- }
-}
diff --git a/plugins/jetpack/_inc/lib/debugger/class-jetpack-debugger.php b/plugins/jetpack/_inc/lib/debugger/class-jetpack-debugger.php
deleted file mode 100644
index 599ccfda..00000000
--- a/plugins/jetpack/_inc/lib/debugger/class-jetpack-debugger.php
+++ /dev/null
@@ -1,530 +0,0 @@
-<?php
-/**
- * Jetpack Debugger functionality allowing for self-service diagnostic information.
- *
- * @package jetpack
- */
-
-/**
- * Class Jetpack_Debugger
- *
- * A namespacing class for functionality related to the in-plugin diagnostic tooling.
- */
-class Jetpack_Debugger {
-
- /**
- * Determine the active plan and normalize it for the debugger results.
- *
- * @return string The plan slug prepended with "JetpackPlan"
- */
- private static function what_jetpack_plan() {
- $plan = Jetpack_Plan::get();
- $plan = ! empty( $plan['class'] ) ? $plan['class'] : 'undefined';
- return 'JetpackPlan' . $plan;
- }
-
- /**
- * Convert seconds to human readable time.
- *
- * A dedication function instead of using Core functionality to allow for output in seconds.
- *
- * @param int $seconds Number of seconds to convert to human time.
- *
- * @return string Human readable time.
- */
- public static function seconds_to_time( $seconds ) {
- $seconds = intval( $seconds );
- $units = array(
- 'week' => WEEK_IN_SECONDS,
- 'day' => DAY_IN_SECONDS,
- 'hour' => HOUR_IN_SECONDS,
- 'minute' => MINUTE_IN_SECONDS,
- 'second' => 1,
- );
- // specifically handle zero.
- if ( 0 === $seconds ) {
- return '0 seconds';
- }
- $human_readable = '';
- foreach ( $units as $name => $divisor ) {
- $quot = intval( $seconds / $divisor );
- if ( $quot ) {
- $human_readable .= "$quot $name";
- $human_readable .= ( abs( $quot ) > 1 ? 's' : '' ) . ', ';
- $seconds -= $quot * $divisor;
- }
- }
- return substr( $human_readable, 0, -2 );
- }
-
- /**
- * Returns 30 for use with a filter.
- *
- * To allow time for WP.com to run upstream testing, this function exists to increase the http_request_timeout value
- * to 30.
- *
- * @return int 30
- */
- public static function jetpack_increase_timeout() {
- return 30; // seconds.
- }
-
- /**
- * Disconnect Jetpack and redirect user to connection flow.
- */
- public static function disconnect_and_redirect() {
- if ( ! ( isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], 'jp_disconnect' ) ) ) {
- return;
- }
-
- if ( isset( $_GET['disconnect'] ) && $_GET['disconnect'] ) {
- if ( Jetpack::is_active() ) {
- Jetpack::disconnect();
- wp_safe_redirect( Jetpack::admin_url() );
- exit;
- }
- }
- }
-
- /**
- * Handles output to the browser for the in-plugin debugger.
- */
- public static function jetpack_debug_display_handler() {
- if ( ! current_user_can( 'manage_options' ) ) {
- wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'jetpack' ) );
- }
-
- $user_id = get_current_user_id();
- $user_tokens = Jetpack_Options::get_option( 'user_tokens' );
- if ( is_array( $user_tokens ) && array_key_exists( $user_id, $user_tokens ) ) {
- $user_token = $user_tokens[ $user_id ];
- } else {
- $user_token = '[this user has no token]';
- }
- unset( $user_tokens );
-
- $debug_info = "\r\n";
- foreach ( array(
- 'CLIENT_ID' => 'id',
- 'BLOG_TOKEN' => 'blog_token',
- 'MASTER_USER' => 'master_user',
- 'CERT' => 'fallback_no_verify_ssl_certs',
- 'TIME_DIFF' => 'time_diff',
- 'VERSION' => 'version',
- 'OLD_VERSION' => 'old_version',
- 'PUBLIC' => 'public',
- ) as $label => $option_name ) {
- $debug_info .= "\r\n" . esc_html( $label . ': ' . Jetpack_Options::get_option( $option_name ) );
- }
-
- $debug_info .= "\r\n" . esc_html( 'USER_ID: ' . $user_id );
- $debug_info .= "\r\n" . esc_html( 'USER_TOKEN: ' . $user_token );
- $debug_info .= "\r\n" . esc_html( 'PHP_VERSION: ' . PHP_VERSION );
- $debug_info .= "\r\n" . esc_html( 'WORDPRESS_VERSION: ' . $GLOBALS['wp_version'] );
- $debug_info .= "\r\n" . esc_html( 'JETPACK__VERSION: ' . JETPACK__VERSION );
- $debug_info .= "\r\n" . esc_html( 'JETPACK__PLUGIN_DIR: ' . JETPACK__PLUGIN_DIR );
- $debug_info .= "\r\n" . esc_html( 'SITE_URL: ' . site_url() );
- $debug_info .= "\r\n" . esc_html( 'HOME_URL: ' . home_url() );
- $debug_info .= "\r\n" . esc_html( 'PLAN: ' . self::what_jetpack_plan() );
-
- $debug_info .= "\r\n";
-
- $debug_info .= "\r\n" . '-- SYNC Status -- ';
- require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-modules.php';
- $sync_module = Jetpack_Sync_Modules::get_module( 'full-sync' );
- if ( $sync_module ) {
- $sync_statuses = $sync_module->get_status();
- $human_readable_sync_status = array();
- foreach ( $sync_statuses as $sync_status => $sync_status_value ) {
- $human_readable_sync_status[ $sync_status ] =
- in_array( $sync_status, array( 'started', 'queue_finished', 'send_started', 'finished' ), true )
- ? date( 'r', $sync_status_value ) : $sync_status_value;
- }
- /* translators: A string reporting status. Example: "started" */
- $debug_info .= "\r\n" . sprintf( esc_html__( 'Jetpack Sync Full Status: `%1$s`', 'jetpack' ), print_r( $human_readable_sync_status, 1 ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
- }
-
- require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
-
- $queue = Jetpack_Sync_Sender::get_instance()->get_sync_queue();
-
- /* translators: The number of items waiting to be synced. */
- $debug_info .= "\r\n" . sprintf( esc_html__( 'Sync Queue size: %1$s', 'jetpack' ), $queue->size() );
- /* translators: Human-readable time since the oldest item in the sync queue. */
- $debug_info .= "\r\n" . sprintf( esc_html__( 'Sync Queue lag: %1$s', 'jetpack' ), self::seconds_to_time( $queue->lag() ) );
-
- $full_sync_queue = Jetpack_Sync_Sender::get_instance()->get_full_sync_queue();
-
- /* translators: The number of items waiting to be synced. */
- $debug_info .= "\r\n" . sprintf( esc_html__( 'Full Sync Queue size: %1$s', 'jetpack' ), $full_sync_queue->size() );
- /* translators: Human-readable time since the oldest item in the sync queue. */
- $debug_info .= "\r\n" . sprintf( esc_html__( 'Full Sync Queue lag: %1$s', 'jetpack' ), self::seconds_to_time( $full_sync_queue->lag() ) );
-
- require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-functions.php';
- $idc_urls = array(
- 'home' => Jetpack_Sync_Functions::home_url(),
- 'siteurl' => Jetpack_Sync_Functions::site_url(),
- 'WP_HOME' => Jetpack_Constants::is_defined( 'WP_HOME' ) ? Jetpack_Constants::get_constant( 'WP_HOME' ) : '',
- 'WP_SITEURL' => Jetpack_Constants::is_defined( 'WP_SITEURL' ) ? Jetpack_Constants::get_constant( 'WP_SITEURL' ) : '',
- );
- /* translators: List of URLs. */
- $debug_info .= "\r\n" . esc_html( sprintf( 'Sync IDC URLs: %s', wp_json_encode( $idc_urls ) ) );
- /* translators: String of a current option. */
- $debug_info .= "\r\n" . esc_html( sprintf( 'Sync error IDC option: %s', wp_json_encode( Jetpack_Options::get_option( 'sync_error_idc' ) ) ) );
- /* translators: String of a current option. */
- $debug_info .= "\r\n" . esc_html( sprintf( 'Sync IDC Optin: %s', (string) Jetpack::sync_idc_optin() ) );
-
- $debug_info .= "\r\n";
-
- foreach ( array(
- 'HTTP_HOST',
- 'SERVER_PORT',
- 'HTTPS',
- 'GD_PHP_HANDLER',
- 'HTTP_AKAMAI_ORIGIN_HOP',
- 'HTTP_CF_CONNECTING_IP',
- 'HTTP_CLIENT_IP',
- 'HTTP_FASTLY_CLIENT_IP',
- 'HTTP_FORWARDED',
- 'HTTP_FORWARDED_FOR',
- 'HTTP_INCAP_CLIENT_IP',
- 'HTTP_TRUE_CLIENT_IP',
- 'HTTP_X_CLIENTIP',
- 'HTTP_X_CLUSTER_CLIENT_IP',
- 'HTTP_X_FORWARDED',
- 'HTTP_X_FORWARDED_FOR',
- 'HTTP_X_IP_TRAIL',
- 'HTTP_X_REAL_IP',
- 'HTTP_X_VARNISH',
- 'REMOTE_ADDR',
- ) as $header ) {
- if ( isset( $_SERVER[ $header ] ) ) {
- $debug_info .= "\r\n" . esc_html( $header . ': ' . $_SERVER[ $header ] );
- }
- }
-
- $debug_info .= "\r\n" . esc_html( 'PROTECT_TRUSTED_HEADER: ' . wp_json_encode( get_site_option( 'trusted_ip_header' ) ) );
-
- $debug_info .= "\r\n\r\nTEST RESULTS:\r\n\r\n";
-
- $cxntests = new Jetpack_Cxn_Tests();
- ?>
- <div class="wrap">
- <h2><?php esc_html_e( 'Debugging Center', 'jetpack' ); ?></h2>
- <h3><?php esc_html_e( "Testing your site's compatibility with Jetpack...", 'jetpack' ); ?></h3>
- <div class="jetpack-debug-test-container">
- <?php
- if ( $cxntests->pass() ) {
- echo '<div class="jetpack-tests-succeed">' . esc_html__( 'Your Jetpack setup looks a-okay!', 'jetpack' ) . '</div>';
- $debug_info .= "All tests passed.\r\n";
- $debug_info .= print_r( $cxntests->raw_results(), true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
- } else {
- $failures = $cxntests->list_fails();
- foreach ( $failures as $fail ) {
- echo '<div class="jetpack-test-error">';
- echo '<p><a class="jetpack-test-heading" href="#">' . esc_html( $fail['message'] );
- echo '<span class="noticon noticon-collapse"></span></a></p>';
- echo '<p class="jetpack-test-details">' . esc_html( $fail['resolution'] ) . '</p>';
- echo '</div>';
-
- $debug_info .= "FAILED TESTS!\r\n";
- $debug_info .= $fail['name'] . ': ' . $fail['message'] . "\r\n";
- $debug_info .= print_r( $cxntests->raw_results(), true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
- }
- }
- ?>
- </div>
- <div class="entry-content">
- <h3><?php esc_html_e( 'Trouble with Jetpack?', 'jetpack' ); ?></h3>
- <h4><?php esc_html_e( 'It may be caused by one of these issues, which you can diagnose yourself:', 'jetpack' ); ?></h4>
- <ol>
- <li><b><em>
- <?php
- esc_html_e( 'A known issue.', 'jetpack' );
- ?>
- </em></b>
- <?php
- echo sprintf(
- wp_kses(
- /* translators: URLs to Jetpack support pages. */
- __( 'Some themes and plugins have <a href="%1$s" target="_blank">known conflicts</a> with Jetpack – check the <a href="%2$s" target="_blank">list</a>. (You can also browse the <a href="%3$s" target="_blank">Jetpack support pages</a> or <a href="%4$s" target="_blank">Jetpack support forum</a> to see if others have experienced and solved the problem.)', 'jetpack' ),
- array(
- 'a' => array(
- 'href' => array(),
- 'target' => array(),
- ),
- )
- ),
- 'http://jetpack.com/support/getting-started-with-jetpack/known-issues/',
- 'http://jetpack.com/support/getting-started-with-jetpack/known-issues/',
- 'http://jetpack.com/support/',
- 'https://wordpress.org/support/plugin/jetpack'
- );
- ?>
- </li>
- <li><b><em><?php esc_html_e( 'An incompatible plugin.', 'jetpack' ); ?></em></b> <?php esc_html_e( "Find out by disabling all plugins except Jetpack. If the problem persists, it's not a plugin issue. If the problem is solved, turn your plugins on one by one until the problem pops up again – there's the culprit! Let us know, and we'll try to help.", 'jetpack' ); ?></li>
- <li>
- <b><em><?php esc_html_e( 'A theme conflict.', 'jetpack' ); ?></em></b>
- <?php
- $default_theme = wp_get_theme( WP_DEFAULT_THEME );
-
- if ( $default_theme->exists() ) {
- /* translators: %s is the name of a theme */
- echo esc_html( sprintf( __( "If your problem isn't known or caused by a plugin, try activating %s (the default WordPress theme).", 'jetpack' ), $default_theme->get( 'Name' ) ) );
- } else {
- esc_html_e( "If your problem isn't known or caused by a plugin, try activating the default WordPress theme.", 'jetpack' );
- }
- ?>
- <?php esc_html_e( "If this solves the problem, something in your theme is probably broken – let the theme's author know.", 'jetpack' ); ?>
- </li>
- <li><b><em><?php esc_html_e( 'A problem with your XMLRPC file.', 'jetpack' ); ?></em></b>
- <?php
- echo sprintf(
- wp_kses(
- /* translators: The URL to the site's xmlrpc.php file. */
- __( 'Load your <a href="%s">XMLRPC file</a>. It should say “XML-RPC server accepts POST requests only.” on a line by itself.', 'jetpack' ),
- array( 'a' => array( 'href' => array() ) )
- ),
- esc_attr( site_url( 'xmlrpc.php' ) )
- );
- ?>
- <ul>
- <li>- <?php esc_html_e( "If it's not by itself, a theme or plugin is displaying extra characters. Try steps 2 and 3.", 'jetpack' ); ?></li>
- <li>- <?php esc_html_e( 'If you get a 404 message, contact your web host. Their security may block XMLRPC.', 'jetpack' ); ?></li>
- </ul>
- </li>
- <?php if ( current_user_can( 'jetpack_disconnect' ) && Jetpack::is_active() ) : ?>
- <li>
- <strong><em><?php esc_html_e( 'A connection problem with WordPress.com.', 'jetpack' ); ?></em></strong>
- <?php
- echo sprintf(
- wp_kses(
- /* translators: URL to disconnect and reconnect Jetpack. */
- __( 'Jetpack works by connecting to WordPress.com for a lot of features. Sometimes, when the connection gets messed up, you need to disconnect and reconnect to get things working properly. <a href="%s">Disconnect from WordPress.com</a>', 'jetpack' ),
- array(
- 'a' => array(
- 'href' => array(),
- 'class' => array(),
- ),
- )
- ),
- esc_attr(
- wp_nonce_url(
- Jetpack::admin_url(
- array(
- 'page' => 'jetpack-debugger',
- 'disconnect' => true,
- )
- ),
- 'jp_disconnect',
- 'nonce'
- )
- )
- );
- ?>
- </li>
- <?php endif; ?>
- </ol>
- <h4><?php esc_html_e( 'Still having trouble?', 'jetpack' ); ?></h4>
- <p><b><em><?php esc_html_e( 'Ask us for help!', 'jetpack' ); ?></em></b>
- <?php
- echo sprintf(
- wp_kses(
- /* translators: URL for Jetpack support. */
- __( '<a href="%s">Contact our Happiness team</a>. When you do, please include the full debug information below.', 'jetpack' ),
- array( 'a' => array( 'href' => array() ) )
- ),
- 'https://jetpack.com/contact-support/'
- );
- ?>
- </p>
- <hr />
- <?php if ( Jetpack::is_active() ) : ?>
- <div id="connected-user-details">
- <h3><?php esc_html_e( 'More details about your Jetpack settings', 'jetpack' ); ?></h3>
- <p>
- <?php
- printf(
- wp_kses(
- /* translators: %s is an e-mail address */
- __( 'The primary connection is owned by <strong>%s</strong>\'s WordPress.com account.', 'jetpack' ),
- array( 'strong' => array() )
- ),
- esc_html( Jetpack::get_master_user_email() )
- );
- ?>
- </p>
- </div>
- <?php else : ?>
- <div id="dev-mode-details">
- <p>
- <?php
- printf(
- wp_kses(
- /* translators: Link to a Jetpack support page. */
- __( 'Would you like to use Jetpack on your local development site? You can do so thanks to <a href="%s">Jetpack\'s development mode</a>.', 'jetpack' ),
- array( 'a' => array( 'href' => array() ) )
- ),
- 'https://jetpack.com/support/development-mode/'
- );
- ?>
- </p>
- </div>
- <?php endif; ?>
- <?php
- if (
- current_user_can( 'jetpack_manage_modules' )
- && ( Jetpack::is_development_mode() || Jetpack::is_active() )
- ) {
- printf(
- wp_kses(
- '<p><a href="%1$s">%2$s</a></p>',
- array(
- 'a' => array( 'href' => array() ),
- 'p' => array(),
- )
- ),
- esc_attr( Jetpack::admin_url( 'page=jetpack_modules' ) ),
- esc_html__( 'Access the full list of Jetpack modules available on your site.', 'jetpack' )
- );
- }
- ?>
- </div>
- <hr />
- <div id="toggle_debug_info"><?php esc_html_e( 'Advanced Debug Results', 'jetpack' ); ?></div>
- <div id="debug_info_div">
- <h4><?php esc_html_e( 'Debug Info', 'jetpack' ); ?></h4>
- <div id="debug_info"><pre><?php echo esc_html( $debug_info ); ?></pre></div>
- </div>
- </div>
- <?php
- }
-
- /**
- * Outputs html needed within the <head> for the in-plugin debugger page.
- */
- public static function jetpack_debug_admin_head() {
-
- Jetpack_Admin_Page::load_wrapper_styles();
- ?>
- <style type="text/css">
-
- .jetpack-debug-test-container {
- margin-top: 20px;
- margin-bottom: 30px;
- }
-
- .jetpack-tests-succeed {
- font-size: large;
- color: #8BAB3E;
- }
-
- .jetpack-test-details {
- margin: 4px 6px;
- padding: 10px;
- overflow: auto;
- display: none;
- }
-
- .jetpack-test-error {
- margin-bottom: 10px;
- background: #FFEBE8;
- border: solid 1px #C00;
- border-radius: 3px;
- }
-
- .jetpack-test-error p {
- margin: 0;
- padding: 0;
- }
-
- p.jetpack-test-details {
- margin: 4px 6px;
- padding: 10px;
- }
-
- .jetpack-test-error a.jetpack-test-heading {
- padding: 4px 6px;
- display: block;
- text-decoration: none;
- color: inherit;
- }
-
- .jetpack-test-error .noticon {
- float: right;
- }
-
- .formbox {
- margin: 0 0 25px 0;
- }
-
- .formbox input[type="text"], .formbox input[type="email"], .formbox input[type="url"], .formbox textarea, #debug_info_div {
- border: 1px solid #e5e5e5;
- border-radius: 11px;
- box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
- color: #666;
- font-size: 14px;
- padding: 10px;
- width: 97%;
- }
- #debug_info_div {
- border-radius: 0;
- margin-top: 16px;
- background: #FFF;
- padding: 16px;
- }
- .formbox .contact-support input[type="submit"] {
- float: right;
- margin: 0 !important;
- border-radius: 20px !important;
- cursor: pointer;
- font-size: 13pt !important;
- height: auto !important;
- margin: 0 0 2em 10px !important;
- padding: 8px 16px !important;
- background-color: #ddd;
- border: 1px solid rgba(0,0,0,0.05);
- border-top-color: rgba(255,255,255,0.1);
- border-bottom-color: rgba(0,0,0,0.15);
- color: #333;
- font-weight: 400;
- display: inline-block;
- text-align: center;
- text-decoration: none;
- }
-
- .formbox span.errormsg {
- margin: 0 0 10px 10px;
- color: #d00;
- display: none;
- }
-
- .formbox.error span.errormsg {
- display: block;
- }
-
- #debug_info_div, #toggle_debug_info, #debug_info_div p {
- font-size: 12px;
- }
-
- #category_div ul li {
- list-style-type: none;
- }
-
- </style>
- <script type="text/javascript">
- jQuery( document ).ready( function($) {
-
- $( '#debug_info' ).prepend( 'jQuery version: ' + jQuery.fn.jquery + "\r\n" );
- $( '#debug_form_info' ).prepend( 'jQuery version: ' + jQuery.fn.jquery + "\r\n" );
-
- $( '.jetpack-test-error .jetpack-test-heading' ).on( 'click', function() {
- $( this ).parents( '.jetpack-test-error' ).find( '.jetpack-test-details' ).slideToggle();
- return false;
- } );
-
- } );
- </script>
- <?php
- }
-}
diff --git a/plugins/jetpack/_inc/lib/functions.wp-notify.php b/plugins/jetpack/_inc/lib/functions.wp-notify.php
deleted file mode 100644
index 6be0c3ac..00000000
--- a/plugins/jetpack/_inc/lib/functions.wp-notify.php
+++ /dev/null
@@ -1,353 +0,0 @@
-<?php
-
-if ( ! function_exists( 'wp_notify_postauthor' ) && Jetpack::is_active() ) :
- /**
- * Notify an author (and/or others) of a comment/trackback/pingback on a post.
- *
- * @since 1.0.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @param string $deprecated Not used
- * @return bool True on completion. False if no email addresses were specified.
- */
- function wp_notify_postauthor( $comment_id, $deprecated = null ) {
- if ( null !== $deprecated ) {
- _deprecated_argument( __FUNCTION__, '3.8.0' );
- }
-
- $comment = get_comment( $comment_id );
-
- if ( empty( $comment ) || empty( $comment->comment_post_ID ) ) {
- return false;
- }
-
- $post = get_post( $comment->comment_post_ID );
- $author = get_userdata( $post->post_author );
-
- // Who to notify? By default, just the post author, but others can be added.
- $emails = array();
- if ( $author ) {
- $emails[] = $author->user_email;
- }
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID );
- $emails = array_filter( $emails );
-
- // If there are no addresses to send the comment to, bail.
- if ( ! count( $emails ) ) {
- return false;
- }
-
- // Facilitate unsetting below without knowing the keys.
- $emails = array_flip( $emails );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID );
-
- // The comment was left by the author
- if ( $author && ! $notify_author && $comment->user_id == $post->post_author ) {
- unset( $emails[ $author->user_email ] );
- }
-
- // The author moderated a comment on their own post
- if ( $author && ! $notify_author && $post->post_author == get_current_user_id() ) {
- unset( $emails[ $author->user_email ] );
- }
-
- // The post author is no longer a member of the blog
- if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) {
- unset( $emails[ $author->user_email ] );
- }
-
- // If there's no email to send the comment to, bail, otherwise flip array back around for use below
- if ( ! count( $emails ) ) {
- return false;
- } else {
- $emails = array_flip( $emails );
- }
-
- $switched_locale = switch_to_locale( get_locale() );
-
- $comment_author_domain = @gethostbyaddr( $comment->comment_author_IP );
-
- // The blogname option is escaped with esc_html on the way into the database in sanitize_option
- // we want to reverse this for the plain text arena of emails.
- $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
- $comment_content = wp_specialchars_decode( $comment->comment_content );
-
- function is_user_connected( $email ) {
- $user = get_user_by( 'email', $email );
- return Jetpack::is_user_connected( $user->ID );
- }
-
- $moderate_on_wpcom = ! in_array( false, array_map( 'is_user_connected', $emails ) );
-
- $primary_site_slug = Jetpack::build_raw_urls( get_home_url() );
-
- switch ( $comment->comment_type ) {
- case 'trackback':
- /* translators: 1: Post title */
- $notify_message = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n";
- /* translators: 1: Trackback/pingback website name, 2: website IP address, 3: website hostname */
- $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
- $notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n";
- /* translators: 1: blog name, 2: post title */
- $subject = sprintf( __( '[%1$s] Trackback: "%2$s"' ), $blogname, $post->post_title );
- break;
- case 'pingback':
- /* translators: 1: Post title */
- $notify_message = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n";
- /* translators: 1: Trackback/pingback website name, 2: website IP address, 3: website hostname */
- $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
- $notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n";
- /* translators: 1: blog name, 2: post title */
- $subject = sprintf( __( '[%1$s] Pingback: "%2$s"' ), $blogname, $post->post_title );
- break;
- default: // Comments
- $notify_message = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n";
- /* translators: 1: comment author, 2: comment author's IP address, 3: comment author's hostname */
- $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
- $notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n";
- /* translators: 1: blog name, 2: post title */
- $subject = sprintf( __( '[%1$s] Comment: "%2$s"' ), $blogname, $post->post_title );
- break;
- }
-
- $notify_message .= $moderate_on_wpcom
- ? "https://wordpress.com/comments/all/{$primary_site_slug}/{$comment->comment_post_ID}/\r\n\r\n"
- : get_permalink( $comment->comment_post_ID ) . "#comments\r\n\r\n";
-
- $notify_message .= sprintf( __( 'Permalink: %s' ), get_comment_link( $comment ) ) . "\r\n";
-
- if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
- if ( EMPTY_TRASH_DAYS ) {
- $notify_message .= sprintf(
- __( 'Trash it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=trash"
- : admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" )
- ) . "\r\n";
- } else {
- $notify_message .= sprintf(
- __( 'Delete it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=delete"
- : admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" )
- ) . "\r\n";
- }
- $notify_message .= sprintf(
- __( 'Spam it: %s' ), $moderate_on_wpcom ?
- "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=spam"
- : admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" )
- ) . "\r\n";
- }
-
- $wp_email = 'wordpress@' . preg_replace( '#^www\.#', '', strtolower( $_SERVER['SERVER_NAME'] ) );
-
- if ( '' == $comment->comment_author ) {
- $from = "From: \"$blogname\" <$wp_email>";
- if ( '' != $comment->comment_author_email ) {
- $reply_to = "Reply-To: $comment->comment_author_email";
- }
- } else {
- $from = "From: \"$comment->comment_author\" <$wp_email>";
- if ( '' != $comment->comment_author_email ) {
- $reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>";
- }
- }
-
- $message_headers = "$from\n"
- . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
-
- if ( isset( $reply_to ) ) {
- $message_headers .= $reply_to . "\n";
- }
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID );
-
- foreach ( $emails as $email ) {
- @wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
- }
-
- if ( $switched_locale ) {
- restore_previous_locale();
- }
-
- return true;
- }
-endif;
-
-if ( ! function_exists( 'wp_notify_moderator' ) && Jetpack::is_active() ) :
- /**
- * Notifies the moderator of the site about a new comment that is awaiting approval.
- *
- * @since 1.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator
- * should be notified, overriding the site setting.
- *
- * @param int $comment_id Comment ID.
- * @return true Always returns true.
- */
- function wp_notify_moderator( $comment_id ) {
- global $wpdb;
-
- $maybe_notify = get_option( 'moderation_notify' );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id );
-
- if ( ! $maybe_notify ) {
- return true;
- }
-
- $comment = get_comment( $comment_id );
- $post = get_post( $comment->comment_post_ID );
- $user = get_userdata( $post->post_author );
- // Send to the administration and to the post author if the author can modify the comment.
- $emails = array( get_option( 'admin_email' ) );
- if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) {
- if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
- $emails[] = $user->user_email;
- }
- }
-
- $switched_locale = switch_to_locale( get_locale() );
-
- $comment_author_domain = @gethostbyaddr( $comment->comment_author_IP );
- $comments_waiting = $wpdb->get_var( "SELECT count(comment_ID) FROM $wpdb->comments WHERE comment_approved = '0'" );
-
- // The blogname option is escaped with esc_html on the way into the database in sanitize_option
- // we want to reverse this for the plain text arena of emails.
- $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
- $comment_content = wp_specialchars_decode( $comment->comment_content );
-
- switch ( $comment->comment_type ) {
- case 'trackback':
- /* translators: 1: Post title */
- $notify_message = sprintf( __( 'A new trackback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
- $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
- /* translators: 1: Trackback/pingback website name, 2: website IP address, 3: website hostname */
- $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- /* translators: 1: Trackback/pingback/comment author URL */
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- $notify_message .= __( 'Trackback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
- break;
- case 'pingback':
- /* translators: 1: Post title */
- $notify_message = sprintf( __( 'A new pingback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
- $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
- /* translators: 1: Trackback/pingback website name, 2: website IP address, 3: website hostname */
- $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- /* translators: 1: Trackback/pingback/comment author URL */
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- $notify_message .= __( 'Pingback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
- break;
- default: // Comments
- /* translators: 1: Post title */
- $notify_message = sprintf( __( 'A new comment on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
- $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
- /* translators: 1: Comment author name, 2: comment author's IP address, 3: comment author's hostname */
- $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
- /* translators: 1: Comment author URL */
- $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
- /* translators: 1: Trackback/pingback/comment author URL */
- $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
- /* translators: 1: Comment text */
- $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
- break;
- }
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id );
-
- function is_user_connected( $email ) {
- $user = get_user_by( 'email', $email );
- return Jetpack::is_user_connected( $user->ID );
- }
-
- $moderate_on_wpcom = ! in_array( false, array_map( 'is_user_connected', $emails ) );
-
- $primary_site_slug = Jetpack::build_raw_urls( get_home_url() );
-
- /* translators: Comment moderation. 1: Comment action URL */
- $notify_message .= sprintf(
- __( 'Approve it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=approve"
- : admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" )
- ) . "\r\n";
-
- if ( EMPTY_TRASH_DAYS ) {
- /* translators: Comment moderation. 1: Comment action URL */
- $notify_message .= sprintf(
- __( 'Trash it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=trash"
- : admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" )
- ) . "\r\n";
- } else {
- /* translators: Comment moderation. 1: Comment action URL */
- $notify_message .= sprintf(
- __( 'Delete it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=delete"
- : admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" )
- ) . "\r\n";
- }
-
- /* translators: Comment moderation. 1: Comment action URL */
- $notify_message .= sprintf(
- __( 'Spam it: %s' ), $moderate_on_wpcom
- ? "https://wordpress.com/comment/{$primary_site_slug}/{$comment_id}?action=spam"
- : admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" )
- ) . "\r\n";
-
- /* translators: Comment moderation. 1: Number of comments awaiting approval */
- $notify_message .= sprintf(
- _n(
- 'Currently %s comment is waiting for approval. Please visit the moderation panel:',
- 'Currently %s comments are waiting for approval. Please visit the moderation panel:', $comments_waiting
- ), number_format_i18n( $comments_waiting )
- ) . "\r\n";
-
- $notify_message .= $moderate_on_wpcom
- ? "https://wordpress.com/comments/pending/{$primary_site_slug}/"
- : admin_url( 'edit-comments.php?comment_status=moderated#wpbody-content' ) . "\r\n";
-
- /* translators: Comment moderation notification email subject. 1: Site name, 2: Post title */
- $subject = sprintf( __( '[%1$s] Please moderate: "%2$s"' ), $blogname, $post->post_title );
- $message_headers = '';
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id );
-
- /** This filter is documented in core/src/wp-includes/pluggable.php */
- $message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id );
-
- foreach ( $emails as $email ) {
- @wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
- }
-
- if ( $switched_locale ) {
- restore_previous_locale();
- }
-
- return true;
- }
-endif;
diff --git a/plugins/jetpack/_inc/lib/icalendar-reader.php b/plugins/jetpack/_inc/lib/icalendar-reader.php
deleted file mode 100644
index f7e047f9..00000000
--- a/plugins/jetpack/_inc/lib/icalendar-reader.php
+++ /dev/null
@@ -1,913 +0,0 @@
-<?php
-
-/**
- * Gets and renders iCal feeds for the Upcoming Events widget and shortcode
- */
-
-class iCalendarReader {
-
- public $todo_count = 0;
- public $event_count = 0;
- public $cal = array();
- public $_lastKeyWord = '';
- public $timezone = null;
-
- /**
- * Class constructor
- *
- * @return void
- */
- public function __construct() {}
-
- /**
- * Return an array of events
- *
- * @param string $url (default: '')
- * @return array | false on failure
- */
- public function get_events( $url = '', $count = 5 ) {
- $count = (int) $count;
- $transient_id = 'icalendar_vcal_' . md5( $url ) . '_' . $count;
-
- $vcal = get_transient( $transient_id );
-
- if ( ! empty( $vcal ) ) {
- if ( isset( $vcal['TIMEZONE'] ) )
- $this->timezone = $this->timezone_from_string( $vcal['TIMEZONE'] );
-
- if ( isset( $vcal['VEVENT'] ) ) {
- $vevent = $vcal['VEVENT'];
-
- if ( $count > 0 )
- $vevent = array_slice( $vevent, 0, $count );
-
- $this->cal['VEVENT'] = $vevent;
-
- return $this->cal['VEVENT'];
- }
- }
-
- if ( ! $this->parse( $url ) )
- return false;
-
- $vcal = array();
-
- if ( $this->timezone ) {
- $vcal['TIMEZONE'] = $this->timezone->getName();
- } else {
- $this->timezone = $this->timezone_from_string( '' );
- }
-
- if ( ! empty( $this->cal['VEVENT'] ) ) {
- $vevent = $this->cal['VEVENT'];
-
- // check for recurring events
- // $vevent = $this->add_recurring_events( $vevent );
-
- // remove before caching - no sense in hanging onto the past
- $vevent = $this->filter_past_and_recurring_events( $vevent );
-
- // order by soonest start date
- $vevent = $this->sort_by_recent( $vevent );
-
- $vcal['VEVENT'] = $vevent;
- }
-
- set_transient( $transient_id, $vcal, HOUR_IN_SECONDS );
-
- if ( !isset( $vcal['VEVENT'] ) )
- return false;
-
- if ( $count > 0 )
- return array_slice( $vcal['VEVENT'], 0, $count );
-
- return $vcal['VEVENT'];
- }
-
- function apply_timezone_offset( $events ) {
- if ( ! $events ) {
- return $events;
- }
-
- // get timezone offset from the timezone name.
- $timezone_name = get_option( 'timezone_string' );
- if ( $timezone_name ) {
- $timezone = new DateTimeZone( $timezone_name );
- $timezone_offset_interval = false;
- } else {
- // If the timezone isn't set then the GMT offset must be set.
- // generate a DateInterval object from the timezone offset
- $gmt_offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
- $timezone_offset_interval = date_interval_create_from_date_string( "{$gmt_offset} seconds" );
- $timezone = new DateTimeZone( 'UTC' );
- }
-
- $offsetted_events = array();
-
- foreach ( $events as $event ) {
- // Don't handle all-day events
- if ( 8 < strlen( $event['DTSTART'] ) ) {
- $start_time = preg_replace( '/Z$/', '', $event['DTSTART'] );
- $start_time = new DateTime( $start_time, $this->timezone );
- $start_time->setTimeZone( $timezone );
-
- $end_time = preg_replace( '/Z$/', '', $event['DTEND'] );
- $end_time = new DateTime( $end_time, $this->timezone );
- $end_time->setTimeZone( $timezone );
-
- if ( $timezone_offset_interval ) {
- $start_time->add( $timezone_offset_interval );
- $end_time->add( $timezone_offset_interval );
- }
-
- $event['DTSTART'] = $start_time->format( 'YmdHis\Z' );
- $event['DTEND'] = $end_time->format( 'YmdHis\Z' );
- }
-
- $offsetted_events[] = $event;
- }
-
- return $offsetted_events;
- }
-
- protected function filter_past_and_recurring_events( $events ) {
- $upcoming = array();
- $set_recurring_events = array();
- $recurrences = array();
- /**
- * This filter allows any time to be passed in for testing or changing timezones, etc...
- *
- * @module widgets
- *
- * @since 3.4.0
- *
- * @param object time() A time object.
- */
- $current = apply_filters( 'ical_get_current_time', time() );
-
- foreach ( $events as $event ) {
-
- $date_from_ics = strtotime( $event['DTSTART'] );
- if ( isset( $event['DTEND'] ) ) {
- $duration = strtotime( $event['DTEND'] ) - strtotime( $event['DTSTART'] );
- } else {
- $duration = 0;
- }
-
- if ( isset( $event['RRULE'] ) && $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
- try {
- $adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone('UTC') );
- $adjusted_time->setTimeZone( new DateTimeZone( $this->timezone->getName() ) );
- $event['DTSTART'] = $adjusted_time->format('Ymd\THis');
- $date_from_ics = strtotime( $event['DTSTART'] );
-
- $event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
-
- if ( isset( $event['EXDATE'] ) ) {
- $exdates = array();
- foreach ( (array) $event['EXDATE'] as $exdate ) {
- try {
- $adjusted_time = new DateTime( $exdate, new DateTimeZone('UTC') );
- $adjusted_time->setTimeZone( new DateTimeZone( $this->timezone->getName() ) );
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $exdates[] = $adjusted_time->format( 'Ymd' );
- } else {
- $exdates[] = $adjusted_time->format( 'Ymd\THis' );
- }
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- $event['EXDATE'] = $exdates;
- } else {
- $event['EXDATE'] = array();
- }
- }
-
- if ( ! isset( $event['DTSTART'] ) ) {
- continue;
- }
-
- // Process events with RRULE before other events
- $rrule = isset( $event['RRULE'] ) ? $event['RRULE'] : false ;
- $uid = $event['UID'];
-
- if ( $rrule && ! in_array( $uid, $set_recurring_events ) ) {
-
- // Break down the RRULE into digestible chunks
- $rrule_array = array();
-
- foreach ( explode( ";", $event['RRULE'] ) as $rline ) {
- list( $rkey, $rvalue ) = explode( "=", $rline, 2 );
- $rrule_array[$rkey] = $rvalue;
- }
-
- $interval = ( isset( $rrule_array['INTERVAL'] ) ) ? $rrule_array['INTERVAL'] : 1;
- $rrule_count = ( isset( $rrule_array['COUNT'] ) ) ? $rrule_array['COUNT'] : 0;
- $until = ( isset( $rrule_array['UNTIL'] ) ) ? strtotime( $rrule_array['UNTIL'] ) : strtotime( '+1 year', $current );
-
- // Used to bound event checks
- $echo_limit = 10;
- $noop = false;
-
- // Set bydays for the event
- $weekdays = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
- $bydays = $weekdays;
-
- // Calculate a recent start date for incrementing depending on the frequency and interval
- switch ( $rrule_array['FREQ'] ) {
-
- case 'DAILY':
- $frequency = 'day';
- $echo_limit = 10;
-
- if ( $date_from_ics >= $current ) {
- $recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
- } else {
- // Interval and count
- $catchup = floor( ( $current - strtotime( $event['DTSTART'] ) ) / ( $interval * DAY_IN_SECONDS ) );
- if ( $rrule_count && $catchup > 0 ) {
- if ( $catchup < $rrule_count ) {
- $rrule_count = $rrule_count - $catchup;
- $recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' days', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- } else {
- $noop = true;
- }
- } else {
- $recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' days', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- }
- }
- break;
-
- case 'WEEKLY':
- $frequency = 'week';
- $echo_limit = 4;
-
- // BYDAY exception to current date
- $day = false;
- if ( ! isset( $rrule_array['BYDAY'] ) ) {
- $day = $rrule_array['BYDAY'] = strtoupper( substr( date( 'D', strtotime( $event['DTSTART'] ) ), 0, 2 ) );
- }
- $bydays = explode( ',', $rrule_array['BYDAY'] );
-
- if ( $date_from_ics >= $current ) {
- $recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
- } else {
- // Interval and count
- $catchup = floor( ( $current - strtotime( $event['DTSTART'] ) ) / ( $interval * WEEK_IN_SECONDS ) );
- if ( $rrule_count && $catchup > 0 ) {
- if ( ( $catchup * count( $bydays ) ) < $rrule_count ) {
- $rrule_count = $rrule_count - ( $catchup * count( $bydays ) ); // Estimate current event count
- $recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' weeks', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- } else {
- $noop = true;
- }
- } else {
- $recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' weeks', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- }
- }
-
- // Set to Sunday start
- if ( ! $noop && 'SU' !== strtoupper( substr( date( 'D', strtotime( $recurring_event_date_start ) ), 0, 2 ) ) ) {
- $recurring_event_date_start = date( 'Ymd', strtotime( "last Sunday", strtotime( $recurring_event_date_start ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- }
- break;
-
- case 'MONTHLY':
- $frequency = 'month';
- $echo_limit = 1;
-
- if ( $date_from_ics >= $current ) {
- $recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
- } else {
- // Describe the date in the month
- if ( isset( $rrule_array['BYDAY'] ) ) {
- $day_number = substr( $rrule_array['BYDAY'], 0, 1 );
- $week_day = substr( $rrule_array['BYDAY'], 1 );
- $day_cardinals = array( 1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth' );
- $weekdays = array( 'SU' => 'Sunday', 'MO' => 'Monday', 'TU' => 'Tuesday', 'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday', 'SA' => 'Saturday' );
- $event_date_desc = "{$day_cardinals[$day_number]} {$weekdays[$week_day]} of ";
- } else {
- $event_date_desc = date( 'd ', strtotime( $event['DTSTART'] ) );
- }
-
- // Interval only
- if ( $interval > 1 ) {
- $catchup = 0;
- $maybe = strtotime( $event['DTSTART'] );
- while ( $maybe < $current ) {
- $maybe = strtotime( '+ ' . ( $interval * $catchup ) . ' months', strtotime( $event['DTSTART'] ) );
- $catchup++;
- }
- $recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', strtotime( '+ ' . ( $interval * ( $catchup - 1 ) ) . ' months', strtotime( $event['DTSTART'] ) ) ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- } else {
- $recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', $current ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- }
-
- // Add one interval if necessary
- if ( strtotime( $recurring_event_date_start ) < $current ) {
- if ( $interval > 1 ) {
- $recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', strtotime( '+ ' . ( $interval * $catchup ) . ' months', strtotime( $event['DTSTART'] ) ) ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- } else {
- try {
- $adjustment = new DateTime( date( 'Y-m-d', $current ) );
- $adjustment->modify( 'first day of next month' );
- $recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . $adjustment->format( 'F Y' ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- }
- }
- break;
-
- case 'YEARLY':
- $frequency = 'year';
- $echo_limit = 1;
-
- if ( $date_from_ics >= $current ) {
- $recurring_event_date_start = date( "Ymd\THis", strtotime( $event['DTSTART'] ) );
- } else {
- $recurring_event_date_start = date( 'Y', $current ) . date( "md\THis", strtotime( $event['DTSTART'] ) );
- if ( strtotime( $recurring_event_date_start ) < $current ) {
- try {
- $next = new DateTime( date( 'Y-m-d', $current ) );
- $next->modify( 'first day of next year' );
- $recurring_event_date_start = $next->format( 'Y' ) . date ( 'md\THis', strtotime( $event['DTSTART'] ) );
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- }
- break;
-
- default:
- $frequency = false;
- }
-
- if ( $frequency !== false && ! $noop ) {
- $count_counter = 1;
-
- // If no COUNT limit, go to 10
- if ( empty( $rrule_count ) ) {
- $rrule_count = 10;
- }
-
- // Set up EXDATE handling for the event
- $exdates = ( isset( $event['EXDATE'] ) ) ? $event['EXDATE'] : array();
-
- for ( $i = 1; $i <= $echo_limit; $i++ ) {
-
- // Weeks need a daily loop and must check for inclusion in BYDAYS
- if ( 'week' == $frequency ) {
- $byday_event_date_start = strtotime( $recurring_event_date_start );
-
- foreach ( $weekdays as $day ) {
-
- $event_start_timestamp = $byday_event_date_start;
- $start_time = date( 'His', $event_start_timestamp );
- $event_end_timestamp = $event_start_timestamp + $duration;
- $end_time = date( 'His', $event_end_timestamp );
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $exdate_compare = date( 'Ymd', $event_start_timestamp );
- } else {
- $exdate_compare = date( 'Ymd\THis', $event_start_timestamp );
- }
-
- if ( in_array( $day, $bydays ) && $event_end_timestamp > $current && $event_start_timestamp < $until && $count_counter <= $rrule_count && $event_start_timestamp >= $date_from_ics && ! in_array( $exdate_compare, $exdates ) ) {
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $event['DTSTART'] = date( 'Ymd', $event_start_timestamp );
- $event['DTEND'] = date( 'Ymd', $event_end_timestamp );
- } else {
- $event['DTSTART'] = date( 'Ymd\THis', $event_start_timestamp );
- $event['DTEND'] = date( 'Ymd\THis', $event_end_timestamp );
- }
- if ( $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
- try {
- $adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone( $this->timezone->getName() ) );
- $adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
- $event['DTSTART'] = $adjusted_time->format('Ymd\THis');
-
- $event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- $upcoming[] = $event;
- $count_counter++;
- }
-
- // Move forward one day
- $byday_event_date_start = strtotime( date( 'Ymd\T', strtotime( '+ 1 day', $event_start_timestamp ) ) . $start_time );
- }
-
- // Restore first event timestamp
- $event_start_timestamp = strtotime( $recurring_event_date_start );
-
- } else {
-
- $event_start_timestamp = strtotime( $recurring_event_date_start );
- $start_time = date( 'His', $event_start_timestamp );
- $event_end_timestamp = $event_start_timestamp + $duration;
- $end_time = date( 'His', $event_end_timestamp );
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $exdate_compare = date( 'Ymd', $event_start_timestamp );
- } else {
- $exdate_compare = date( 'Ymd\THis', $event_start_timestamp );
- }
-
- if ( $event_end_timestamp > $current && $event_start_timestamp < $until && $count_counter <= $rrule_count && $event_start_timestamp >= $date_from_ics && ! in_array( $exdate_compare, $exdates ) ) {
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $event['DTSTART'] = date( 'Ymd', $event_start_timestamp );
- $event['DTEND'] = date( 'Ymd', $event_end_timestamp );
- } else {
- $event['DTSTART'] = date( 'Ymd\T', $event_start_timestamp ) . $start_time;
- $event['DTEND'] = date( 'Ymd\T', $event_end_timestamp ) . $end_time;
- }
- if ( $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
- try {
- $adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone( $this->timezone->getName() ) );
- $adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
- $event['DTSTART'] = $adjusted_time->format('Ymd\THis');
-
- $event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- $upcoming[] = $event;
- $count_counter++;
- }
- }
-
- // Set up next interval and reset $event['DTSTART'] and $event['DTEND'], keeping timestamps intact
- $next_start_timestamp = strtotime( "+ {$interval} {$frequency}s", $event_start_timestamp );
- if ( 8 == strlen( $event['DTSTART'] ) ) {
- $event['DTSTART'] = date( 'Ymd', $next_start_timestamp );
- $event['DTEND'] = date( 'Ymd', strtotime( $event['DTSTART'] ) + $duration );
- } else {
- $event['DTSTART'] = date( 'Ymd\THis', $next_start_timestamp );
- $event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
- }
-
- // Move recurring event date forward
- $recurring_event_date_start = $event['DTSTART'];
- }
- $set_recurring_events[] = $uid;
-
- }
-
- } else {
- // Process normal events
- if ( strtotime( isset( $event['DTEND'] ) ? $event['DTEND'] : $event['DTSTART'] ) >= $current ) {
- $upcoming[] = $event;
- }
- }
- }
- return $upcoming;
- }
-
- /**
- * Parse events from an iCalendar feed
- *
- * @param string $url (default: '')
- * @return array | false on failure
- */
- public function parse( $url = '' ) {
- $cache_group = 'icalendar_reader_parse';
- $disable_get_key = 'disable:' . md5( $url );
-
- // Check to see if previous attempts have failed
- if ( false !== wp_cache_get( $disable_get_key, $cache_group ) )
- return false;
-
- // rewrite webcal: URI schem to HTTP
- $url = preg_replace('/^webcal/', 'http', $url );
- // try to fetch
- $r = wp_remote_get( $url, array( 'timeout' => 3, 'sslverify' => false ) );
- if ( 200 !== wp_remote_retrieve_response_code( $r ) ) {
- // We were unable to fetch any content, so don't try again for another 60 seconds
- wp_cache_set( $disable_get_key, 1, $cache_group, 60 );
- return false;
- }
-
- $body = wp_remote_retrieve_body( $r );
- if ( empty( $body ) )
- return false;
-
- $body = str_replace( "\r\n", "\n", $body );
- $lines = preg_split( "/\n(?=[A-Z])/", $body );
-
- if ( empty( $lines ) )
- return false;
-
- if ( false === stristr( $lines[0], 'BEGIN:VCALENDAR' ) )
- return false;
-
- foreach ( $lines as $line ) {
- $add = $this->key_value_from_string( $line );
- if ( ! $add ) {
- $this->add_component( $type, false, $line );
- continue;
- }
- list( $keyword, $value ) = $add;
-
- switch ( $keyword ) {
- case 'BEGIN':
- case 'END':
- switch ( $line ) {
- case 'BEGIN:VTODO':
- $this->todo_count++;
- $type = 'VTODO';
- break;
- case 'BEGIN:VEVENT':
- $this->event_count++;
- $type = 'VEVENT';
- break;
- case 'BEGIN:VCALENDAR':
- case 'BEGIN:DAYLIGHT':
- case 'BEGIN:VTIMEZONE':
- case 'BEGIN:STANDARD':
- $type = $value;
- break;
- case 'END:VTODO':
- case 'END:VEVENT':
- case 'END:VCALENDAR':
- case 'END:DAYLIGHT':
- case 'END:VTIMEZONE':
- case 'END:STANDARD':
- $type = 'VCALENDAR';
- break;
- }
- break;
- case 'TZID':
- if ( 'VTIMEZONE' == $type && ! $this->timezone )
- $this->timezone = $this->timezone_from_string( $value );
- break;
- case 'X-WR-TIMEZONE':
- if ( ! $this->timezone )
- $this->timezone = $this->timezone_from_string( $value );
- break;
- default:
- $this->add_component( $type, $keyword, $value );
- break;
- }
- }
-
- // Filter for RECURRENCE-IDs
- $recurrences = array();
- if ( array_key_exists( 'VEVENT', $this->cal ) ) {
- foreach ( $this->cal['VEVENT'] as $event ) {
- if ( isset( $event['RECURRENCE-ID'] ) ) {
- $recurrences[] = $event;
- }
- }
- foreach ( $recurrences as $recurrence ) {
- for ( $i = 0; $i < count( $this->cal['VEVENT'] ); $i++ ) {
- if ( $this->cal['VEVENT'][ $i ]['UID'] == $recurrence['UID'] && ! isset( $this->cal['VEVENT'][ $i ]['RECURRENCE-ID'] ) ) {
- $this->cal['VEVENT'][ $i ]['EXDATE'][] = $recurrence['RECURRENCE-ID'];
- break;
- }
- }
- }
- }
-
- return $this->cal;
- }
-
- /**
- * Parse key:value from a string
- *
- * @param string $text (default: '')
- * @return array
- */
- public function key_value_from_string( $text = '' ) {
- preg_match( '/([^:]+)(;[^:]+)?[:]([\w\W]*)/', $text, $matches );
-
- if ( 0 == count( $matches ) )
- return false;
-
- return array( $matches[1], $matches[3] );
- }
-
- /**
- * Convert a timezone name into a timezone object.
- *
- * @param string $text Timezone name. Example: America/Chicago
- * @return object|null A DateTimeZone object if the conversion was successful.
- */
- private function timezone_from_string( $text ) {
- try {
- $timezone = new DateTimeZone( $text );
- } catch ( Exception $e ) {
- $blog_timezone = get_option( 'timezone_string' );
- if ( ! $blog_timezone ) {
- $blog_timezone = 'Etc/UTC';
- }
-
- $timezone = new DateTimeZone( $blog_timezone );
- }
-
- return $timezone;
- }
-
- /**
- * Add a component to the calendar array
- *
- * @param string $component (default: '')
- * @param string $keyword (default: '')
- * @param string $value (default: '')
- * @return void
- */
- public function add_component( $component = '', $keyword = '', $value = '' ) {
- if ( false == $keyword ) {
- $keyword = $this->last_keyword;
- switch ( $component ) {
- case 'VEVENT':
- $value = $this->cal[ $component ][ $this->event_count - 1 ][ $keyword ] . $value;
- break;
- case 'VTODO' :
- $value = $this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ] . $value;
- break;
- }
- }
-
- /*
- * Some events have a specific timezone set in their start/end date,
- * and it may or may not be different than the calendar timzeone.
- * Valid formats include:
- * DTSTART;TZID=Pacific Standard Time:20141219T180000
- * DTEND;TZID=Pacific Standard Time:20141219T200000
- * EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z
- * EXDATE;VALUE=DATE:2015050
- * EXDATE;TZID=America/New_York:20150424T170000
- * EXDATE;TZID=Pacific Standard Time:20120615T140000,20120629T140000,20120706T140000
- */
-
- // Always store EXDATE as an array
- if ( stristr( $keyword, 'EXDATE' ) ) {
- $value = explode( ',', $value );
- }
-
- // Adjust DTSTART, DTEND, and EXDATE according to their TZID if set
- if ( strpos( $keyword, ';' ) && ( stristr( $keyword, 'DTSTART' ) || stristr( $keyword, 'DTEND' ) || stristr( $keyword, 'EXDATE' ) || stristr( $keyword, 'RECURRENCE-ID' ) ) ) {
- $keyword = explode( ';', $keyword );
-
- $tzid = false;
- if ( 2 == count( $keyword ) ) {
- $tparam = $keyword[1];
-
- if ( strpos( $tparam, "TZID" ) !== false ) {
- $tzid = $this->timezone_from_string( str_replace( 'TZID=', '', $tparam ) );
- }
- }
-
- // Normalize all times to default UTC
- if ( $tzid ) {
- $adjusted_times = array();
- foreach ( (array) $value as $v ) {
- try {
- $adjusted_time = new DateTime( $v, $tzid );
- $adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
- $adjusted_times[] = $adjusted_time->format('Ymd\THis');
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- return;
- }
- }
- $value = $adjusted_times;
- }
-
- // Format for adding to event
- $keyword = $keyword[0];
- if ( 'EXDATE' != $keyword ) {
- $value = implode( (array) $value );
- }
- }
-
- foreach ( (array) $value as $v ) {
- switch ($component) {
- case 'VTODO':
- if ( 'EXDATE' == $keyword ) {
- $this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ][] = $v;
- } else {
- $this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ] = $v;
- }
- break;
- case 'VEVENT':
- if ( 'EXDATE' == $keyword ) {
- $this->cal[ $component ][ $this->event_count - 1 ][ $keyword ][] = $v;
- } else {
- $this->cal[ $component ][ $this->event_count - 1 ][ $keyword ] = $v;
- }
- break;
- default:
- $this->cal[ $component ][ $keyword ] = $v;
- break;
- }
- }
- $this->last_keyword = $keyword;
- }
-
- /**
- * Escape strings with wp_kses, allow links
- *
- * @param string $string (default: '')
- * @return string
- */
- public function escape( $string = '' ) {
- // Unfold content lines per RFC 5545
- $string = str_replace( "\n\t", '', $string );
- $string = str_replace( "\n ", '', $string );
-
- $allowed_html = array(
- 'a' => array(
- 'href' => array(),
- 'title' => array()
- )
- );
-
- $allowed_tags = '';
- foreach ( array_keys( $allowed_html ) as $tag ) {
- $allowed_tags .= "<{$tag}>";
- }
-
- // Running strip_tags() first with allowed tags to get rid of remaining gallery markup, etc
- // because wp_kses() would only htmlentity'fy that. Then still running wp_kses(), for extra
- // safety and good measure.
- return wp_kses( strip_tags( $string, $allowed_tags ), $allowed_html );
- }
-
- /**
- * Render the events
- *
- * @param string $url (default: '')
- * @param string $context (default: 'widget') or 'shortcode'
- * @return mixed bool|string false on failure, rendered HTML string on success.
- */
- public function render( $url = '', $args = array() ) {
-
- $args = wp_parse_args( $args, array(
- 'context' => 'widget',
- 'number' => 5
- ) );
-
- $events = $this->get_events( $url, $args['number'] );
- $events = $this->apply_timezone_offset( $events );
-
- if ( empty( $events ) )
- return false;
-
- ob_start();
-
- if ( 'widget' == $args['context'] ) : ?>
- <ul class="upcoming-events">
- <?php foreach ( $events as $event ) : ?>
- <li>
- <strong class="event-summary"><?php echo $this->escape( stripslashes( $event['SUMMARY'] ) ); ?></strong>
- <span class="event-when"><?php echo $this->formatted_date( $event ); ?></span>
- <?php if ( ! empty( $event['LOCATION'] ) ) : ?>
- <span class="event-location"><?php echo $this->escape( stripslashes( $event['LOCATION'] ) ); ?></span>
- <?php endif; ?>
- <?php if ( ! empty( $event['DESCRIPTION'] ) ) : ?>
- <span class="event-description"><?php echo wp_trim_words( $this->escape( stripcslashes( $event['DESCRIPTION'] ) ) ); ?></span>
- <?php endif; ?>
- </li>
- <?php endforeach; ?>
- </ul>
- <?php endif;
-
- if ( 'shortcode' == $args['context'] ) : ?>
- <table class="upcoming-events">
- <thead>
- <tr>
- <th><?php esc_html_e( 'Location', 'jetpack' ); ?></th>
- <th><?php esc_html_e( 'When', 'jetpack' ); ?></th>
- <th><?php esc_html_e( 'Summary', 'jetpack' ); ?></th>
- <th><?php esc_html_e( 'Description', 'jetpack' ); ?></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ( $events as $event ) : ?>
- <tr>
- <td><?php echo empty( $event['LOCATION'] ) ? '&nbsp;' : $this->escape( stripslashes( $event['LOCATION'] ) ); ?></td>
- <td><?php echo $this->formatted_date( $event ); ?></td>
- <td><?php echo empty( $event['SUMMARY'] ) ? '&nbsp;' : $this->escape( stripslashes( $event['SUMMARY'] ) ); ?></td>
- <td><?php echo empty( $event['DESCRIPTION'] ) ? '&nbsp;' : wp_trim_words( $this->escape( stripcslashes( $event['DESCRIPTION'] ) ) ); ?></td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- <?php endif;
-
- $rendered = ob_get_clean();
-
- if ( empty( $rendered ) )
- return false;
-
- return $rendered;
- }
-
- public function formatted_date( $event ) {
-
- $date_format = get_option( 'date_format' );
- $time_format = get_option( 'time_format' );
- $start = strtotime( $event['DTSTART'] );
- $end = isset( $event['DTEND'] ) ? strtotime( $event['DTEND'] ) : false;
-
- $all_day = ( 8 == strlen( $event['DTSTART'] ) );
-
- if ( !$all_day && $this->timezone ) {
- try {
- $start_time = new DateTime( $event['DTSTART'] );
- $timezone_offset = $this->timezone->getOffset( $start_time );
- $start += $timezone_offset;
-
- if ( $end ) {
- $end += $timezone_offset;
- }
- } catch ( Exception $e ) {
- // Invalid argument to DateTime
- }
- }
- $single_day = $end ? ( $end - $start ) <= DAY_IN_SECONDS : true;
-
- /* translators: Date and time */
- $date_with_time = __( '%1$s at %2$s' , 'jetpack' );
- /* translators: Two dates with a separator */
- $two_dates = __( '%1$s &ndash; %2$s' , 'jetpack' );
-
- // we'll always have the start date. Maybe with time
- if ( $all_day )
- $date = date_i18n( $date_format, $start );
- else
- $date = sprintf( $date_with_time, date_i18n( $date_format, $start ), date_i18n( $time_format, $start ) );
-
- // single day, timed
- if ( $single_day && ! $all_day && false !== $end )
- $date = sprintf( $two_dates, $date, date_i18n( $time_format, $end ) );
-
- // multi-day
- if ( ! $single_day ) {
-
- if ( $all_day ) {
- // DTEND for multi-day events represents "until", not "including", so subtract one minute
- $end_date = date_i18n( $date_format, $end - 60 );
- } else {
- $end_date = sprintf( $date_with_time, date_i18n( $date_format, $end ), date_i18n( $time_format, $end ) );
- }
-
- $date = sprintf( $two_dates, $date, $end_date );
-
- }
-
- return $date;
- }
-
- protected function sort_by_recent( $list ) {
- $dates = $sorted_list = array();
-
- foreach ( $list as $key => $row ) {
- $date = $row['DTSTART'];
- // pad some time onto an all day date
- if ( 8 === strlen( $date ) )
- $date .= 'T000000Z';
- $dates[$key] = $date;
- }
- asort( $dates );
- foreach( $dates as $key => $value ) {
- $sorted_list[$key] = $list[$key];
- }
- unset($list);
- return $sorted_list;
- }
-
-}
-
-
-/**
- * Wrapper function for iCalendarReader->get_events()
- *
- * @param string $url (default: '')
- * @return array
- */
-function icalendar_get_events( $url = '', $count = 5 ) {
- // Find your calendar's address http://support.google.com/calendar/bin/answer.py?hl=en&answer=37103
- $ical = new iCalendarReader();
- return $ical->get_events( $url, $count );
-}
-
-/**
- * Wrapper function for iCalendarReader->render()
- *
- * @param string $url (default: '')
- * @param string $context (default: 'widget') or 'shortcode'
- * @return mixed bool|string false on failure, rendered HTML string on success.
- */
-function icalendar_render_events( $url = '', $args = array() ) {
- $ical = new iCalendarReader();
- return $ical->render( $url, $args );
-}
diff --git a/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-builder.php b/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-builder.php
deleted file mode 100644
index d3481ce5..00000000
--- a/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-builder.php
+++ /dev/null
@@ -1,341 +0,0 @@
-<?php
-
-
-/**
- * Provides an interface for easily building a complex search query that
- * combines multiple ranking signals.
- *
- *
- * $bldr = new Jetpack_WPES_Query_Builder();
- * $bldr->add_filter( ... );
- * $bldr->add_filter( ... );
- * $bldr->add_query( ... );
- * $es_query = $bldr->build_query();
- *
- *
- * All ES queries take a standard form with main query (with some filters),
- * wrapped in a function_score
- *
- * Bucketed queries use an aggregation to diversify results. eg a bunch
- * of separate filters where to get different sets of results.
- *
- */
-
-class Jetpack_WPES_Query_Builder {
-
- protected $es_filters = array();
-
- // Custom boosting with function_score
- protected $functions = array();
- protected $decays = array();
- protected $scripts = array();
- protected $functions_max_boost = 2.0;
- protected $functions_score_mode = 'multiply';
- protected $query_bool_boost = null;
-
- // General aggregations for buckets and metrics
- protected $aggs_query = false;
- protected $aggs = array();
-
- // The set of top level text queries to combine
- protected $must_queries = array();
- protected $should_queries = array();
- protected $dis_max_queries = array();
-
- protected $diverse_buckets_query = false;
- protected $bucket_filters = array();
- protected $bucket_sub_aggs = array();
-
- ////////////////////////////////////
- // Methods for building a query
-
- public function add_filter( $filter ) {
- $this->es_filters[] = $filter;
- }
-
- public function add_query( $query, $type = 'must' ) {
- switch ( $type ) {
- case 'dis_max':
- $this->dis_max_queries[] = $query;
- break;
-
- case 'should':
- $this->should_queries[] = $query;
- break;
-
- case 'must':
- default:
- $this->must_queries[] = $query;
- break;
- }
- }
-
- /**
- * Add a scoring function to the query
- *
- * NOTE: For decays (linear, exp, or gauss), use Jetpack_WPES_Query_Builder::add_decay() instead
- *
- * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
- *
- * @param $function string name of the function
- * @param $params array functions parameters
- *
- * @return void
- */
- public function add_function( $function, $params ) {
- $this->functions[ $function ][] = $params;
- }
-
- /**
- * Add a decay function to score results
- *
- * This method should be used instead of Jetpack_WPES_Query_Builder::add_function() for decays, as the internal ES structure
- * is slightly different for them.
- *
- * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/decay-functions.html
- *
- * @param $function string name of the decay function - linear, exp, or gauss
- * @param $params array The decay functions parameters, passed to ES directly
- *
- * @return void
- */
- public function add_decay( $function, $params ) {
- $this->decays[ $function ][] = $params;
- }
-
- /**
- * Add a scoring mode to the query
- *
- * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
- *
- * @param $mode string name of how to score
- *
- * @return void
- */
- public function add_score_mode_to_functions( $mode='multiply' ) {
- $this->functions_score_mode = $mode;
- }
-
- public function add_max_boost_to_functions( $boost ) {
- $this->functions_max_boost = $boost;
- }
-
- public function add_boost_to_query_bool( $boost ) {
- $this->query_bool_boost = $boost;
- }
-
- public function add_aggs( $aggs_name, $aggs ) {
- $this->aggs_query = true;
- $this->aggs[$aggs_name] = $aggs;
- }
-
- public function add_aggs_sub_aggs( $aggs_name, $sub_aggs ) {
- if ( ! array_key_exists( 'aggs', $this->aggs[$aggs_name] ) ) {
- $this->aggs[$aggs_name]['aggs'] = array();
- }
- $this->aggs[$aggs_name]['aggs'] = $sub_aggs;
- }
-
- public function add_bucketed_query( $name, $query ) {
- $this->_add_bucket_filter( $name, $query );
-
- $this->add_query( $query, 'dis_max' );
- }
-
- public function add_bucketed_terms( $name, $field, $terms, $boost = 1 ) {
- if ( ! is_array( $terms ) ) {
- $terms = array( $terms );
- }
-
- $this->_add_bucket_filter( $name, array(
- 'terms' => array(
- $field => $terms,
- ),
- ));
-
- $this->add_query( array(
- 'constant_score' => array(
- 'filter' => array(
- 'terms' => array(
- $field => $terms,
- ),
- ),
- 'boost' => $boost,
- ),
- ), 'dis_max' );
- }
-
- public function add_bucket_sub_aggs( $agg ) {
- $this->bucket_sub_aggs = array_merge( $this->bucket_sub_aggs, $agg );
- }
-
- protected function _add_bucket_filter( $name, $filter ) {
- $this->diverse_buckets_query = true;
- $this->bucket_filters[ $name ] = $filter;
- }
-
- ////////////////////////////////////
- // Building Final Query
-
- /**
- * Combine all the queries, functions, decays, scripts, and max_boost into an ES query
- *
- * @return array Array representation of the built ES query
- */
- public function build_query() {
- $query = array();
-
- //dis_max queries just become a single must query
- if ( ! empty( $this->dis_max_queries ) ) {
- $this->must_queries[] = array(
- 'dis_max' => array(
- 'queries' => $this->dis_max_queries,
- ),
- );
- }
-
- if ( empty( $this->must_queries ) ) {
- $this->must_queries = array(
- array(
- 'match_all' => array(),
- ),
- );
- }
-
- if ( empty( $this->should_queries ) ) {
- if ( 1 == count( $this->must_queries ) ) {
- $query = $this->must_queries[0];
- } else {
- $query = array(
- 'bool' => array(
- 'must' => $this->must_queries,
- ),
- );
- }
- } else {
- $query = array(
- 'bool' => array(
- 'must' => $this->must_queries,
- 'should' => $this->should_queries,
- ),
- );
- }
-
- if ( ! is_null( $this->query_bool_boost ) && isset( $query['bool'] ) ) {
- $query['bool']['boost'] = $this->query_bool_boost;
- }
-
- // If there are any function score adjustments, then combine those
- if ( $this->functions || $this->decays || $this->scripts ) {
- $weighting_functions = array();
-
- if ( $this->functions ) {
- foreach ( $this->functions as $function_type => $configs ) {
- foreach ( $configs as $config ) {
- foreach ( $config as $field => $params ) {
- $func_arr = $params;
-
- $func_arr['field'] = $field;
-
- $weighting_functions[] = array(
- $function_type => $func_arr,
- );
- }
- }
- }
- }
-
- if ( $this->decays ) {
- foreach ( $this->decays as $decay_type => $configs ) {
- foreach ( $configs as $config ) {
- foreach ( $config as $field => $params ) {
- $weighting_functions[] = array(
- $decay_type => array(
- $field => $params,
- ),
- );
- }
- }
- }
- }
-
- if ( $this->scripts ) {
- foreach ( $this->scripts as $script ) {
- $weighting_functions[] = array(
- 'script_score' => array(
- 'script' => $script,
- ),
- );
- }
- }
-
- $query = array(
- 'function_score' => array(
- 'query' => $query,
- 'functions' => $weighting_functions,
- 'max_boost' => $this->functions_max_boost,
- 'score_mode' => $this->functions_score_mode,
- ),
- );
- } // End if().
-
- return $query;
- }
-
- /**
- * Assemble the 'filter' portion of an ES query, from all registered filters
- *
- * @return array|null Combined ES filters, or null if none have been defined
- */
- public function build_filter() {
- if ( empty( $this->es_filters ) ) {
- $filter = null;
- } elseif ( 1 == count( $this->es_filters ) ) {
- $filter = $this->es_filters[0];
- } else {
- $filter = array(
- 'and' => $this->es_filters,
- );
- }
-
- return $filter;
- }
-
- /**
- * Assemble the 'aggregation' portion of an ES query, from all general aggregations.
- *
- * @return array An aggregation query as an array of topics, filters, and bucket names
- */
- public function build_aggregation() {
- if ( empty( $this->bucket_sub_aggs ) && empty( $this->aggs_query ) ) {
- return array();
- }
-
- if ( ! $this->diverse_buckets_query && empty( $this->aggs_query ) ) {
- return $this->bucket_sub_aggs;
- }
-
- $aggregations = array(
- 'topics' => array(
- 'filters' => array(
- 'filters' => array(),
- ),
- ),
- );
-
- if ( ! empty( $this->bucket_sub_aggs ) ) {
- $aggregations['topics']['aggs'] = $this->bucket_sub_aggs;
- }
-
- foreach ( $this->bucket_filters as $bucket_name => $filter ) {
- $aggregations['topics']['filters']['filters'][ $bucket_name ] = $filter;
- }
-
- if ( ! empty( $this->aggs_query ) ) {
- $aggregations = $this->aggs;
- }
-
- return $aggregations;
- }
-
-}
diff --git a/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-parser.php b/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-parser.php
deleted file mode 100644
index 2b7710cb..00000000
--- a/plugins/jetpack/_inc/lib/jetpack-wpes-query-builder/jetpack-wpes-query-parser.php
+++ /dev/null
@@ -1,683 +0,0 @@
-<?php
-
-/**
- * Parse a pure text query into WordPress Elasticsearch query. This builds on
- * the Jetpack_WPES_Query_Builder() to provide search query parsing.
- *
- * The key part of this parser is taking a user's query string typed into a box
- * and converting it into an ES search query.
- *
- * This varies by application, but roughly it means extracting some parts of the query
- * (authors, tags, and phrases) that are treated as a filter. Then taking the
- * remaining words and building the correct query (possibly with prefix searching
- * if we are doing search as you type)
- *
- * This class only supports ES 2.x+
- *
- * This parser builds queries of the form:
- * bool:
- * must:
- * AND match of a single field (ideally an edgengram field)
- * filter:
- * filter clauses from context (eg @gibrown, #news, etc)
- * should:
- * boosting of results by various fields
- *
- * Features supported:
- * - search as you type
- * - phrases
- * - supports querying across multiple languages at once
- *
- * Example usage (from Search on Reader Manage):
- *
- * require_lib( 'jetpack-wpes-query-builder/jetpack-wpes-search-query-parser' );
- * $parser = new WPES_Search_Query_Parser( $args['q'], array( $lang ) );
- *
- * //author
- * $parser->author_field_filter( array(
- * 'prefixes' => array( '@' ),
- * 'wpcom_id_field' => 'author_id',
- * 'must_query_fields' => array( 'author.engram', 'author_login.engram' ),
- * 'boost_query_fields' => array( 'author^2', 'author_login^2', 'title.default.engram' ),
- * ) );
- *
- * //remainder of query
- * $match_content_fields = $parser->merge_ml_fields(
- * array(
- * 'all_content' => 0.1,
- * ),
- * array(
- * 'all_content.default.engram^0.1',
- * )
- * );
- * $boost_content_fields = $parser->merge_ml_fields(
- * array(
- * 'title' => 2,
- * 'description' => 1,
- * 'tags' => 1,
- * ),
- * array(
- * 'author_login^2',
- * 'author^2',
- * )
- * );
- *
- * $parser->phrase_filter( array(
- * 'must_query_fields' => $match_content_fields,
- * 'boost_query_fields' => $boost_content_fields,
- * ) );
- * $parser->remaining_query( array(
- * 'must_query_fields' => $match_content_fields,
- * 'boost_query_fields' => $boost_content_fields,
- * ) );
- *
- * //Boost on phrases
- * $parser->remaining_query( array(
- * 'boost_query_fields' => $boost_content_fields,
- * 'boost_query_type' => 'phrase',
- * ) );
- *
- * //boosting
- * $parser->add_max_boost_to_functions( 20 );
- * $parser->add_function( 'field_value_factor', array(
- * 'follower_count' => array(
- * 'modifier' => 'sqrt',
- * 'factor' => 1,
- * 'missing' => 0,
- * ) ) );
- *
- * //Filtering
- * $parser->add_filter( array(
- * 'exists' => array( 'field' => 'langs.' . $lang )
- * ) );
- *
- * //run the query
- * $es_query_args = array(
- * 'name' => 'feeds',
- * 'blog_id' => false,
- * 'security_strategy' => 'a8c',
- * 'type' => 'feed,blog',
- * 'fields' => array( 'blog_id', 'feed_id' ),
- * 'query' => $parser->build_query(),
- * 'filter' => $parser->build_filter(),
- * 'size' => $size,
- * 'from' => $from
- * );
- * $es_results = es_api_search_index( $es_query_args, 'api-feed-find' );
- *
- */
-
-jetpack_require_lib( 'jetpack-wpes-query-builder' );
-
-class Jetpack_WPES_Search_Query_Parser extends Jetpack_WPES_Query_Builder {
-
- protected $orig_query = '';
- protected $current_query = '';
- protected $langs;
- protected $avail_langs = array( 'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'eu', 'fa', 'fi', 'fr', 'he', 'hi', 'hu', 'hy', 'id', 'it', 'ja', 'ko', 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' );
-
- public function __construct( $user_query, $langs ) {
- $this->orig_query = $user_query;
- $this->current_query = $this->orig_query;
- $this->langs = $this->norm_langs( $langs );
- }
-
- protected $extracted_phrases = array();
-
- ///////////////////////////////////////////////////////
- // Methods for Building arrays of multilingual fields
-
- /*
- * Normalize language codes
- */
- public function norm_langs( $langs ) {
- $lst = array();
- foreach( $langs as $l ) {
- $l = strtok( $l, '-_' );
- if ( in_array( $l, $this->avail_langs ) ) {
- $lst[$l] = true;
- } else {
- $lst['default'] = true;
- }
- }
- return array_keys( $lst );
- }
-
- /*
- * Take a list of field prefixes and expand them for multi-lingual
- * with the provided boostings.
- */
- public function merge_ml_fields( $fields2boosts, $additional_fields ) {
- $flds = array();
- foreach( $fields2boosts as $f => $b ) {
- foreach( $this->langs as $l ) {
- $flds[] = $f . '.' . $l . '^' . $b;
- }
- }
- foreach( $additional_fields as $f ) {
- $flds[] = $f;
- }
- return $flds;
- }
-
- ////////////////////////////////////
- // Extract Fields for Filtering on
-
- /*
- * Extract any @mentions from the user query
- * use them as a filter if we can find a wp.com id
- * otherwise use them as a
- *
- * args:
- * wpcom_id_field: wp.com id field
- * must_query_fields: array of fields to search for matching results (optional)
- * boost_query_fields: array of fields to search in for boosting results (optional)
- * prefixes: array of prefixes that the user can use to indicate an author
- *
- * returns true/false of whether any were found
- *
- * See also: https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java
- */
- public function author_field_filter( $args ) {
- $defaults = array(
- 'wpcom_id_field' => 'author_id',
- 'must_query_fields' => null,
- 'boost_query_fields' => null,
- 'prefixes' => array( '@' ),
- );
- $args = wp_parse_args( $args, $defaults );
-
- $names = array();
- foreach( $args['prefixes'] as $p ) {
- $found = $this->get_fields( $p );
- if ( $found ) {
- foreach( $found as $f ) {
- $names[] = $f;
- }
- }
- }
-
- if ( empty( $names ) ) {
- return false;
- }
-
- foreach( $args['prefixes'] as $p ) {
- $this->remove_fields( $p );
- }
-
- $user_ids = array();
- $query_names = array();
-
- //loop through the matches and separate into filters and queries
- foreach( $names as $n ) {
- //check for exact match on login
- $userdata = get_user_by( 'login', strtolower( $n ) );
- $filtering = false;
- if ( $userdata ) {
- $user_ids[ $userdata->ID ] = true;
- $filtering = true;
- }
-
- $is_phrase = false;
- if ( preg_match( '/"/', $n ) ) {
- $is_phrase = true;
- $n = preg_replace( '/"/', '', $n );
- }
-
- if ( !empty( $args['must_query_fields'] ) && !$filtering ) {
- if ( $is_phrase ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $n,
- 'type' => 'phrase',
- ) ) );
- } else {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $n,
- ) ) );
- }
- }
-
- if ( !empty( $args['boost_query_fields'] ) ) {
- if ( $is_phrase ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $n,
- 'type' => 'phrase',
- ) ), 'should' );
- } else {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $n,
- ) ), 'should' );
- }
- }
- }
-
- if ( ! empty( $user_ids ) ) {
- $user_ids = array_keys( $user_ids );
- $this->add_filter( array( 'terms' => array( $args['wpcom_id_field'] => $user_ids ) ) );
- }
-
- return true;
- }
-
- /*
- * Extract any prefix followed by text use them as a must clause,
- * and optionally as a boost to the should query
- * This can be used for hashtags. eg #News, or #"current events",
- * but also works for any arbitrary field. eg from:Greg
- *
- * args:
- * must_query_fields: array of fields that must match the tag (optional)
- * boost_query_fields: array of fields to boost search on (optional)
- * prefixes: array of prefixes that the user can use to indicate a tag
- *
- * returns true/false of whether any were found
- *
- */
- public function text_field_filter( $args ) {
- $defaults = array(
- 'must_query_fields' => array( 'tag.name' ),
- 'boost_query_fields' => array( 'tag.name' ),
- 'prefixes' => array( '#' ),
- );
- $args = wp_parse_args( $args, $defaults );
-
- $tags = array();
- foreach( $args['prefixes'] as $p ) {
- $found = $this->get_fields( $p );
- if ( $found ) {
- foreach( $found as $f ) {
- $tags[] = $f;
- }
- }
- }
-
- if ( empty( $tags ) ) {
- return false;
- }
-
- foreach( $args['prefixes'] as $p ) {
- $this->remove_fields( $p );
- }
-
- foreach( $tags as $t ) {
- $is_phrase = false;
- if ( preg_match( '/"/', $t ) ) {
- $is_phrase = true;
- $t = preg_replace( '/"/', '', $t );
- }
-
- if ( ! empty( $args['must_query_fields'] ) ) {
- if ( $is_phrase ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $t,
- 'type' => 'phrase',
- ) ) );
- } else {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $t,
- ) ) );
- }
- }
-
- if ( ! empty( $args['boost_query_fields'] ) ) {
- if ( $is_phrase ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $t,
- 'type' => 'phrase',
- ) ), 'should' );
- } else {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $t,
- ) ), 'should' );
- }
- }
- }
-
- return true;
- }
-
- /*
- * Extract anything surrounded by quotes or if there is an opening quote
- * that is not complete, and add them to the query as a phrase query.
- * Quotes can be either '' or ""
- *
- * args:
- * must_query_fields: array of fields that must match the phrases
- * boost_query_fields: array of fields to boost the phrases on (optional)
- *
- * returns true/false of whether any were found
- *
- */
- public function phrase_filter( $args ) {
- $defaults = array(
- 'must_query_fields' => array( 'all_content' ),
- 'boost_query_fields' => array( 'title' ),
- );
- $args = wp_parse_args( $args, $defaults );
-
- $phrases = array();
- if ( preg_match_all( '/"([^"]+)"/', $this->current_query, $matches ) ) {
- foreach ( $matches[1] as $match ) {
- $phrases[] = $match;
- }
- $this->current_query = preg_replace( '/"([^"]+)"/', '', $this->current_query );
- }
-
- if ( preg_match_all( "/'([^']+)'/", $this->current_query, $matches ) ) {
- foreach ( $matches[1] as $match ) {
- $phrases[] = $match;
- }
- $this->current_query = preg_replace( "/'([^']+)'/", '', $this->current_query );
- }
-
- //look for a final, uncompleted phrase
- $phrase_prefix = false;
- if ( preg_match_all( '/"([^"]+)$/', $this->current_query, $matches ) ) {
- $phrase_prefix = $matches[1][0];
- $this->current_query = preg_replace( '/"([^"]+)$/', '', $this->current_query );
- }
- if ( preg_match_all( "/(?:'\B|\B')([^']+)$/", $this->current_query, $matches ) ) {
- $phrase_prefix = $matches[1][0];
- $this->current_query = preg_replace( "/(?:'\B|\B')([^']+)$/", '', $this->current_query );
- }
-
- if ( $phrase_prefix ) {
- $phrases[] = $phrase_prefix;
- }
- if ( empty( $phrases ) ) {
- return false;
- }
-
- foreach ( $phrases as $p ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $p,
- 'type' => 'phrase',
- ) ) );
-
- if ( ! empty( $args['boost_query_fields'] ) ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $p,
- 'operator' => 'and',
- ) ), 'should' );
- }
- }
-
- return true;
- }
-
- /*
- * Query fields based on the remaining parts of the query
- * This could be the final AND part of the query terms to match, or it
- * could be boosting certain elements of the query
- *
- * args:
- * must_query_fields: array of fields that must match the remaining terms (optional)
- * boost_query_fields: array of fields to boost the remaining terms on (optional)
- *
- */
- public function remaining_query( $args ) {
- $defaults = array(
- 'must_query_fields' => null,
- 'boost_query_fields' => null,
- 'boost_operator' => 'and',
- 'boost_query_type' => 'best_fields',
- );
- $args = wp_parse_args( $args, $defaults );
-
- if ( empty( $this->current_query ) || ctype_space( $this->current_query ) ) {
- return;
- }
-
- if ( ! empty( $args['must_query_fields'] ) ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $this->current_query,
- 'operator' => 'and',
- ) ) );
- }
-
- if ( ! empty( $args['boost_query_fields'] ) ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $this->current_query,
- 'operator' => $args['boost_operator'],
- 'type' => $args['boost_query_type'],
- ) ), 'should' );
- }
-
- }
-
- /*
- * Query fields using a prefix query (alphabetical expansions on the index).
- * This is not recommended. Slower performance and worse relevancy.
- *
- * (UNTESTED! Copied from old prefix expansion code)
- *
- * args:
- * must_query_fields: array of fields that must match the remaining terms (optional)
- * boost_query_fields: array of fields to boost the remaining terms on (optional)
- *
- */
- public function remaining_prefix_query( $args ) {
- $defaults = array(
- 'must_query_fields' => array( 'all_content' ),
- 'boost_query_fields' => array( 'title' ),
- 'boost_operator' => 'and',
- 'boost_query_type' => 'best_fields',
- );
- $args = wp_parse_args( $args, $defaults );
-
- if ( empty( $this->current_query ) || ctype_space( $this->current_query ) ) {
- return;
- }
-
- //////////////////////////////////
- // Example cases to think about:
- // "elasticse"
- // "elasticsearch"
- // "elasticsearch "
- // "elasticsearch lucen"
- // "elasticsearch lucene"
- // "the future" - note the stopword which will match nothing!
- // "F1" - an exact match that also has tons of expansions
- // "こんにちは" ja "hello"
- // "こんにちは友人" ja "hello friend" - we just rely on the prefix phrase and ES to split words
- // - this could still be better I bet. Maybe we need to analyze with ES first?
- //
-
- /////////////////////////////
- //extract pieces of query
- // eg: "PREFIXREMAINDER PREFIXWORD"
- // "elasticsearch lucen"
-
- $prefix_word = false;
- $prefix_remainder = false;
- if ( preg_match_all( '/([^ ]+)$/', $this->current_query, $matches ) ) {
- $prefix_word = $matches[1][0];
- }
-
- $prefix_remainder = preg_replace( '/([^ ]+)$/', '', $this->current_query );
- if ( ctype_space( $prefix_remainder ) ) {
- $prefix_remainder = false;
- }
-
- if ( ! $prefix_word ) {
- //Space at the end of the query, so skip using a prefix query
- if ( ! empty( $args['must_query_fields'] ) ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['must_query_fields'],
- 'query' => $this->current_query,
- 'operator' => 'and',
- ) ) );
- }
-
- if ( ! empty( $args['boost_query_fields'] ) ) {
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $this->current_query,
- 'operator' => $args['boost_operator'],
- 'type' => $args['boost_query_type'],
- ) ), 'should' );
- }
- } else {
-
- //must match the prefix word and the prefix remainder
- if ( ! empty( $args['must_query_fields'] ) ) {
- //need to do an OR across a few fields to handle all cases
- $must_q = array( 'bool' => array( 'should' => array( ), 'minimum_should_match' => 1 ) );
-
- //treat all words as an exact search (boosts complete word like "news"
- //from prefixes of "newspaper")
- $must_q['bool']['should'][] = array( 'multi_match' => array(
- 'fields' => $this->all_fields,
- 'query' => $full_text,
- 'operator' => 'and',
- 'type' => 'cross_fields',
- ) );
-
- //always optimistically try and match the full text as a phrase
- //prefix "the futu" should try to match "the future"
- //otherwise the first stopword kinda breaks
- //This also works as the prefix match for a single word "elasticsea"
- $must_q['bool']['should'][] = array( 'multi_match' => array(
- 'fields' => $this->phrase_fields,
- 'query' => $full_text,
- 'operator' => 'and',
- 'type' => 'phrase_prefix',
- 'max_expansions' => 100,
- ) );
-
- if ( $prefix_remainder ) {
- //Multiple words found, so treat each word on its own and not just as
- //a part of a phrase
- //"elasticsearch lucen" => "elasticsearch" exact AND "lucen" prefix
- $q['bool']['should'][] = array( 'bool' => array(
- 'must' => array(
- array( 'multi_match' => array(
- 'fields' => $this->phrase_fields,
- 'query' => $prefix_word,
- 'operator' => 'and',
- 'type' => 'phrase_prefix',
- 'max_expansions' => 100,
- ) ),
- array( 'multi_match' => array(
- 'fields' => $this->all_fields,
- 'query' => $prefix_remainder,
- 'operator' => 'and',
- 'type' => 'cross_fields',
- ) ),
- )
- ) );
- }
-
- $this->add_query( $must_q );
- }
-
- //Now add any boosting of the query
- if ( ! empty( $args['boost_query_fields'] ) ) {
- //treat all words as an exact search (boosts complete word like "news"
- //from prefixes of "newspaper")
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $this->current_query,
- 'operator' => $args['boost_query_operator'],
- 'type' => $args['boost_query_type'],
- ) ), 'should' );
-
- //optimistically boost the full phrase prefix match
- $this->add_query( array(
- 'multi_match' => array(
- 'fields' => $args['boost_query_fields'],
- 'query' => $this->current_query,
- 'operator' => 'and',
- 'type' => 'phrase_prefix',
- 'max_expansions' => 100,
- ) ) );
- }
- }
- }
-
- /*
- * Boost results based on the lang probability overlaps
- *
- * args:
- * langs2prob: list of languages to search in with associated boosts
- */
- public function boost_lang_probs( $langs2prob ) {
- foreach( $langs2prob as $l => $p ) {
- $this->add_function( 'field_value_factor', array(
- 'modifier' => 'none',
- 'factor' => $p,
- 'missing' => 0.01, //1% chance doc did not have right lang detected
- ) );
- }
- }
-
- ////////////////////////////////////
- // Helper Methods
-
- //Get the text after some prefix. eg @gibrown, or @"Greg Brown"
- protected function get_fields( $field_prefix ) {
- $regex = '/' . $field_prefix . '(("[^"]+")|([^\\p{Z}]+))/';
- if ( preg_match_all( $regex, $this->current_query, $match ) ) {
- return $match[1];
- }
- return false;
- }
-
- //Remove the prefix and text from the query
- protected function remove_fields( $field_name ) {
- $regex = '/' . $field_name . '(("[^"]+")|([^\\p{Z}]+))/';
- $this->current_query = preg_replace( $regex, '', $this->current_query );
- }
-
- //Best effort string truncation that splits on word breaks
- protected function truncate_string( $string, $limit, $break=" " ) {
- if ( mb_strwidth( $string ) <= $limit ) {
- return $string;
- }
-
- // walk backwards from $limit to find first break
- $breakpoint = $limit;
- $broken = false;
- while ( $breakpoint > 0 ) {
- if ( $break === mb_strimwidth( $string, $breakpoint, 1 ) ) {
- $string = mb_strimwidth( $string, 0, $breakpoint );
- $broken = true;
- break;
- }
- $breakpoint--;
- }
- // if we weren't able to find a break, need to chop mid-word
- if ( !$broken ) {
- $string = mb_strimwidth( $string, 0, $limit );
- }
- return $string;
- }
-
-}
diff --git a/plugins/jetpack/_inc/lib/markdown/0-load.php b/plugins/jetpack/_inc/lib/markdown/0-load.php
deleted file mode 100644
index bf5993e3..00000000
--- a/plugins/jetpack/_inc/lib/markdown/0-load.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-if ( ! class_exists( 'MarkdownExtra_Parser' ) )
- jetpack_require_lib( 'markdown/extra' );
-
-jetpack_require_lib( 'markdown/gfm' );
diff --git a/plugins/jetpack/_inc/lib/markdown/README.md b/plugins/jetpack/_inc/lib/markdown/README.md
deleted file mode 100644
index 45f298d1..00000000
--- a/plugins/jetpack/_inc/lib/markdown/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Markdown parsing library
-
-Contains two libraries:
-
-* `/extra`
- - Gives you `MardownExtra_Parser` and `Markdown_Parser`
- - Docs at http://michelf.ca/projects/php-markdown/extra/
-
-* `/gfm` -- Github Flavored Markdown
- - Gives you `WPCom_GHF_Markdown_Parser`
- - It has the same interface as `MarkdownExtra_Parser`
- - Adds support for fenced code blocks: https://help.github.com/articles/creating-and-highlighting-code-blocks/#fenced-code-blocks
- - By default it replaces them with a code shortcode
- - You can change this using the `$use_code_shortcode` member variable
- - You can change the code shortcode wrapping with `$shortcode_start` and `$shortcode_end` member variables
- - The `$preserve_shortcodes` member variable will preserve all registered shortcodes untouched. Requires WordPress to be loaded for `get_shortcode_regex()`
- - The `$preserve_latex` member variable will preserve oldskool $latex yer-latex$ codes untouched.
- - The `$strip_paras` member variable will strip <p> tags because that's what WordPress likes.
- - See `WPCom_GHF_Markdown_Parser::__construct()` for how the above member variable defaults are set.
diff --git a/plugins/jetpack/_inc/lib/markdown/extra.php b/plugins/jetpack/_inc/lib/markdown/extra.php
deleted file mode 100644
index fd85a3c8..00000000
--- a/plugins/jetpack/_inc/lib/markdown/extra.php
+++ /dev/null
@@ -1,3207 +0,0 @@
-<?php
-#
-# Markdown Extra - A text-to-HTML conversion tool for web writers
-#
-# PHP Markdown & Extra
-# Copyright (c) 2004-2013 Michel Fortin
-# <http://michelf.ca/projects/php-markdown/>
-#
-# Original Markdown
-# Copyright (c) 2004-2006 John Gruber
-# <http://daringfireball.net/projects/markdown/>
-#
-# Tweaked to remove WordPress interface
-
-
-define( 'MARKDOWN_VERSION', "1.0.2" ); # 29 Nov 2013
-define( 'MARKDOWNEXTRA_VERSION', "1.2.8" ); # 29 Nov 2013
-
-
-#
-# Global default settings:
-#
-
-# Change to ">" for HTML output
-@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />");
-
-# Define the width of a tab for code blocks.
-@define( 'MARKDOWN_TAB_WIDTH', 4 );
-
-# Optional title attribute for footnote links and backlinks.
-@define( 'MARKDOWN_FN_LINK_TITLE', "" );
-@define( 'MARKDOWN_FN_BACKLINK_TITLE', "" );
-
-# Optional class attribute for footnote links and backlinks.
-@define( 'MARKDOWN_FN_LINK_CLASS', "jetpack-footnote" );
-@define( 'MARKDOWN_FN_BACKLINK_CLASS', "" );
-
-# Optional class prefix for fenced code block.
-@define( 'MARKDOWN_CODE_CLASS_PREFIX', "language-" );
-
-# Class attribute for code blocks goes on the `code` tag;
-# setting this to true will put attributes on the `pre` tag instead.
-@define( 'MARKDOWN_CODE_ATTR_ON_PRE', false );
-
-
-
-### Standard Function Interface ###
-
-@define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' );
-
-function Markdown($text) {
-#
-# Initialize the parser and return the result of its transform method.
-#
- # Setup static parser variable.
- static $parser;
- if (!isset($parser)) {
- $parser_class = MARKDOWN_PARSER_CLASS;
- $parser = new $parser_class;
- }
-
- # Transform text using parser.
- return $parser->transform($text);
-}
-
-/**
- * Returns the length of $text loosely counting the number of UTF-8 characters with regular expression.
- * Used by the Markdown_Parser class when mb_strlen is not available.
- *
- * @since 5.9
- *
- * @return string Length of the multibyte string
- *
- */
-function jetpack_utf8_strlen( $text ) {
- return preg_match_all( "/[\\x00-\\xBF]|[\\xC0-\\xFF][\\x80-\\xBF]*/", $text, $m );
-}
-
-#
-# Markdown Parser Class
-#
-
-class Markdown_Parser {
-
- ### Configuration Variables ###
-
- # Change to ">" for HTML output.
- public $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
- public $tab_width = MARKDOWN_TAB_WIDTH;
-
- # Change to `true` to disallow markup or entities.
- public $no_markup = false;
- public $no_entities = false;
-
- # Predefined urls and titles for reference links and images.
- public $predef_urls = array();
- public $predef_titles = array();
-
-
- ### Parser Implementation ###
-
- # Regex to match balanced [brackets].
- # Needed to insert a maximum bracked depth while converting to PHP.
- public $nested_brackets_depth = 6;
- public $nested_brackets_re;
-
- public $nested_url_parenthesis_depth = 4;
- public $nested_url_parenthesis_re;
-
- # Table of hash values for escaped characters:
- public $escape_chars = '\`*_{}[]()>#+-.!';
- public $escape_chars_re;
-
-
- function __construct() {
- #
- # Constructor function. Initialize appropriate member variables.
- #
- $this->_initDetab();
- $this->prepareItalicsAndBold();
-
- $this->nested_brackets_re =
- str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
- str_repeat('\])*', $this->nested_brackets_depth);
-
- $this->nested_url_parenthesis_re =
- str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
- str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
-
- $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
-
- # Sort document, block, and span gamut in ascendent priority order.
- asort($this->document_gamut);
- asort($this->block_gamut);
- asort($this->span_gamut);
- }
-
-
- # Internal hashes used during transformation.
- public $urls = array();
- public $titles = array();
- public $html_hashes = array();
-
- # Status flag to avoid invalid nesting.
- public $in_anchor = false;
-
-
- function setup() {
- #
- # Called before the transformation process starts to setup parser
- # states.
- #
- # Clear global hashes.
- $this->urls = $this->predef_urls;
- $this->titles = $this->predef_titles;
- $this->html_hashes = array();
-
- $this->in_anchor = false;
- }
-
- function teardown() {
- #
- # Called after the transformation process to clear any variable
- # which may be taking up memory unnecessarly.
- #
- $this->urls = array();
- $this->titles = array();
- $this->html_hashes = array();
- }
-
-
- function transform($text) {
- #
- # Main function. Performs some preprocessing on the input text
- # and pass it through the document gamut.
- #
- $this->setup();
-
- # Remove UTF-8 BOM and marker character in input, if present.
- $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
-
- # Standardize line endings:
- # DOS to Unix and Mac to Unix
- $text = preg_replace('{\r\n?}', "\n", $text);
-
- # Make sure $text ends with a couple of newlines:
- $text .= "\n\n";
-
- # Convert all tabs to spaces.
- $text = $this->detab($text);
-
- # Turn block-level HTML blocks into hash entries
- $text = $this->hashHTMLBlocks($text);
-
- # Strip any lines consisting only of spaces and tabs.
- # This makes subsequent regexen easier to write, because we can
- # match consecutive blank lines with /\n+/ instead of something
- # contorted like /[ ]*\n+/ .
- $text = preg_replace('/^[ ]+$/m', '', $text);
-
- # Run document gamut methods.
- foreach ($this->document_gamut as $method => $priority) {
- $text = $this->$method($text);
- }
-
- $this->teardown();
-
- return $text . "\n";
- }
-
- public $document_gamut = array(
- # Strip link definitions, store in hashes.
- "stripLinkDefinitions" => 20,
-
- "runBasicBlockGamut" => 30,
- );
-
-
- function stripLinkDefinitions($text) {
- #
- # Strips link definitions from text, stores the URLs and titles in
- # hash references.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Link defs are in the form: ^[id]: url "optional title"
- $text = preg_replace_callback('{
- ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
- [ ]*
- \n? # maybe *one* newline
- [ ]*
- (?:
- <(.+?)> # url = $2
- |
- (\S+?) # url = $3
- )
- [ ]*
- \n? # maybe one newline
- [ ]*
- (?:
- (?<=\s) # lookbehind for whitespace
- ["(]
- (.*?) # title = $4
- [")]
- [ ]*
- )? # title is optional
- (?:\n+|\Z)
- }xm',
- array(&$this, '_stripLinkDefinitions_callback'),
- $text);
- return $text;
- }
- function _stripLinkDefinitions_callback($matches) {
- $link_id = strtolower($matches[1]);
- $url = $matches[2] == '' ? $matches[3] : $matches[2];
- $this->urls[$link_id] = $url;
- $this->titles[$link_id] =& $matches[4];
- return ''; # String that will replace the block
- }
-
-
- function hashHTMLBlocks($text) {
- if ($this->no_markup) return $text;
-
- $less_than_tab = $this->tab_width - 1;
-
- # Hashify HTML blocks:
- # We only want to do this for block-level HTML tags, such as headers,
- # lists, and tables. That's because we still want to wrap <p>s around
- # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
- # phrase emphasis, and spans. The list of tags we're looking for is
- # hard-coded:
- #
- # * List "a" is made of tags which can be both inline or block-level.
- # These will be treated block-level when the start tag is alone on
- # its line, otherwise they're not matched here and will be taken as
- # inline later.
- # * List "b" is made of tags which are always block-level;
- #
- $block_tags_a_re = 'ins|del';
- $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
- 'script|noscript|form|fieldset|iframe|math|svg|'.
- 'article|section|nav|aside|hgroup|header|footer|'.
- 'figure';
-
- # Regular expression for the content of a block tag.
- $nested_tags_level = 4;
- $attr = '
- (?> # optional tag attributes
- \s # starts with whitespace
- (?>
- [^>"/]+ # text outside quotes
- |
- /+(?!>) # slash not followed by ">"
- |
- "[^"]*" # text inside double quotes (tolerate ">")
- |
- \'[^\']*\' # text inside single quotes (tolerate ">")
- )*
- )?
- ';
- $content =
- str_repeat('
- (?>
- [^<]+ # content without tag
- |
- <\2 # nested opening tag
- '.$attr.' # attributes
- (?>
- />
- |
- >', $nested_tags_level). # end of opening tag
- '.*?'. # last level nested tag content
- str_repeat('
- </\2\s*> # closing nested tag
- )
- |
- <(?!/\2\s*> # other tags with a different name
- )
- )*',
- $nested_tags_level);
- $content2 = str_replace('\2', '\3', $content);
-
- # First, look for nested blocks, e.g.:
- # <div>
- # <div>
- # tags for inner block must be indented.
- # </div>
- # </div>
- #
- # The outermost tags must start at the left margin for this to match, and
- # the inner nested divs must be indented.
- # We need to do this before the next, more liberal match, because the next
- # match will start at the first `<div>` and stop at the first `</div>`.
- $text = preg_replace_callback('{(?>
- (?>
- (?<=\n\n) # Starting after a blank line
- | # or
- \A\n? # the beginning of the doc
- )
- ( # save in $1
-
- # Match from `\n<tag>` to `</tag>\n`, handling nested tags
- # in between.
-
- [ ]{0,'.$less_than_tab.'}
- <('.$block_tags_b_re.')# start tag = $2
- '.$attr.'> # attributes followed by > and \n
- '.$content.' # content, support nesting
- </\2> # the matching end tag
- [ ]* # trailing spaces/tabs
- (?=\n+|\Z) # followed by a newline or end of document
-
- | # Special version for tags of group a.
-
- [ ]{0,'.$less_than_tab.'}
- <('.$block_tags_a_re.')# start tag = $3
- '.$attr.'>[ ]*\n # attributes followed by >
- '.$content2.' # content, support nesting
- </\3> # the matching end tag
- [ ]* # trailing spaces/tabs
- (?=\n+|\Z) # followed by a newline or end of document
-
- | # Special case just for <hr />. It was easier to make a special
- # case than to make the other regex more complicated.
-
- [ ]{0,'.$less_than_tab.'}
- <(hr) # start tag = $2
- '.$attr.' # attributes
- /?> # the matching end tag
- [ ]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
-
- | # Special case for standalone HTML comments:
-
- [ ]{0,'.$less_than_tab.'}
- (?s:
- <!-- .*? -->
- )
- [ ]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
-
- | # PHP and ASP-style processor instructions (<? and <%)
-
- [ ]{0,'.$less_than_tab.'}
- (?s:
- <([?%]) # $2
- .*?
- \2>
- )
- [ ]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
-
- )
- )}Sxmi',
- array(&$this, '_hashHTMLBlocks_callback'),
- $text);
-
- return $text;
- }
- function _hashHTMLBlocks_callback($matches) {
- $text = $matches[1];
- $key = $this->hashBlock($text);
- return "\n\n$key\n\n";
- }
-
-
- function hashPart($text, $boundary = 'X') {
- #
- # Called whenever a tag must be hashed when a function insert an atomic
- # element in the text stream. Passing $text to through this function gives
- # a unique text-token which will be reverted back when calling unhash.
- #
- # The $boundary argument specify what character should be used to surround
- # the token. By convension, "B" is used for block elements that needs not
- # to be wrapped into paragraph tags at the end, ":" is used for elements
- # that are word separators and "X" is used in the general case.
- #
- # Swap back any tag hash found in $text so we do not have to `unhash`
- # multiple times at the end.
- $text = $this->unhash($text);
-
- # Then hash the block.
- static $i = 0;
- $key = "$boundary\x1A" . ++$i . $boundary;
- $this->html_hashes[$key] = $text;
- return $key; # String that will replace the tag.
- }
-
-
- function hashBlock($text) {
- #
- # Shortcut function for hashPart with block-level boundaries.
- #
- return $this->hashPart($text, 'B');
- }
-
-
- public $block_gamut = array(
- #
- # These are all the transformations that form block-level
- # tags like paragraphs, headers, and list items.
- #
- "doHeaders" => 10,
- "doHorizontalRules" => 20,
-
- "doLists" => 40,
- "doCodeBlocks" => 50,
- "doBlockQuotes" => 60,
- );
-
- function runBlockGamut($text) {
- #
- # Run block gamut tranformations.
- #
- # We need to escape raw HTML in Markdown source before doing anything
- # else. This need to be done for each block, and not only at the
- # beginning in the Markdown function since hashed blocks can be part of
- # list items and could have been indented. Indented blocks would have
- # been seen as a code block in a previous pass of hashHTMLBlocks.
- $text = $this->hashHTMLBlocks($text);
-
- return $this->runBasicBlockGamut($text);
- }
-
- function runBasicBlockGamut($text) {
- #
- # Run block gamut tranformations, without hashing HTML blocks. This is
- # useful when HTML blocks are known to be already hashed, like in the first
- # whole-document pass.
- #
- foreach ($this->block_gamut as $method => $priority) {
- $text = $this->$method($text);
- }
-
- # Finally form paragraph and restore hashed blocks.
- $text = $this->formParagraphs($text);
-
- return $text;
- }
-
-
- function doHorizontalRules($text) {
- # Do Horizontal Rules:
- return preg_replace(
- '{
- ^[ ]{0,3} # Leading space
- ([-*_]) # $1: First marker
- (?> # Repeated marker group
- [ ]{0,2} # Zero, one, or two spaces.
- \1 # Marker character
- ){2,} # Group repeated at least twice
- [ ]* # Tailing spaces
- $ # End of line.
- }mx',
- "\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n",
- $text);
- }
-
-
- public $span_gamut = array(
- #
- # These are all the transformations that occur *within* block-level
- # tags like paragraphs, headers, and list items.
- #
- # Process character escapes, code spans, and inline HTML
- # in one shot.
- "parseSpan" => -30,
-
- # Process anchor and image tags. Images must come first,
- # because ![foo][f] looks like an anchor.
- "doImages" => 10,
- "doAnchors" => 20,
-
- # Make links out of things like `<http://example.com/>`
- # Must come after doAnchors, because you can use < and >
- # delimiters in inline links like [this](<url>).
- "doAutoLinks" => 30,
- "encodeAmpsAndAngles" => 40,
-
- "doItalicsAndBold" => 50,
- "doHardBreaks" => 60,
- );
-
- function runSpanGamut($text) {
- #
- # Run span gamut tranformations.
- #
- foreach ($this->span_gamut as $method => $priority) {
- $text = $this->$method($text);
- }
-
- return $text;
- }
-
-
- function doHardBreaks($text) {
- # Do hard breaks:
- return preg_replace_callback('/ {2,}\n/',
- array(&$this, '_doHardBreaks_callback'), $text);
- }
- function _doHardBreaks_callback($matches) {
- return $this->hashPart("<br$this->empty_element_suffix\n");
- }
-
-
- function doAnchors($text) {
- #
- # Turn Markdown link shortcuts into XHTML <a> tags.
- #
- if ($this->in_anchor) return $text;
- $this->in_anchor = true;
-
- #
- # First, handle reference-style links: [link text] [id]
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ('.$this->nested_brackets_re.') # link text = $2
- \]
-
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
-
- \[
- (.*?) # id = $3
- \]
- )
- }xs',
- array(&$this, '_doAnchors_reference_callback'), $text);
-
- #
- # Next, inline-style links: [link text](url "optional title")
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ('.$this->nested_brackets_re.') # link text = $2
- \]
- \( # literal paren
- [ \n]*
- (?:
- <(.+?)> # href = $3
- |
- ('.$this->nested_url_parenthesis_re.') # href = $4
- )
- [ \n]*
- ( # $5
- ([\'"]) # quote char = $6
- (.*?) # Title = $7
- \6 # matching quote
- [ \n]* # ignore any spaces/tabs between closing quote and )
- )? # title is optional
- \)
- )
- }xs',
- array(&$this, '_doAnchors_inline_callback'), $text);
-
- #
- # Last, handle reference-style shortcuts: [link text]
- # These must come last in case you've also got [link text][1]
- # or [link text](/foo)
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ([^\[\]]+) # link text = $2; can\'t contain [ or ]
- \]
- )
- }xs',
- array(&$this, '_doAnchors_reference_callback'), $text);
-
- $this->in_anchor = false;
- return $text;
- }
- function _doAnchors_reference_callback($matches) {
- $whole_match = $matches[1];
- $link_text = $matches[2];
- $link_id =& $matches[3];
-
- if ($link_id == "") {
- # for shortcut links like [this][] or [this].
- $link_id = $link_text;
- }
-
- # lower-case and turn embedded newlines into spaces
- $link_id = strtolower($link_id);
- $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
-
- if (isset($this->urls[$link_id])) {
- $url = $this->urls[$link_id];
- $url = $this->encodeAttribute($url);
-
- $result = "<a href=\"$url\"";
- if ( isset( $this->titles[$link_id] ) ) {
- $title = $this->titles[$link_id];
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
-
- $link_text = $this->runSpanGamut($link_text);
- $result .= ">$link_text</a>";
- $result = $this->hashPart($result);
- }
- else {
- $result = $whole_match;
- }
- return $result;
- }
- function _doAnchors_inline_callback($matches) {
- $whole_match = $matches[1];
- $link_text = $this->runSpanGamut($matches[2]);
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
- $title =& $matches[7];
-
- $url = $this->encodeAttribute($url);
-
- $result = "<a href=\"$url\"";
- if (isset($title)) {
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
-
- $link_text = $this->runSpanGamut($link_text);
- $result .= ">$link_text</a>";
-
- return $this->hashPart($result);
- }
-
-
- function doImages($text) {
- #
- # Turn Markdown image shortcuts into <img> tags.
- #
- #
- # First, handle reference-style labeled images: ![alt text][id]
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- !\[
- ('.$this->nested_brackets_re.') # alt text = $2
- \]
-
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
-
- \[
- (.*?) # id = $3
- \]
-
- )
- }xs',
- array(&$this, '_doImages_reference_callback'), $text);
-
- #
- # Next, handle inline images: ![alt text](url "optional title")
- # Don't forget: encode * and _
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- !\[
- ('.$this->nested_brackets_re.') # alt text = $2
- \]
- \s? # One optional whitespace character
- \( # literal paren
- [ \n]*
- (?:
- <(\S*)> # src url = $3
- |
- ('.$this->nested_url_parenthesis_re.') # src url = $4
- )
- [ \n]*
- ( # $5
- ([\'"]) # quote char = $6
- (.*?) # title = $7
- \6 # matching quote
- [ \n]*
- )? # title is optional
- \)
- )
- }xs',
- array(&$this, '_doImages_inline_callback'), $text);
-
- return $text;
- }
- function _doImages_reference_callback($matches) {
- $whole_match = $matches[1];
- $alt_text = $matches[2];
- $link_id = strtolower($matches[3]);
-
- if ($link_id == "") {
- $link_id = strtolower($alt_text); # for shortcut links like ![this][].
- }
-
- $alt_text = $this->encodeAttribute($alt_text);
- if (isset($this->urls[$link_id])) {
- $url = $this->encodeAttribute($this->urls[$link_id]);
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (isset($this->titles[$link_id])) {
- $title = $this->titles[$link_id];
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
- $result .= $this->empty_element_suffix;
- $result = $this->hashPart($result);
- }
- else {
- # If there's no such link ID, leave intact:
- $result = $whole_match;
- }
-
- return $result;
- }
- function _doImages_inline_callback($matches) {
- $whole_match = $matches[1];
- $alt_text = $matches[2];
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
- $title =& $matches[7];
-
- $alt_text = $this->encodeAttribute($alt_text);
- $url = $this->encodeAttribute($url);
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (isset($title)) {
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\""; # $title already quoted
- }
- $result .= $this->empty_element_suffix;
-
- return $this->hashPart($result);
- }
-
-
- function doHeaders($text) {
- # Setext-style headers:
- # Header 1
- # ========
- #
- # Header 2
- # --------
- #
- $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
- array(&$this, '_doHeaders_callback_setext'), $text);
-
- # atx-style headers:
- # # Header 1
- # ## Header 2
- # ## Header 2 with closing hashes ##
- # ...
- # ###### Header 6
- #
- $text = preg_replace_callback('{
- ^(\#{1,6}) # $1 = string of #\'s
- [ ]*
- (.+?) # $2 = Header text
- [ ]*
- \#* # optional closing #\'s (not counted)
- \n+
- }xm',
- array(&$this, '_doHeaders_callback_atx'), $text);
-
- return $text;
- }
- function _doHeaders_callback_setext($matches) {
- # Terrible hack to check we haven't found an empty list item.
- if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
- return $matches[0];
-
- $level = $matches[2]{0} == '=' ? 1 : 2;
- $block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
- return "\n" . $this->hashBlock($block) . "\n\n";
- }
- function _doHeaders_callback_atx($matches) {
- $level = strlen($matches[1]);
- $block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
- return "\n" . $this->hashBlock($block) . "\n\n";
- }
-
-
- function doLists($text) {
- #
- # Form HTML ordered (numbered) and unordered (bulleted) lists.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Re-usable patterns to match list item bullets and number markers:
- $marker_ul_re = '[*+-]';
- $marker_ol_re = '\d+[\.]';
- $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
-
- $markers_relist = array(
- $marker_ul_re => $marker_ol_re,
- $marker_ol_re => $marker_ul_re,
- );
-
- foreach ($markers_relist as $marker_re => $other_marker_re) {
- # Re-usable pattern to match any entirel ul or ol list:
- $whole_list_re = '
- ( # $1 = whole list
- ( # $2
- ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces
- ('.$marker_re.') # $4 = first list item marker
- [ ]+
- )
- (?s:.+?)
- ( # $5
- \z
- |
- \n{2,}
- (?=\S)
- (?! # Negative lookahead for another list item marker
- [ ]*
- '.$marker_re.'[ ]+
- )
- |
- (?= # Lookahead for another kind of list
- \n
- \3 # Must have the same indentation
- '.$other_marker_re.'[ ]+
- )
- )
- )
- '; // mx
-
- # We use a different prefix before nested lists than top-level lists.
- # See extended comment in _ProcessListItems().
-
- if ($this->list_level) {
- $text = preg_replace_callback('{
- ^
- '.$whole_list_re.'
- }mx',
- array(&$this, '_doLists_callback'), $text);
- }
- else {
- $text = preg_replace_callback('{
- (?:(?<=\n)\n|\A\n?) # Must eat the newline
- '.$whole_list_re.'
- }mx',
- array(&$this, '_doLists_callback'), $text);
- }
- }
-
- return $text;
- }
- function _doLists_callback($matches) {
- # Re-usable patterns to match list item bullets and number markers:
- $marker_ul_re = '[*+-]';
- $marker_ol_re = '\d+[\.]';
- $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
-
- $list = $matches[1];
- $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
-
- $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
-
- $list .= "\n";
- $result = $this->processListItems($list, $marker_any_re);
-
- $result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
- return "\n". $result ."\n\n";
- }
-
- public $list_level = 0;
-
- function processListItems($list_str, $marker_any_re) {
- #
- # Process the contents of a single ordered or unordered list, splitting it
- # into individual list items.
- #
- # The $this->list_level global keeps track of when we're inside a list.
- # Each time we enter a list, we increment it; when we leave a list,
- # we decrement. If it's zero, we're not in a list anymore.
- #
- # We do this because when we're not inside a list, we want to treat
- # something like this:
- #
- # I recommend upgrading to version
- # 8. Oops, now this line is treated
- # as a sub-list.
- #
- # As a single paragraph, despite the fact that the second line starts
- # with a digit-period-space sequence.
- #
- # Whereas when we're inside a list (or sub-list), that line will be
- # treated as the start of a sub-list. What a kludge, huh? This is
- # an aspect of Markdown's syntax that's hard to parse perfectly
- # without resorting to mind-reading. Perhaps the solution is to
- # change the syntax rules such that sub-lists must start with a
- # starting cardinal number; e.g. "1." or "a.".
-
- $this->list_level++;
-
- # trim trailing blank lines:
- $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
-
- $list_str = preg_replace_callback('{
- (\n)? # leading line = $1
- (^[ ]*) # leading whitespace = $2
- ('.$marker_any_re.' # list marker and space = $3
- (?:[ ]+|(?=\n)) # space only required if item is not empty
- )
- ((?s:.*?)) # list item text = $4
- (?:(\n+(?=\n))|\n) # tailing blank line = $5
- (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
- }xm',
- array(&$this, '_processListItems_callback'), $list_str);
-
- $this->list_level--;
- return $list_str;
- }
- function _processListItems_callback($matches) {
- $item = $matches[4];
- $leading_line =& $matches[1];
- $leading_space =& $matches[2];
- $marker_space = $matches[3];
- $tailing_blank_line =& $matches[5];
-
- if ($leading_line || $tailing_blank_line ||
- preg_match('/\n{2,}/', $item))
- {
- # Replace marker with the appropriate whitespace indentation
- $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
- $item = $this->runBlockGamut($this->outdent($item)."\n");
- }
- else {
- # Recursion for sub-lists:
- $item = $this->doLists($this->outdent($item));
- $item = preg_replace('/\n+$/', '', $item);
- $item = $this->runSpanGamut($item);
- }
-
- return "<li>" . $item . "</li>\n";
- }
-
-
- function doCodeBlocks($text) {
- #
- # Process Markdown `<pre><code>` blocks.
- #
- $text = preg_replace_callback('{
- (?:\n\n|\A\n?)
- ( # $1 = the code block -- one or more lines, starting with a space/tab
- (?>
- [ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
- .*\n+
- )+
- )
- ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
- }xm',
- array(&$this, '_doCodeBlocks_callback'), $text);
-
- return $text;
- }
- function _doCodeBlocks_callback($matches) {
- $codeblock = $matches[1];
-
- $codeblock = $this->outdent($codeblock);
- $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
-
- # trim leading newlines and trailing newlines
- $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
-
- $codeblock = "<pre><code>$codeblock\n</code></pre>";
- return "\n\n".$this->hashBlock($codeblock)."\n\n";
- }
-
-
- function makeCodeSpan($code) {
- #
- # Create a code span markup for $code. Called from handleSpanToken.
- #
- $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
- return $this->hashPart("<code>$code</code>");
- }
-
-
- public $em_relist = array(
- '' => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)',
- '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
- '_' => '(?<=\S|^)(?<!_)_(?!_)',
- );
- public $strong_relist = array(
- '' => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)',
- '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
- '__' => '(?<=\S|^)(?<!_)__(?!_)',
- );
- public $em_strong_relist = array(
- '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)',
- '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
- '___' => '(?<=\S|^)(?<!_)___(?!_)',
- );
- public $em_strong_prepared_relist;
-
- function prepareItalicsAndBold() {
- #
- # Prepare regular expressions for searching emphasis tokens in any
- # context.
- #
- foreach ($this->em_relist as $em => $em_re) {
- foreach ($this->strong_relist as $strong => $strong_re) {
- # Construct list of allowed token expressions.
- $token_relist = array();
- if (isset($this->em_strong_relist["$em$strong"])) {
- $token_relist[] = $this->em_strong_relist["$em$strong"];
- }
- $token_relist[] = $em_re;
- $token_relist[] = $strong_re;
-
- # Construct master expression from list.
- $token_re = '{('. implode('|', $token_relist) .')}';
- $this->em_strong_prepared_relist["$em$strong"] = $token_re;
- }
- }
- }
-
- function doItalicsAndBold($text) {
- $token_stack = array('');
- $text_stack = array('');
- $em = '';
- $strong = '';
- $tree_char_em = false;
-
- while (1) {
- #
- # Get prepared regular expression for seraching emphasis tokens
- # in current context.
- #
- $token_re = $this->em_strong_prepared_relist["$em$strong"];
-
- #
- # Each loop iteration search for the next emphasis token.
- # Each token is then passed to handleSpanToken.
- #
- $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
- $text_stack[0] .= $parts[0];
- $token =& $parts[1];
- $text =& $parts[2];
-
- if (empty($token)) {
- # Reached end of text span: empty stack without emitting.
- # any more emphasis.
- while ($token_stack[0]) {
- $text_stack[1] .= array_shift($token_stack);
- $text_stack[0] .= array_shift($text_stack);
- }
- break;
- }
-
- $token_len = strlen($token);
- if ($tree_char_em) {
- # Reached closing marker while inside a three-char emphasis.
- if ($token_len == 3) {
- # Three-char closing marker, close em and strong.
- array_shift($token_stack);
- $span = array_shift($text_stack);
- $span = $this->runSpanGamut($span);
- $span = "<strong><em>$span</em></strong>";
- $text_stack[0] .= $this->hashPart($span);
- $em = '';
- $strong = '';
- } else {
- # Other closing marker: close one em or strong and
- # change current token state to match the other
- $token_stack[0] = str_repeat($token{0}, 3-$token_len);
- $tag = $token_len == 2 ? "strong" : "em";
- $span = $text_stack[0];
- $span = $this->runSpanGamut($span);
- $span = "<$tag>$span</$tag>";
- $text_stack[0] = $this->hashPart($span);
- $$tag = ''; # $$tag stands for $em or $strong
- }
- $tree_char_em = false;
- } else if ($token_len == 3) {
- if ($em) {
- # Reached closing marker for both em and strong.
- # Closing strong marker:
- for ($i = 0; $i < 2; ++$i) {
- $shifted_token = array_shift($token_stack);
- $tag = strlen($shifted_token) == 2 ? "strong" : "em";
- $span = array_shift($text_stack);
- $span = $this->runSpanGamut($span);
- $span = "<$tag>$span</$tag>";
- $text_stack[0] .= $this->hashPart($span);
- $$tag = ''; # $$tag stands for $em or $strong
- }
- } else {
- # Reached opening three-char emphasis marker. Push on token
- # stack; will be handled by the special condition above.
- $em = $token{0};
- $strong = "$em$em";
- array_unshift($token_stack, $token);
- array_unshift($text_stack, '');
- $tree_char_em = true;
- }
- } else if ($token_len == 2) {
- if ($strong) {
- # Unwind any dangling emphasis marker:
- if (strlen($token_stack[0]) == 1) {
- $text_stack[1] .= array_shift($token_stack);
- $text_stack[0] .= array_shift($text_stack);
- }
- # Closing strong marker:
- array_shift($token_stack);
- $span = array_shift($text_stack);
- $span = $this->runSpanGamut($span);
- $span = "<strong>$span</strong>";
- $text_stack[0] .= $this->hashPart($span);
- $strong = '';
- } else {
- array_unshift($token_stack, $token);
- array_unshift($text_stack, '');
- $strong = $token;
- }
- } else {
- # Here $token_len == 1
- if ($em) {
- if (strlen($token_stack[0]) == 1) {
- # Closing emphasis marker:
- array_shift($token_stack);
- $span = array_shift($text_stack);
- $span = $this->runSpanGamut($span);
- $span = "<em>$span</em>";
- $text_stack[0] .= $this->hashPart($span);
- $em = '';
- } else {
- $text_stack[0] .= $token;
- }
- } else {
- array_unshift($token_stack, $token);
- array_unshift($text_stack, '');
- $em = $token;
- }
- }
- }
- return $text_stack[0];
- }
-
-
- function doBlockQuotes($text) {
- $text = preg_replace_callback('/
- ( # Wrap whole match in $1
- (?>
- ^[ ]*>[ ]? # ">" at the start of a line
- .+\n # rest of the first line
- (.+\n)* # subsequent consecutive lines
- \n* # blanks
- )+
- )
- /xm',
- array(&$this, '_doBlockQuotes_callback'), $text);
-
- return $text;
- }
- function _doBlockQuotes_callback($matches) {
- $bq = $matches[1];
- # trim one level of quoting - trim whitespace-only lines
- $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
- $bq = $this->runBlockGamut($bq); # recurse
-
- $bq = preg_replace('/^/m', " ", $bq);
- # These leading spaces cause problem with <pre> content,
- # so we need to fix that:
- $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',
- array(&$this, '_doBlockQuotes_callback2'), $bq);
-
- return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
- }
- function _doBlockQuotes_callback2($matches) {
- $pre = $matches[1];
- $pre = preg_replace('/^ /m', '', $pre);
- return $pre;
- }
-
-
- function formParagraphs($text) {
- #
- # Params:
- # $text - string to process with html <p> tags
- #
- # Strip leading and trailing lines:
- $text = preg_replace('/\A\n+|\n+\z/', '', $text);
-
- $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
-
- #
- # Wrap <p> tags and unhashify HTML blocks
- #
- foreach ($grafs as $key => $value) {
- if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
- # Is a paragraph.
- $value = $this->runSpanGamut($value);
- $value = preg_replace('/^([ ]*)/', "<p>", $value);
- $value .= "</p>";
- $grafs[$key] = $this->unhash($value);
- }
- else {
- # Is a block.
- # Modify elements of @grafs in-place...
- $graf = $value;
- $block = $this->html_hashes[$graf];
- $graf = $block;
-// if (preg_match('{
-// \A
-// ( # $1 = <div> tag
-// <div \s+
-// [^>]*
-// \b
-// markdown\s*=\s* ([\'"]) # $2 = attr quote char
-// 1
-// \2
-// [^>]*
-// >
-// )
-// ( # $3 = contents
-// .*
-// )
-// (</div>) # $4 = closing tag
-// \z
-// }xs', $block, $matches))
-// {
-// list(, $div_open, , $div_content, $div_close) = $matches;
-//
-// # We can't call Markdown(), because that resets the hash;
-// # that initialization code should be pulled into its own sub, though.
-// $div_content = $this->hashHTMLBlocks($div_content);
-//
-// # Run document gamut methods on the content.
-// foreach ($this->document_gamut as $method => $priority) {
-// $div_content = $this->$method($div_content);
-// }
-//
-// $div_open = preg_replace(
-// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
-//
-// $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
-// }
- $grafs[$key] = $graf;
- }
- }
-
- return implode("\n\n", $grafs);
- }
-
-
- function encodeAttribute($text) {
- #
- # Encode text for a double-quoted HTML attribute. This function
- # is *not* suitable for attributes enclosed in single quotes.
- #
- $text = $this->encodeAmpsAndAngles($text);
- $text = str_replace('"', '&quot;', $text);
- return $text;
- }
-
-
- function encodeAmpsAndAngles($text) {
- #
- # Smart processing for ampersands and angle brackets that need to
- # be encoded. Valid character entities are left alone unless the
- # no-entities mode is set.
- #
- if ($this->no_entities) {
- $text = str_replace('&', '&amp;', $text);
- } else {
- # Ampersand-encoding based entirely on Nat Irons's Amputator
- # MT plugin: <http://bumppo.net/projects/amputator/>
- $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
- '&amp;', $text);;
- }
- # Encode remaining <'s
- $text = str_replace('<', '&lt;', $text);
-
- return $text;
- }
-
-
- function doAutoLinks($text) {
- $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',
- array(&$this, '_doAutoLinks_url_callback'), $text);
-
- # Email addresses: <address@domain.foo>
- $text = preg_replace_callback('{
- <
- (?:mailto:)?
- (
- (?:
- [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
- |
- ".*?"
- )
- \@
- (?:
- [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
- |
- \[[\d.a-fA-F:]+\] # IPv4 & IPv6
- )
- )
- >
- }xi',
- array(&$this, '_doAutoLinks_email_callback'), $text);
- $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
-
- return $text;
- }
- function _doAutoLinks_tel_callback($matches) {
- $url = $this->encodeAttribute($matches[1]);
- $tel = $this->encodeAttribute($matches[2]);
- $link = "<a href=\"$url\">$tel</a>";
- return $this->hashPart($link);
- }
- function _doAutoLinks_url_callback($matches) {
- $url = $this->encodeAttribute($matches[1]);
- $link = "<a href=\"$url\">$url</a>";
- return $this->hashPart($link);
- }
- function _doAutoLinks_email_callback($matches) {
- $address = $matches[1];
- $link = $this->encodeEmailAddress($address);
- return $this->hashPart($link);
- }
-
-
- function encodeEmailAddress($addr) {
- #
- # Input: an email address, e.g. "foo@example.com"
- #
- # Output: the email address as a mailto link, with each character
- # of the address encoded as either a decimal or hex entity, in
- # the hopes of foiling most address harvesting spam bots. E.g.:
- #
- # <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
- # &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
- # &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
- # &#101;&#46;&#x63;&#111;&#x6d;</a></p>
- #
- # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
- # With some optimizations by Milian Wolff.
- #
- $addr = "mailto:" . $addr;
- $chars = preg_split('/(?<!^)(?!$)/', $addr);
- $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
-
- foreach ($chars as $key => $char) {
- $ord = ord($char);
- # Ignore non-ascii chars.
- if ($ord < 128) {
- $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
- # roughly 10% raw, 45% hex, 45% dec
- # '@' *must* be encoded. I insist.
- if ($r > 90 && $char != '@') /* do nothing */;
- else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
- else $chars[$key] = '&#'.$ord.';';
- }
- }
-
- $addr = implode('', $chars);
- $text = implode('', array_slice($chars, 7)); # text without `mailto:`
- $addr = "<a href=\"$addr\">$text</a>";
-
- return $addr;
- }
-
-
- function parseSpan($str) {
- #
- # Take the string $str and parse it into tokens, hashing embedded HTML,
- # escaped characters and handling code spans.
- #
- $output = '';
-
- $span_re = '{
- (
- \\\\'.$this->escape_chars_re.'
- |
- (?<![`\\\\])
- `+ # code span marker
- '.( $this->no_markup ? '' : '
- |
- <!-- .*? --> # comment
- |
- <\?.*?\?> | <%.*?%> # processing instruction
- |
- <[!$]?[-a-zA-Z0-9:_]+ # regular tags
- (?>
- \s
- (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
- )?
- >
- |
- <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag
- |
- </[-a-zA-Z0-9:_]+\s*> # closing tag
- ').'
- )
- }xs';
-
- while (1) {
- #
- # Each loop iteration search for either the next tag, the next
- # openning code span marker, or the next escaped character.
- # Each token is then passed to handleSpanToken.
- #
- $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
-
- # Create token from text preceding tag.
- if ($parts[0] != "") {
- $output .= $parts[0];
- }
-
- # Check if we reach the end.
- if (isset($parts[1])) {
- $output .= $this->handleSpanToken($parts[1], $parts[2]);
- $str = $parts[2];
- }
- else {
- break;
- }
- }
-
- return $output;
- }
-
-
- function handleSpanToken($token, &$str) {
- #
- # Handle $token provided by parseSpan by determining its nature and
- # returning the corresponding value that should replace it.
- #
- switch ($token{0}) {
- case "\\":
- return $this->hashPart("&#". ord($token{1}). ";");
- case "`":
- # Search for end marker in remaining text.
- if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
- $str, $matches))
- {
- $str = $matches[2];
- $codespan = $this->makeCodeSpan($matches[1]);
- return $this->hashPart($codespan);
- }
- return $token; // return as text since no ending marker found.
- default:
- return $this->hashPart($token);
- }
- }
-
-
- function outdent($text) {
- #
- # Remove one level of line-leading tabs or spaces
- #
- return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
- }
-
-
- # String length function for detab. `_initDetab` will create a function to
- # hanlde UTF-8 if the default function does not exist.
- public $utf8_strlen = 'mb_strlen';
-
- function detab($text) {
- #
- # Replace tabs with the appropriate amount of space.
- #
- # For each line we separate the line in blocks delemited by
- # tab characters. Then we reconstruct every line by adding the
- # appropriate number of space between each blocks.
-
- $text = preg_replace_callback('/^.*\t.*$/m',
- array(&$this, '_detab_callback'), $text);
-
- return $text;
- }
- function _detab_callback($matches) {
- $line = $matches[0];
- $strlen = $this->utf8_strlen; # strlen function for UTF-8.
-
- # Split in blocks.
- $blocks = explode("\t", $line);
- # Add each blocks to the line.
- $line = $blocks[0];
- unset($blocks[0]); # Do not add first block twice.
- foreach ($blocks as $block) {
- # Calculate amount of space, insert spaces, insert block.
- $amount = $this->tab_width -
- $strlen($line, 'UTF-8') % $this->tab_width;
- $line .= str_repeat(" ", $amount) . $block;
- }
- return $line;
- }
- function _initDetab() {
- #
- # Check for the availability of the function in the `utf8_strlen` property
- # (initially `mb_strlen`). If the function is not available, use jetpack_utf8_strlen
- # that will loosely count the number of UTF-8 characters with a
- # regular expression.
- #
- if ( function_exists( $this->utf8_strlen ) ) {
- return;
- }
- $this->utf8_strlen = 'jetpack_utf8_strlen';
- }
-
-
- function unhash($text) {
- #
- # Swap back in all the tags hashed by _HashHTMLBlocks.
- #
- return preg_replace_callback('/(.)\x1A[0-9]+\1/',
- array(&$this, '_unhash_callback'), $text);
- }
- function _unhash_callback($matches) {
- return $this->html_hashes[$matches[0]];
- }
-
-}
-
-
-#
-# Markdown Extra Parser Class
-#
-
-class MarkdownExtra_Parser extends Markdown_Parser {
-
- ### Configuration Variables ###
-
- # Prefix for footnote ids.
- public $fn_id_prefix = "";
-
- # Optional title attribute for footnote links and backlinks.
- public $fn_link_title = MARKDOWN_FN_LINK_TITLE;
- public $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE;
-
- # Optional class attribute for footnote links and backlinks.
- public $fn_link_class = MARKDOWN_FN_LINK_CLASS;
- public $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
-
- # Optional class prefix for fenced code block.
- public $code_class_prefix = MARKDOWN_CODE_CLASS_PREFIX;
- # Class attribute for code blocks goes on the `code` tag;
- # setting this to true will put attributes on the `pre` tag instead.
- public $code_attr_on_pre = MARKDOWN_CODE_ATTR_ON_PRE;
-
- # Predefined abbreviations.
- public $predef_abbr = array();
-
-
- ### Parser Implementation ###
-
- function __construct() {
- #
- # Constructor function. Initialize the parser object.
- #
- # Add extra escapable characters before parent constructor
- # initialize the table.
- $this->escape_chars .= ':|';
-
- # Insert extra document, block, and span transformations.
- # Parent constructor will do the sorting.
- $this->document_gamut += array(
- "doFencedCodeBlocks" => 5,
- "stripFootnotes" => 15,
- "stripAbbreviations" => 25,
- "appendFootnotes" => 50,
- );
- $this->block_gamut += array(
- "doFencedCodeBlocks" => 5,
- "doTables" => 15,
- "doDefLists" => 45,
- );
- $this->span_gamut += array(
- "doFootnotes" => 5,
- "doAbbreviations" => 70,
- );
-
- parent::__construct();
- }
-
-
- # Extra variables used during extra transformations.
- public $footnotes = array();
- public $footnotes_ordered = array();
- public $footnotes_ref_count = array();
- public $footnotes_numbers = array();
- public $abbr_desciptions = array();
- public $abbr_word_re = '';
-
- # Give the current footnote number.
- public $footnote_counter = 1;
-
-
- function setup() {
- #
- # Setting up Extra-specific variables.
- #
- parent::setup();
-
- $this->footnotes = array();
- $this->footnotes_ordered = array();
- $this->footnotes_ref_count = array();
- $this->footnotes_numbers = array();
- $this->abbr_desciptions = array();
- $this->abbr_word_re = '';
- $this->footnote_counter = 1;
-
- foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
- if ($this->abbr_word_re)
- $this->abbr_word_re .= '|';
- $this->abbr_word_re .= preg_quote($abbr_word);
- $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
- }
- }
-
- function teardown() {
- #
- # Clearing Extra-specific variables.
- #
- $this->footnotes = array();
- $this->footnotes_ordered = array();
- $this->footnotes_ref_count = array();
- $this->footnotes_numbers = array();
- $this->abbr_desciptions = array();
- $this->abbr_word_re = '';
-
- parent::teardown();
- }
-
-
- ### Extra Attribute Parser ###
-
- # Expression to use to catch attributes (includes the braces)
- public $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
- # Expression to use when parsing in a context when no capture is desired
- public $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
-
- function doExtraAttributes($tag_name, $attr) {
- #
- # Parse attributes caught by the $this->id_class_attr_catch_re expression
- # and return the HTML-formatted list of attributes.
- #
- # Currently supported attributes are .class and #id.
- #
- if (empty($attr)) return "";
-
- # Split on components
- preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
- $elements = $matches[0];
-
- # handle classes and ids (only first id taken into account)
- $classes = array();
- $id = false;
- foreach ($elements as $element) {
- if ($element{0} == '.') {
- $classes[] = substr($element, 1);
- } else if ($element{0} == '#') {
- if ($id === false) $id = substr($element, 1);
- }
- }
-
- # compose attributes as string
- $attr_str = "";
- if (!empty($id)) {
- $attr_str .= ' id="'.$id.'"';
- }
- if (!empty($classes)) {
- $attr_str .= ' class="'.implode(" ", $classes).'"';
- }
- return $attr_str;
- }
-
-
- function stripLinkDefinitions($text) {
- #
- # Strips link definitions from text, stores the URLs and titles in
- # hash references.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Link defs are in the form: ^[id]: url "optional title"
- $text = preg_replace_callback('{
- ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
- [ ]*
- \n? # maybe *one* newline
- [ ]*
- (?:
- <(.+?)> # url = $2
- |
- (\S+?) # url = $3
- )
- [ ]*
- \n? # maybe one newline
- [ ]*
- (?:
- (?<=\s) # lookbehind for whitespace
- ["(]
- (.*?) # title = $4
- [")]
- [ ]*
- )? # title is optional
- (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr
- (?:\n+|\Z)
- }xm',
- array(&$this, '_stripLinkDefinitions_callback'),
- $text);
- return $text;
- }
- function _stripLinkDefinitions_callback($matches) {
- $link_id = strtolower($matches[1]);
- $url = $matches[2] == '' ? $matches[3] : $matches[2];
- $this->urls[$link_id] = $url;
- $this->titles[$link_id] =& $matches[4];
- $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
- return ''; # String that will replace the block
- }
-
-
- ### HTML Block Parser ###
-
- # Tags that are always treated as block tags:
- public $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
-
- # Tags treated as block tags only if the opening tag is alone on its line:
- public $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
-
- # Tags where markdown="1" default to span mode:
- public $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
-
- # Tags which must not have their contents modified, no matter where
- # they appear:
- public $clean_tags_re = 'script|math|svg';
-
- # Tags that do not need to be closed.
- public $auto_close_tags_re = 'hr|img|param|source|track';
-
-
- function hashHTMLBlocks($text) {
- #
- # Hashify HTML Blocks and "clean tags".
- #
- # We only want to do this for block-level HTML tags, such as headers,
- # lists, and tables. That's because we still want to wrap <p>s around
- # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
- # phrase emphasis, and spans. The list of tags we're looking for is
- # hard-coded.
- #
- # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
- # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
- # attribute is found within a tag, _HashHTMLBlocks_InHTML calls back
- # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
- # These two functions are calling each other. It's recursive!
- #
- if ($this->no_markup) return $text;
-
- #
- # Call the HTML-in-Markdown hasher.
- #
- list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
-
- return $text;
- }
- function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
- $enclosing_tag_re = '', $span = false)
- {
- #
- # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
- #
- # * $indent is the number of space to be ignored when checking for code
- # blocks. This is important because if we don't take the indent into
- # account, something like this (which looks right) won't work as expected:
- #
- # <div>
- # <div markdown="1">
- # Hello World. <-- Is this a Markdown code block or text?
- # </div> <-- Is this a Markdown code block or a real tag?
- # <div>
- #
- # If you don't like this, just don't indent the tag on which
- # you apply the markdown="1" attribute.
- #
- # * If $enclosing_tag_re is not empty, stops at the first unmatched closing
- # tag with that name. Nested tags supported.
- #
- # * If $span is true, text inside must treated as span. So any double
- # newline will be replaced by a single newline so that it does not create
- # paragraphs.
- #
- # Returns an array of that form: ( processed text , remaining text )
- #
- if ($text === '') return array('', '');
-
- # Regex to check for the presence of newlines around a block tag.
- $newline_before_re = '/(?:^\n?|\n\n)*$/';
- $newline_after_re =
- '{
- ^ # Start of text following the tag.
- (?>[ ]*<!--.*?-->)? # Optional comment.
- [ ]*\n # Must be followed by newline.
- }xs';
-
- # Regex to match any tag.
- $block_tag_re =
- '{
- ( # $2: Capture whole tag.
- </? # Any opening or closing tag.
- (?> # Tag name.
- '.$this->block_tags_re.' |
- '.$this->context_block_tags_re.' |
- '.$this->clean_tags_re.' |
- (?!\s)'.$enclosing_tag_re.'
- )
- (?:
- (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
- (?>
- ".*?" | # Double quotes (can contain `>`)
- \'.*?\' | # Single quotes (can contain `>`)
- .+? # Anything but quotes and `>`.
- )*?
- )?
- > # End of tag.
- |
- <!-- .*? --> # HTML Comment
- |
- <\?.*?\?> | <%.*?%> # Processing instruction
- |
- <!\[CDATA\[.*?\]\]> # CData Block
- '. ( !$span ? ' # If not in span.
- |
- # Indented code block
- (?: ^[ ]*\n | ^ | \n[ ]*\n )
- [ ]{'.($indent+4).'}[^\n]* \n
- (?>
- (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
- )*
- |
- # Fenced code block marker
- (?<= ^ | \n )
- [ ]{0,'.($indent+3).'}(?:~{3,}|`{3,})
- [ ]*
- (?:
- \.?[-_:a-zA-Z0-9]+ # standalone class name
- |
- '.$this->id_class_attr_nocatch_re.' # extra attributes
- )?
- [ ]*
- (?= \n )
- ' : '' ). ' # End (if not is span).
- |
- # Code span marker
- # Note, this regex needs to go after backtick fenced
- # code blocks but it should also be kept outside of the
- # "if not in span" condition adding backticks to the parser
- `+
- )
- }xs';
-
-
- $depth = 0; # Current depth inside the tag tree.
- $parsed = ""; # Parsed text that will be returned.
-
- #
- # Loop through every tag until we find the closing tag of the parent
- # or loop until reaching the end of text if no parent tag specified.
- #
- do {
- #
- # Split the text using the first $tag_match pattern found.
- # Text before pattern will be first in the array, text after
- # pattern will be at the end, and between will be any catches made
- # by the pattern.
- #
- $parts = preg_split($block_tag_re, $text, 2,
- PREG_SPLIT_DELIM_CAPTURE);
-
- # If in Markdown span mode, add a empty-string span-level hash
- # after each newline to prevent triggering any block element.
- if ($span) {
- $void = $this->hashPart("", ':');
- $newline = "$void\n";
- $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
- }
-
- $parsed .= $parts[0]; # Text before current tag.
-
- # If end of $text has been reached. Stop loop.
- if (count($parts) < 3) {
- $text = "";
- break;
- }
-
- $tag = $parts[1]; # Tag to handle.
- $text = $parts[2]; # Remaining text after current tag.
- $tag_re = preg_quote($tag); # For use in a regular expression.
-
- #
- # Check for: Fenced code block marker.
- # Note: need to recheck the whole tag to disambiguate backtick
- # fences from code spans
- #
- if (preg_match('{^\n?([ ]{0,'.($indent+3).'})(~{3,}|`{3,})[ ]*(?:\.?[-_:a-zA-Z0-9]+|'.$this->id_class_attr_nocatch_re.')?[ ]*\n?$}', $tag, $capture)) {
- # Fenced code block marker: find matching end marker.
- $fence_indent = strlen($capture[1]); # use captured indent in re
- $fence_re = $capture[2]; # use captured fence in re
- if (preg_match('{^(?>.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text,
- $matches))
- {
- # End marker found: pass text unchanged until marker.
- $parsed .= $tag . $matches[0];
- $text = substr($text, strlen($matches[0]));
- }
- else {
- # No end marker: just skip it.
- $parsed .= $tag;
- }
- }
- #
- # Check for: Indented code block.
- #
- else if ($tag{0} == "\n" || $tag{0} == " ") {
- # Indented code block: pass it unchanged, will be handled
- # later.
- $parsed .= $tag;
- }
- #
- # Check for: Code span marker
- # Note: need to check this after backtick fenced code blocks
- #
- else if ($tag{0} == "`") {
- # Find corresponding end marker.
- $tag_re = preg_quote($tag);
- if (preg_match('{^(?>.+?|\n(?!\n))*?(?<!`)'.$tag_re.'(?!`)}',
- $text, $matches))
- {
- # End marker found: pass text unchanged until marker.
- $parsed .= $tag . $matches[0];
- $text = substr($text, strlen($matches[0]));
- }
- else {
- # Unmatched marker: just skip it.
- $parsed .= $tag;
- }
- }
- #
- # Check for: Opening Block level tag or
- # Opening Context Block tag (like ins and del)
- # used as a block tag (tag is alone on it's line).
- #
- else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) ||
- ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) &&
- preg_match($newline_before_re, $parsed) &&
- preg_match($newline_after_re, $text) )
- )
- {
- # Need to parse tag and following text using the HTML parser.
- list($block_text, $text) =
- $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
-
- # Make sure it stays outside of any paragraph by adding newlines.
- $parsed .= "\n\n$block_text\n\n";
- }
- #
- # Check for: Clean tag (like script, math)
- # HTML Comments, processing instructions.
- #
- else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) ||
- $tag{1} == '!' || $tag{1} == '?')
- {
- # Need to parse tag and following text using the HTML parser.
- # (don't check for markdown attribute)
- list($block_text, $text) =
- $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
-
- $parsed .= $block_text;
- }
- #
- # Check for: Tag with same name as enclosing tag.
- #
- else if ($enclosing_tag_re !== '' &&
- # Same name as enclosing tag.
- preg_match('{^</?(?:'.$enclosing_tag_re.')\b}', $tag))
- {
- #
- # Increase/decrease nested tag count.
- #
- if ($tag{1} == '/') $depth--;
- else if ($tag{strlen($tag)-2} != '/') $depth++;
-
- if ($depth < 0) {
- #
- # Going out of parent element. Clean up and break so we
- # return to the calling function.
- #
- $text = $tag . $text;
- break;
- }
-
- $parsed .= $tag;
- }
- else {
- $parsed .= $tag;
- }
- } while ($depth >= 0);
-
- return array($parsed, $text);
- }
- function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) {
- #
- # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
- #
- # * Calls $hash_method to convert any blocks.
- # * Stops when the first opening tag closes.
- # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
- # (it is not inside clean tags)
- #
- # Returns an array of that form: ( processed text , remaining text )
- #
- if ($text === '') return array('', '');
-
- # Regex to match `markdown` attribute inside of a tag.
- $markdown_attr_re = '
- {
- \s* # Eat whitespace before the `markdown` attribute
- markdown
- \s*=\s*
- (?>
- (["\']) # $1: quote delimiter
- (.*?) # $2: attribute value
- \1 # matching delimiter
- |
- ([^\s>]*) # $3: unquoted attribute value
- )
- () # $4: make $3 always defined (avoid warnings)
- }xs';
-
- # Regex to match any tag.
- $tag_re = '{
- ( # $2: Capture whole tag.
- </? # Any opening or closing tag.
- [\w:$]+ # Tag name.
- (?:
- (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
- (?>
- ".*?" | # Double quotes (can contain `>`)
- \'.*?\' | # Single quotes (can contain `>`)
- .+? # Anything but quotes and `>`.
- )*?
- )?
- > # End of tag.
- |
- <!-- .*? --> # HTML Comment
- |
- <\?.*?\?> | <%.*?%> # Processing instruction
- |
- <!\[CDATA\[.*?\]\]> # CData Block
- )
- }xs';
-
- $original_text = $text; # Save original text in case of faliure.
-
- $depth = 0; # Current depth inside the tag tree.
- $block_text = ""; # Temporary text holder for current text.
- $parsed = ""; # Parsed text that will be returned.
-
- #
- # Get the name of the starting tag.
- # (This pattern makes $base_tag_name_re safe without quoting.)
- #
- if (preg_match('/^<([\w:$]*)\b/', $text, $matches))
- $base_tag_name_re = $matches[1];
-
- #
- # Loop through every tag until we find the corresponding closing tag.
- #
- do {
- #
- # Split the text using the first $tag_match pattern found.
- # Text before pattern will be first in the array, text after
- # pattern will be at the end, and between will be any catches made
- # by the pattern.
- #
- $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
-
- if (count($parts) < 3) {
- #
- # End of $text reached with unbalenced tag(s).
- # In that case, we return original text unchanged and pass the
- # first character as filtered to prevent an infinite loop in the
- # parent function.
- #
- return array($original_text{0}, substr($original_text, 1));
- }
-
- $block_text .= $parts[0]; # Text before current tag.
- $tag = $parts[1]; # Tag to handle.
- $text = $parts[2]; # Remaining text after current tag.
-
- #
- # Check for: Auto-close tag (like <hr/>)
- # Comments and Processing Instructions.
- #
- if (preg_match('{^</?(?:'.$this->auto_close_tags_re.')\b}', $tag) ||
- $tag{1} == '!' || $tag{1} == '?')
- {
- # Just add the tag to the block as if it was text.
- $block_text .= $tag;
- }
- else {
- #
- # Increase/decrease nested tag count. Only do so if
- # the tag's name match base tag's.
- #
- if (preg_match('{^</?'.$base_tag_name_re.'\b}', $tag)) {
- if ($tag{1} == '/') $depth--;
- else if ($tag{strlen($tag)-2} != '/') $depth++;
- }
-
- #
- # Check for `markdown="1"` attribute and handle it.
- #
- if ($md_attr &&
- preg_match($markdown_attr_re, $tag, $attr_m) &&
- preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
- {
- # Remove `markdown` attribute from opening tag.
- $tag = preg_replace($markdown_attr_re, '', $tag);
-
- # Check if text inside this tag must be parsed in span mode.
- $this->mode = $attr_m[2] . $attr_m[3];
- $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
- preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag);
-
- # Calculate indent before tag.
- if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
- $strlen = $this->utf8_strlen;
- $indent = $strlen($matches[1], 'UTF-8');
- } else {
- $indent = 0;
- }
-
- # End preceding block with this tag.
- $block_text .= $tag;
- $parsed .= $this->$hash_method($block_text);
-
- # Get enclosing tag name for the ParseMarkdown function.
- # (This pattern makes $tag_name_re safe without quoting.)
- preg_match('/^<([\w:$]*)\b/', $tag, $matches);
- $tag_name_re = $matches[1];
-
- # Parse the content using the HTML-in-Markdown parser.
- list ($block_text, $text)
- = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
- $tag_name_re, $span_mode);
-
- # Outdent markdown text.
- if ($indent > 0) {
- $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
- $block_text);
- }
-
- # Append tag content to parsed text.
- if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
- else $parsed .= "$block_text";
-
- # Start over with a new block.
- $block_text = "";
- }
- else $block_text .= $tag;
- }
-
- } while ($depth > 0);
-
- #
- # Hash last block text that wasn't processed inside the loop.
- #
- $parsed .= $this->$hash_method($block_text);
-
- return array($parsed, $text);
- }
-
-
- function hashClean($text) {
- #
- # Called whenever a tag must be hashed when a function inserts a "clean" tag
- # in $text, it passes through this function and is automaticaly escaped,
- # blocking invalid nested overlap.
- #
- return $this->hashPart($text, 'C');
- }
-
-
- function doAnchors($text) {
- #
- # Turn Markdown link shortcuts into XHTML <a> tags.
- #
- if ($this->in_anchor) return $text;
- $this->in_anchor = true;
-
- #
- # First, handle reference-style links: [link text] [id]
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ('.$this->nested_brackets_re.') # link text = $2
- \]
-
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
-
- \[
- (.*?) # id = $3
- \]
- )
- }xs',
- array(&$this, '_doAnchors_reference_callback'), $text);
-
- #
- # Next, inline-style links: [link text](url "optional title")
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ('.$this->nested_brackets_re.') # link text = $2
- \]
- \( # literal paren
- [ \n]*
- (?:
- <(.+?)> # href = $3
- |
- ('.$this->nested_url_parenthesis_re.') # href = $4
- )
- [ \n]*
- ( # $5
- ([\'"]) # quote char = $6
- (.*?) # Title = $7
- \6 # matching quote
- [ \n]* # ignore any spaces/tabs between closing quote and )
- )? # title is optional
- \)
- (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
- )
- }xs',
- array(&$this, '_doAnchors_inline_callback'), $text);
-
- #
- # Last, handle reference-style shortcuts: [link text]
- # These must come last in case you've also got [link text][1]
- # or [link text](/foo)
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- \[
- ([^\[\]]+) # link text = $2; can\'t contain [ or ]
- \]
- )
- }xs',
- array(&$this, '_doAnchors_reference_callback'), $text);
-
- $this->in_anchor = false;
- return $text;
- }
- function _doAnchors_reference_callback($matches) {
- $whole_match = $matches[1];
- $link_text = $matches[2];
- $link_id =& $matches[3];
-
- if ($link_id == "") {
- # for shortcut links like [this][] or [this].
- $link_id = $link_text;
- }
-
- # lower-case and turn embedded newlines into spaces
- $link_id = strtolower($link_id);
- $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
-
- if (isset($this->urls[$link_id])) {
- $url = $this->urls[$link_id];
- $url = $this->encodeAttribute($url);
-
- $result = "<a href=\"$url\"";
- if ( isset( $this->titles[$link_id] ) ) {
- $title = $this->titles[$link_id];
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
- if (isset($this->ref_attr[$link_id]))
- $result .= $this->ref_attr[$link_id];
-
- $link_text = $this->runSpanGamut($link_text);
- $result .= ">$link_text</a>";
- $result = $this->hashPart($result);
- }
- else {
- $result = $whole_match;
- }
- return $result;
- }
- function _doAnchors_inline_callback($matches) {
- $whole_match = $matches[1];
- $link_text = $this->runSpanGamut($matches[2]);
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
- $title =& $matches[7];
- $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]);
-
-
- $url = $this->encodeAttribute($url);
-
- $result = "<a href=\"$url\"";
- if (isset($title)) {
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
- $result .= $attr;
-
- $link_text = $this->runSpanGamut($link_text);
- $result .= ">$link_text</a>";
-
- return $this->hashPart($result);
- }
-
-
- function doImages($text) {
- #
- # Turn Markdown image shortcuts into <img> tags.
- #
- #
- # First, handle reference-style labeled images: ![alt text][id]
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- !\[
- ('.$this->nested_brackets_re.') # alt text = $2
- \]
-
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
-
- \[
- (.*?) # id = $3
- \]
-
- )
- }xs',
- array(&$this, '_doImages_reference_callback'), $text);
-
- #
- # Next, handle inline images: ![alt text](url "optional title")
- # Don't forget: encode * and _
- #
- $text = preg_replace_callback('{
- ( # wrap whole match in $1
- !\[
- ('.$this->nested_brackets_re.') # alt text = $2
- \]
- \s? # One optional whitespace character
- \( # literal paren
- [ \n]*
- (?:
- <(\S*)> # src url = $3
- |
- ('.$this->nested_url_parenthesis_re.') # src url = $4
- )
- [ \n]*
- ( # $5
- ([\'"]) # quote char = $6
- (.*?) # title = $7
- \6 # matching quote
- [ \n]*
- )? # title is optional
- \)
- (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
- )
- }xs',
- array(&$this, '_doImages_inline_callback'), $text);
-
- return $text;
- }
- function _doImages_reference_callback($matches) {
- $whole_match = $matches[1];
- $alt_text = $matches[2];
- $link_id = strtolower($matches[3]);
-
- if ($link_id == "") {
- $link_id = strtolower($alt_text); # for shortcut links like ![this][].
- }
-
- $alt_text = $this->encodeAttribute($alt_text);
- if (isset($this->urls[$link_id])) {
- $url = $this->encodeAttribute($this->urls[$link_id]);
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (isset($this->titles[$link_id])) {
- $title = $this->titles[$link_id];
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\"";
- }
- if (isset($this->ref_attr[$link_id]))
- $result .= $this->ref_attr[$link_id];
- $result .= $this->empty_element_suffix;
- $result = $this->hashPart($result);
- }
- else {
- # If there's no such link ID, leave intact:
- $result = $whole_match;
- }
-
- return $result;
- }
- function _doImages_inline_callback($matches) {
- $whole_match = $matches[1];
- $alt_text = $matches[2];
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
- $title =& $matches[7];
- $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]);
-
- $alt_text = $this->encodeAttribute($alt_text);
- $url = $this->encodeAttribute($url);
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (isset($title)) {
- $title = $this->encodeAttribute($title);
- $result .= " title=\"$title\""; # $title already quoted
- }
- $result .= $attr;
- $result .= $this->empty_element_suffix;
-
- return $this->hashPart($result);
- }
-
-
- function doHeaders($text) {
- #
- # Redefined to add id and class attribute support.
- #
- # Setext-style headers:
- # Header 1 {#header1}
- # ========
- #
- # Header 2 {#header2 .class1 .class2}
- # --------
- #
- $text = preg_replace_callback(
- '{
- (^.+?) # $1: Header text
- (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
- [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
- }mx',
- array(&$this, '_doHeaders_callback_setext'), $text);
-
- # atx-style headers:
- # # Header 1 {#header1}
- # ## Header 2 {#header2}
- # ## Header 2 with closing hashes ## {#header3.class1.class2}
- # ...
- # ###### Header 6 {.class2}
- #
- $text = preg_replace_callback('{
- ^(\#{1,6}) # $1 = string of #\'s
- [ ]*
- (.+?) # $2 = Header text
- [ ]*
- \#* # optional closing #\'s (not counted)
- (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
- [ ]*
- \n+
- }xm',
- array(&$this, '_doHeaders_callback_atx'), $text);
-
- return $text;
- }
- function _doHeaders_callback_setext($matches) {
- if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
- return $matches[0];
- $level = $matches[3]{0} == '=' ? 1 : 2;
- $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
- $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
- return "\n" . $this->hashBlock($block) . "\n\n";
- }
- function _doHeaders_callback_atx($matches) {
- $level = strlen($matches[1]);
- $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
- $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
- return "\n" . $this->hashBlock($block) . "\n\n";
- }
-
-
- function doTables($text) {
- #
- # Form HTML tables.
- #
- $less_than_tab = $this->tab_width - 1;
- #
- # Find tables with leading pipe.
- #
- # | Header 1 | Header 2
- # | -------- | --------
- # | Cell 1 | Cell 2
- # | Cell 3 | Cell 4
- #
- $text = preg_replace_callback('
- {
- ^ # Start of a line
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
- [|] # Optional leading pipe (present)
- (.+) \n # $1: Header row (at least one pipe)
-
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
- [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
-
- ( # $3: Cells
- (?>
- [ ]* # Allowed whitespace.
- [|] .* \n # Row content.
- )*
- )
- (?=\n|\Z) # Stop at final double newline.
- }xm',
- array(&$this, '_doTable_leadingPipe_callback'), $text);
-
- #
- # Find tables without leading pipe.
- #
- # Header 1 | Header 2
- # -------- | --------
- # Cell 1 | Cell 2
- # Cell 3 | Cell 4
- #
- $text = preg_replace_callback('
- {
- ^ # Start of a line
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
- (\S.*[|].*) \n # $1: Header row (at least one pipe)
-
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
- ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
-
- ( # $3: Cells
- (?>
- .* [|] .* \n # Row content
- )*
- )
- (?=\n|\Z) # Stop at final double newline.
- }xm',
- array(&$this, '_DoTable_callback'), $text);
-
- return $text;
- }
- function _doTable_leadingPipe_callback($matches) {
- $head = $matches[1];
- $underline = $matches[2];
- $content = $matches[3];
-
- # Remove leading pipe for each row.
- $content = preg_replace('/^ *[|]/m', '', $content);
-
- return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
- }
- function _doTable_callback($matches) {
- $head = $matches[1];
- $underline = $matches[2];
- $content = $matches[3];
-
- # Remove any tailing pipes for each line.
- $head = preg_replace('/[|] *$/m', '', $head);
- $underline = preg_replace('/[|] *$/m', '', $underline);
- $content = preg_replace('/[|] *$/m', '', $content);
-
- # Reading alignement from header underline.
- $separators = preg_split('/ *[|] */', $underline);
- foreach ($separators as $n => $s) {
- if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"';
- else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
- else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
- else $attr[$n] = '';
- }
-
- # Parsing span elements, including code spans, character escapes,
- # and inline HTML tags, so that pipes inside those gets ignored.
- $head = $this->parseSpan($head);
- $headers = preg_split('/ *[|] */', $head);
- $col_count = count($headers);
- $attr = array_pad($attr, $col_count, '');
-
- # Write column headers.
- $text = "<table>\n";
- $text .= "<thead>\n";
- $text .= "<tr>\n";
- foreach ($headers as $n => $header)
- $text .= " <th$attr[$n]>".$this->runSpanGamut(trim($header))."</th>\n";
- $text .= "</tr>\n";
- $text .= "</thead>\n";
-
- # Split content by row.
- $rows = explode("\n", trim($content, "\n"));
-
- $text .= "<tbody>\n";
- foreach ($rows as $row) {
- # Parsing span elements, including code spans, character escapes,
- # and inline HTML tags, so that pipes inside those gets ignored.
- $row = $this->parseSpan($row);
-
- # Split row by cell.
- $row_cells = preg_split('/ *[|] */', $row, $col_count);
- $row_cells = array_pad($row_cells, $col_count, '');
-
- $text .= "<tr>\n";
- foreach ($row_cells as $n => $cell)
- $text .= " <td$attr[$n]>".$this->runSpanGamut(trim($cell))."</td>\n";
- $text .= "</tr>\n";
- }
- $text .= "</tbody>\n";
- $text .= "</table>";
-
- return $this->hashBlock($text) . "\n";
- }
-
-
- function doDefLists($text) {
- #
- # Form HTML definition lists.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Re-usable pattern to match any entire dl list:
- $whole_list_re = '(?>
- ( # $1 = whole list
- ( # $2
- [ ]{0,'.$less_than_tab.'}
- ((?>.*\S.*\n)+) # $3 = defined term
- \n?
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
- )
- (?s:.+?)
- ( # $4
- \z
- |
- \n{2,}
- (?=\S)
- (?! # Negative lookahead for another term
- [ ]{0,'.$less_than_tab.'}
- (?: \S.*\n )+? # defined term
- \n?
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
- )
- (?! # Negative lookahead for another definition
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
- )
- )
- )
- )'; // mx
-
- $text = preg_replace_callback('{
- (?>\A\n?|(?<=\n\n))
- '.$whole_list_re.'
- }mx',
- array(&$this, '_doDefLists_callback'), $text);
-
- return $text;
- }
- function _doDefLists_callback($matches) {
- # Re-usable patterns to match list item bullets and number markers:
- $list = $matches[1];
-
- # Turn double returns into triple returns, so that we can make a
- # paragraph for the last item in a list, if necessary:
- $result = trim($this->processDefListItems($list));
- $result = "<dl>\n" . $result . "\n</dl>";
- return $this->hashBlock($result) . "\n\n";
- }
-
-
- function processDefListItems($list_str) {
- #
- # Process the contents of a single definition list, splitting it
- # into individual term and definition list items.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # trim trailing blank lines:
- $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
-
- # Process definition terms.
- $list_str = preg_replace_callback('{
- (?>\A\n?|\n\n+) # leading line
- ( # definition terms = $1
- [ ]{0,'.$less_than_tab.'} # leading whitespace
- (?!\:[ ]|[ ]) # negative lookahead for a definition
- # mark (colon) or more whitespace.
- (?> \S.* \n)+? # actual term (not whitespace).
- )
- (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
- # with a definition mark.
- }xm',
- array(&$this, '_processDefListItems_callback_dt'), $list_str);
-
- # Process actual definitions.
- $list_str = preg_replace_callback('{
- \n(\n+)? # leading line = $1
- ( # marker space = $2
- [ ]{0,'.$less_than_tab.'} # whitespace before colon
- \:[ ]+ # definition mark (colon)
- )
- ((?s:.+?)) # definition text = $3
- (?= \n+ # stop at next definition mark,
- (?: # next term or end of text
- [ ]{0,'.$less_than_tab.'} \:[ ] |
- <dt> | \z
- )
- )
- }xm',
- array(&$this, '_processDefListItems_callback_dd'), $list_str);
-
- return $list_str;
- }
- function _processDefListItems_callback_dt($matches) {
- $terms = explode("\n", trim($matches[1]));
- $text = '';
- foreach ($terms as $term) {
- $term = $this->runSpanGamut(trim($term));
- $text .= "\n<dt>" . $term . "</dt>";
- }
- return $text . "\n";
- }
- function _processDefListItems_callback_dd($matches) {
- $leading_line = $matches[1];
- $marker_space = $matches[2];
- $def = $matches[3];
-
- if ($leading_line || preg_match('/\n{2,}/', $def)) {
- # Replace marker with the appropriate whitespace indentation
- $def = str_repeat(' ', strlen($marker_space)) . $def;
- $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
- $def = "\n". $def ."\n";
- }
- else {
- $def = rtrim($def);
- $def = $this->runSpanGamut($this->outdent($def));
- }
-
- return "\n<dd>" . $def . "</dd>\n";
- }
-
-
- function doFencedCodeBlocks($text) {
- #
- # Adding the fenced code block syntax to regular Markdown:
- #
- # ~~~
- # Code block
- # ~~~
- #
- $less_than_tab = $this->tab_width;
-
- $text = preg_replace_callback('{
- (?:\n|\A)
- # 1: Opening marker
- (
- (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
- )
- [ ]*
- (?:
- \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
- |
- '.$this->id_class_attr_catch_re.' # 3: Extra attributes
- )?
- [ ]* \n # Whitespace and newline following marker.
-
- # 4: Content
- (
- (?>
- (?!\1 [ ]* \n) # Not a closing marker.
- .*\n+
- )+
- )
-
- # Closing marker.
- \1 [ ]* (?= \n )
- }xm',
- array(&$this, '_doFencedCodeBlocks_callback'), $text);
-
- return $text;
- }
- function _doFencedCodeBlocks_callback($matches) {
- $classname =& $matches[2];
- $attrs =& $matches[3];
- $codeblock = $matches[4];
- $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
- $codeblock = preg_replace_callback('/^\n+/',
- array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
-
- if ($classname != "") {
- if ($classname{0} == '.')
- $classname = substr($classname, 1);
- $attr_str = ' class="'.$this->code_class_prefix.$classname.'"';
- } else {
- $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
- }
- $pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
- $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
- $codeblock = "<pre$pre_attr_str><code$code_attr_str>$codeblock</code></pre>";
-
- return "\n\n".$this->hashBlock($codeblock)."\n\n";
- }
- function _doFencedCodeBlocks_newlines($matches) {
- return str_repeat("<br$this->empty_element_suffix",
- strlen($matches[0]));
- }
-
-
- #
- # Redefining emphasis markers so that emphasis by underscore does not
- # work in the middle of a word.
- #
- public $em_relist = array(
- '' => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![\.,:;]\s)',
- '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
- '_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
- );
- public $strong_relist = array(
- '' => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![\.,:;]\s)',
- '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
- '__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
- );
- public $em_strong_relist = array(
- '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![\.,:;]\s)',
- '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
- '___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
- );
-
-
- function formParagraphs($text) {
- #
- # Params:
- # $text - string to process with html <p> tags
- #
- # Strip leading and trailing lines:
- $text = preg_replace('/\A\n+|\n+\z/', '', $text);
-
- $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
-
- #
- # Wrap <p> tags and unhashify HTML blocks
- #
- foreach ($grafs as $key => $value) {
- $value = trim($this->runSpanGamut($value));
-
- # Check if this should be enclosed in a paragraph.
- # Clean tag hashes & block tag hashes are left alone.
- $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
-
- if ($is_p) {
- $value = "<p>$value</p>";
- }
- $grafs[$key] = $value;
- }
-
- # Join grafs in one text, then unhash HTML tags.
- $text = implode("\n\n", $grafs);
-
- # Finish by removing any tag hashes still present in $text.
- $text = $this->unhash($text);
-
- return $text;
- }
-
-
- ### Footnotes
-
- function stripFootnotes($text) {
- #
- # Strips link definitions from text, stores the URLs and titles in
- # hash references.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Link defs are in the form: [^id]: url "optional title"
- $text = preg_replace_callback('{
- ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
- [ ]*
- \n? # maybe *one* newline
- ( # text = $2 (no blank lines allowed)
- (?:
- .+ # actual text
- |
- \n # newlines but
- (?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
- (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
- # by non-indented content
- )*
- )
- }xm',
- array(&$this, '_stripFootnotes_callback'),
- $text);
- return $text;
- }
- function _stripFootnotes_callback($matches) {
- $note_id = $this->fn_id_prefix . $matches[1];
- $this->footnotes[$note_id] = $this->outdent($matches[2]);
- return ''; # String that will replace the block
- }
-
-
- function doFootnotes($text) {
- #
- # Replace footnote references in $text [^id] with a special text-token
- # which will be replaced by the actual footnote marker in appendFootnotes.
- #
- if (!$this->in_anchor) {
- $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
- }
- return $text;
- }
-
-
- function appendFootnotes($text) {
- #
- # Append footnote list to text.
- #
- $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
- array(&$this, '_appendFootnotes_callback'), $text);
-
- if (!empty($this->footnotes_ordered)) {
- $text .= "\n\n";
- $text .= "<div class=\"footnotes\">\n";
- $text .= "<hr". $this->empty_element_suffix ."\n";
- $text .= "<ol>\n\n";
-
- $attr = "";
- if ($this->fn_backlink_class != "") {
- $class = $this->fn_backlink_class;
- $class = $this->encodeAttribute($class);
- $attr .= " class=\"$class\"";
- }
- if ($this->fn_backlink_title != "") {
- $title = $this->fn_backlink_title;
- $title = $this->encodeAttribute($title);
- $attr .= " title=\"$title\"";
- }
- $num = 0;
-
- while (!empty($this->footnotes_ordered)) {
- $footnote = reset($this->footnotes_ordered);
- $note_id = key($this->footnotes_ordered);
- unset($this->footnotes_ordered[$note_id]);
- $ref_count = $this->footnotes_ref_count[$note_id];
- unset($this->footnotes_ref_count[$note_id]);
- unset($this->footnotes[$note_id]);
-
- $footnote .= "\n"; # Need to append newline before parsing.
- $footnote = $this->runBlockGamut("$footnote\n");
- $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
- array(&$this, '_appendFootnotes_callback'), $footnote);
-
- $attr = str_replace("%%", ++$num, $attr);
- $note_id = $this->encodeAttribute($note_id);
-
- # Prepare backlink, multiple backlinks if multiple references
- $backlink = "<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";
- for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) {
- $backlink .= " <a href=\"#fnref$ref_num:$note_id\"$attr>&#8617;</a>";
- }
- # Add backlink to last paragraph; create new paragraph if needed.
- if (preg_match('{</p>$}', $footnote)) {
- $footnote = substr($footnote, 0, -4) . "&#160;$backlink</p>";
- } else {
- $footnote .= "\n\n<p>$backlink</p>";
- }
-
- $text .= "<li id=\"fn:$note_id\">\n";
- $text .= $footnote . "\n";
- $text .= "</li>\n\n";
- }
-
- $text .= "</ol>\n";
- $text .= "</div>";
- }
- return $text;
- }
- function _appendFootnotes_callback($matches) {
- $node_id = $this->fn_id_prefix . $matches[1];
-
- # Create footnote marker only if it has a corresponding footnote *and*
- # the footnote hasn't been used by another marker.
- if (isset($this->footnotes[$node_id])) {
- $num =& $this->footnotes_numbers[$node_id];
- if (!isset($num)) {
- # Transfer footnote content to the ordered list and give it its
- # number
- $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
- $this->footnotes_ref_count[$node_id] = 1;
- $num = $this->footnote_counter++;
- $ref_count_mark = '';
- } else {
- $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
- }
-
- $attr = "";
- if ($this->fn_link_class != "") {
- $class = $this->fn_link_class;
- $class = $this->encodeAttribute($class);
- $attr .= " class=\"$class\"";
- }
- if ($this->fn_link_title != "") {
- $title = $this->fn_link_title;
- $title = $this->encodeAttribute($title);
- $attr .= " title=\"$title\"";
- }
-
- $attr = str_replace("%%", $num, $attr);
- $node_id = $this->encodeAttribute($node_id);
-
- return
- "<sup id=\"fnref$ref_count_mark:$node_id\">".
- "<a href=\"#fn:$node_id\"$attr>$num</a>".
- "</sup>";
- }
-
- return "[^".$matches[1]."]";
- }
-
-
- ### Abbreviations ###
-
- function stripAbbreviations($text) {
- #
- # Strips abbreviations from text, stores titles in hash references.
- #
- $less_than_tab = $this->tab_width - 1;
-
- # Link defs are in the form: [id]*: url "optional title"
- $text = preg_replace_callback('{
- ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
- (.*) # text = $2 (no blank lines allowed)
- }xm',
- array(&$this, '_stripAbbreviations_callback'),
- $text);
- return $text;
- }
- function _stripAbbreviations_callback($matches) {
- $abbr_word = $matches[1];
- $abbr_desc = $matches[2];
- if ($this->abbr_word_re)
- $this->abbr_word_re .= '|';
- $this->abbr_word_re .= preg_quote($abbr_word);
- $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
- return ''; # String that will replace the block
- }
-
-
- function doAbbreviations($text) {
- #
- # Find defined abbreviations in text and wrap them in <abbr> elements.
- #
- if ($this->abbr_word_re) {
- // cannot use the /x modifier because abbr_word_re may
- // contain significant spaces:
- $text = preg_replace_callback('{'.
- '(?<![\w\x1A])'.
- '(?:'.$this->abbr_word_re.')'.
- '(?![\w\x1A])'.
- '}',
- array(&$this, '_doAbbreviations_callback'), $text);
- }
- return $text;
- }
- function _doAbbreviations_callback($matches) {
- $abbr = $matches[0];
- if (isset($this->abbr_desciptions[$abbr])) {
- $desc = $this->abbr_desciptions[$abbr];
- if (empty($desc)) {
- return $this->hashPart("<abbr>$abbr</abbr>");
- } else {
- $desc = $this->encodeAttribute($desc);
- return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");
- }
- } else {
- return $matches[0];
- }
- }
-
-}
-
-
-/*
-
-PHP Markdown Extra
-==================
-
-Description
------------
-
-This is a PHP port of the original Markdown formatter written in Perl
-by John Gruber. This special "Extra" version of PHP Markdown features
-further enhancements to the syntax for making additional constructs
-such as tables and definition list.
-
-Markdown is a text-to-HTML filter; it translates an easy-to-read /
-easy-to-write structured text format into HTML. Markdown's text format
-is mostly similar to that of plain text email, and supports features such
-as headers, *emphasis*, code blocks, blockquotes, and links.
-
-Markdown's syntax is designed not as a generic markup language, but
-specifically to serve as a front-end to (X)HTML. You can use span-level
-HTML tags anywhere in a Markdown document, and you can use block level
-HTML tags (like <div> and <table> as well).
-
-For more information about Markdown's syntax, see:
-
-<http://daringfireball.net/projects/markdown/>
-
-
-Bugs
-----
-
-To file bug reports please send email to:
-
-<michel.fortin@michelf.ca>
-
-Please include with your report: (1) the example input; (2) the output you
-expected; (3) the output Markdown actually produced.
-
-
-Version History
----------------
-
-See the readme file for detailed release notes for this version.
-
-
-Copyright and License
----------------------
-
-PHP Markdown & Extra
-Copyright (c) 2004-2013 Michel Fortin
-<http://michelf.ca/>
-All rights reserved.
-
-Based on Markdown
-Copyright (c) 2003-2006 John Gruber
-<http://daringfireball.net/>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-* Neither the name "Markdown" nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-This software is provided by the copyright holders and contributors "as
-is" and any express or implied warranties, including, but not limited
-to, the implied warranties of merchantability and fitness for a
-particular purpose are disclaimed. In no event shall the copyright owner
-or contributors be liable for any direct, indirect, incidental, special,
-exemplary, or consequential damages (including, but not limited to,
-procurement of substitute goods or services; loss of use, data, or
-profits; or business interruption) however caused and on any theory of
-liability, whether in contract, strict liability, or tort (including
-negligence or otherwise) arising in any way out of the use of this
-software, even if advised of the possibility of such damage.
-
-*/
-?>
diff --git a/plugins/jetpack/_inc/lib/markdown/gfm.php b/plugins/jetpack/_inc/lib/markdown/gfm.php
deleted file mode 100644
index 081e1a11..00000000
--- a/plugins/jetpack/_inc/lib/markdown/gfm.php
+++ /dev/null
@@ -1,400 +0,0 @@
-<?php
-/**
- * GitHub-Flavoured Markdown. Inspired by Evan's plugin, but modified.
- *
- * @author Evan Solomon
- * @author Matt Wiebe <wiebe@automattic.com>
- * @link https://github.com/evansolomon/wp-github-flavored-markdown-comments
- *
- * Add a few extras from GitHub's Markdown implementation. Must be used in a WordPress environment.
- */
-
-class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
-
- /**
- * Hooray somewhat arbitrary numbers that are fearful of 1.0.x.
- */
- const WPCOM_GHF_MARDOWN_VERSION = '0.9.0';
-
- /**
- * Use a [code] shortcode when encountering a fenced code block
- * @var boolean
- */
- public $use_code_shortcode = true;
-
- /**
- * Preserve shortcodes, untouched by Markdown.
- * This requires use within a WordPress installation.
- * @var boolean
- */
- public $preserve_shortcodes = true;
-
- /**
- * Preserve the legacy $latex your-latex-code-here$ style
- * LaTeX markup
- */
- public $preserve_latex = true;
-
- /**
- * Preserve single-line <code> blocks.
- * @var boolean
- */
- public $preserve_inline_code_blocks = true;
-
- /**
- * Strip paragraphs from the output. This is the right default for WordPress,
- * which generally wants to create its own paragraphs with `wpautop`
- * @var boolean
- */
- public $strip_paras = true;
-
- // Will run through sprintf - you can supply your own syntax if you want
- public $shortcode_start = '[code lang=%s]';
- public $shortcode_end = '[/code]';
-
- // Stores shortcodes we remove and then replace
- protected $preserve_text_hash = array();
-
- /**
- * Set environment defaults based on presence of key functions/classes.
- */
- public function __construct() {
- $this->use_code_shortcode = class_exists( 'SyntaxHighlighter' );
- /**
- * Allow processing shortcode contents.
- *
- * @module markdown
- *
- * @since 4.4.0
- *
- * @param boolean $preserve_shortcodes Defaults to $this->preserve_shortcodes.
- */
- $this->preserve_shortcodes = apply_filters( 'jetpack_markdown_preserve_shortcodes', $this->preserve_shortcodes ) && function_exists( 'get_shortcode_regex' );
- $this->preserve_latex = function_exists( 'latex_markup' );
- $this->strip_paras = function_exists( 'wpautop' );
-
- parent::__construct();
- }
-
- /**
- * Overload to specify heading styles only if the hash has space(s) after it. This is actually in keeping with
- * the documentation and eases the semantic overload of the hash character.
- * #Will Not Produce a Heading 1
- * # This Will Produce a Heading 1
- *
- * @param string $text Markdown text
- * @return string HTML-transformed text
- */
- public function transform( $text ) {
- // Preserve anything inside a single-line <code> element
- if ( $this->preserve_inline_code_blocks ) {
- $text = $this->single_line_code_preserve( $text );
- }
- // Remove all shortcodes so their interiors are left intact
- if ( $this->preserve_shortcodes ) {
- $text = $this->shortcode_preserve( $text );
- }
- // Remove legacy LaTeX so it's left intact
- if ( $this->preserve_latex ) {
- $text = $this->latex_preserve( $text );
- }
-
- // escape line-beginning # chars that do not have a space after them.
- $text = preg_replace_callback( '|^#{1,6}( )?|um', array( $this, '_doEscapeForHashWithoutSpacing' ), $text );
-
- /**
- * Allow third-party plugins to define custom patterns that won't be processed by Markdown.
- *
- * @module markdown
- *
- * @since 3.9.2
- *
- * @param array $custom_patterns Array of custom patterns to be ignored by Markdown.
- */
- $custom_patterns = apply_filters( 'jetpack_markdown_preserve_pattern', array() );
- if ( is_array( $custom_patterns ) && ! empty( $custom_patterns ) ) {
- foreach ( $custom_patterns as $pattern ) {
- $text = preg_replace_callback( $pattern, array( $this, '_doRemoveText'), $text );
- }
- }
-
- // run through core Markdown
- $text = parent::transform( $text );
-
- // Occasionally Markdown Extra chokes on a para structure, producing odd paragraphs.
- $text = str_replace( "<p>&lt;</p>\n\n<p>p>", '<p>', $text );
-
- // put start-of-line # chars back in place
- $text = $this->restore_leading_hash( $text );
-
- // Strip paras if set
- if ( $this->strip_paras ) {
- $text = $this->unp( $text );
- }
-
- // Restore preserved things like shortcodes/LaTeX
- $text = $this->do_restore( $text );
-
- return $text;
- }
-
- /**
- * Prevents blocks like <code>__this__</code> from turning into <code><strong>this</strong></code>
- * @param string $text Text that may need preserving
- * @return string Text that was preserved if needed
- */
- public function single_line_code_preserve( $text ) {
- return preg_replace_callback( '|<code\b[^>]*>(.*?)</code>|', array( $this, 'do_single_line_code_preserve' ), $text );
- }
-
- /**
- * Regex callback for inline code presevation
- * @param array $matches Regex matches
- * @return string Hashed content for later restoration
- */
- public function do_single_line_code_preserve( $matches ) {
- return '<code>' . $this->hash_block( $matches[1] ) . '</code>';
- }
-
- /**
- * Preserve code block contents by HTML encoding them. Useful before getting to KSES stripping.
- * @param string $text Markdown/HTML content
- * @return string Markdown/HTML content with escaped code blocks
- */
- public function codeblock_preserve( $text ) {
- return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_preserve' ), $text );
- }
-
- /**
- * Regex callback for code block preservation.
- * @param array $matches Regex matches
- * @return string Codeblock with escaped interior
- */
- public function do_codeblock_preserve( $matches ) {
- $block = stripslashes( $matches[3] );
- $block = esc_html( $block );
- $block = str_replace( '\\', '\\\\', $block );
- $open = $matches[1] . $matches[2] . "\n";
- return $open . $block . $matches[4];
- }
-
- /**
- * Restore previously preserved (i.e. escaped) code block contents.
- * @param string $text Markdown/HTML content with escaped code blocks
- * @return string Markdown/HTML content
- */
- public function codeblock_restore( $text ) {
- return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_restore' ), $text );
- }
-
- /**
- * Regex callback for code block restoration (unescaping).
- * @param array $matches Regex matches
- * @return string Codeblock with unescaped interior
- */
- public function do_codeblock_restore( $matches ) {
- $block = html_entity_decode( $matches[3], ENT_QUOTES );
- $open = $matches[1] . $matches[2] . "\n";
- return $open . $block . $matches[4];
- }
-
- /**
- * Called to preserve legacy LaTeX like $latex some-latex-text $
- * @param string $text Text in which to preserve LaTeX
- * @return string Text with LaTeX replaced by a hash that will be restored later
- */
- protected function latex_preserve( $text ) {
- // regex from latex_remove()
- $regex = '%
- \$latex(?:=\s*|\s+)
- ((?:
- [^$]+ # Not a dollar
- |
- (?<=(?<!\\\\)\\\\)\$ # Dollar preceded by exactly one slash
- )+)
- (?<!\\\\)\$ # Dollar preceded by zero slashes
- %ix';
- $text = preg_replace_callback( $regex, array( $this, '_doRemoveText'), $text );
- return $text;
- }
-
- /**
- * Called to preserve WP shortcodes from being formatted by Markdown in any way.
- * @param string $text Text in which to preserve shortcodes
- * @return string Text with shortcodes replaced by a hash that will be restored later
- */
- protected function shortcode_preserve( $text ) {
- $text = preg_replace_callback( $this->get_shortcode_regex(), array( $this, '_doRemoveText' ), $text );
- return $text;
- }
-
- /**
- * Restores any text preserved by $this->hash_block()
- * @param string $text Text that may have hashed preservation placeholders
- * @return string Text with hashed preseravtion placeholders replaced by original text
- */
- protected function do_restore( $text ) {
- // Reverse hashes to ensure nested blocks are restored.
- $hashes = array_reverse( $this->preserve_text_hash, true );
- foreach( $hashes as $hash => $value ) {
- $placeholder = $this->hash_maker( $hash );
- $text = str_replace( $placeholder, $value, $text );
- }
- // reset the hash
- $this->preserve_text_hash = array();
- return $text;
- }
-
- /**
- * Regex callback for text preservation
- * @param array $m Regex $matches array
- * @return string A placeholder that will later be replaced by the original text
- */
- protected function _doRemoveText( $m ) {
- return $this->hash_block( $m[0] );
- }
-
- /**
- * Call this to store a text block for later restoration.
- * @param string $text Text to preserve for later
- * @return string Placeholder that will be swapped out later for the original text
- */
- protected function hash_block( $text ) {
- $hash = md5( $text );
- $this->preserve_text_hash[ $hash ] = $text;
- $placeholder = $this->hash_maker( $hash );
- return $placeholder;
- }
-
- /**
- * Less glamorous than the Keymaker
- * @param string $hash An md5 hash
- * @return string A placeholder hash
- */
- protected function hash_maker( $hash ) {
- return 'MARKDOWN_HASH' . $hash . 'MARKDOWN_HASH';
- }
-
- /**
- * Remove bare <p> elements. <p>s with attributes will be preserved.
- * @param string $text HTML content
- * @return string <p>-less content
- */
- public function unp( $text ) {
- return preg_replace( "#<p>(.*?)</p>(\n|$)#ums", '$1$2', $text );
- }
-
- /**
- * A regex of all shortcodes currently registered by the current
- * WordPress installation
- * @uses get_shortcode_regex()
- * @return string A regex for grabbing shortcodes.
- */
- protected function get_shortcode_regex() {
- $pattern = get_shortcode_regex();
-
- // don't match markdown link anchors that could be mistaken for shortcodes.
- $pattern .= '(?!\()';
-
- return "/$pattern/s";
- }
-
- /**
- * Since we escape unspaced #Headings, put things back later.
- * @param string $text text with a leading escaped hash
- * @return string text with leading hashes unescaped
- */
- protected function restore_leading_hash( $text ) {
- return preg_replace( "/^(<p>)?(&#35;|\\\\#)/um", "$1#", $text );
- }
-
- /**
- * Overload to support ```-fenced code blocks for pre-Markdown Extra 1.2.8
- * https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
- */
- public function doFencedCodeBlocks( $text ) {
- // If we're at least at 1.2.8, native fenced code blocks are in.
- // Below is just copied from it in case we somehow got loaded on
- // top of someone else's Markdown Extra
- if ( version_compare( MARKDOWNEXTRA_VERSION, '1.2.8', '>=' ) )
- return parent::doFencedCodeBlocks( $text );
-
- #
- # Adding the fenced code block syntax to regular Markdown:
- #
- # ~~~
- # Code block
- # ~~~
- #
- $less_than_tab = $this->tab_width;
-
- $text = preg_replace_callback('{
- (?:\n|\A)
- # 1: Opening marker
- (
- (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
- )
- [ ]*
- (?:
- \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
- |
- '.$this->id_class_attr_catch_re.' # 3: Extra attributes
- )?
- [ ]* \n # Whitespace and newline following marker.
-
- # 4: Content
- (
- (?>
- (?!\1 [ ]* \n) # Not a closing marker.
- .*\n+
- )+
- )
-
- # Closing marker.
- \1 [ ]* (?= \n )
- }xm',
- array($this, '_doFencedCodeBlocks_callback'), $text);
-
- return $text;
- }
-
- /**
- * Callback for pre-processing start of line hashes to slyly escape headings that don't
- * have a leading space
- * @param array $m preg_match matches
- * @return string possibly escaped start of line hash
- */
- public function _doEscapeForHashWithoutSpacing( $m ) {
- if ( ! isset( $m[1] ) )
- $m[0] = '\\' . $m[0];
- return $m[0];
- }
-
- /**
- * Overload to support Viper's [code] shortcode. Because awesome.
- */
- public function _doFencedCodeBlocks_callback( $matches ) {
- // in case we have some escaped leading hashes right at the start of the block
- $matches[4] = $this->restore_leading_hash( $matches[4] );
- // just MarkdownExtra_Parser if we're not going ultra-deluxe
- if ( ! $this->use_code_shortcode ) {
- return parent::_doFencedCodeBlocks_callback( $matches );
- }
-
- // default to a "text" class if one wasn't passed. Helps with encoding issues later.
- if ( empty( $matches[2] ) ) {
- $matches[2] = 'text';
- }
-
- $classname =& $matches[2];
- $codeblock = preg_replace_callback('/^\n+/', array( $this, '_doFencedCodeBlocks_newlines' ), $matches[4] );
-
- if ( $classname{0} == '.' )
- $classname = substr( $classname, 1 );
-
- $codeblock = esc_html( $codeblock );
- $codeblock = sprintf( $this->shortcode_start, $classname ) . "\n{$codeblock}" . $this->shortcode_end;
- return "\n\n" . $this->hashBlock( $codeblock ). "\n\n";
- }
-
-}
diff --git a/plugins/jetpack/_inc/lib/plugins.php b/plugins/jetpack/_inc/lib/plugins.php
deleted file mode 100644
index 9c8e3bc4..00000000
--- a/plugins/jetpack/_inc/lib/plugins.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * Plugins Library
- *
- * Helper functions for installing and activating plugins.
- *
- * Used by the REST API
- *
- * @autounit api plugins
- */
-
-include_once( 'class.jetpack-automatic-install-skin.php' );
-
-class Jetpack_Plugins {
-
- /**
- * Install and activate a plugin.
- *
- * @since 5.8.0
- *
- * @param string $slug Plugin slug.
- *
- * @return bool|WP_Error True if installation succeeded, error object otherwise.
- */
- public static function install_and_activate_plugin( $slug ) {
- $plugin_id = self::get_plugin_id_by_slug( $slug );
-
- if ( ! $plugin_id ) {
- $installed = self::install_plugin( $slug );
- if ( is_wp_error( $installed ) ) {
- return $installed;
- }
- $plugin_id = self::get_plugin_id_by_slug( $slug );
- } else if ( is_plugin_active( $plugin_id ) ) {
- return true; // Already installed and active
- }
-
- if ( ! current_user_can( 'activate_plugins' ) ) {
- return new WP_Error( 'not_allowed', __( 'You are not allowed to activate plugins on this site.', 'jetpack' ) );
- }
-
- $activated = activate_plugin( $plugin_id );
- if ( is_wp_error( $activated ) ) {
- return $activated;
- }
-
- return true;
- }
-
- /**
- * Install a plugin.
- *
- * @since 5.8.0
- *
- * @param string $slug Plugin slug.
- *
- * @return bool|WP_Error True if installation succeeded, error object otherwise.
- */
- public static function install_plugin( $slug ) {
- if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
- return new WP_Error( 'not_allowed', __( 'You are not allowed to install plugins on this site.', 'jetpack' ) );
- }
-
- $skin = new Jetpack_Automatic_Install_Skin();
- $upgrader = new Plugin_Upgrader( $skin );
- $zip_url = self::generate_wordpress_org_plugin_download_link( $slug );
-
- $result = $upgrader->install( $zip_url );
-
- if ( is_wp_error( $result ) ) {
- return $result;
- }
-
- $plugin = Jetpack_Plugins::get_plugin_id_by_slug( $slug );
- $error_code = 'install_error';
- if ( ! $plugin ) {
- $error = __( 'There was an error installing your plugin', 'jetpack' );
- }
-
- if ( ! $result ) {
- $error_code = $upgrader->skin->get_main_error_code();
- $message = $upgrader->skin->get_main_error_message();
- $error = $message ? $message : __( 'An unknown error occurred during installation', 'jetpack' );
- }
-
- if ( ! empty( $error ) ) {
- if ( 'download_failed' === $error_code ) {
- // For backwards compatibility: versions prior to 3.9 would return no_package instead of download_failed.
- $error_code = 'no_package';
- }
-
- return new WP_Error( $error_code, $error, 400 );
- }
-
- return (array) $upgrader->skin->get_upgrade_messages();
- }
-
- protected static function generate_wordpress_org_plugin_download_link( $plugin_slug ) {
- return "https://downloads.wordpress.org/plugin/$plugin_slug.latest-stable.zip";
- }
-
- public static function get_plugin_id_by_slug( $slug ) {
- // Check if get_plugins() function exists. This is required on the front end of the
- // site, since it is in a file that is normally only loaded in the admin.
- if ( ! function_exists( 'get_plugins' ) ) {
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
- }
-
- /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
- $plugins = apply_filters( 'all_plugins', get_plugins() );
- if ( ! is_array( $plugins ) ) {
- return false;
- }
- foreach ( $plugins as $plugin_file => $plugin_data ) {
- if ( self::get_slug_from_file_path( $plugin_file ) === $slug ) {
- return $plugin_file;
- }
- }
-
- return false;
- }
-
- protected static function get_slug_from_file_path( $plugin_file ) {
- // Similar to get_plugin_slug() method.
- $slug = dirname( $plugin_file );
- if ( '.' === $slug ) {
- $slug = preg_replace( "/(.+)\.php$/", "$1", $plugin_file );
- }
-
- return $slug;
- }
-}
diff --git a/plugins/jetpack/_inc/lib/tonesque.php b/plugins/jetpack/_inc/lib/tonesque.php
deleted file mode 100644
index 17158e3d..00000000
--- a/plugins/jetpack/_inc/lib/tonesque.php
+++ /dev/null
@@ -1,237 +0,0 @@
-<?php
-/*
-Plugin Name: Tonesque
-Plugin URI: http://automattic.com/
-Description: Grab an average color representation from an image.
-Version: 1.0
-Author: Automattic, Matias Ventura
-Author URI: http://automattic.com/
-License: GNU General Public License v2 or later
-License URI: http://www.gnu.org/licenses/gpl-2.0.html
-*/
-
-class Tonesque {
-
- private $image_url = '';
- private $image_obj = NULL;
- private $color = '';
-
- function __construct( $image_url ) {
- if ( ! class_exists( 'Jetpack_Color' ) ) {
- jetpack_require_lib( 'class.color' );
- }
-
- $this->image_url = esc_url_raw( $image_url );
- $this->image_url = trim( $this->image_url );
- /**
- * Allows any image URL to be passed in for $this->image_url.
- *
- * @module theme-tools
- *
- * @since 2.5.0
- *
- * @param string $image_url The URL to any image
- */
- $this->image_url = apply_filters( 'tonesque_image_url', $this->image_url );
-
- $this->image_obj = self::imagecreatefromurl( $this->image_url );
- }
-
- public static function imagecreatefromurl( $image_url ) {
- $data = null;
-
- // If it's a URL:
- if ( preg_match( '#^https?://#i', $image_url ) ) {
- // If it's a url pointing to a local media library url:
- $content_url = content_url();
- $_image_url = set_url_scheme( $image_url );
- if ( wp_startswith( $_image_url, $content_url ) ) {
- $_image_path = str_replace( $content_url, WP_CONTENT_DIR, $_image_url );
- if ( file_exists( $_image_path ) ) {
- $filetype = wp_check_filetype( $_image_path );
- $ext = $filetype['ext'];
- $type = $filetype['type'];
-
- if ( wp_startswith( $type, 'image/' ) ) {
- $data = file_get_contents( $_image_path );
- }
- }
- }
-
- if ( empty( $data ) ) {
- $response = wp_remote_get( $image_url );
- if ( is_wp_error( $response ) ) {
- return false;
- }
- $data = wp_remote_retrieve_body( $response );
- }
- }
-
- // If it's a local path in our WordPress install:
- if ( file_exists( $image_url ) ) {
- $filetype = wp_check_filetype( $image_url );
- $ext = $filetype['ext'];
- $type = $filetype['type'];
-
- if ( wp_startswith( $type, 'image/' ) ) {
- $data = file_get_contents( $image_url );
- }
- }
-
- // Now turn it into an image and return it.
- return imagecreatefromstring( $data );
- }
-
- /**
- *
- * Construct object from image.
- *
- * @param optional $type (hex, rgb, hsv)
- * @return color as a string formatted as $type
- *
- */
- function color( $type = 'hex' ) {
- // Bail if there is no image to work with
- if ( ! $this->image_obj )
- return false;
-
- // Finds dominant color
- $color = self::grab_color();
- // Passes value to Color class
- $color = self::get_color( $color, $type );
- return $color;
- }
-
- /**
- *
- * Grabs the color index for each of five sample points of the image
- *
- * @param $image
- * @param $type can be 'index' or 'hex'
- * @return array() with color indices
- *
- */
- function grab_points( $type = 'index' ) {
- $img = $this->image_obj;
- if ( ! $img )
- return false;
-
- $height = imagesy( $img );
- $width = imagesx( $img );
-
- // Sample five points in the image
- // Based on rule of thirds and center
- $topy = round( $height / 3 );
- $bottomy = round( ( $height / 3 ) * 2 );
- $leftx = round( $width / 3 );
- $rightx = round( ( $width / 3 ) * 2 );
- $centery = round( $height / 2 );
- $centerx = round( $width / 2 );
-
- // Cast those colors into an array
- $points = array(
- imagecolorat( $img, $leftx, $topy ),
- imagecolorat( $img, $rightx, $topy ),
- imagecolorat( $img, $leftx, $bottomy ),
- imagecolorat( $img, $rightx, $bottomy ),
- imagecolorat( $img, $centerx, $centery ),
- );
-
- if ( 'hex' == $type ) {
- foreach ( $points as $i => $p ) {
- $c = imagecolorsforindex( $img, $p );
- $points[ $i ] = self::get_color( array(
- 'r' => $c['red'],
- 'g' => $c['green'],
- 'b' => $c['blue'],
- ), 'hex' );
- }
- }
-
- return $points;
- }
-
- /**
- *
- * Finds the average color of the image based on five sample points
- *
- * @param $image
- * @return array() with rgb color
- *
- */
- function grab_color() {
- $img = $this->image_obj;
- if ( ! $img )
- return false;
-
- $rgb = self::grab_points();
-
- // Process the color points
- // Find the average representation
- foreach ( $rgb as $color ) {
- $index = imagecolorsforindex( $img, $color );
- $r[] = $index['red'];
- $g[] = $index['green'];
- $b[] = $index['blue'];
-
- $red = round( array_sum( $r ) / 5 );
- $green = round( array_sum( $g ) / 5 );
- $blue = round( array_sum( $b ) / 5 );
- }
-
- // The average color of the image as rgb array
- $color = array(
- 'r' => $red,
- 'g' => $green,
- 'b' => $blue,
- );
-
- return $color;
- }
-
- /**
- *
- * Get a Color object using /lib class.color
- * Convert to appropriate type
- *
- * @return string
- *
- */
- function get_color( $color, $type ) {
- $c = new Jetpack_Color( $color, 'rgb' );
- $this->color = $c;
-
- switch ( $type ) {
- case 'rgb' :
- $color = implode( $c->toRgbInt(), ',' );
- break;
- case 'hex' :
- $color = $c->toHex();
- break;
- case 'hsv' :
- $color = implode( $c->toHsvInt(), ',' );
- break;
- default:
- return $color = $c->toHex();
- }
-
- return $color;
- }
-
- /**
- *
- * Checks contrast against main color
- * Gives either black or white for using with opacity
- *
- * @return string
- *
- */
- function contrast() {
- if ( ! $this->color )
- return false;
-
- $c = $this->color->getMaxContrastColor();
- return implode( $c->toRgbInt(), ',' );
- }
-
-};
diff --git a/plugins/jetpack/_inc/lib/tracks/class.tracks-client.php b/plugins/jetpack/_inc/lib/tracks/class.tracks-client.php
deleted file mode 100644
index b83c94f1..00000000
--- a/plugins/jetpack/_inc/lib/tracks/class.tracks-client.php
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-
-/**
- * Jetpack_Tracks_Client
- * @autounit nosara tracks-client
- *
- * Send Tracks events on behalf of a user
- *
- * Example Usage:
-```php
- require( dirname(__FILE__).'path/to/tracks/class.tracks-client' );
-
- $result = Jetpack_Tracks_Client::record_event( array(
- '_en' => $event_name, // required
- '_ui' => $user_id, // required unless _ul is provided
- '_ul' => $user_login, // required unless _ui is provided
-
- // Optional, but recommended
- '_ts' => $ts_in_ms, // Default: now
- '_via_ip' => $client_ip, // we use it for geo, etc.
-
- // Possibly useful to set some context for the event
- '_via_ua' => $client_user_agent,
- '_via_url' => $client_url,
- '_via_ref' => $client_referrer,
-
- // For user-targeted tests
- 'abtest_name' => $abtest_name,
- 'abtest_variation' => $abtest_variation,
-
- // Your application-specific properties
- 'custom_property' => $some_value,
- ) );
-
- if ( is_wp_error( $result ) ) {
- // Handle the error in your app
- }
-```
- */
-
-require_once( dirname(__FILE__).'/class.tracks-client.php' );
-
-class Jetpack_Tracks_Client {
- const PIXEL = 'https://pixel.wp.com/t.gif';
- const BROWSER_TYPE = 'php-agent';
- const USER_AGENT_SLUG = 'tracks-client';
- const VERSION = '0.3';
-
- /**
- * record_event
- * @param mixed $event Event object to send to Tracks. An array will be cast to object. Required.
- * Properties are included directly in the pixel query string after light validation.
- * @return mixed True on success, WP_Error on failure
- */
- static function record_event( $event ) {
- if ( ! Jetpack::jetpack_tos_agreed() || ! empty( $_COOKIE['tk_opt-out'] ) ) {
- return false;
- }
-
- if ( ! $event instanceof Jetpack_Tracks_Event ) {
- $event = new Jetpack_Tracks_Event( $event );
- }
- if ( is_wp_error( $event ) ) {
- return $event;
- }
-
- $pixel = $event->build_pixel_url( $event );
-
- if ( ! $pixel ) {
- return new WP_Error( 'invalid_pixel', 'cannot generate tracks pixel for given input', 400 );
- }
-
- return self::record_pixel( $pixel );
- }
-
- /**
- * Synchronously request the pixel
- */
- static function record_pixel( $pixel ) {
- // Add the Request Timestamp and URL terminator just before the HTTP request.
- $pixel .= '&_rt=' . self::build_timestamp() . '&_=_';
-
- $response = wp_remote_get( $pixel, array(
- 'blocking' => true, // The default, but being explicit here :)
- 'timeout' => 1,
- 'redirection' => 2,
- 'httpversion' => '1.1',
- 'user-agent' => self::get_user_agent(),
- ) );
-
- if ( is_wp_error( $response ) ) {
- return $response;
- }
-
- $code = isset( $response['response']['code'] ) ? $response['response']['code'] : 0;
-
- if ( $code !== 200 ) {
- return new WP_Error( 'request_failed', 'Tracks pixel request failed', $code );
- }
-
- return true;
- }
-
- static function get_user_agent() {
- return Jetpack_Tracks_Client::USER_AGENT_SLUG . '-v' . Jetpack_Tracks_Client::VERSION;
- }
-
- /**
- * Build an event and return its tracking URL
- * @deprecated Call the `build_pixel_url` method on a Jetpack_Tracks_Event object instead.
- * @param array $event Event keys and values
- * @return string URL of a tracking pixel
- */
- static function build_pixel_url( $event ) {
- $_event = new Jetpack_Tracks_Event( $event );
- return $_event->build_pixel_url();
- }
-
- /**
- * Validate input for a tracks event.
- * @deprecated Instantiate a Jetpack_Tracks_Event object instead
- * @param array $event Event keys and values
- * @return mixed Validated keys and values or WP_Error on failure
- */
- private static function validate_and_sanitize( $event ) {
- $_event = new Jetpack_Tracks_Event( $event );
- if ( is_wp_error( $_event ) ) {
- return $_event;
- }
- return get_object_vars( $_event );
- }
-
- // Milliseconds since 1970-01-01
- static function build_timestamp() {
- $ts = round( microtime( true ) * 1000 );
- return number_format( $ts, 0, '', '' );
- }
-
- /**
- * Grabs the user's anon id from cookies, or generates and sets a new one
- *
- * @return string An anon id for the user
- */
- static function get_anon_id() {
- static $anon_id = null;
-
- if ( ! isset( $anon_id ) ) {
-
- // Did the browser send us a cookie?
- if ( isset( $_COOKIE[ 'tk_ai' ] ) && preg_match( '#^[A-Za-z0-9+/=]{24}$#', $_COOKIE[ 'tk_ai' ] ) ) {
- $anon_id = $_COOKIE[ 'tk_ai' ];
- } else {
-
- $binary = '';
-
- // Generate a new anonId and try to save it in the browser's cookies
- // Note that base64-encoding an 18 character string generates a 24-character anon id
- for ( $i = 0; $i < 18; ++$i ) {
- $binary .= chr( mt_rand( 0, 255 ) );
- }
-
- $anon_id = 'jetpack:' . base64_encode( $binary );
-
- if ( ! headers_sent()
- && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
- && ! ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
- ) {
- setcookie( 'tk_ai', $anon_id );
- }
- }
- }
-
- return $anon_id;
- }
-
- /**
- * Gets the WordPress.com user's Tracks identity, if connected.
- *
- * @return array|bool
- */
- static function get_connected_user_tracks_identity() {
- if ( ! $user_data = Jetpack::get_connected_user_data() ) {
- return false;
- }
-
- return array(
- 'userid' => $user_data['ID'],
- 'username' => $user_data['login'],
- );
- }
-}
diff --git a/plugins/jetpack/_inc/lib/tracks/class.tracks-event.php b/plugins/jetpack/_inc/lib/tracks/class.tracks-event.php
deleted file mode 100644
index fb86e0ba..00000000
--- a/plugins/jetpack/_inc/lib/tracks/class.tracks-event.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-/**
- * @autounit nosara tracks-client
- *
- * Example Usage:
-```php
- require_once( dirname(__FILE__) . 'path/to/tracks/class.tracks-event' );
-
- $event = new Jetpack_Tracks_Event( array(
- '_en' => $event_name, // required
- '_ui' => $user_id, // required unless _ul is provided
- '_ul' => $user_login, // required unless _ui is provided
-
- // Optional, but recommended
- '_via_ip' => $client_ip, // for geo, etc.
-
- // Possibly useful to set some context for the event
- '_via_ua' => $client_user_agent,
- '_via_url' => $client_url,
- '_via_ref' => $client_referrer,
-
- // For user-targeted tests
- 'abtest_name' => $abtest_name,
- 'abtest_variation' => $abtest_variation,
-
- // Your application-specific properties
- 'custom_property' => $some_value,
- ) );
-
- if ( is_wp_error( $event->error ) ) {
- // Handle the error in your app
- }
-
- $bump_and_redirect_pixel = $event->build_signed_pixel_url();
-```
- */
-
-require_once( dirname(__FILE__) . '/class.tracks-client.php' );
-
-class Jetpack_Tracks_Event {
- const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/';
- const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';
- public $error;
-
- function __construct( $event ) {
- $_event = self::validate_and_sanitize( $event );
- if ( is_wp_error( $_event ) ) {
- $this->error = $_event;
- return;
- }
-
- foreach( $_event as $key => $value ) {
- $this->{$key} = $value;
- }
- }
-
- function record() {
- return Jetpack_Tracks_Client::record_event( $this );
- }
-
- /**
- * Annotate the event with all relevant info.
- * @param mixed $event Object or (flat) array
- * @return mixed The transformed event array or WP_Error on failure.
- */
- static function validate_and_sanitize( $event ) {
- $event = (object) $event;
-
- // Required
- if ( ! $event->_en ) {
- return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 );
- }
-
- // delete non-routable addresses otherwise geoip will discard the record entirely
- if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) {
- unset($event->_via_ip);
- }
-
- $validated = array(
- 'browser_type' => Jetpack_Tracks_Client::BROWSER_TYPE,
- '_aua' => Jetpack_Tracks_Client::get_user_agent(),
- );
-
- $_event = (object) array_merge( (array) $event, $validated );
-
- // If you want to blacklist property names, do it here.
-
- // Make sure we have an event timestamp.
- if ( ! isset( $_event->_ts ) ) {
- $_event->_ts = Jetpack_Tracks_Client::build_timestamp();
- }
-
- return $_event;
- }
-
- /**
- * Build a pixel URL that will send a Tracks event when fired.
- * On error, returns an empty string ('').
- *
- * @return string A pixel URL or empty string ('') if there were invalid args.
- */
- function build_pixel_url() {
- if ( $this->error ) {
- return '';
- }
-
- $args = get_object_vars( $this );
-
- // Request Timestamp and URL Terminator must be added just before the HTTP request or not at all.
- unset( $args['_rt'] );
- unset( $args['_'] );
-
- $validated = self::validate_and_sanitize( $args );
-
- if ( is_wp_error( $validated ) )
- return '';
-
- return Jetpack_Tracks_Client::PIXEL . '?' . http_build_query( $validated );
- }
-
- static function event_name_is_valid( $name ) {
- return preg_match( Jetpack_Tracks_Event::EVENT_NAME_REGEX, $name );
- }
-
- static function prop_name_is_valid( $name ) {
- return preg_match( Jetpack_Tracks_Event::PROP_NAME_REGEX, $name );
- }
-
- static function scrutinize_event_names( $event ) {
- if ( ! Jetpack_Tracks_Event::event_name_is_valid( $event->_en ) ) {
- return;
- }
-
- $whitelisted_key_names = array(
- 'anonId',
- 'Browser_Type',
- );
-
- foreach ( array_keys( (array) $event ) as $key ) {
- if ( in_array( $key, $whitelisted_key_names ) ) {
- continue;
- }
- if ( ! Jetpack_Tracks_Event::prop_name_is_valid( $key ) ) {
- return;
- }
- }
- }
-}
diff --git a/plugins/jetpack/_inc/lib/tracks/client.php b/plugins/jetpack/_inc/lib/tracks/client.php
deleted file mode 100644
index ef7cfaea..00000000
--- a/plugins/jetpack/_inc/lib/tracks/client.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-/**
- * PHP Tracks Client
- * @autounit nosara tracks-client
- * Example Usage:
- *
-```php
- include( plugin_dir_path( __FILE__ ) . 'lib/tracks/client.php');
- $result = jetpack_tracks_record_event( $user, $event_name, $properties );
-
- if ( is_wp_error( $result ) ) {
- // Handle the error in your app
- }
-```
- */
-
-// Load the client classes
-require_once( dirname(__FILE__) . '/class.tracks-event.php' );
-require_once( dirname(__FILE__) . '/class.tracks-client.php' );
-
-// Now, let's export a sprinkling of syntactic sugar!
-
-/**
- * Procedurally (vs. Object-oriented), track an event object (or flat array)
- * NOTE: Use this only when the simpler jetpack_tracks_record_event() function won't work for you.
- * @param \Jetpack_Tracks_Event $event The event object.
- * @return \Jetpack_Tracks_Event|\WP_Error
- */
-function jetpack_tracks_record_event_raw( $event ) {
- return Jetpack_Tracks_Client::record_event( $event );
-}
-
-/**
- * Procedurally build a Tracks Event Object.
- * NOTE: Use this only when the simpler jetpack_tracks_record_event() function won't work for you.
- * @param $identity WP_user object
- * @param string $event_name The name of the event
- * @param array $properties Custom properties to send with the event
- * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred
- * @return \Jetpack_Tracks_Event|\WP_Error
- */
-function jetpack_tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
-
- $identity = jetpack_tracks_get_identity( $user->ID );
-
- $properties['user_lang'] = $user->get( 'WPLANG' );
-
- $blog_details = array(
- 'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' )
- );
-
- $timestamp = ( $event_timestamp_millis !== false ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
- $timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
-
- return new Jetpack_Tracks_Event( array_merge( $blog_details, (array) $properties, $identity, array(
- '_en' => $event_name,
- '_ts' => $timestamp_string
- ) ) );
-}
-
-/*
- * Get the identity to send to tracks.
- *
- * @param int $user_id The user id of the local user
- * @return array $identity
- */
-function jetpack_tracks_get_identity( $user_id ) {
-
- // Meta is set, and user is still connected. Use WPCOM ID
- $wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
- if ( $wpcom_id && Jetpack::is_user_connected( $user_id ) ) {
- return array(
- '_ut' => 'wpcom:user_id',
- '_ui' => $wpcom_id
- );
- }
-
- // User is connected, but no meta is set yet. Use WPCOM ID and set meta.
- if ( Jetpack::is_user_connected( $user_id ) ) {
- $wpcom_user_data = Jetpack::get_connected_user_data( $user_id );
- add_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'], true );
-
- return array(
- '_ut' => 'wpcom:user_id',
- '_ui' => $wpcom_user_data['ID']
- );
- }
-
- // User isn't linked at all. Fall back to anonymous ID.
- $anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
- if ( ! $anon_id ) {
- $anon_id = Jetpack_Tracks_Client::get_anon_id();
- add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
- }
-
- if ( ! isset( $_COOKIE[ 'tk_ai' ] ) && ! headers_sent() ) {
- setcookie( 'tk_ai', $anon_id );
- }
-
- return array(
- '_ut' => 'anon',
- '_ui' => $anon_id
- );
-
-}
-
-/**
- * Record an event in Tracks - this is the preferred way to record events from PHP.
- *
- * @param mixed $identity username, user_id, or WP_user object
- * @param string $event_name The name of the event
- * @param array $properties Custom properties to send with the event
- * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred
- * @return bool true for success | \WP_Error if the event pixel could not be fired
- */
-function jetpack_tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
-
- // We don't want to track user events during unit tests/CI runs.
- if ( $user instanceof WP_User && 'wptests_capabilities' === $user->cap_key ) {
- return false;
- }
-
- $event_obj = jetpack_tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
-
- if ( is_wp_error( $event_obj->error ) ) {
- return $event_obj->error;
- }
-
- return $event_obj->record();
-}
diff --git a/plugins/jetpack/_inc/lib/tracks/tracks-ajax.js b/plugins/jetpack/_inc/lib/tracks/tracks-ajax.js
deleted file mode 100644
index f9ed5cfd..00000000
--- a/plugins/jetpack/_inc/lib/tracks/tracks-ajax.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* global jpTracksAJAX, jQuery */
-(function( $, jpTracksAJAX ) {
- window.jpTracksAJAX = window.jpTracksAJAX || {};
- const debugSet = localStorage.getItem( 'debug' ) === 'dops:analytics';
-
- window.jpTracksAJAX.record_ajax_event = function ( eventName, eventType, eventProp ) {
- var data = {
- tracksNonce: jpTracksAJAX.jpTracksAJAX_nonce,
- action: 'jetpack_tracks',
- tracksEventType: eventType,
- tracksEventName: eventName,
- tracksEventProp: eventProp || false
- };
-
- return $.ajax( {
- type: 'POST',
- url: jpTracksAJAX.ajaxurl,
- data: data,
- success: function( response ) {
- if ( debugSet ) {
- // eslint-disable-next-line
- console.log( 'AJAX tracks event recorded: ', data, response );
- }
- }
- } );
- };
-
- $( document ).ready( function() {
- $( 'body' ).on( 'click', '.jptracks a, a.jptracks', function( event ) {
- // We know that the jptracks element is either this, or its ancestor
- var $jptracks = $( this ).closest( '.jptracks' );
-
- // We need an event name at least
- var eventName = $jptracks.attr( 'data-jptracks-name' );
- if ( undefined === eventName ) {
- return;
- }
-
- var eventProp = $jptracks.attr( 'data-jptracks-prop' ) || false;
-
- var url = $( this ).attr( 'href' );
- var target = $( this ).get( 0 ).target;
- if ( url && target && '_self' !== target ) {
- var newTabWindow = window.open( '', target );
- newTabWindow.opener = null;
- }
-
- event.preventDefault();
-
- window.jpTracksAJAX.record_ajax_event( eventName, 'click', eventProp ).always( function() {
- // Continue on to whatever url they were trying to get to.
- if ( url ) {
- if ( newTabWindow ) {
- newTabWindow.location = url;
- return;
- }
- window.location = url;
- }
- } );
- } );
- } );
-
-} )( jQuery, jpTracksAJAX );
diff --git a/plugins/jetpack/_inc/lib/tracks/tracks-callables.js b/plugins/jetpack/_inc/lib/tracks/tracks-callables.js
deleted file mode 100644
index 7ce54a94..00000000
--- a/plugins/jetpack/_inc/lib/tracks/tracks-callables.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * This was abstracted from wp-calypso's analytics lib: https://github.com/Automattic/wp-calypso/blob/master/client/lib/analytics/README.md
- * Some stuff was removed like GA tracking and other things not necessary for Jetpack tracking.
- *
- * This library should only be used and loaded if the Jetpack site is connected.
- */
-
-// Load tracking scripts
-window._tkq = window._tkq || [];
-
-function buildQuerystring( group, name ) {
- var uriComponent = '';
-
- if ( 'object' === typeof group ) {
- for ( var key in group ) {
- uriComponent += '&x_' + encodeURIComponent( key ) + '=' + encodeURIComponent( group[ key ] );
- }
- } else {
- uriComponent = '&x_' + encodeURIComponent( group ) + '=' + encodeURIComponent( name );
- }
-
- return uriComponent;
-}
-
-var analytics = {
-
- initialize: function( userId, username ) {
- analytics.setUser( userId, username );
- analytics.identifyUser();
- },
-
- mc: {
- bumpStat: function( group, name ) {
- var uriComponent = buildQuerystring( group, name ); // prints debug info
- new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?v=wpcom-no-pv' + uriComponent + '&t=' + Math.random();
- }
- },
-
- tracks: {
- recordEvent: function( eventName, eventProperties ) {
- eventProperties = eventProperties || {};
-
- if ( eventName.indexOf( 'jetpack_' ) !== 0 ) {
- debug( '- Event name must be prefixed by "jetpack_"' );
- return;
- }
-
- window._tkq.push( [ 'recordEvent', eventName, eventProperties ] );
- },
-
- recordPageView: function( urlPath ) {
- analytics.tracks.recordEvent( 'jetpack_page_view', {
- 'path': urlPath
- } );
- }
- },
-
- setUser: function( userId, username ) {
- _user = { ID: userId, username: username };
- },
-
- identifyUser: function() {
- // Don't identify the user if we don't have one
- if ( _user ) {
- window._tkq.push( [ 'identifyUser', _user.ID, _user.username ] );
- }
- },
-
- clearedIdentity: function() {
- window._tkq.push( [ 'clearIdentity' ] );
- }
-};
diff --git a/plugins/jetpack/_inc/lib/widgets.php b/plugins/jetpack/_inc/lib/widgets.php
deleted file mode 100644
index 3f072b75..00000000
--- a/plugins/jetpack/_inc/lib/widgets.php
+++ /dev/null
@@ -1,776 +0,0 @@
-<?php
-/**
- * Widgets and Sidebars Library
- *
- * Helper functions for manipulating widgets on a per-blog basis.
- * Only helpful on `wp_loaded` or later (currently requires widgets to be registered and the theme context to already be loaded).
- *
- * Used by the REST API
- *
- * @autounit api widgets
- */
-
-class Jetpack_Widgets {
-
- /**
- * Returns the `sidebars_widgets` option with the `array_version` element removed.
- *
- * @return array The current value of sidebars_widgets
- */
- public static function get_sidebars_widgets() {
- $sidebars = get_option( 'sidebars_widgets', array() );
- if ( isset( $sidebars['array_version'] ) ) {
- unset( $sidebars['array_version'] );
- }
- return $sidebars;
- }
-
- /**
- * Format widget data for output and for use by other widget functions.
- *
- * The output looks like:
- *
- * array(
- * 'id' => 'text-3',
- * 'sidebar' => 'sidebar-1',
- * 'position' => '0',
- * 'settings' => array(
- * 'title' => 'hello world'
- * )
- * )
- *
- *
- * @param string|integer $position The position of the widget in its sidebar.
- * @param string $widget_id The widget's id (eg: 'text-3').
- * @param string $sidebar The widget's sidebar id (eg: 'sidebar-1').
- * @param array (Optional) $settings The settings for the widget.
- *
- * @return array A normalized array representing this widget.
- */
- public static function format_widget( $position, $widget_id, $sidebar, $settings = null ) {
- if ( ! $settings ) {
- $all_settings = get_option( self::get_widget_option_name( $widget_id ) );
- $instance = self::get_widget_instance_key( $widget_id );
- $settings = $all_settings[$instance];
- }
- $widget = array();
-
- $widget['id'] = $widget_id;
- $widget['id_base'] = self::get_widget_id_base( $widget_id );
- $widget['settings'] = $settings;
- $widget['sidebar'] = $sidebar;
- $widget['position'] = $position;
-
- return $widget;
- }
-
- /**
- * Return a widget's id_base from its id.
- *
- * @param string $widget_id The id of a widget. (eg: 'text-3')
- *
- * @return string The id_base of a widget (eg: 'text').
- */
- public static function get_widget_id_base( $widget_id ) {
- // Grab what's before the hyphen.
- return substr( $widget_id, 0, strrpos( $widget_id, '-' ) );
- }
-
- /**
- * Determine a widget's option name (the WP option where the widget's settings
- * are stored - generally `widget_` + the widget's id_base).
- *
- * @param string $widget_id The id of a widget. (eg: 'text-3')
- *
- * @return string The option name of the widget's settings. (eg: 'widget_text')
- */
- public static function get_widget_option_name( $widget_id ) {
- return 'widget_' . self::get_widget_id_base( $widget_id );
- }
-
- /**
- * Determine a widget instance key from its ID. (eg: 'text-3' becomes '3').
- * Used to access the widget's settings.
- *
- * @param string $widget_id The id of a widget.
- *
- * @return integer The instance key of that widget.
- */
- public static function get_widget_instance_key( $widget_id ) {
- // Grab all numbers from the end of the id.
- preg_match('/(\d+)$/', $widget_id, $matches );
-
- return intval( $matches[0] );
- }
-
- /**
- * Return a widget by ID (formatted for output) or null if nothing is found.
- *
- * @param string $widget_id The id of a widget to look for.
- *
- * @return array|null The matching formatted widget (see format_widget).
- */
- public static function get_widget_by_id( $widget_id ) {
- $found = null;
- foreach ( self::get_all_widgets() as $widget ) {
- if ( $widget['id'] === $widget_id ) {
- $found = $widget;
- }
- }
- return $found;
- }
-
- /**
- * Return an array of all widgets (active and inactive) formatted for output.
- *
- * @return array An array of all widgets (see format_widget).
- */
- public static function get_all_widgets() {
- $all_widgets = array();
- $sidebars_widgets = self::get_all_sidebars();
-
- foreach ( $sidebars_widgets as $sidebar => $widgets ) {
- if ( ! is_array( $widgets ) ) {
- continue;
- }
- foreach ( $widgets as $key => $widget_id ) {
- array_push( $all_widgets, self::format_widget( $key, $widget_id, $sidebar ) );
- }
- }
-
- return $all_widgets;
- }
-
- /**
- * Return an array of all active widgets formatted for output.
- *
- * @return array An array of all active widgets (see format_widget).
- */
- public static function get_active_widgets() {
- $active_widgets = array();
- $all_widgets = self::get_all_widgets();
- foreach( $all_widgets as $widget ) {
- if ( 'wp_inactive_widgets' === $widget['sidebar'] ) {
- continue;
- }
- array_push( $active_widgets, $widget );
- }
- return $active_widgets;
- }
-
- /**
- * Return an array of all widget IDs (active and inactive)
- *
- * @return array An array of all widget IDs.
- */
- public static function get_all_widget_ids() {
- $all_widgets = array();
- $sidebars_widgets = self::get_all_sidebars();
- foreach ( array_values( $sidebars_widgets ) as $widgets ) {
- if ( ! is_array( $widgets ) ) {
- continue;
- }
- foreach ( array_values( $widgets ) as $widget_id ) {
- array_push( $all_widgets, $widget_id );
- }
- }
- return $all_widgets;
- }
-
- /**
- * Return an array of widgets with a specific id_base (eg: `text`).
- *
- * @param string $id_base The id_base of a widget type.
- *
- * @return array All the formatted widgets matching that widget type (see format_widget).
- */
- public static function get_widgets_with_id_base( $id_base ) {
- $matching_widgets = array();
- foreach ( self::get_all_widgets() as $widget ) {
- if ( self::get_widget_id_base( $widget['id'] ) === $id_base ) {
- array_push( $matching_widgets, $widget );
- }
- }
- return $matching_widgets;
- }
-
- /**
- * Return the array of widget IDs in a sidebar or null if that sidebar does
- * not exist. Will return an empty array for an existing empty sidebar.
- *
- * @param string $sidebar The id of a sidebar.
- *
- * @return array|null The array of widget IDs in the sidebar.
- */
- public static function get_widgets_in_sidebar( $sidebar ) {
- $sidebars = self::get_all_sidebars();
-
-
- if ( ! $sidebars || ! is_array( $sidebars ) ) {
- return null;
- }
- if ( ! $sidebars[ $sidebar ] && array_key_exists( $sidebar, $sidebars ) ) {
- return array();
- }
- return $sidebars[ $sidebar ];
- }
-
- /**
- * Return an associative array of all registered sidebars for this theme,
- * active and inactive, including the hidden disabled widgets sidebar (keyed
- * by `wp_inactive_widgets`). Each sidebar is keyed by the ID of the sidebar
- * and its value is an array of widget IDs for that sidebar.
- *
- * @return array An associative array of all sidebars and their widget IDs.
- */
- public static function get_all_sidebars() {
- $sidebars_widgets = self::get_sidebars_widgets();
-
- if ( ! is_array( $sidebars_widgets ) ) {
- return array();
- }
- return $sidebars_widgets;
- }
-
- /**
- * Return an associative array of all active sidebars for this theme, Each
- * sidebar is keyed by the ID of the sidebar and its value is an array of
- * widget IDs for that sidebar.
- *
- * @return array An associative array of all active sidebars and their widget IDs.
- */
- public static function get_active_sidebars() {
- $sidebars = array();
- foreach ( self::get_all_sidebars() as $sidebar => $widgets ) {
- if ( 'wp_inactive_widgets' === $sidebar || ! isset( $widgets ) || ! is_array( $widgets ) ) {
- continue;
- }
- $sidebars[ $sidebar ] = $widgets;
- }
- return $sidebars;
- }
-
- /**
- * Activates a widget in a sidebar. Does not validate that the sidebar exists,
- * so please do that first. Also does not save the widget's settings. Please
- * do that with `set_widget_settings`.
- *
- * If position is not set, it will be set to the next available position.
- *
- * @param string $widget_id The newly-formed id of the widget to be added.
- * @param string $sidebar The id of the sidebar where the widget will be added.
- * @param string|integer $position (Optional) The position within the sidebar where the widget will be added.
- *
- * @return bool
- */
- public static function add_widget_to_sidebar( $widget_id, $sidebar, $position ) {
- return self::move_widget_to_sidebar( array( 'id' => $widget_id ), $sidebar, $position );
- }
-
- /**
- * Removes a widget from a sidebar. Does not validate that the sidebar exists
- * or remove any settings from the widget, so please do that separately.
- *
- * @param array $widget The widget to be removed.
- */
- public static function remove_widget_from_sidebar( $widget ) {
- $sidebars_widgets = self::get_sidebars_widgets();
- // Remove the widget from its old location and reflow the positions of the remaining widgets.
- array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
-
- update_option( 'sidebars_widgets', $sidebars_widgets );
- }
-
- /**
- * Moves a widget to a sidebar. Does not validate that the sidebar exists,
- * so please do that first. Also does not save the widget's settings. Please
- * do that with `set_widget_settings`. The first argument should be a
- * widget as returned by `format_widget` including `id`, `sidebar`, and
- * `position`.
- *
- * If $position is not set, it will be set to the next available position.
- *
- * Can be used to add a new widget to a sidebar if
- * $widget['sidebar'] === NULL
- *
- * Can be used to move a widget within a sidebar as well if
- * $widget['sidebar'] === $sidebar.
- *
- * @param array $widget The widget to be moved (see format_widget).
- * @param string $sidebar The sidebar where this widget will be moved.
- * @param string|integer $position (Optional) The position where this widget will be moved in the sidebar.
- *
- * @return bool
- */
- public static function move_widget_to_sidebar( $widget, $sidebar, $position ) {
- $sidebars_widgets = self::get_sidebars_widgets();
-
- // If a position is passed and the sidebar isn't empty,
- // splice the widget into the sidebar, update the sidebar option, and return the result
- if ( isset( $widget['sidebar'] ) && isset( $widget['position'] ) ) {
- array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
- }
-
- // Sometimes an existing empty sidebar is NULL, so initialize it.
- if ( array_key_exists( $sidebar, $sidebars_widgets ) && ! is_array( $sidebars_widgets[ $sidebar ] ) ) {
- $sidebars_widgets[ $sidebar ] = array();
- }
-
- // If no position is passed, set one from items in sidebar
- if ( ! isset( $position ) ) {
- $position = 0;
- $last_position = self::get_last_position_in_sidebar( $sidebar );
- if ( isset( $last_position ) && is_numeric( $last_position ) ) {
- $position = $last_position + 1;
- }
- }
-
- // Add the widget to the sidebar and reflow the positions of the other widgets.
- if ( empty( $sidebars_widgets[ $sidebar ] ) ) {
- $sidebars_widgets[ $sidebar ][] = $widget['id'];
- } else {
- array_splice( $sidebars_widgets[ $sidebar ], (int)$position, 0, $widget['id'] );
- }
-
- set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $sidebars_widgets ) );
- return update_option( 'sidebars_widgets', $sidebars_widgets );
- }
-
- /**
- * Return an integer containing the largest position number in a sidebar or
- * null if there are no widgets in that sidebar.
- *
- * @param string $sidebar The id of a sidebar.
- *
- * @return integer|null The last index position of a widget in that sidebar.
- */
- public static function get_last_position_in_sidebar( $sidebar ) {
- $widgets = self::get_widgets_in_sidebar( $sidebar );
- if ( ! $widgets ) {
- return null;
- }
- $last_position = 0;
- foreach ( $widgets as $widget_id ) {
- $widget = self::get_widget_by_id( $widget_id );
- if ( intval( $widget['position'] ) > intval( $last_position ) ) {
- $last_position = intval( $widget['position'] );
- }
- }
- return $last_position;
- }
-
- /**
- * Saves settings for a widget. Does not add that widget to a sidebar. Please
- * do that with `move_widget_to_sidebar` first. Will merge the settings of
- * any existing widget with the same `$widget_id`.
- *
- * @param string $widget_id The id of a widget.
- * @param array $settings An associative array of settings to merge with any existing settings on this widget.
- *
- * @return boolean|WP_Error True if update was successful.
- */
- public static function set_widget_settings( $widget_id, $settings ) {
- $widget_option_name = self::get_widget_option_name( $widget_id );
- $widget_settings = get_option( $widget_option_name );
- $instance_key = self::get_widget_instance_key( $widget_id );
- $old_settings = $widget_settings[ $instance_key ];
-
- if ( ! $settings = self::sanitize_widget_settings( $widget_id, $settings, $old_settings ) ) {
- return new WP_Error( 'invalid_data', 'Update failed.', 500 );
- }
- if ( is_array( $old_settings ) ) {
- // array_filter prevents empty arguments from replacing existing ones
- $settings = wp_parse_args( array_filter( $settings ), $old_settings );
- }
-
- $widget_settings[ $instance_key ] = $settings;
-
- return update_option( $widget_option_name, $widget_settings );
- }
-
- /**
- * Sanitize an associative array for saving.
- *
- * @param string $widget_id The id of a widget.
- * @param array $settings A widget settings array.
- * @param array $old_settings The existing widget settings array.
- *
- * @return array|false The settings array sanitized by `WP_Widget::update` or false if sanitization failed.
- */
- private static function sanitize_widget_settings( $widget_id, $settings, $old_settings ) {
- if ( ! $widget = self::get_registered_widget_object( self::get_widget_id_base( $widget_id ) ) ) {
- return false;
- }
- $new_settings = $widget->update( $settings, $old_settings );
- if ( ! is_array( $new_settings ) ) {
- return false;
- }
- return $new_settings;
- }
-
- /**
- * Deletes settings for a widget. Does not remove that widget to a sidebar. Please
- * do that with `remove_widget_from_sidebar` first.
- *
- * @param array $widget The widget which will have its settings removed (see format_widget).
- */
- public static function remove_widget_settings( $widget ) {
- $widget_option_name = self::get_widget_option_name( $widget['id'] );
- $widget_settings = get_option( $widget_option_name );
- unset( $widget_settings[ self::get_widget_instance_key( $widget['id'] ) ] );
- update_option( $widget_option_name, $widget_settings );
- }
-
- /**
- * Update a widget's settings, sidebar, and position. Returns the (updated)
- * formatted widget if successful or a WP_Error if it fails.
- *
- * @param string $widget_id The id of a widget to update.
- * @param string $sidebar (Optional) A sidebar to which this widget will be moved.
- * @param string|integer (Optional) A new position to which this widget will be moved within its new or existing sidebar.
- * @param array|object|string $settings Settings to merge with the existing settings of the widget (will be passed through `decode_settings`).
- *
- * @return array|WP_Error The newly added widget as an associative array with all the above properties.
- */
- public static function update_widget( $widget_id, $sidebar, $position, $settings ) {
- $settings = self::decode_settings( $settings );
- if ( isset( $settings ) && ! is_array( $settings ) ) {
- return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
- }
- // Default to an empty array if nothing is specified.
- if ( ! is_array( $settings ) ) {
- $settings = array();
- }
- $widget = self::get_widget_by_id( $widget_id );
- if ( ! $widget ) {
- return new WP_Error( 'not_found', 'No widget found.', 400 );
- }
- if ( ! $sidebar ) {
- $sidebar = $widget['sidebar'];
- }
- if ( ! isset( $position ) ) {
- $position = $widget['position'];
- }
- if ( ! is_numeric( $position ) ) {
- return new WP_Error( 'invalid_data', 'Invalid position', 400 );
- }
- $widgets_in_sidebar = self::get_widgets_in_sidebar( $sidebar );
- if ( ! isset( $widgets_in_sidebar ) ) {
- return new WP_Error( 'invalid_data', 'No such sidebar exists', 400 );
- }
- self::move_widget_to_sidebar( $widget, $sidebar, $position );
- $widget_save_status = self::set_widget_settings( $widget_id, $settings );
- if ( is_wp_error( $widget_save_status ) ) {
- return $widget_save_status;
- }
- return self::get_widget_by_id( $widget_id );
- }
-
- /**
- * Deletes a widget entirely including all its settings. Returns a WP_Error if
- * the widget could not be found. Otherwise returns an empty array.
- *
- * @param string $widget_id The id of a widget to delete. (eg: 'text-2')
- *
- * @return array|WP_Error An empty array if successful.
- */
- public static function delete_widget( $widget_id ) {
- $widget = self::get_widget_by_id( $widget_id );
- if ( ! $widget ) {
- return new WP_Error( 'not_found', 'No widget found.', 400 );
- }
- self::remove_widget_from_sidebar( $widget );
- self::remove_widget_settings( $widget );
- return array();
- }
-
- /**
- * Return an array of settings. The input can be either an object, a JSON
- * string, or an array.
- *
- * @param array|string|object $settings The settings of a widget as passed into the API.
- *
- * @return array Decoded associative array of settings.
- */
- public static function decode_settings( $settings ) {
- // Treat as string in case JSON was passed
- if ( is_object( $settings ) && property_exists( $settings, 'scalar' ) ) {
- $settings = $settings->scalar;
- }
- if ( is_object( $settings ) ) {
- $settings = (array) $settings;
- }
- // Attempt to decode JSON string
- if ( is_string( $settings ) ) {
- $settings = (array) json_decode( $settings );
- }
- return $settings;
- }
-
- /**
- * Activate a new widget.
- *
- * @param string $id_base The id_base of the new widget (eg: 'text')
- * @param string $sidebar The id of the sidebar where this widget will go. Dependent on theme. (eg: 'sidebar-1')
- * @param string|integer $position (Optional) The position of the widget in the sidebar. Defaults to the last position.
- * @param array|object|string $settings (Optional) An associative array of settings for this widget (will be passed through `decode_settings`). Varies by widget.
- *
- * @return array|WP_Error The newly added widget as an associative array with all the above properties except 'id_base' replaced with the generated 'id'.
- */
- public static function activate_widget( $id_base, $sidebar, $position, $settings ) {
- if ( ! isset( $id_base ) || ! self::validate_id_base( $id_base ) ) {
- return new WP_Error( 'invalid_data', 'Invalid ID base', 400 );
- }
-
- if ( ! isset( $sidebar ) ) {
- return new WP_Error( 'invalid_data', 'No sidebar provided', 400 );
- }
-
- if ( isset( $position ) && ! is_numeric( $position ) ) {
- return new WP_Error( 'invalid_data', 'Invalid position', 400 );
- }
-
- $settings = self::decode_settings( $settings );
- if ( isset( $settings ) && ! is_array( $settings ) ) {
- return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
- }
-
- // Default to an empty array if nothing is specified.
- if ( ! is_array( $settings ) ) {
- $settings = array();
- }
-
- $widget_counter = 1 + self::get_last_widget_instance_key_with_id_base( $id_base );
- $widget_id = $id_base . '-' . $widget_counter;
- if ( 0 >= $widget_counter ) {
- return new WP_Error( 'invalid_data', 'Error creating widget ID' . $widget_id, 500 );
- }
- if ( self::get_widget_by_id( $widget_id ) ) {
- return new WP_Error( 'invalid_data', 'Widget ID already exists', 500 );
- }
-
- self::add_widget_to_sidebar( $widget_id, $sidebar, $position );
- $widget_save_status = self::set_widget_settings( $widget_id, $settings );
- if ( is_wp_error( $widget_save_status ) ) {
- return $widget_save_status;
- }
-
- // Add a Tracks event for non-Headstart activity.
- if ( ! defined( 'HEADSTART' ) ) {
- jetpack_require_lib( 'tracks/client' );
- jetpack_tracks_record_event( wp_get_current_user(), 'wpcom_widgets_activate_widget', array(
- 'widget' => $id_base,
- 'settings' => json_encode( $settings ),
- ) );
- }
-
- return self::get_widget_by_id( $widget_id );
- }
-
- /**
- * Activate an array of new widgets. Like calling `activate_widget` multiple times.
- *
- * @param array $widgets An array of widget arrays. Each sub-array must be of the format required by `activate_widget`.
- *
- * @return array|WP_Error The newly added widgets in the form returned by `get_all_widgets`.
- */
- public static function activate_widgets( $widgets ) {
- if ( ! is_array( $widgets ) ) {
- return new WP_Error( 'invalid_data', 'Invalid widgets', 400 );
- }
-
- $added_widgets = array();
-
- foreach( $widgets as $widget ) {
- $added_widgets[] = self::activate_widget( $widget['id_base'], $widget['sidebar'], $widget['position'], $widget['settings'] );
- }
-
- return $added_widgets;
- }
-
- /**
- * Return the last instance key (integer) of an existing widget matching
- * `$id_base`. So if you pass in `text`, and there is a widget with the id
- * `text-2`, this function will return `2`.
- *
- * @param string $id_base The id_base of a type of widget. (eg: 'rss')
- *
- * @return integer The last instance key of that type of widget.
- */
- public static function get_last_widget_instance_key_with_id_base( $id_base ) {
- $similar_widgets = self::get_widgets_with_id_base( $id_base );
-
- if ( ! empty( $similar_widgets ) ) {
- // If the last widget with the same name is `text-3`, we want `text-4`
- usort( $similar_widgets, __CLASS__ . '::sort_widgets' );
-
- $last_widget = array_pop( $similar_widgets );
- $last_val = intval( self::get_widget_instance_key( $last_widget['id'] ) );
-
- return $last_val;
- }
-
- return 0;
- }
-
- /**
- * Method used to sort widgets
- *
- * @since 5.4
- *
- * @param array $a
- * @param array $b
- *
- * @return int
- */
- public static function sort_widgets( $a, $b ) {
- $a_val = intval( self::get_widget_instance_key( $a['id'] ) );
- $b_val = intval( self::get_widget_instance_key( $b['id'] ) );
- if ( $a_val > $b_val ) {
- return 1;
- }
- if ( $a_val < $b_val ) {
- return -1;
- }
- return 0;
- }
-
- /**
- * Retrieve a given widget object instance by ID base (eg. 'text' or 'archives').
- *
- * @param string $id_base The id_base of a type of widget.
- *
- * @return WP_Widget|false The found widget object or false if the id_base was not found.
- */
- public static function get_registered_widget_object( $id_base ) {
- if ( ! $id_base ) {
- return false;
- }
-
- // Get all of the registered widgets.
- global $wp_widget_factory;
- if ( ! isset( $wp_widget_factory ) ) {
- return false;
- }
-
- $registered_widgets = $wp_widget_factory->widgets;
- if ( empty( $registered_widgets ) ) {
- return false;
- }
-
- foreach ( array_values( $registered_widgets ) as $registered_widget_object ) {
- if ( $registered_widget_object->id_base === $id_base ) {
- return $registered_widget_object;
- }
- }
- return false;
- }
-
- /**
- * Validate a given widget ID base (eg. 'text' or 'archives').
- *
- * @param string $id_base The id_base of a type of widget.
- *
- * @return boolean True if the widget is of a known type.
- */
- public static function validate_id_base( $id_base ) {
- return ( false !== self::get_registered_widget_object( $id_base ) );
- }
-
- /**
- * Insert a new widget in a given sidebar.
- *
- * @param string $widget_id ID of the widget.
- * @param array $widget_options Content of the widget.
- * @param string $sidebar ID of the sidebar to which the widget will be added.
- *
- * @return WP_Error|true True when data has been saved correctly, error otherwise.
- */
- static function insert_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
- // Retrieve sidebars, widgets and their instances
- $sidebars_widgets = get_option( 'sidebars_widgets', array() );
- $widget_instances = get_option( 'widget_' . $widget_id, array() );
-
- // Retrieve the key of the next widget instance
- $numeric_keys = array_filter( array_keys( $widget_instances ), 'is_int' );
- $next_key = $numeric_keys ? max( $numeric_keys ) + 1 : 2;
-
- // Add this widget to the sidebar
- if ( ! isset( $sidebars_widgets[ $sidebar ] ) ) {
- $sidebars_widgets[ $sidebar ] = array();
- }
- $sidebars_widgets[ $sidebar ][] = $widget_id . '-' . $next_key;
-
- // Add the new widget instance
- $widget_instances[ $next_key ] = $widget_options;
-
- // Store updated sidebars, widgets and their instances
- if (
- ! ( update_option( 'sidebars_widgets', $sidebars_widgets ) )
- || ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) )
- ) {
- return new WP_Error( 'widget_update_failed', 'Failed to update widget or sidebar.', 400 );
- };
-
- return true;
- }
-
- /**
- * Update the content of an existing widget in a given sidebar.
- *
- * @param string $widget_id ID of the widget.
- * @param array $widget_options New content for the update.
- * @param string $sidebar ID of the sidebar to which the widget will be added.
- *
- * @return WP_Error|true True when data has been updated correctly, error otherwise.
- */
- static function update_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
- // Retrieve sidebars, widgets and their instances
- $sidebars_widgets = get_option( 'sidebars_widgets', array() );
- $widget_instances = get_option( 'widget_' . $widget_id, array() );
-
- // Retrieve index of first widget instance in that sidebar
- $widget_key = false;
- foreach ( $sidebars_widgets[ $sidebar ] as $widget ) {
- if ( strpos( $widget, $widget_id ) !== false ) {
- $widget_key = absint( str_replace( $widget_id . '-', '', $widget ) );
- break;
- }
- }
-
- // There is no widget instance
- if ( ! $widget_key ) {
- return new WP_Error( 'invalid_data', 'No such widget.', 400 );
- }
-
- // Update the widget instance and option if the data has changed
- if ( $widget_instances[ $widget_key ]['title'] !== $widget_options['title']
- || $widget_instances[ $widget_key ]['address'] !== $widget_options['address']
- ) {
-
- $widget_instances[ $widget_key ] = array_merge( $widget_instances[ $widget_key ], $widget_options );
-
- // Store updated widget instances and return Error when not successful
- if ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) ) {
- return new WP_Error( 'widget_update_failed', 'Failed to update widget.', 400 );
- };
- };
- return true;
- }
-
- /**
- * Retrieve the first active sidebar.
- *
- * @return string|WP_Error First active sidebar, error if none exists.
- */
- static function get_first_sidebar() {
- $active_sidebars = get_option( 'sidebars_widgets', array() );
- unset( $active_sidebars[ 'wp_inactive_widgets' ], $active_sidebars[ 'array_version' ] );
-
- if ( empty( $active_sidebars ) ) {
- return false;
- }
- $active_sidebars_keys = array_keys( $active_sidebars );
- return array_shift( $active_sidebars_keys );
- }
-}