Нет описания

map.php 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /**
  3. * Map block.
  4. *
  5. * @since 6.8.0
  6. *
  7. * @package automattic/jetpack
  8. */
  9. namespace Automattic\Jetpack\Extensions\Map;
  10. use Automattic\Jetpack\Blocks;
  11. use Automattic\Jetpack\Tracking;
  12. use Jetpack;
  13. use Jetpack_Gutenberg;
  14. use Jetpack_Mapbox_Helper;
  15. const FEATURE_NAME = 'map';
  16. const BLOCK_NAME = 'jetpack/' . FEATURE_NAME;
  17. if ( ! class_exists( 'Jetpack_Mapbox_Helper' ) ) {
  18. \jetpack_require_lib( 'class-jetpack-mapbox-helper' );
  19. }
  20. /**
  21. * Registers the block for use in Gutenberg
  22. * This is done via an action so that we can disable
  23. * registration if we need to.
  24. */
  25. function register_block() {
  26. Blocks::jetpack_register_block(
  27. BLOCK_NAME,
  28. array(
  29. 'render_callback' => __NAMESPACE__ . '\load_assets',
  30. )
  31. );
  32. }
  33. add_action( 'init', __NAMESPACE__ . '\register_block' );
  34. /**
  35. * Record a Tracks event every time the Map block is loaded on WordPress.com and Atomic.
  36. *
  37. * @param string $access_token_source The Mapbox API access token source.
  38. */
  39. function wpcom_load_event( $access_token_source ) {
  40. if ( 'wpcom' !== $access_token_source ) {
  41. return;
  42. }
  43. $event_name = 'map_block_mapbox_wpcom_key_load';
  44. if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
  45. jetpack_require_lib( 'tracks/client' );
  46. tracks_record_event( wp_get_current_user(), $event_name );
  47. } elseif ( jetpack_is_atomic_site() && Jetpack::is_connection_ready() ) {
  48. $tracking = new Tracking();
  49. $tracking->record_user_event( $event_name );
  50. }
  51. }
  52. /**
  53. * Map block registration/dependency declaration.
  54. *
  55. * @param array $attr Array containing the map block attributes.
  56. * @param string $content String containing the map block content.
  57. *
  58. * @return string
  59. */
  60. function load_assets( $attr, $content ) {
  61. $access_token = Jetpack_Mapbox_Helper::get_access_token();
  62. wpcom_load_event( $access_token['source'] );
  63. if ( Blocks::is_amp_request() ) {
  64. static $map_block_counter = array();
  65. $id = get_the_ID();
  66. if ( ! isset( $map_block_counter[ $id ] ) ) {
  67. $map_block_counter[ $id ] = 0;
  68. }
  69. $map_block_counter[ $id ]++;
  70. $iframe_url = add_query_arg(
  71. array(
  72. 'map-block-counter' => absint( $map_block_counter[ $id ] ),
  73. 'map-block-post-id' => $id,
  74. ),
  75. get_permalink()
  76. );
  77. $placeholder = preg_replace( '/(?<=<div\s)/', 'placeholder ', $content );
  78. return sprintf(
  79. '<amp-iframe src="%s" width="%d" height="%d" layout="responsive" allowfullscreen sandbox="allow-scripts">%s</amp-iframe>',
  80. esc_url( $iframe_url ),
  81. 4,
  82. 3,
  83. $placeholder
  84. );
  85. }
  86. Jetpack_Gutenberg::load_assets_as_required( FEATURE_NAME );
  87. return preg_replace( '/<div /', '<div data-api-key="' . esc_attr( $access_token['key'] ) . '" ', $content, 1 );
  88. }
  89. /**
  90. * Render a page containing only a single Map block.
  91. */
  92. function render_single_block_page() {
  93. // phpcs:ignore WordPress.Security.NonceVerification
  94. $map_block_counter = isset( $_GET, $_GET['map-block-counter'] ) ? absint( $_GET['map-block-counter'] ) : null;
  95. // phpcs:ignore WordPress.Security.NonceVerification
  96. $map_block_post_id = isset( $_GET, $_GET['map-block-post-id'] ) ? absint( $_GET['map-block-post-id'] ) : null;
  97. if ( ! $map_block_counter || ! $map_block_post_id ) {
  98. return;
  99. }
  100. /* Create an array of all root-level DIVs that are Map Blocks */
  101. $post = get_post( $map_block_post_id );
  102. if ( ! class_exists( 'DOMDocument' ) ) {
  103. return;
  104. }
  105. $post_html = new \DOMDocument();
  106. /** This filter is already documented in core/wp-includes/post-template.php */
  107. $content = apply_filters( 'the_content', $post->post_content );
  108. /* Suppress warnings */
  109. libxml_use_internal_errors( true );
  110. @$post_html->loadHTML( $content ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
  111. libxml_use_internal_errors( false );
  112. $xpath = new \DOMXPath( $post_html );
  113. $container = $xpath->query( '//div[ contains( @class, "wp-block-jetpack-map" ) ]' )->item( $map_block_counter - 1 );
  114. /* Check that we have a block matching the counter position */
  115. if ( ! $container ) {
  116. return;
  117. }
  118. /* Compile scripts and styles */
  119. ob_start();
  120. add_filter( 'jetpack_is_amp_request', '__return_false' );
  121. Jetpack_Gutenberg::load_assets_as_required( FEATURE_NAME );
  122. wp_scripts()->do_items();
  123. wp_styles()->do_items();
  124. add_filter( 'jetpack_is_amp_request', '__return_true' );
  125. $head_content = ob_get_clean();
  126. /* Put together a new complete document containing only the requested block markup and the scripts/styles needed to render it */
  127. $block_markup = $post_html->saveHTML( $container );
  128. $access_token = Jetpack_Mapbox_Helper::get_access_token();
  129. $page_html = sprintf(
  130. '<!DOCTYPE html><head><style>html, body { margin: 0; padding: 0; }</style>%s</head><body>%s</body>',
  131. $head_content,
  132. preg_replace( '/(?<=<div\s)/', 'data-api-key="' . esc_attr( $access_token['key'] ) . '" ', $block_markup, 1 )
  133. );
  134. echo $page_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
  135. exit;
  136. }
  137. add_action( 'wp', __NAMESPACE__ . '\render_single_block_page' );
  138. /**
  139. * Helper function to generate the markup of the block in PHP.
  140. *
  141. * @param Array $points - Array containing geo location points.
  142. *
  143. * @return string Markup for the jetpack/map block.
  144. */
  145. function map_block_from_geo_points( $points ) {
  146. $map_block_data = array(
  147. 'points' => $points,
  148. 'zoom' => 8,
  149. 'mapCenter' => array(
  150. 'lng' => $points[0]['coordinates']['longitude'],
  151. 'lat' => $points[0]['coordinates']['latitude'],
  152. ),
  153. );
  154. $list_items = array_map(
  155. function ( $point ) {
  156. $link = add_query_arg(
  157. array(
  158. 'api' => 1,
  159. 'query' => $point['coordinates']['latitude'] . ',' . $point['coordinates']['longitude'],
  160. ),
  161. 'https://www.google.com/maps/search/'
  162. );
  163. return sprintf( '<li><a href="%s">%s</a></li>', esc_url( $link ), $point['title'] );
  164. },
  165. $points
  166. );
  167. $map_block = '<!-- wp:jetpack/map ' . wp_json_encode( $map_block_data ) . ' -->' . PHP_EOL;
  168. $map_block .= sprintf(
  169. '<div class="wp-block-jetpack-map" data-map-style="default" data-map-details="true" data-points="%1$s" data-zoom="%2$d" data-map-center="%3$s" data-marker-color="red" data-show-fullscreen-button="true">',
  170. esc_html( wp_json_encode( $map_block_data['points'] ) ),
  171. (int) $map_block_data['zoom'],
  172. esc_html( wp_json_encode( $map_block_data['mapCenter'] ) )
  173. );
  174. $map_block .= '<ul>' . implode( "\n", $list_items ) . '</ul>';
  175. $map_block .= '</div>' . PHP_EOL;
  176. $map_block .= '<!-- /wp:jetpack/map -->';
  177. return $map_block;
  178. }