Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.34% covered (warning)
75.34%
55 / 73
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Settings_Page
75.34% covered (warning)
75.34%
55 / 73
66.67% covered (warning)
66.67%
4 / 6
21.33
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 register_menu
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 render_sections
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 render_saved_notice
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 render_multibyte_warning
10.53% covered (danger)
10.53%
2 / 19
0.00% covered (danger)
0.00%
0 / 1
4.87
 render
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * The Settings > Sharing screen.
4 *
5 * @package automattic/jetpack-sharing-likes
6 */
7
8declare( strict_types = 1 );
9
10namespace Automattic\Jetpack\Sharing_Likes\Settings;
11
12/**
13 * Registers Settings > Sharing and renders its sections.
14 *
15 * Registration does not depend on any module being active, so the screen and
16 * every section on it exist whatever the site is running, as long as it can
17 * run them at all: see `Environment::settings_screen_supported()`.
18 */
19final class Settings_Page {
20
21    /**
22     * Submenu slug. Unchanged from when sharedaddy registered this screen, so
23     * existing links and bookmarks keep working.
24     */
25    public const SLUG = 'sharing';
26
27    /**
28     * Query argument a save adds when the Comment Likes switch did not take.
29     */
30    public const COMMENT_LIKES_UNCHANGED = 'comment-likes-unchanged';
31
32    /**
33     * Hook the screen up.
34     */
35    public static function init(): void {
36        add_action( 'admin_menu', array( __CLASS__, 'register_menu' ) );
37    }
38
39    /**
40     * Add the submenu entry under Settings.
41     */
42    public static function register_menu(): void {
43        if ( ! Environment::settings_screen_supported() ) {
44            return;
45        }
46
47        add_submenu_page(
48            'options-general.php',
49            __( 'Sharing Settings', 'jetpack-sharing-likes' ),
50            __( 'Sharing', 'jetpack-sharing-likes' ),
51            'manage_options',
52            self::SLUG,
53            array( __CLASS__, 'render' )
54        );
55    }
56
57    /**
58     * Render whichever sections apply, ruled off from one another, then the
59     * one Save button that covers them all.
60     */
61    private static function render_sections(): void {
62        // The submenu registers against this too. Repeated here because the sections
63        // mint the nonces that authorise every save, and the screen is public API.
64        if ( ! current_user_can( 'manage_options' ) ) {
65            return;
66        }
67
68        $sections = array(
69            array( Sharing_Section::class, 'render' ),
70            array( Likes_Section::class, 'render' ),
71            array( Comment_Likes_Section::class, 'render' ),
72        );
73
74        $sharing_state = Sharing_Section::state();
75
76        if ( Section_State::shows_placement( $sharing_state, Likes_Section::state(), Environment::comment_likes_follow_likes_settings() ) ) {
77            $sections[] = array( Placement_Section::class, 'render' );
78        }
79
80        if ( ! Section_State::configures( $sharing_state ) ) {
81            $sections[] = array( Extras_Section::class, 'render' );
82        }
83
84        $rendered = 0;
85
86        foreach ( $sections as $section ) {
87            ob_start();
88            call_user_func( $section );
89            $markup = trim( (string) ob_get_clean() );
90
91            // A section can decline to render, and a rule with nothing after it is worse than no rule.
92            if ( '' === $markup ) {
93                continue;
94            }
95
96            if ( $rendered > 0 ) {
97                echo '<hr />';
98            }
99
100            echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- each section escapes its own output.
101            ++$rendered;
102        }
103
104        Settings_Form::render();
105    }
106
107    /**
108     * Confirm a save, when one just happened.
109     */
110    private static function render_saved_notice(): void {
111        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only decides whether to print a confirmation.
112        if ( ! isset( $_GET['update'] ) || 'saved' !== $_GET['update'] ) {
113            return;
114        }
115
116        printf(
117            '<div class="updated"><p>%s</p></div>',
118            esc_html__( 'Settings have been saved', 'jetpack-sharing-likes' )
119        );
120
121        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only decides whether to print a warning.
122        if ( isset( $_GET[ self::COMMENT_LIKES_UNCHANGED ] ) ) {
123            printf(
124                '<div class="error"><p>%s</p></div>',
125                esc_html__( 'Comment Likes could not be switched on or off on this site.', 'jetpack-sharing-likes' )
126            );
127        }
128    }
129
130    /**
131     * Warn when PHP has no mbstring extension.
132     *
133     * Sharing sources fall back to byte-wise string functions without it, which
134     * can cut multibyte text mid-character when a service truncates it.
135     */
136    private static function render_multibyte_warning(): void {
137        if ( function_exists( 'mb_stripos' ) ) {
138            return;
139        }
140
141        printf(
142            '<div class="notice notice-warning"><p><strong>%1$s</strong></p><p>%2$s</p></div>',
143            esc_html__( 'Warning! Multibyte support missing!', 'jetpack-sharing-likes' ),
144            wp_kses(
145                sprintf(
146                    /* translators: placeholder is a link to a PHP support document. */
147                    __( 'This plugin will work without it, but multibyte support is used <a href="%s" rel="noopener noreferrer" target="_blank">if available</a>. You may see minor problems with Tweets and other sharing services.', 'jetpack-sharing-likes' ),
148                    'https://www.php.net/manual/en/mbstring.installation.php'
149                ),
150                array(
151                    'a' => array(
152                        'href'   => array(),
153                        'rel'    => array(),
154                        'target' => array(),
155                    ),
156                )
157            )
158        );
159    }
160
161    /**
162     * Render the screen.
163     */
164    public static function render(): void {
165        ?>
166        <div class="wrap">
167            <h1><?php esc_html_e( 'Sharing Settings', 'jetpack-sharing-likes' ); ?></h1>
168            <?php
169            self::render_multibyte_warning();
170            self::render_saved_notice();
171
172            /**
173             * Fires at the top of the admin sharing settings screen.
174             *
175             * @module sharedaddy
176             *
177             * @since jetpack-1.6.0
178             */
179            do_action( 'pre_admin_screen_sharing' );
180
181            self::render_sections();
182            ?>
183        </div>
184        <?php
185    }
186}