diff --git a/projects/plugins/jetpack/.phan/baseline.php b/projects/plugins/jetpack/.phan/baseline.php index 60c2077e00526..f194e666f4e5a 100644 --- a/projects/plugins/jetpack/.phan/baseline.php +++ b/projects/plugins/jetpack/.phan/baseline.php @@ -25,8 +25,8 @@ // PhanParamTooMany : 40+ occurrences // PhanPluginDuplicateAdjacentStatement : 40+ occurrences // PhanTypeMismatchArgumentInternal : 40+ occurrences - // PhanUndeclaredProperty : 40+ occurrences // PhanTypeExpectedObjectPropAccess : 35+ occurrences + // PhanUndeclaredProperty : 35+ occurrences // PhanParamSignatureMismatch : 25+ occurrences // PhanTypeMismatchDefault : 25+ occurrences // PhanTypeMismatchPropertyProbablyReal : 25+ occurrences @@ -320,8 +320,6 @@ 'modules/comments/subscription-modal-on-comment/class-jetpack-subscription-modal-on-comment.php' => ['PhanTypeMismatchReturnNullable'], 'modules/copy-post.php' => ['PhanNoopNew'], 'modules/custom-content-types.php' => ['PhanRedefineFunction'], - 'modules/geo-location.php' => ['PhanTypeMismatchArgumentNullable'], - 'modules/geo-location/class.jetpack-geo-location.php' => ['PhanTypeMismatchArgument'], 'modules/google-fonts/current/class-jetpack-google-font-face.php' => ['PhanUndeclaredFunctionInCallable'], 'modules/google-fonts/current/load-google-fonts.php' => ['PhanNoopNew', 'PhanTypeArraySuspicious', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchReturnProbablyReal'], 'modules/gravatar-hovercards.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchArgumentProbablyReal'], @@ -506,7 +504,6 @@ 'tests/php/json-api/test_class.json-api-platform-jetpack.php' => ['PhanTypeMismatchArgument'], 'tests/php/media/test-class.jetpack-media-extractor.php' => ['PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal'], 'tests/php/media/test-class.jetpack-post-images.php' => ['PhanTypeMismatchArgumentProbablyReal'], - 'tests/php/modules/geo-location/test_class.jetpack-geo-location.php' => ['PhanTypeMismatchArgumentProbablyReal'], 'tests/php/modules/photon/test_class.jetpack-photon-static-asset-cdn.php' => ['PhanTypeMismatchProperty'], 'tests/php/modules/post-by-email/test-class.post-by-email-api.php' => ['PhanParamTooMany'], 'tests/php/modules/publicize/test_class.publicize.php' => ['PhanPluginDuplicateConditionalNullCoalescing'], diff --git a/projects/plugins/jetpack/changelog/remove-remove-jetpack-geo-location-2 b/projects/plugins/jetpack/changelog/remove-remove-jetpack-geo-location-2 new file mode 100644 index 0000000000000..7ad726083cde6 --- /dev/null +++ b/projects/plugins/jetpack/changelog/remove-remove-jetpack-geo-location-2 @@ -0,0 +1,4 @@ +Significance: minor +Type: other + +Deprecate Jetpack geo location module diff --git a/projects/plugins/jetpack/modules/geo-location.php b/projects/plugins/jetpack/modules/geo-location.php index 59fb2c0db6b39..7bb85a0bce383 100644 --- a/projects/plugins/jetpack/modules/geo-location.php +++ b/projects/plugins/jetpack/modules/geo-location.php @@ -1,97 +1,9 @@ - null, - 'id' => null, - ), - $attributes - ); - return jetpack_geo_get_location( $attributes['post'] ? $attributes['post'] : $attributes['id'] ); -} -add_shortcode( 'geo-location', 'jetpack_geo_shortcode' ); - -/** - * Get the geo-location data associated with the supplied post ID, if it's available - * and marked as being available for public display. The returned array will contain - * "latitude", "longitude" and "label" keys. - * - * If you do not supply a value for $post_id, the global $post will be used, if - * available. - * - * @param integer|null $post_id Post ID. - * - * @return array|null - */ -function jetpack_geo_get_data( $post_id = null ) { - $geo = Jetpack_Geo_Location::init(); - - if ( ! $post_id ) { - $post_id = $geo->get_post_id(); - } - - $meta_values = $geo->get_meta_values( $post_id ); - - if ( ! $meta_values['is_public'] || ! $meta_values['is_populated'] ) { - return null; - } - - return array( - 'latitude' => $meta_values['latitude'], - 'longitude' => $meta_values['longitude'], - 'label' => $meta_values['label'], - ); -} - -/** - * Display the label HTML for the geo-location information associated with the supplied - * post ID. - * - * If you do not supply a value for $post_id, the global $post will be used, if - * available. - * - * @param integer|null $post_id Post ID. - * - * @return void - */ -function jetpack_geo_display_location( $post_id = null ) { - echo jetpack_geo_get_location( $post_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in `Jetpack_Geo_Location::get_location_label`. -} - -/** - * Return the label HTML for the geo-location information associated with the supplied - * post ID. - * - * If you do not supply a value for $post_id, the global $post will be used, if - * available. - * - * @param integer|null $post_id Post ID. - * - * @return string - */ -function jetpack_geo_get_location( $post_id = null ) { - return Jetpack_Geo_Location::init()->get_location_label( $post_id ); -} diff --git a/projects/plugins/jetpack/modules/geo-location/class.jetpack-geo-location.php b/projects/plugins/jetpack/modules/geo-location/class.jetpack-geo-location.php index 17f5643365a68..450edb92ee352 100644 --- a/projects/plugins/jetpack/modules/geo-location/class.jetpack-geo-location.php +++ b/projects/plugins/jetpack/modules/geo-location/class.jetpack-geo-location.php @@ -38,15 +38,6 @@ class Jetpack_Geo_Location { */ private static $instance; - /** - * Whether dashicons are enqueued. - * - * @since 6.6.0 - * - * @var bool - */ - private static $style_enqueued = false; - /** * Jetpack_Geo_Location instance init. */ @@ -58,22 +49,11 @@ public static function init() { return self::$instance; } - /** - * This is mostly just used for testing purposes. - */ - public static function reset_instance() { - self::$instance = null; - } - /** * Jetpack_Geo_Location class constructor. */ public function __construct() { add_action( 'init', array( $this, 'wordpress_init' ) ); - add_action( 'wp_head', array( $this, 'wp_head' ) ); - add_filter( 'the_content', array( $this, 'the_content_microformat' ) ); - - $this->register_rss_hooks(); } /** @@ -83,357 +63,8 @@ public function __construct() { public function wordpress_init() { // Only render location label after post content, if the theme claims to support "geo-location". if ( current_theme_supports( 'jetpack-geo-location' ) ) { - add_filter( 'the_content', array( $this, 'the_content_location_display' ), 15, 1 ); - } - - add_post_type_support( 'post', 'geo-location' ); - add_post_type_support( 'page', 'geo-location' ); - - register_meta( - 'post', - 'geo_public', - array( - 'sanitize_callback' => array( $this, 'sanitize_public' ), - 'type' => 'boolean', - 'single' => true, - ) - ); - - register_meta( - 'post', - 'geo_latitude', - array( - 'sanitize_callback' => array( $this, 'sanitize_coordinate' ), - 'type' => 'float', - 'single' => true, - ) - ); - - register_meta( - 'post', - 'geo_longitude', - array( - 'sanitize_callback' => array( $this, 'sanitize_coordinate' ), - 'type' => 'float', - 'single' => true, - ) - ); - - register_meta( - 'post', - 'geo_address', - array( - 'sanitize_callback' => 'sanitize_text_field', - 'type' => 'string', - 'single' => true, - ) - ); - } - - /** - * Filter "public" input to always be either 1 or 0. - * - * @param mixed $public Value to normalize. - * - * @return int - */ - public function sanitize_public( $public ) { - return absint( $public ) ? 1 : 0; - } - - /** - * Filter geo coordinates and normalize them to floats with 7 digits of precision. - * - * @param mixed $coordinate Latitude or longitude coordinate. - * - * @return float|null - */ - public function sanitize_coordinate( $coordinate ) { - if ( ! $coordinate ) { - return null; + _deprecated_class( 'Jetpack_Geo_Location', '$$next-version$$', '' ); } - - return round( (float) $coordinate, 7 ); - } - - /** - * Render geo.position and ICBM meta tags with public geo meta values when rendering - * a single post. - */ - public function wp_head() { - if ( ! is_single() ) { - return; - } - - $meta_values = $this->get_meta_values( $this->get_post_id() ); - - if ( ! $meta_values['is_public'] ) { - return; - } - - if ( ! self::$style_enqueued ) { - // only enqueue scripts and styles when needed. - self::enqueue_scripts(); - self::$style_enqueued = true; - } - - echo "\n\n"; - - if ( $meta_values['label'] ) { - printf( - '', - esc_attr( $meta_values['label'] ) - ); - } - - printf( - '' . PHP_EOL, - esc_attr( $meta_values['latitude'] ), - esc_attr( $meta_values['longitude'] ) - ); - - printf( - '' . PHP_EOL, - esc_attr( $meta_values['latitude'] ), - esc_attr( $meta_values['longitude'] ) - ); - - echo "\n\n"; - } - - /** - * Append public meta values in the Geo microformat (https://en.wikipedia.org/wiki/Geo_(microformat) - * to the supplied content. - * - * Note that we cannot render the microformat in the context of an excerpt because tags are stripped - * in that context, making our microformat data visible. - * - * @param string $content Current post content. - * - * @return string - */ - public function the_content_microformat( $content ) { - if ( is_feed() || $this->is_currently_excerpt_filter() ) { - return $content; - } - - $meta_values = $this->get_meta_values( $this->get_post_id() ); - - if ( ! $meta_values['is_public'] ) { - return $content; - } - - $microformat = sprintf( - ''; - - return $content . $microformat; - } - - /** - * Register a range of hooks for integrating geo data with various feeds. - */ - public function register_rss_hooks() { - add_action( 'rss2_ns', array( $this, 'rss_namespace' ) ); - add_action( 'atom_ns', array( $this, 'rss_namespace' ) ); - add_action( 'rdf_ns', array( $this, 'rss_namespace' ) ); - add_action( 'rss_item', array( $this, 'rss_item' ) ); - add_action( 'rss2_item', array( $this, 'rss_item' ) ); - add_action( 'atom_entry', array( $this, 'rss_item' ) ); - add_action( 'rdf_item', array( $this, 'rss_item' ) ); - } - - /** - * Add the georss namespace during RSS generation. - */ - public function rss_namespace() { - echo PHP_EOL . "\t" . 'xmlns:georss="http://www.georss.org/georss"'; - echo PHP_EOL . "\t" . 'xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"'; - echo PHP_EOL . "\t"; - } - - /** - * Output georss data for RSS items, assuming we have data for the currently rendered post and - * that data as marked as public. - */ - public function rss_item() { - $meta_values = $this->get_meta_values( $this->get_post_id() ); - - if ( ! $meta_values['is_public'] ) { - return; - } - - printf( - "\t%s %s\n", - ent2ncr( esc_html( $meta_values['latitude'] ) ), - ent2ncr( esc_html( $meta_values['longitude'] ) ) - ); - - printf( "\t\t%s\n", ent2ncr( esc_html( $meta_values['latitude'] ) ) ); - printf( "\t\t%s\n", ent2ncr( esc_html( $meta_values['longitude'] ) ) ); - } - - /** - * Enqueue CSS for rendering post flair with geo-location. - */ - private static function enqueue_scripts() { - wp_enqueue_style( 'dashicons' ); - } - - /** - * If we're rendering a single post and public geo-location data is available for it, - * include the human-friendly location label in the output. - * - * @param string $content Current post content. - * - * @return string - */ - public function the_content_location_display( $content ) { - if ( ! is_single() ) { - return $content; - } - - return $content . $this->get_location_label(); - } - - /** - * Get the HTML for displaying a label representing the location associated with the - * supplied post ID. If no post ID is given, we'll use the global $post variable, if - * it is available. - * - * @param integer|null $post_id Post ID. - * - * @return string - */ - public function get_location_label( $post_id = null ) { - $meta_values = $this->get_meta_values( $post_id ? $post_id : $this->get_post_id() ); - - if ( ! $meta_values['is_public'] ) { - return ''; - } - - // If the location has not been labeled, do not show the location. - if ( ! $meta_values['label'] ) { - return ''; - } - - $html = '
'; - $html .= ' '; - $html .= esc_html( $meta_values['label'] ); - $html .= '
'; - - /** - * Allow modification or replacement of the default geo-location display HTML. - * - * @module geo-location - * - * @param array $html The default HTML for displaying a geo-location label. - * @param array $geo_data An array containing "latitude", "longitude" and "label". - */ - $html = apply_filters( 'jetpack_geo_location_display', $html, $meta_values ); - - return $html; - } - - /** - * Get the ID of the current global post object, if available. Otherwise, return null. - * - * This isolates the access of the global scope to this single method, making it easier to - * safeguard against unexpected missing $post objects in other hook functions. - * - * @return int|null - */ - public function get_post_id() { - global $post; - - if ( ! isset( $post ) || ! $post || ! is_object( $post ) || ! isset( $post->ID ) ) { - return null; - } - - return $post->ID; - } - - /** - * Retrieve geo-location post_meta data for the specified post ID. - * - * This method always returns an array with the following structure: - * - * array(is_public => bool, latitude => float, longitude => float, label => string, is_populated => bool) - * - * So, regardless of whether your post actually has values in postmeta for the geo-location fields, - * you can be sure that you can reference those array keys in calling code without having to juggle - * isset(), array_key_exists(), etc. - * - * Mocking this method during testing can also be useful for testing output and logic in various - * hook functions. - * - * @param integer $post_id Post ID. - * - * @return array A predictably structured array representing the meta values for the supplied post ID. - */ - public function get_meta_values( $post_id ) { - $meta_values = array( - 'is_public' => (bool) $this->sanitize_public( $this->get_meta_value( $post_id, 'public' ) ), - 'latitude' => $this->sanitize_coordinate( $this->get_meta_value( $post_id, 'latitude' ) ), - 'longitude' => $this->sanitize_coordinate( $this->get_meta_value( $post_id, 'longitude' ) ), - 'label' => trim( (string) $this->get_meta_value( $post_id, 'address' ) ), - 'is_populated' => false, - ); - - if ( $meta_values['latitude'] && $meta_values['longitude'] && $meta_values['label'] ) { - $meta_values['is_populated'] = true; - } - - return $meta_values; - } - - /** - * This function wraps get_post_meta() to enable us to keep the "geo_" prefix isolated to a single - * location in the code and to assist in mocking during testing. - * - * @param integer $post_id Post ID. - * @param string $meta_field_name The meta field to retrieve. - * - * @return mixed - */ - public function get_meta_value( $post_id, $meta_field_name ) { - if ( ! $post_id ) { - return null; - } - - return get_post_meta( $post_id, 'geo_' . $meta_field_name, true ); - } - - /** - * Check to see if the current filter is the get_the_excerpt filter. - * - * Just checking current_filter() here is not adequate because current_filter() only looks - * at the last element in the $wp_current_filter array. In the context of rendering an - * excerpt, however, both get_the_excerpt and the_content are present in that array. - * - * @return bool - */ - public function is_currently_excerpt_filter() { - if ( ! isset( $GLOBALS['wp_current_filter'] ) ) { - return false; - } - - $current_filters = (array) $GLOBALS['wp_current_filter']; - - return in_array( 'get_the_excerpt', $current_filters, true ); } } diff --git a/projects/plugins/jetpack/phpunit.xml.dist b/projects/plugins/jetpack/phpunit.xml.dist index a0a8c38084781..58073536202dc 100644 --- a/projects/plugins/jetpack/phpunit.xml.dist +++ b/projects/plugins/jetpack/phpunit.xml.dist @@ -87,9 +87,6 @@ tests/php/modules/carousel - - tests/php/modules/geo-location - tests/php/test_deprecation.php diff --git a/projects/plugins/jetpack/tests/php/modules/geo-location/test_class.jetpack-geo-location.php b/projects/plugins/jetpack/tests/php/modules/geo-location/test_class.jetpack-geo-location.php deleted file mode 100644 index 397a5969143ee..0000000000000 --- a/projects/plugins/jetpack/tests/php/modules/geo-location/test_class.jetpack-geo-location.php +++ /dev/null @@ -1,454 +0,0 @@ -ID = 1; - $post->post_type = 'post'; - - $this->original_wp_query = $wp_query; - } - - /** - * Tear down. - */ - public function tear_down() { - global $wp_query; - - Jetpack_Geo_Location::reset_instance(); - - $wp_query = $this->original_wp_query; - - parent::tear_down(); - } - - public function test_location_display_filter_skipped_when_lacking_theme_support() { - $instance = $this->create_mock_instance( - array( 'the_content_location_display' ), - array( 'current_theme_supports' ), - self::ENABLE_CONSTRUCTOR - ); - - $instance->method( 'current_theme_supports' )->willReturn( false ); - - $instance->expects( $this->never() ) - ->method( 'the_content_location_display' ); - - $instance->wordpress_init(); - - apply_filters( 'the_content', 'Test' ); - } - - public function test_location_display_filter_called_when_theme_supports_geo_location() { - $theme_support = current_theme_supports( 'jetpack-geo-location' ); - - if ( ! $theme_support ) { - add_theme_support( 'jetpack-geo-location' ); - } - - $instance = $this->create_mock_instance( - array( 'the_content_location_display' ), - array(), - self::ENABLE_CONSTRUCTOR - ); - - $instance->expects( $this->atLeastOnce() ) - ->method( 'the_content_location_display' ); - - $instance->wordpress_init(); - - apply_filters( 'the_content', 'Test' ); - - // Remove theme support again if it was missing originally. - if ( ! $theme_support ) { - remove_theme_support( 'jetpack-geo-location' ); - } - } - - public function test_get_meta_values_returns_valid_array_for_nonexistent_post() { - $instance = $this->get_instance(); - $meta_values = $instance->get_meta_values( 100 ); - - $this->assertIsArray( $meta_values ); - - $this->assertArrayHasKey( 'is_public', $meta_values ); - $this->assertArrayHasKey( 'latitude', $meta_values ); - $this->assertArrayHasKey( 'longitude', $meta_values ); - $this->assertArrayHasKey( 'label', $meta_values ); - $this->assertArrayHasKey( 'is_populated', $meta_values ); - - $this->assertFalse( $meta_values['is_public'] ); - $this->assertNull( $meta_values['latitude'] ); - $this->assertNull( $meta_values['longitude'] ); - $this->assertSame( '', $meta_values['label'] ); - $this->assertFalse( $meta_values['is_populated'] ); - } - - public function test_get_meta_values_returns_valid_array_for_null_post() { - $instance = $this->get_instance(); - $meta_values = $instance->get_meta_values( null ); - - $this->assertIsArray( $meta_values ); - - $this->assertArrayHasKey( 'is_public', $meta_values ); - $this->assertArrayHasKey( 'latitude', $meta_values ); - $this->assertArrayHasKey( 'longitude', $meta_values ); - $this->assertArrayHasKey( 'label', $meta_values ); - $this->assertArrayHasKey( 'is_populated', $meta_values ); - - $this->assertFalse( $meta_values['is_public'] ); - $this->assertNull( $meta_values['latitude'] ); - $this->assertNull( $meta_values['longitude'] ); - $this->assertSame( '', $meta_values['label'] ); - $this->assertFalse( $meta_values['is_populated'] ); - } - - public function test_get_meta_values_with_existing_post_returns_expected_values() { - $instance = $this->get_instance_with_mock_public_post(); - $meta_values = $instance->get_meta_values( 1 ); - - $this->assertIsArray( $meta_values ); - - $this->assertArrayHasKey( 'is_public', $meta_values ); - $this->assertArrayHasKey( 'latitude', $meta_values ); - $this->assertArrayHasKey( 'longitude', $meta_values ); - $this->assertArrayHasKey( 'label', $meta_values ); - $this->assertArrayHasKey( 'is_populated', $meta_values ); - - $this->assertTrue( $meta_values['is_public'] ); - $this->assertEquals( (float) self::MOCK_LAT, $meta_values['latitude'] ); - $this->assertEquals( (float) self::MOCK_LONG, $meta_values['longitude'] ); - $this->assertEquals( self::MOCK_ADDRESS, $meta_values['label'] ); - $this->assertTrue( $meta_values['is_populated'] ); - } - - public function test_rss_namespace_method_renders_the_namespace() { - ob_start(); - $this->get_instance()->rss_namespace(); - $this->assertStringContainsString( 'georss.org', ob_get_clean() ); - } - - public function test_rss_item_does_not_render_private_post() { - $instance = $this->get_instance_with_mock_private_post(); - - ob_start(); - $instance->rss_item(); - $output = ob_get_clean(); - - $this->assertStringNotContainsString( self::MOCK_LAT, $output ); - $this->assertStringNotContainsString( self::MOCK_LONG, $output ); - } - - public function test_rss_item_does_render_public_post() { - $instance = $this->get_instance_with_mock_public_post(); - - ob_start(); - $instance->rss_item(); - $output = ob_get_clean(); - - $this->assertStringContainsString( self::MOCK_LAT, $output ); - $this->assertStringContainsString( self::MOCK_LONG, $output ); - } - - public function test_rss_item_does_escape_malicious_post() { - $instance = $this->get_instance_with_mock_malicious_post(); - - ob_start(); - $instance->rss_item(); - $output = ob_get_clean(); - - $this->assertStringNotContainsString( '', $output ); - $this->assertStringContainsString( '<', $output ); - $this->assertStringContainsString( '>', $output ); - } - - public function test_wp_head_aborts_when_not_a_single_post_response() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_not_single(); - - ob_start(); - $instance->wp_head(); - $output = ob_get_clean(); - - $this->assertSame( '', trim( $output ) ); - } - - public function test_wp_head_aborts_when_meta_values_are_private() { - $instance = $this->get_instance_with_mock_private_post(); - - $this->mock_is_single(); - - ob_start(); - $instance->wp_head(); - $output = ob_get_clean(); - - $this->assertSame( '', trim( $output ) ); - } - - public function test_wp_head_renders_public_meta_values() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_single(); - - ob_start(); - $instance->wp_head(); - $output = ob_get_clean(); - - $this->assertStringContainsString( self::MOCK_LAT, $output ); - $this->assertStringContainsString( self::MOCK_LONG, $output ); - } - - public function test_wp_head_escapes_malicious_meta_values() { - $instance = $this->get_instance_with_mock_malicious_post(); - - $this->mock_is_single(); - - ob_start(); - $instance->wp_head(); - $output = ob_get_clean(); - - $this->assertStringNotContainsString( '', $output ); - $this->assertStringContainsString( '<', $output ); - $this->assertStringContainsString( '>', $output ); - } - - public function test_the_content_microformat_aborts_when_is_feed() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_feed(); - - $this->assertEquals( 'Original content', $instance->the_content_microformat( 'Original content' ) ); - } - - public function test_the_content_microformat_aborts_when_meta_values_are_private() { - $instance = $this->get_instance_with_mock_private_post(); - - $this->mock_is_not_feed(); - - $this->assertEquals( 'Original content', $instance->the_content_microformat( 'Original content' ) ); - } - - public function test_the_content_microformat_appends_microformat_when_meta_values_are_public() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_not_feed(); - - $modified_content = $instance->the_content_microformat( 'Original content' ); - - $this->assertStringStartsWith( 'Original content', $modified_content ); - $this->assertStringContainsString( self::MOCK_LAT, $modified_content ); - $this->assertStringContainsString( self::MOCK_LONG, $modified_content ); - $this->assertStringContainsString( '', $modified_content ); - $this->assertStringContainsString( '', $modified_content ); - } - - public function test_the_content_microformat_escapes_malicious_meta_values() { - $instance = $this->get_instance_with_mock_malicious_post(); - - $this->mock_is_not_feed(); - - $modified_content = $instance->the_content_microformat( 'Original content' ); - - $this->assertStringStartsWith( 'Original content', $modified_content ); - $this->assertStringNotContainsString( '', $modified_content ); - $this->assertStringContainsString( '<', $modified_content ); - $this->assertStringContainsString( '>', $modified_content ); - } - - public function test_the_content_location_display_aborts_when_is_not_single() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_not_single(); - - $this->assertEquals( 'Original content', $instance->the_content_location_display( 'Original content' ) ); - } - - public function test_the_content_location_display_aborts_when_meta_values_are_private() { - $instance = $this->get_instance_with_mock_private_post(); - - $this->mock_is_single(); - - $this->assertEquals( 'Original content', $instance->the_content_location_display( 'Original content' ) ); - } - - public function test_the_content_location_display_appends_microformat_when_meta_values_are_public() { - $instance = $this->get_instance_with_mock_public_post(); - - $this->mock_is_single(); - - $modified_content = $instance->the_content_location_display( 'Original content' ); - - $this->assertStringStartsWith( 'Original content', $modified_content ); - $this->assertStringContainsString( self::MOCK_ADDRESS, $modified_content ); - } - - public function test_the_content_location_display_escapes_malicious_meta_values() { - $instance = $this->get_instance_with_mock_malicious_post(); - - $this->mock_is_single(); - - $modified_content = $instance->the_content_location_display( 'Original content' ); - - $this->assertStringStartsWith( 'Original content', $modified_content ); - $this->assertStringNotContainsString( '', $modified_content ); - $this->assertStringContainsString( '<', $modified_content ); - $this->assertStringContainsString( '>', $modified_content ); - } - - private function get_instance() { - return Jetpack_Geo_Location::init(); - } - - private function get_instance_with_mock_public_post() { - $instance = $this->create_mock_instance(); - - $instance->method( 'get_meta_value' ) - ->willReturnMap( - array( - array( 1, 'public', '1' ), - array( 1, 'latitude', self::MOCK_LAT ), - array( 1, 'longitude', self::MOCK_LONG ), - array( 1, 'address', self::MOCK_ADDRESS ), - ) - ); - - return $instance; - } - - private function get_instance_with_mock_malicious_post() { - $instance = $this->create_mock_instance( array( 'get_meta_values' ) ); - - $instance->method( 'get_meta_values' ) - ->willReturn( - array( - 'is_public' => true, - 'latitude' => '', - 'longitude' => '', - 'label' => '', - 'is_populated' => true, - ) - ); - - return $instance; - } - - private function get_instance_with_mock_private_post() { - $instance = $this->create_mock_instance(); - - $instance->method( 'get_meta_value' ) - ->willReturnMap( - array( - array( 1, 'public', '0' ), - array( 1, 'latitude', self::MOCK_LAT ), - array( 1, 'longitude', self::MOCK_LONG ), - array( 1, 'address', self::MOCK_ADDRESS ), - ) - ); - - return $instance; - } - - /** - * @param string[] $existing_methods_to_mock Methods to mock that exist on the class. - * @param string[] $new_methods_to_mock Methods to mock that do not on the class. - * @param boolean $disable_constructor - * @return Jetpack_Geo_Location&\PHPUnit\Framework\MockObject\MockObject - */ - private function create_mock_instance( - $existing_methods_to_mock = array(), - $new_methods_to_mock = array(), - $disable_constructor = self::DISABLE_CONSTRUCTOR - ) { - $existing_methods_to_mock = array_merge( - array( 'get_meta_value' ), - $existing_methods_to_mock - ); - - $builder = $this->getMockBuilder( Jetpack_Geo_Location::class ) - ->onlyMethods( $existing_methods_to_mock ); - // This throws an error if passed an empty array. - if ( ! empty( $new_methods_to_mock ) ) { - $builder->addMethods( $new_methods_to_mock ); - } - - if ( $disable_constructor ) { - $builder->disableOriginalConstructor(); - } - - return $builder->getMock(); - } - - private function mock_is_single() { - global $wp_query; - - $wp_query = $this->getMockBuilder( WP_Query::class ) - ->onlyMethods( array( 'is_feed', 'is_single' ) ) - ->getMock(); - - $wp_query->expects( $this->any() ) - ->method( 'is_single' ) - ->willReturn( true ); - } - - private function mock_is_not_single() { - global $wp_query; - - $wp_query = $this->getMockBuilder( WP_Query::class ) - ->onlyMethods( array( 'is_feed', 'is_single' ) ) - ->getMock(); - - $wp_query->expects( $this->any() ) - ->method( 'is_single' ) - ->willReturn( false ); - } - - private function mock_is_feed() { - global $wp_query; - - $wp_query = $this->getMockBuilder( WP_Query::class ) - ->onlyMethods( array( 'is_feed' ) ) - ->getMock(); - - $wp_query->expects( $this->any() ) - ->method( 'is_feed' ) - ->willReturn( true ); - } - - private function mock_is_not_feed() { - global $wp_query; - - $wp_query = $this->getMockBuilder( WP_Query::class ) - ->onlyMethods( array( 'is_feed' ) ) - ->getMock(); - - $wp_query->expects( $this->any() ) - ->method( 'is_feed' ) - ->willReturn( false ); - } -}