Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
Twitter_Site_Tag
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
4 / 4
8
100.00% covered (success)
100.00%
1 / 1
 is_available
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 save
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 update
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * The Twitter Site Tag setting on Settings > Sharing.
4 *
5 * @package automattic/jetpack-sharing-likes
6 */
7
8declare( strict_types = 1 );
9
10namespace Automattic\Jetpack\Sharing_Likes\Settings;
11
12/**
13 * The Twitter username Twitter Cards name as the site's owner.
14 *
15 * Not gated on the Sharing module: the Sharing Buttons block reads it too, so
16 * whichever section hosts the settings table renders it.
17 */
18final class Twitter_Site_Tag {
19
20    /**
21     * Option holding the username, without its leading `@`. Also the field name.
22     */
23    public const OPTION = 'jetpack-twitter-cards-site-tag';
24
25    /**
26     * Whether the setting has anything to configure on this site.
27     */
28    public static function is_available(): bool {
29        return Environment::twitter_site_tag_used();
30    }
31
32    /**
33     * Table rows for the setting, headed by the feature it configures.
34     */
35    public static function render(): string {
36        if ( ! self::is_available() ) {
37            return '';
38        }
39
40        // `style="width: auto"` on the description is load-bearing: sharedaddy's admin
41        // stylesheet constrains `.description` to 180px for the services table's columns.
42        return sprintf(
43            '<tr><td colspan="2"><h3>%1$s</h3></td></tr>
44            <tr valign="top">
45                <th scope="row"><label for="%2$s">%3$s</label></th>
46                <td>
47                    <input type="text" id="%2$s" class="regular-text" name="%2$s" value="%4$s" />
48                    <p class="description" style="width: auto;">%5$s</p>
49                </td>
50            </tr>',
51            esc_html__( 'Twitter Cards', 'jetpack-sharing-likes' ),
52            esc_attr( self::OPTION ),
53            esc_html__( 'Twitter Site Tag', 'jetpack-sharing-likes' ),
54            esc_attr( (string) get_option( self::OPTION, '' ) ),
55            esc_html__( 'The Twitter username of the owner of this site’s domain.', 'jetpack-sharing-likes' )
56        );
57    }
58
59    /**
60     * Save the posted username. Callers verify the nonce.
61     */
62    public static function save(): void {
63        if ( ! self::is_available() ) {
64            return;
65        }
66
67        // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- verified by the caller; update() strips tags.
68        $posted = isset( $_POST[ self::OPTION ] ) && is_string( $_POST[ self::OPTION ] ) ? wp_unslash( $_POST[ self::OPTION ] ) : '';
69
70        self::update( $posted );
71    }
72
73    /**
74     * Store a username, with or without its leading `@`.
75     *
76     * @param string $username Username as entered.
77     */
78    public static function update( string $username ): void {
79        // Not `sanitize_text_field()`, which encodes a stray `<` rather than dropping it:
80        // the REST writer in plugins/jetpack sanitizes this same option with tag stripping.
81        $username = wp_strip_all_tags( $username );
82
83        // Trim after the `@`, not just before it: `@ jetpack` would otherwise store a leading space.
84        update_option( self::OPTION, trim( ltrim( $username, '@' ) ) );
85    }
86}