PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / extensions / blocks / sharing-button / sharing-button.php

sharing-button.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at extensions/blocks/sharing-button/sharing-button.php

357 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sharing Buttons Block.
4 *
5 * @since 13.1
6 *
7 * @package automattic/jetpack
8 */
9
10 namespace Automattic\Jetpack\Extensions\Sharing_Button_Block;
11
12 use Automattic\Jetpack\Blocks;
13 use Automattic\Jetpack\Modules;
14 use Automattic\Jetpack\Status\Host;
15 use Jetpack_Gutenberg;
16 use WP_Block_Template;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit( 0 );
20 }
21
22 require_once __DIR__ . '/class-sharing-source-block.php';
23 require_once __DIR__ . '/components/social-icons.php';
24
25 const PARENT_BLOCK_NAME = 'jetpack/sharing-buttons';
26 const INNER_BLOCK_NAME = 'jetpack/sharing-button';
27
28 /**
29 * Registers the block for use in Gutenberg
30 * This is done via an action so that we can disable
31 * registration if we need to.
32 */
33 function register_block() {
34 Blocks::jetpack_register_block(
35 __DIR__,
36 array( 'render_callback' => __NAMESPACE__ . '\render_block' )
37 );
38
39 /*
40 * Automatically add the sharing block to the end of single posts.
41 */
42 add_filter( 'hooked_block_types', __NAMESPACE__ . '\add_block_to_single_posts_template', 10, 4 );
43 add_filter( 'hooked_block_' . PARENT_BLOCK_NAME, __NAMESPACE__ . '\add_default_services_to_block', 10, 5 );
44 }
45 add_action( 'init', __NAMESPACE__ . '\register_block' );
46
47 /**
48 * Sharing Buttons block registration/dependency declaration.
49 *
50 * @param array $attr Array containing the Sharing Buttons block attributes.
51 * @param string $content String containing the Sharing Buttons block content.
52 * @param object $block Object containing block data.
53 *
54 * @return string
55 */
56 function render_block( $attr, $content, $block ) {
57 $services = get_services();
58 if ( ! isset( $attr['service'] ) || ! array_key_exists( $attr['service'], $services ) ) {
59 return $content;
60 }
61
62 $service_name = $attr['service'];
63 $title = $attr['label'] ?? $service_name;
64 $icon = get_social_logo( $service_name );
65 $style_type = $block->context['styleType'] ?? 'icon-text';
66 $post_id = $block->context['postId'] ?? 0;
67 $data_shared = sprintf(
68 'sharing-%1$s-%2$d',
69 $service_name,
70 $post_id
71 );
72
73 $service = new $services[ $service_name ]( $service_name, array() );
74 $link_props = $service->get_link(
75 $post_id,
76 'share=' . esc_attr( $service_name ) . '&nb=1',
77 esc_attr( $data_shared )
78 );
79 $link_url = $link_props['url'];
80 $link_classes = sprintf(
81 'jetpack-sharing-button__button style-%1$s share-%2$s',
82 $style_type,
83 $service_name
84 );
85
86 $accessible_name = sprintf(
87 /* translators: %s refers to a string representation of sharing service, e.g. Facebook */
88 esc_html__( 'Share on %s', 'jetpack' ),
89 esc_html( $title )
90 );
91
92 $accessible_name .= __( ' (Opens in new window)', 'jetpack' );
93
94 $block_class_name = 'jetpack-sharing-button__list-item';
95
96 if ( $service_name === 'share' ) {
97 $block_class_name .= ' tooltip';
98 /* translators: aria label for SMS sharing button */
99 $accessible_name = esc_attr__( 'Share using Native tools', 'jetpack' );
100 $link_props = $service->get_link( $post_id );
101 }
102
103 $link_url = $link_props['url'];
104 $link_classes = sprintf(
105 'jetpack-sharing-button__button style-%1$s share-%2$s',
106 $style_type,
107 $service_name
108 );
109
110 $styles = array();
111 if (
112 array_key_exists( 'iconColorValue', $block->context )
113 && ! empty( $block->context['iconColorValue'] )
114 ) {
115 $styles['color'] = $block->context['iconColorValue'];
116 }
117 if (
118 array_key_exists( 'iconBackgroundColorValue', $block->context )
119 && ! empty( $block->context['iconBackgroundColorValue'] )
120 ) {
121 $styles['background-color'] = $block->context['iconBackgroundColorValue'];
122 }
123 $link_styles = '';
124 foreach ( $styles as $property => $value ) {
125 $link_styles .= $property . ':' . $value . ';';
126 }
127
128 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
129
130 $component = '<li class="' . esc_attr( $block_class_name ) . '">';
131 $component .= sprintf(
132 '<a href="%1$s" target="_blank" rel="nofollow noopener noreferrer" class="%2$s" style="%3$s" data-service="%4$s" data-shared="%5$s" aria-labelledby="%5$s">',
133 esc_url( $link_url ),
134 esc_attr( $link_classes ),
135 esc_attr( $link_styles ),
136 esc_attr( $service_name ),
137 esc_attr( $data_shared )
138 );
139
140 // Add hidden span with accessible name
141 $component .= sprintf(
142 '<span id="%s" hidden>%s</span>',
143 esc_attr( $data_shared ),
144 esc_html( $accessible_name )
145 );
146
147 $component .= $style_type !== 'text' ? $icon : '';
148 $component .= '<span class="jetpack-sharing-button__service-label" aria-hidden="true">' . esc_html( $title ) . '</span>';
149 if ( $service_name === 'share' ) {
150 $component .= '<span class="tooltiptext" aria-live="assertive">' . esc_html__( 'Copied to clipboard', 'jetpack' ) . '</span>';
151 }
152 $component .= '</a>';
153 $component .= '</li>';
154
155 return $component;
156 }
157
158 /**
159 * Get services for the Sharing Buttons block.
160 *
161 * @return array Array of services.
162 */
163 function get_services() {
164 $services = array(
165 'print' => Share_Print_Block::class,
166 'mail' => Share_Email_Block::class,
167 'facebook' => Share_Facebook_Block::class,
168 'linkedin' => Share_LinkedIn_Block::class,
169 'reddit' => Share_Reddit_Block::class,
170 'twitter' => Share_Twitter_Block::class,
171 'tumblr' => Share_Tumblr_Block::class,
172 'pinterest' => Share_Pinterest_Block::class,
173 'telegram' => Share_Telegram_Block::class,
174 'threads' => Share_Threads_Block::class,
175 'whatsapp' => Jetpack_Share_WhatsApp_Block::class,
176 'mastodon' => Share_Mastodon_Block::class,
177 'nextdoor' => Share_Nextdoor_Block::class,
178 'bluesky' => Share_Bluesky_Block::class,
179 'x' => Share_X_Block::class,
180 'share' => Share_Native_Block::class,
181 );
182
183 return $services;
184 }
185
186 /**
187 * Launch sharing requests on page load when a specific query string is used.
188 *
189 * @return void
190 */
191 function sharing_process_requests() {
192 global $post;
193
194 // Only process if: single post and share={service} defined
195 if ( ( is_page() || is_single() ) && isset( $_GET['share'] ) && is_string( $_GET['share'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
196 $services = get_services();
197 $service_name = sanitize_text_field( wp_unslash( $_GET['share'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
198
199 // Only allow services that have been defined in get_services().
200 if ( ! array_key_exists( $service_name, $services ) ) {
201 return;
202 }
203
204 $service = new $services[ ( $service_name ) ]( $service_name, array() );
205 $service->process_request( $post, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
206 }
207 }
208
209 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking for the data being present.
210 if ( isset( $_GET['share'] ) ) {
211 add_action( 'template_redirect', __NAMESPACE__ . '\sharing_process_requests', 9 );
212 }
213
214 /**
215 * Automatically add the Sharing Buttons block to the end of the Single Posts template.
216 *
217 * @since 13.2
218 *
219 * @param array $hooked_block_types The list of hooked block types.
220 * @param string $relative_position The relative position of the hooked blocks. Can be one of 'before', 'after', 'first_child', or 'last_child'.
221 * @param string $anchor_block_type The anchor block type.
222 * @param WP_Block_Template|array $context The block template, template part, or pattern that the anchor block belongs to.
223 *
224 * @return array
225 */
226 function add_block_to_single_posts_template( $hooked_block_types, $relative_position, $anchor_block_type, $context ) {
227 // Add the block at the end of the post content.
228 if (
229 'after' !== $relative_position
230 || 'core/post-content' !== $anchor_block_type
231 ) {
232 return $hooked_block_types;
233 }
234
235 // Only automate the addition of the block in block-based themes.
236 if ( ! wp_is_block_theme() ) {
237 return $hooked_block_types;
238 }
239
240 // Proceed if the user has toggled the auto-addition in Jetpack settings.
241 if ( ! get_option( 'jetpack_sharing_buttons_auto_add' ) ) {
242 return $hooked_block_types;
243 }
244
245 /*
246 * The Sharing module must be disabled.
247 * We do not want to automatically insert sharing buttons twice.
248 * On WordPress.com Simple the module is always active so we must check differently.
249 * There, we check if buttons are enabled on single posts and pages.
250 */
251 if ( ( new Host() )->is_wpcom_simple() ) {
252 if ( ! class_exists( 'Sharing_Service' ) ) {
253 include_once JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php';
254 }
255
256 $sharer = new \Sharing_Service();
257 $global = $sharer->get_global_options();
258 if (
259 ! $global['show']
260 || in_array( 'post', $global['show'], true )
261 || in_array( 'page', $global['show'], true )
262 ) {
263 return $hooked_block_types;
264 }
265 } elseif ( ( new Modules() )->is_active( 'sharedaddy' ) ) {
266 return $hooked_block_types;
267 }
268
269 // Only hook into page and single post templates.
270 if (
271 ! $context instanceof WP_Block_Template
272 || ! property_exists( $context, 'slug' )
273 || empty( $context->slug )
274 || ! preg_match( '/^(page|single)/', $context->slug )
275 ) {
276 return $hooked_block_types;
277 }
278
279 $hooked_block_types[] = PARENT_BLOCK_NAME;
280 return $hooked_block_types;
281 }
282
283 /**
284 * Add default services to the block we add to the post content by default.
285 *
286 * @since 13.2
287 *
288 * @param array $parsed_hooked_block The parsed block array for the given hooked block type.
289 * @param string $hooked_block_type The hooked block type name.
290 * @param string $relative_position The relative position of the hooked block.
291 * @param array $parsed_anchor_block The anchor block, in parsed block array format.
292 * @param WP_Block_Template|array $context The block template, template part, or pattern that the anchor block.
293 *
294 * @return array
295 */
296 function add_default_services_to_block( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
297 // Is the hooked block adjacent to the anchor block?
298 if ( 'after' !== $relative_position ) {
299 return $parsed_hooked_block;
300 }
301
302 // Use the icon style by default.
303 $parsed_hooked_block['attrs']['styleType'] = 'icon';
304
305 // Add default services (inner blocks) to the block.
306 $parsed_hooked_block['innerBlocks'] = array(
307 array(
308 'blockName' => INNER_BLOCK_NAME,
309 'innerContent' => array(),
310 'attrs' => array(
311 'service' => 'facebook',
312 'label' => esc_html__( 'Facebook', 'jetpack' ),
313 ),
314 ),
315 array(
316 'blockName' => INNER_BLOCK_NAME,
317 'innerContent' => array(),
318 'attrs' => array(
319 'service' => 'x',
320 'label' => esc_html__( 'X', 'jetpack' ),
321 ),
322 ),
323 array(
324 'blockName' => INNER_BLOCK_NAME,
325 'innerContent' => array(),
326 'attrs' => array(
327 'service' => 'mastodon',
328 'label' => esc_html__( 'Mastodon', 'jetpack' ),
329 ),
330 ),
331 );
332
333 // Wrap inner blocks in our sharing buttons markup.
334 $parsed_hooked_block['innerContent'] = array(
335 '<ul class="wp-block-jetpack-sharing-buttons has-normal-icon-size jetpack-sharing-buttons__services-list" id="jetpack-sharing-serivces-list">',
336 null,
337 null,
338 null,
339 '</ul>',
340 );
341
342 // Wrap the whole thing in a group block.
343 return array(
344 'blockName' => 'core/group',
345 'attrs' => array(
346 // Does the anchor block have a layout attribute? If so, use it in the group to maintain the same alignment.
347 'layout' => $parsed_anchor_block['attrs']['layout'] ?? 'null',
348 ),
349 'innerBlocks' => array( $parsed_hooked_block ),
350 'innerContent' => array(
351 '<div class="wp-block-group">',
352 null,
353 '</div>',
354 ),
355 );
356 }
357