Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 147
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
jetpack_register_widget_authors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
Jetpack_Widget_Authors
0.00% covered (danger)
0.00%
0 / 143
0.00% covered (danger)
0.00%
0 / 6
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 enqueue_style
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 flush_cache
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 widget
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 1
272
 form
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
6
 update
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2/**
3 * Disable direct access/execution to/of the widget code.
4 */
5if ( ! defined( 'ABSPATH' ) ) {
6    exit( 0 );
7}
8
9// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
10
11/**
12 * Widget to display blog authors with avatars and recent posts.
13 *
14 * Configurable parameters include:
15 * 1. Whether to display authors who haven't written any posts
16 * 2. The number of posts to be displayed per author (defaults to 0)
17 * 3. Avatar size
18 *
19 * @since 4.5.0
20 */
21class Jetpack_Widget_Authors extends WP_Widget {
22    /**
23     * Jetpack_Widget_Authors contructor.
24     */
25    public function __construct() {
26        parent::__construct(
27            'authors',
28            /** This filter is documented in modules/widgets/facebook-likebox.php */
29            apply_filters( 'jetpack_widget_name', __( 'Authors', 'jetpack' ) ),
30            array(
31                'classname'                   => 'widget_authors',
32                'description'                 => __( 'Display blogs authors with avatars and recent posts.', 'jetpack' ),
33                'customize_selective_refresh' => true,
34            )
35        );
36
37        add_action( 'publish_post', array( __CLASS__, 'flush_cache' ) );
38        add_action( 'deleted_post', array( __CLASS__, 'flush_cache' ) );
39        add_action( 'switch_theme', array( __CLASS__, 'flush_cache' ) );
40    }
41
42    /**
43     * Enqueue stylesheet to adapt the widget to various themes.
44     *
45     * @since 4.5.0
46     */
47    public function enqueue_style() {
48        wp_register_style( 'jetpack-authors-widget', plugins_url( 'authors/style.css', __FILE__ ), array(), '20161228' );
49        wp_enqueue_style( 'jetpack-authors-widget' );
50    }
51
52    /**
53     * Flush Authors widget cached data.
54     */
55    public static function flush_cache() {
56        wp_cache_delete( 'widget_authors', 'widget' );
57        wp_cache_delete( 'widget_authors_ssl', 'widget' );
58    }
59
60    /**
61     * Echoes the widget content.
62     *
63     * @param array $args Display arguments.
64     * @param array $instance Widget settings for the instance.
65     */
66    public function widget( $args, $instance ) {
67        // Enqueue front end assets.
68        $this->enqueue_style();
69
70        $cache_bucket = is_ssl() ? 'widget_authors_ssl' : 'widget_authors';
71
72        if ( '%BEG_OF_TITLE%' !== $args['before_title'] ) {
73            $output = wp_cache_get( $cache_bucket, 'widget' );
74            if ( $output ) {
75                echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Cached widget display.
76                return;
77            }
78
79            ob_start();
80        }
81
82        $instance           = wp_parse_args(
83            $instance,
84            array(
85                'title'       => __( 'Authors', 'jetpack' ),
86                'all'         => false,
87                'number'      => 5,
88                'avatar_size' => 48,
89            )
90        );
91        $instance['number'] = min( 10, max( 0, (int) $instance['number'] ) );
92
93        // We need to query at least one post to determine whether an author has written any posts or not.
94        $query_number = max( $instance['number'], 1 );
95
96        /**
97         * Filter authors from the Widget Authors widget.
98         *
99         * @module widgets
100         *
101         * @deprecated 7.7.0 Use jetpack_widget_authors_params instead.
102         *
103         * @since 4.5.0
104         *
105         * @param array $default_excluded_authors Array of user ID's that will be excluded
106         */
107        $excluded_authors = apply_filters( 'jetpack_widget_authors_exclude', array() );
108
109        /**
110         * Filter the parameters of `get_users` call in the Widget Authors widget.
111         *
112         * See the following for `get_users` default arguments:
113         * https://codex.wordpress.org/Function_Reference/get_users
114         *
115         * @module widgets
116         *
117         * @since 7.7.0
118         *
119         * @param array $get_author_params Array of params used in `get_user`
120         */
121        $get_author_params = apply_filters(
122            'jetpack_widget_authors_params',
123            array(
124                'capability' => array( 'edit_posts' ),
125                'exclude'    => (array) $excluded_authors,
126            )
127        );
128
129        $authors = get_users( $get_author_params );
130
131        echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
132        /** This filter is documented in core/src/wp-includes/default-widgets.php */
133        $title = apply_filters( 'widget_title', $instance['title'] );
134        echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
135        echo '<ul>';
136
137        $default_post_type = 'post';
138        /**
139         * Filter types of posts that will be counted in the widget
140         *
141         * @module widgets
142         *
143         * @since 4.5.0
144         *
145         * @param string|array $default_post_type type(s) of posts to count for the widget.
146         */
147        $post_types = apply_filters( 'jetpack_widget_authors_post_types', $default_post_type );
148
149        foreach ( $authors as $author ) {
150            $r = new WP_Query(
151                array(
152                    'author'         => $author->ID,
153                    'posts_per_page' => $query_number,
154                    'post_type'      => $post_types,
155                    'post_status'    => 'publish',
156                    'no_found_rows'  => true,
157                    'has_password'   => false,
158                )
159            );
160
161            if ( ! $r->have_posts() && ! $instance['all'] ) {
162                continue;
163            }
164
165            echo '<li>';
166
167            // Display avatar and author name.
168            if ( $r->have_posts() ) {
169                echo '<a href="' . esc_url( get_author_posts_url( $author->ID ) ) . '">';
170
171                if ( $instance['avatar_size'] > 1 ) {
172                    echo ' ' . get_avatar( $author->ID, $instance['avatar_size'], '', true ) . ' ';
173                }
174
175                echo '<strong>' . esc_html( $author->display_name ) . '</strong>';
176                echo '</a>';
177            } elseif ( $instance['all'] ) {
178                if ( $instance['avatar_size'] > 1 ) {
179                    echo get_avatar( $author->ID, $instance['avatar_size'], '', true ) . ' ';
180                }
181
182                echo '<strong>' . esc_html( $author->display_name ) . '</strong>';
183            }
184
185            if ( 0 === (int) $instance['number'] ) {
186                echo '</li>';
187                continue;
188            }
189
190            // Display a short list of recent posts for this author.
191            if ( $r->have_posts() ) {
192                echo '<ul>';
193
194                while ( $r->have_posts() ) {
195                    $r->the_post();
196
197                    printf(
198                        '<li><a href="%1$s" title="%2$s"%3$s>%4$s</a></li>',
199                        esc_url( get_permalink() ),
200                        esc_attr( wp_kses( get_the_title(), array() ) ),
201                        ( get_queried_object_id() === get_the_ID() ? ' aria-current="page"' : '' ),
202                        esc_html( wp_kses( get_the_title(), array() ) )
203                    );
204                }
205
206                echo '</ul>';
207            }
208
209            echo '</li>';
210        }
211
212        echo '</ul>';
213        echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
214
215        wp_reset_postdata();
216
217        if ( '%BEG_OF_TITLE%' !== $args['before_title'] ) {
218            wp_cache_add( $cache_bucket, ob_get_flush(), 'widget' );
219        }
220
221        /** This action is documented in modules/widgets/gravatar-profile.php */
222        do_action( 'jetpack_stats_extra', 'widget_view', 'authors' );
223    }
224
225    /**
226     * Outputs the widget settings form.
227     *
228     * @param array $instance Current settings.
229     * @return string|void
230     */
231    public function form( $instance ) {
232        $instance = wp_parse_args(
233            $instance,
234            array(
235                'title'       => '',
236                'all'         => false,
237                'avatar_size' => 48,
238                'number'      => 5,
239            )
240        );
241
242        ?>
243        <p>
244            <label>
245                <?php esc_html_e( 'Title:', 'jetpack' ); ?>
246                <input class="widefat" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
247            </label>
248        </p>
249        <p>
250            <label>
251                <input class="checkbox" type="checkbox" <?php checked( $instance['all'] ); ?> name="<?php echo esc_attr( $this->get_field_name( 'all' ) ); ?>" />
252                <?php esc_html_e( 'Display all authors (including those who have not written any posts)', 'jetpack' ); ?>
253            </label>
254        </p>
255        <p>
256            <label>
257                <?php esc_html_e( 'Number of posts to show for each author:', 'jetpack' ); ?>
258                <input style="width: 50px; text-align: center;" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['number'] ); ?>" />
259                <?php esc_html_e( '(at most 10)', 'jetpack' ); ?>
260            </label>
261        </p>
262        <p>
263            <label>
264                <?php esc_html_e( 'Avatar Size (px):', 'jetpack' ); ?>
265                <select name="<?php echo esc_attr( $this->get_field_name( 'avatar_size' ) ); ?>">
266                    <?php
267                    foreach ( array(
268                        '1'   => __( 'No Avatars', 'jetpack' ),
269                        '16'  => '16x16',
270                        '32'  => '32x32',
271                        '48'  => '48x48',
272                        '96'  => '96x96',
273                        '128' => '128x128',
274                    ) as $value => $label ) {
275                        ?>
276                        <option value="<?php echo esc_attr( $value ); ?><?php selected( $value, $instance['avatar_size'] ); ?>><?php echo esc_html( $label ); ?></option>
277                    <?php } ?>
278                </select>
279            </label>
280        </p>
281        <?php
282    }
283
284    /**
285     * Updates the widget on save and flushes cache.
286     *
287     * @param array $new_instance New widget instance data.
288     * @param array $old_instance Old widget instance data.
289     * @return array
290     */
291    public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
292        $new_instance['title']       = wp_strip_all_tags( $new_instance['title'] );
293        $new_instance['all']         = isset( $new_instance['all'] ) ? (bool) $new_instance['all'] : false;
294        $new_instance['number']      = (int) $new_instance['number'];
295        $new_instance['avatar_size'] = (int) $new_instance['avatar_size'];
296
297        self::flush_cache();
298
299        return $new_instance;
300    }
301}
302
303add_action( 'widgets_init', 'jetpack_register_widget_authors' );
304/**
305 * Register the Authors widget.
306 */
307function jetpack_register_widget_authors() {
308    register_widget( 'Jetpack_Widget_Authors' );
309}