~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-admin/includes/dashboard.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * WordPress Dashboard Widget Administration Screen API
 
4
 *
 
5
 * @package WordPress
 
6
 * @subpackage Administration
 
7
 */
 
8
 
 
9
/**
 
10
 * Registers dashboard widgets.
 
11
 *
 
12
 * Handles POST data, sets up filters.
 
13
 *
 
14
 * @since 2.5.0
 
15
 */
 
16
function wp_dashboard_setup() {
 
17
        global $wp_registered_widgets, $wp_registered_widget_controls, $wp_dashboard_control_callbacks;
 
18
        $wp_dashboard_control_callbacks = array();
 
19
        $screen = get_current_screen();
 
20
 
 
21
        /* Register Widgets and Controls */
 
22
 
 
23
        $response = wp_check_browser_version();
 
24
 
 
25
        if ( $response && $response['upgrade'] ) {
 
26
                add_filter( 'postbox_classes_dashboard_dashboard_browser_nag', 'dashboard_browser_nag_class' );
 
27
                if ( $response['insecure'] )
 
28
                        wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'You are using an insecure browser!' ), 'wp_dashboard_browser_nag' );
 
29
                else
 
30
                        wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'Your browser is out of date!' ), 'wp_dashboard_browser_nag' );
 
31
        }
 
32
 
 
33
        // Right Now
 
34
        if ( is_blog_admin() && current_user_can('edit_posts') )
 
35
                wp_add_dashboard_widget( 'dashboard_right_now', __( 'At a Glance' ), 'wp_dashboard_right_now' );
 
36
 
 
37
        if ( is_network_admin() )
 
38
                wp_add_dashboard_widget( 'network_dashboard_right_now', __( 'Right Now' ), 'wp_network_dashboard_right_now' );
 
39
 
 
40
        // Activity Widget
 
41
        if ( is_blog_admin() ) {
 
42
                wp_add_dashboard_widget( 'dashboard_activity', __( 'Activity' ), 'wp_dashboard_site_activity' );
 
43
        }
 
44
 
 
45
        // QuickPress Widget
 
46
        if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) {
 
47
                $quick_draft_title = sprintf( '<span class="hide-if-no-js">%1$s</span> <span class="hide-if-js">%2$s</span>', __( 'Quick Draft' ), __( 'Drafts' ) );
 
48
                wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' );
 
49
        }
 
50
 
 
51
        // WordPress News
 
52
        wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress News' ), 'wp_dashboard_primary' );
 
53
 
 
54
        if ( is_network_admin() ) {
 
55
 
 
56
                /**
 
57
                 * Fires after core widgets for the Network Admin dashboard have been registered.
 
58
                 *
 
59
                 * @since 3.1.0
 
60
                 */
 
61
                do_action( 'wp_network_dashboard_setup' );
 
62
 
 
63
                /**
 
64
                 * Filter the list of widgets to load for the Network Admin dashboard.
 
65
                 *
 
66
                 * @since 3.1.0
 
67
                 *
 
68
                 * @param array $dashboard_widgets An array of dashboard widgets.
 
69
                 */
 
70
                $dashboard_widgets = apply_filters( 'wp_network_dashboard_widgets', array() );
 
71
        } elseif ( is_user_admin() ) {
 
72
 
 
73
                /**
 
74
                 * Fires after core widgets for the User Admin dashboard have been registered.
 
75
                 *
 
76
                 * @since 3.1.0
 
77
                 */
 
78
                do_action( 'wp_user_dashboard_setup' );
 
79
 
 
80
                /**
 
81
                 * Filter the list of widgets to load for the User Admin dashboard.
 
82
                 *
 
83
                 * @since 3.1.0
 
84
                 *
 
85
                 * @param array $dashboard_widgets An array of dashboard widgets.
 
86
                 */
 
87
                $dashboard_widgets = apply_filters( 'wp_user_dashboard_widgets', array() );
 
88
        } else {
 
89
 
 
90
                /**
 
91
                 * Fires after core widgets for the admin dashboard have been registered.
 
92
                 *
 
93
                 * @since 2.5.0
 
94
                 */
 
95
                do_action( 'wp_dashboard_setup' );
 
96
 
 
97
                /**
 
98
                 * Filter the list of widgets to load for the admin dashboard.
 
99
                 *
 
100
                 * @since 2.5.0
 
101
                 *
 
102
                 * @param array $dashboard_widgets An array of dashboard widgets.
 
103
                 */
 
104
                $dashboard_widgets = apply_filters( 'wp_dashboard_widgets', array() );
 
105
        }
 
106
 
 
107
        foreach ( $dashboard_widgets as $widget_id ) {
 
108
                $name = empty( $wp_registered_widgets[$widget_id]['all_link'] ) ? $wp_registered_widgets[$widget_id]['name'] : $wp_registered_widgets[$widget_id]['name'] . " <a href='{$wp_registered_widgets[$widget_id]['all_link']}' class='edit-box open-box'>" . __('View all') . '</a>';
 
109
                wp_add_dashboard_widget( $widget_id, $name, $wp_registered_widgets[$widget_id]['callback'], $wp_registered_widget_controls[$widget_id]['callback'] );
 
110
        }
 
111
 
 
112
        if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['widget_id']) ) {
 
113
                check_admin_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' );
 
114
                ob_start(); // hack - but the same hack wp-admin/widgets.php uses
 
115
                wp_dashboard_trigger_widget_control( $_POST['widget_id'] );
 
116
                ob_end_clean();
 
117
                wp_redirect( remove_query_arg( 'edit' ) );
 
118
                exit;
 
119
        }
 
120
 
 
121
        /** This action is documented in wp-admin/edit-form-advanced.php */
 
122
        do_action( 'do_meta_boxes', $screen->id, 'normal', '' );
 
123
 
 
124
        /** This action is documented in wp-admin/edit-form-advanced.php */
 
125
        do_action( 'do_meta_boxes', $screen->id, 'side', '' );
 
126
}
 
127
 
 
128
function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null ) {
 
129
        $screen = get_current_screen();
 
130
        global $wp_dashboard_control_callbacks;
 
131
 
 
132
        if ( $control_callback && current_user_can( 'edit_dashboard' ) && is_callable( $control_callback ) ) {
 
133
                $wp_dashboard_control_callbacks[$widget_id] = $control_callback;
 
134
                if ( isset( $_GET['edit'] ) && $widget_id == $_GET['edit'] ) {
 
135
                        list($url) = explode( '#', add_query_arg( 'edit', false ), 2 );
 
136
                        $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( $url ) . '">' . __( 'Cancel' ) . '</a></span>';
 
137
                        $callback = '_wp_dashboard_control_callback';
 
138
                } else {
 
139
                        list($url) = explode( '#', add_query_arg( 'edit', $widget_id ), 2 );
 
140
                        $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( "$url#$widget_id" ) . '" class="edit-box open-box">' . __( 'Configure' ) . '</a></span>';
 
141
                }
 
142
        }
 
143
 
 
144
        $side_widgets = array( 'dashboard_quick_press', 'dashboard_primary' );
 
145
 
 
146
        $location = 'normal';
 
147
        if ( in_array($widget_id, $side_widgets) )
 
148
                $location = 'side';
 
149
 
 
150
        $priority = 'core';
 
151
        if ( 'dashboard_browser_nag' === $widget_id )
 
152
                $priority = 'high';
 
153
 
 
154
        add_meta_box( $widget_id, $widget_name, $callback, $screen, $location, $priority, $callback_args );
 
155
}
 
156
 
 
157
function _wp_dashboard_control_callback( $dashboard, $meta_box ) {
 
158
        echo '<form action="" method="post" class="dashboard-widget-control-form">';
 
159
        wp_dashboard_trigger_widget_control( $meta_box['id'] );
 
160
        wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' );
 
161
        echo '<input type="hidden" name="widget_id" value="' . esc_attr($meta_box['id']) . '" />';
 
162
        submit_button( __('Submit') );
 
163
        echo '</form>';
 
164
}
 
165
 
 
166
/**
 
167
 * Displays the dashboard.
 
168
 *
 
169
 * @since 2.5.0
 
170
 */
 
171
function wp_dashboard() {
 
172
        $screen = get_current_screen();
 
173
        $columns = absint( $screen->get_columns() );
 
174
        $columns_css = '';
 
175
        if ( $columns ) {
 
176
                $columns_css = " columns-$columns";
 
177
        }
 
178
 
 
179
?>
 
180
<div id="dashboard-widgets" class="metabox-holder<?php echo $columns_css; ?>">
 
181
        <div id="postbox-container-1" class="postbox-container">
 
182
        <?php do_meta_boxes( $screen->id, 'normal', '' ); ?>
 
183
        </div>
 
184
        <div id="postbox-container-2" class="postbox-container">
 
185
        <?php do_meta_boxes( $screen->id, 'side', '' ); ?>
 
186
        </div>
 
187
        <div id="postbox-container-3" class="postbox-container">
 
188
        <?php do_meta_boxes( $screen->id, 'column3', '' ); ?>
 
189
        </div>
 
190
        <div id="postbox-container-4" class="postbox-container">
 
191
        <?php do_meta_boxes( $screen->id, 'column4', '' ); ?>
 
192
        </div>
 
193
</div>
 
194
 
 
195
<?php
 
196
        wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
 
197
        wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
 
198
 
 
199
}
 
200
 
 
201
//
 
202
// Dashboard Widgets
 
203
//
 
204
 
 
205
/**
 
206
 * Dashboard widget that displays some basic stats about the site.
 
207
 *
 
208
 * Formerly 'Right Now'. A streamlined 'At a Glance' as of 3.8.
 
209
 *
 
210
 * @since 2.7.0
 
211
 */
 
212
function wp_dashboard_right_now() {
 
213
?>
 
214
        <div class="main">
 
215
        <ul>
 
216
        <?php
 
217
        // Posts and Pages
 
218
        foreach ( array( 'post', 'page' ) as $post_type ) {
 
219
                $num_posts = wp_count_posts( $post_type );
 
220
                if ( $num_posts && $num_posts->publish ) {
 
221
                        if ( 'post' == $post_type ) {
 
222
                                $text = _n( '%s Post', '%s Posts', $num_posts->publish );
 
223
                        } else {
 
224
                                $text = _n( '%s Page', '%s Pages', $num_posts->publish );
 
225
                        }
 
226
                        $text = sprintf( $text, number_format_i18n( $num_posts->publish ) );
 
227
                        $post_type_object = get_post_type_object( $post_type );
 
228
                        if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) {
 
229
                                printf( '<li class="%1$s-count"><a href="edit.php?post_type=%1$s">%2$s</a></li>', $post_type, $text );
 
230
                        } else {
 
231
                                printf( '<li class="%1$s-count"><span>%2$s</span></li>', $post_type, $text );
 
232
                        }
 
233
 
 
234
                }
 
235
        }
 
236
        // Comments
 
237
        $num_comm = wp_count_comments();
 
238
        if ( $num_comm && $num_comm->total_comments ) {
 
239
                $text = sprintf( _n( '%s Comment', '%s Comments', $num_comm->total_comments ), number_format_i18n( $num_comm->total_comments ) );
 
240
                ?>
 
241
                <li class="comment-count"><a href="edit-comments.php"><?php echo $text; ?></a></li>
 
242
                <?php
 
243
                if ( $num_comm->moderated ) {
 
244
                        /* translators: Number of comments in moderation */
 
245
                        $text = sprintf( _nx( '%s in moderation', '%s in moderation', $num_comm->moderated, 'comments' ), number_format_i18n( $num_comm->moderated ) );
 
246
                        ?>
 
247
                        <li class="comment-mod-count"><a href="edit-comments.php?comment_status=moderated"><?php echo $text; ?></a></li>
 
248
                        <?php
 
249
                }
 
250
        }
 
251
 
 
252
        /**
 
253
         * Filter the array of extra elements to list in the 'At a Glance'
 
254
         * dashboard widget.
 
255
         *
 
256
         * Prior to 3.8.0, the widget was named 'Right Now'. Each element
 
257
         * is wrapped in list-item tags on output.
 
258
         *
 
259
         * @since 3.8.0
 
260
         *
 
261
         * @param array $items Array of extra 'At a Glance' widget items.
 
262
         */
 
263
        $elements = apply_filters( 'dashboard_glance_items', array() );
 
264
 
 
265
        if ( $elements ) {
 
266
                echo '<li>' . implode( "</li>\n<li>", $elements ) . "</li>\n";
 
267
        }
 
268
 
 
269
        ?>
 
270
        </ul>
 
271
        <?php
 
272
        update_right_now_message();
 
273
 
 
274
        // Check if search engines are asked not to index this site.
 
275
        if ( ! is_network_admin() && ! is_user_admin() && current_user_can( 'manage_options' ) && '1' != get_option( 'blog_public' ) ) {
 
276
 
 
277
                /**
 
278
                 * Filter the link title attribute for the 'Search Engines Discouraged'
 
279
                 * message displayed in the 'At a Glance' dashboard widget.
 
280
                 *
 
281
                 * Prior to 3.8.0, the widget was named 'Right Now'.
 
282
                 *
 
283
                 * @since 3.0.0
 
284
                 *
 
285
                 * @param string $title Default attribute text.
 
286
                 */
 
287
                $title = apply_filters( 'privacy_on_link_title', __( 'Your site is asking search engines not to index its content' ) );
 
288
 
 
289
                /**
 
290
                 * Filter the link label for the 'Search Engines Discouraged' message
 
291
                 * displayed in the 'At a Glance' dashboard widget.
 
292
                 *
 
293
                 * Prior to 3.8.0, the widget was named 'Right Now'.
 
294
                 *
 
295
                 * @since 3.0.0
 
296
                 *
 
297
                 * @param string $content Default text.
 
298
                 */
 
299
                $content = apply_filters( 'privacy_on_link_text' , __( 'Search Engines Discouraged' ) );
 
300
 
 
301
                echo "<p><a href='options-reading.php' title='$title'>$content</a></p>";
 
302
        }
 
303
        ?>
 
304
        </div>
 
305
        <?php
 
306
        /*
 
307
         * activity_box_end has a core action, but only prints content when multisite.
 
308
         * Using an output buffer is the only way to really check if anything's displayed here.
 
309
         */
 
310
        ob_start();
 
311
 
 
312
        /**
 
313
         * Fires at the end of the 'At a Glance' dashboard widget.
 
314
         *
 
315
         * Prior to 3.8.0, the widget was named 'Right Now'.
 
316
         *
 
317
         * @since 2.5.0
 
318
         */
 
319
        do_action( 'rightnow_end' );
 
320
 
 
321
        /**
 
322
         * Fires at the end of the 'At a Glance' dashboard widget.
 
323
         *
 
324
         * Prior to 3.8.0, the widget was named 'Right Now'.
 
325
         *
 
326
         * @since 2.0.0
 
327
         */
 
328
        do_action( 'activity_box_end' );
 
329
 
 
330
        $actions = ob_get_clean();
 
331
 
 
332
        if ( !empty( $actions ) ) : ?>
 
333
        <div class="sub">
 
334
                <?php echo $actions; ?>
 
335
        </div>
 
336
        <?php endif;
 
337
}
 
338
 
 
339
function wp_network_dashboard_right_now() {
 
340
        $actions = array();
 
341
        if ( current_user_can('create_sites') )
 
342
                $actions['create-site'] = '<a href="' . network_admin_url('site-new.php') . '">' . __( 'Create a New Site' ) . '</a>';
 
343
        if ( current_user_can('create_users') )
 
344
                $actions['create-user'] = '<a href="' . network_admin_url('user-new.php') . '">' . __( 'Create a New User' ) . '</a>';
 
345
 
 
346
        $c_users = get_user_count();
 
347
        $c_blogs = get_blog_count();
 
348
 
 
349
        $user_text = sprintf( _n( '%s user', '%s users', $c_users ), number_format_i18n( $c_users ) );
 
350
        $blog_text = sprintf( _n( '%s site', '%s sites', $c_blogs ), number_format_i18n( $c_blogs ) );
 
351
 
 
352
        $sentence = sprintf( __( 'You have %1$s and %2$s.' ), $blog_text, $user_text );
 
353
 
 
354
        if ( $actions ) {
 
355
                echo '<ul class="subsubsub">';
 
356
                foreach ( $actions as $class => $action ) {
 
357
                         $actions[ $class ] = "\t<li class='$class'>$action";
 
358
                }
 
359
                echo implode( " |</li>\n", $actions ) . "</li>\n";
 
360
                echo '</ul>';
 
361
        }
 
362
?>
 
363
        <br class="clear" />
 
364
 
 
365
        <p class="youhave"><?php echo $sentence; ?></p>
 
366
 
 
367
 
 
368
        <?php
 
369
                /**
 
370
                 * Fires in the Network Admin 'Right Now' dashboard widget
 
371
                 * just before the user and site search form fields.
 
372
                 *
 
373
                 * @since MU
 
374
                 *
 
375
                 * @param null $unused
 
376
                 */
 
377
                do_action( 'wpmuadminresult', '' );
 
378
        ?>
 
379
 
 
380
        <form action="<?php echo network_admin_url('users.php'); ?>" method="get">
 
381
                <p>
 
382
                        <input type="search" name="s" value="" size="30" autocomplete="off" />
 
383
                        <?php submit_button( __( 'Search Users' ), 'button', 'submit', false, array( 'id' => 'submit_users' ) ); ?>
 
384
                </p>
 
385
        </form>
 
386
 
 
387
        <form action="<?php echo network_admin_url('sites.php'); ?>" method="get">
 
388
                <p>
 
389
                        <input type="search" name="s" value="" size="30" autocomplete="off" />
 
390
                        <?php submit_button( __( 'Search Sites' ), 'button', 'submit', false, array( 'id' => 'submit_sites' ) ); ?>
 
391
                </p>
 
392
        </form>
 
393
<?php
 
394
        /**
 
395
         * Fires at the end of the 'Right Now' widget in the Network Admin dashboard.
 
396
         *
 
397
         * @since MU
 
398
         */
 
399
        do_action( 'mu_rightnow_end' );
 
400
 
 
401
        /**
 
402
         * Fires at the end of the 'Right Now' widget in the Network Admin dashboard.
 
403
         *
 
404
         * @since MU
 
405
         */
 
406
        do_action( 'mu_activity_box_end' );
 
407
}
 
408
 
 
409
/**
 
410
 * The Quick Draft widget display and creation of drafts.
 
411
 *
 
412
 * @since 3.8.0
 
413
 *
 
414
 * @param string $error_msg Optional. Error message. Default false.
 
415
 */
 
416
function wp_dashboard_quick_press( $error_msg = false ) {
 
417
        global $post_ID;
 
418
 
 
419
        /* Check if a new auto-draft (= no new post_ID) is needed or if the old can be used */
 
420
        $last_post_id = (int) get_user_option( 'dashboard_quick_press_last_post_id' ); // Get the last post_ID
 
421
        if ( $last_post_id ) {
 
422
                $post = get_post( $last_post_id );
 
423
                if ( empty( $post ) || $post->post_status != 'auto-draft' ) { // auto-draft doesn't exists anymore
 
424
                        $post = get_default_post_to_edit( 'post', true );
 
425
                        update_user_option( get_current_user_id(), 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID
 
426
                } else {
 
427
                        $post->post_title = ''; // Remove the auto draft title
 
428
                }
 
429
        } else {
 
430
                $post = get_default_post_to_edit( 'post' , true);
 
431
                $user_id = get_current_user_id();
 
432
                // Don't create an option if this is a super admin who does not belong to this site.
 
433
                if ( ! ( is_super_admin( $user_id ) && ! in_array( get_current_blog_id(), array_keys( get_blogs_of_user( $user_id ) ) ) ) )
 
434
                        update_user_option( $user_id, 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID
 
435
        }
 
436
 
 
437
        $post_ID = (int) $post->ID;
 
438
?>
 
439
 
 
440
        <form name="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>" method="post" id="quick-press" class="initial-form hide-if-no-js">
 
441
 
 
442
                <?php if ( $error_msg ) : ?>
 
443
                <div class="error"><?php echo $error_msg; ?></div>
 
444
                <?php endif; ?>
 
445
 
 
446
                <div class="input-text-wrap" id="title-wrap">
 
447
                        <label class="screen-reader-text prompt" for="title" id="title-prompt-text">
 
448
 
 
449
                                <?php
 
450
                                /** This filter is documented in wp-admin/edit-form-advanced.php */
 
451
                                echo apply_filters( 'enter_title_here', __( 'Title' ), $post );
 
452
                                ?>
 
453
                        </label>
 
454
                        <input type="text" name="post_title" id="title" autocomplete="off" />
 
455
                </div>
 
456
 
 
457
                <div class="textarea-wrap" id="description-wrap">
 
458
                        <label class="screen-reader-text prompt" for="content" id="content-prompt-text"><?php _e( 'What&#8217;s on your mind?' ); ?></label>
 
459
                        <textarea name="content" id="content" class="mceEditor" rows="3" cols="15" autocomplete="off"></textarea>
 
460
                </div>
 
461
 
 
462
                <p class="submit">
 
463
                        <input type="hidden" name="action" id="quickpost-action" value="post-quickdraft-save" />
 
464
                        <input type="hidden" name="post_ID" value="<?php echo $post_ID; ?>" />
 
465
                        <input type="hidden" name="post_type" value="post" />
 
466
                        <?php wp_nonce_field( 'add-post' ); ?>
 
467
                        <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?>
 
468
                        <br class="clear" />
 
469
                </p>
 
470
 
 
471
        </form>
 
472
        <?php
 
473
        wp_dashboard_recent_drafts();
 
474
}
 
475
 
 
476
/**
 
477
 * Show recent drafts of the user on the dashboard.
 
478
 *
 
479
 * @since 2.7.0
 
480
 */
 
481
function wp_dashboard_recent_drafts( $drafts = false ) {
 
482
        if ( ! $drafts ) {
 
483
                $query_args = array(
 
484
                        'post_type'      => 'post',
 
485
                        'post_status'    => 'draft',
 
486
                        'author'         => get_current_user_id(),
 
487
                        'posts_per_page' => 4,
 
488
                        'orderby'        => 'modified',
 
489
                        'order'          => 'DESC'
 
490
                );
 
491
                $drafts = get_posts( $query_args );
 
492
                if ( ! $drafts ) {
 
493
                        return;
 
494
                }
 
495
        }
 
496
 
 
497
        echo '<div class="drafts">';
 
498
        if ( count( $drafts ) > 3 ) {
 
499
                echo '<p class="view-all"><a href="' . esc_url( admin_url( 'edit.php?post_status=draft' ) ) . '">' . _x( 'View all', 'drafts' ) . "</a></p>\n";
 
500
        }
 
501
        echo '<h4 class="hide-if-no-js">' . __( 'Drafts' ) . "</h4>\n<ul>";
 
502
 
 
503
        $drafts = array_slice( $drafts, 0, 3 );
 
504
        foreach ( $drafts as $draft ) {
 
505
                $url = get_edit_post_link( $draft->ID );
 
506
                $title = _draft_or_post_title( $draft->ID );
 
507
                echo "<li>\n";
 
508
                echo '<div class="draft-title"><a href="' . esc_url( $url ) . '" title="' . esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $title ) ) . '">' . esc_html( $title ) . '</a>';
 
509
                echo '<time datetime="' . get_the_time( 'c', $draft ) . '">' . get_the_time( get_option( 'date_format' ), $draft ) . '</time></div>';
 
510
                if ( $the_content = wp_trim_words( $draft->post_content, 10 ) ) {
 
511
                        echo '<p>' . $the_content . '</p>';
 
512
                }
 
513
                echo "</li>\n";
 
514
        }
 
515
        echo "</ul>\n</div>";
 
516
}
 
517
 
 
518
function _wp_dashboard_recent_comments_row( &$comment, $show_date = true ) {
 
519
        $GLOBALS['comment'] =& $comment;
 
520
 
 
521
        $comment_post_title = strip_tags(get_the_title( $comment->comment_post_ID ));
 
522
 
 
523
        if ( current_user_can( 'edit_post', $comment->comment_post_ID ) ) {
 
524
                $comment_post_url = get_edit_post_link( $comment->comment_post_ID );
 
525
                $comment_post_link = "<a href='$comment_post_url'>$comment_post_title</a>";
 
526
        } else {
 
527
                $comment_post_link = $comment_post_title;
 
528
        }
 
529
 
 
530
        $comment_link = '<a class="comment-link" href="' . esc_url(get_comment_link()) . '">#</a>';
 
531
 
 
532
        $actions_string = '';
 
533
        if ( current_user_can( 'edit_comment', $comment->comment_ID ) ) {
 
534
                // Pre-order it: Approve | Reply | Edit | Spam | Trash.
 
535
                $actions = array(
 
536
                        'approve' => '', 'unapprove' => '',
 
537
                        'reply' => '',
 
538
                        'edit' => '',
 
539
                        'spam' => '',
 
540
                        'trash' => '', 'delete' => ''
 
541
                );
 
542
 
 
543
                $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) );
 
544
                $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) );
 
545
 
 
546
                $approve_url = esc_url( "comment.php?action=approvecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" );
 
547
                $unapprove_url = esc_url( "comment.php?action=unapprovecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" );
 
548
                $spam_url = esc_url( "comment.php?action=spamcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" );
 
549
                $trash_url = esc_url( "comment.php?action=trashcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" );
 
550
                $delete_url = esc_url( "comment.php?action=deletecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" );
 
551
 
 
552
                $actions['approve'] = "<a href='$approve_url' data-wp-lists='dim:the-comment-list:comment-$comment->comment_ID:unapproved:e7e7d3:e7e7d3:new=approved' class='vim-a' title='" . esc_attr__( 'Approve this comment' ) . "'>" . __( 'Approve' ) . '</a>';
 
553
                $actions['unapprove'] = "<a href='$unapprove_url' data-wp-lists='dim:the-comment-list:comment-$comment->comment_ID:unapproved:e7e7d3:e7e7d3:new=unapproved' class='vim-u' title='" . esc_attr__( 'Unapprove this comment' ) . "'>" . __( 'Unapprove' ) . '</a>';
 
554
                $actions['edit'] = "<a href='comment.php?action=editcomment&amp;c={$comment->comment_ID}' title='" . esc_attr__('Edit comment') . "'>". __('Edit') . '</a>';
 
555
                $actions['reply'] = '<a onclick="window.commentReply && commentReply.open(\''.$comment->comment_ID.'\',\''.$comment->comment_post_ID.'\');return false;" class="vim-r hide-if-no-js" title="'.esc_attr__('Reply to this comment').'" href="#">' . __('Reply') . '</a>';
 
556
                $actions['spam'] = "<a href='$spam_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::spam=1' class='vim-s vim-destructive' title='" . esc_attr__( 'Mark this comment as spam' ) . "'>" . /* translators: mark as spam link */ _x( 'Spam', 'verb' ) . '</a>';
 
557
                if ( !EMPTY_TRASH_DAYS )
 
558
                        $actions['delete'] = "<a href='$delete_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::trash=1' class='delete vim-d vim-destructive'>" . __('Delete Permanently') . '</a>';
 
559
                else
 
560
                        $actions['trash'] = "<a href='$trash_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::trash=1' class='delete vim-d vim-destructive' title='" . esc_attr__( 'Move this comment to the trash' ) . "'>" . _x('Trash', 'verb') . '</a>';
 
561
 
 
562
                /**
 
563
                 * Filter the action links displayed for each comment in the 'Recent Comments'
 
564
                 * dashboard widget.
 
565
                 *
 
566
                 * @since 2.6.0
 
567
                 *
 
568
                 * @param array  $actions An array of comment actions. Default actions include:
 
569
                 *                        'Approve', 'Unapprove', 'Edit', 'Reply', 'Spam',
 
570
                 *                        'Delete', and 'Trash'.
 
571
                 * @param object $comment The comment object.
 
572
                 */
 
573
                $actions = apply_filters( 'comment_row_actions', array_filter($actions), $comment );
 
574
 
 
575
                $i = 0;
 
576
                foreach ( $actions as $action => $link ) {
 
577
                        ++$i;
 
578
                        ( ( ('approve' == $action || 'unapprove' == $action) && 2 === $i ) || 1 === $i ) ? $sep = '' : $sep = ' | ';
 
579
 
 
580
                        // Reply and quickedit need a hide-if-no-js span
 
581
                        if ( 'reply' == $action || 'quickedit' == $action )
 
582
                                $action .= ' hide-if-no-js';
 
583
 
 
584
                        $actions_string .= "<span class='$action'>$sep$link</span>";
 
585
                }
 
586
        }
 
587
 
 
588
?>
 
589
 
 
590
                <div id="comment-<?php echo $comment->comment_ID; ?>" <?php comment_class( array( 'comment-item', wp_get_comment_status($comment->comment_ID) ) ); ?>>
 
591
 
 
592
                        <?php echo get_avatar( $comment, 50, 'mystery' ); ?>
 
593
 
 
594
                        <?php if ( !$comment->comment_type || 'comment' == $comment->comment_type ) : ?>
 
595
 
 
596
                        <div class="dashboard-comment-wrap">
 
597
                        <h4 class="comment-meta">
 
598
                                <?php printf( /* translators: 1: comment author, 2: post link, 3: notification if the comment is pending */__( 'From %1$s on %2$s%3$s' ),
 
599
                                        '<cite class="comment-author">' . get_comment_author_link() . '</cite>', $comment_post_link.' '.$comment_link, ' <span class="approve">' . __( '[Pending]' ) . '</span>' ); ?>
 
600
                        </h4>
 
601
 
 
602
                        <?php
 
603
                        else :
 
604
                                switch ( $comment->comment_type ) {
 
605
                                        case 'pingback' :
 
606
                                                $type = __( 'Pingback' );
 
607
                                                break;
 
608
                                        case 'trackback' :
 
609
                                                $type = __( 'Trackback' );
 
610
                                                break;
 
611
                                        default :
 
612
                                                $type = ucwords( $comment->comment_type );
 
613
                                }
 
614
                                $type = esc_html( $type );
 
615
                        ?>
 
616
                        <div class="dashboard-comment-wrap">
 
617
                        <?php /* translators: %1$s is type of comment, %2$s is link to the post */ ?>
 
618
                        <h4 class="comment-meta"><?php printf( _x( '%1$s on %2$s', 'dashboard' ), "<strong>$type</strong>", $comment_post_link." ".$comment_link ); ?></h4>
 
619
                        <p class="comment-author"><?php comment_author_link(); ?></p>
 
620
 
 
621
                        <?php endif; // comment_type ?>
 
622
                        <blockquote><p><?php comment_excerpt(); ?></p></blockquote>
 
623
                        <p class="row-actions"><?php echo $actions_string; ?></p>
 
624
                        </div>
 
625
                </div>
 
626
<?php
 
627
}
 
628
 
 
629
/**
 
630
 * Callback function for Activity widget.
 
631
 *
 
632
 * @since 3.8.0
 
633
 */
 
634
function wp_dashboard_site_activity() {
 
635
 
 
636
        echo '<div id="activity-widget">';
 
637
 
 
638
        $future_posts = wp_dashboard_recent_posts( array(
 
639
                'max'     => 5,
 
640
                'status'  => 'future',
 
641
                'order'   => 'ASC',
 
642
                'title'   => __( 'Publishing Soon' ),
 
643
                'id'      => 'future-posts',
 
644
        ) );
 
645
        $recent_posts = wp_dashboard_recent_posts( array(
 
646
                'max'     => 5,
 
647
                'status'  => 'publish',
 
648
                'order'   => 'DESC',
 
649
                'title'   => __( 'Recently Published' ),
 
650
                'id'      => 'published-posts',
 
651
        ) );
 
652
 
 
653
        $recent_comments = wp_dashboard_recent_comments();
 
654
 
 
655
        if ( !$future_posts && !$recent_posts && !$recent_comments ) {
 
656
                echo '<div class="no-activity">';
 
657
                echo '<p class="smiley"></p>';
 
658
                echo '<p>' . __( 'No activity yet!' ) . '</p>';
 
659
                echo '</div>';
 
660
        }
 
661
 
 
662
        echo '</div>';
 
663
}
 
664
 
 
665
/**
 
666
 * Generates Publishing Soon and Recently Published sections.
 
667
 *
 
668
 * @since 3.8.0
 
669
 *
 
670
 * @param array $args {
 
671
 *     An array of query and display arguments.
 
672
 *
 
673
 *     @type int    $max     Number of posts to display.
 
674
 *     @type string $status  Post status.
 
675
 *     @type string $order   Designates ascending ('ASC') or descending ('DESC') order.
 
676
 *     @type string $title   Section title.
 
677
 *     @type string $id      The container id.
 
678
 * }
 
679
 * @return bool False if no posts were found. True otherwise.
 
680
 */
 
681
function wp_dashboard_recent_posts( $args ) {
 
682
        $query_args = array(
 
683
                'post_type'      => 'post',
 
684
                'post_status'    => $args['status'],
 
685
                'orderby'        => 'date',
 
686
                'order'          => $args['order'],
 
687
                'posts_per_page' => intval( $args['max'] ),
 
688
                'no_found_rows'  => true,
 
689
                'cache_results'  => false,
 
690
                'perm'           => ( 'future' === $args['status'] ) ? 'editable' : 'readable',
 
691
        );
 
692
        $posts = new WP_Query( $query_args );
 
693
 
 
694
        if ( $posts->have_posts() ) {
 
695
 
 
696
                echo '<div id="' . $args['id'] . '" class="activity-block">';
 
697
 
 
698
                echo '<h4>' . $args['title'] . '</h4>';
 
699
 
 
700
                echo '<ul>';
 
701
 
 
702
                $today    = date( 'Y-m-d', current_time( 'timestamp' ) );
 
703
                $tomorrow = date( 'Y-m-d', strtotime( '+1 day', current_time( 'timestamp' ) ) );
 
704
 
 
705
                while ( $posts->have_posts() ) {
 
706
                        $posts->the_post();
 
707
 
 
708
                        $time = get_the_time( 'U' );
 
709
                        if ( date( 'Y-m-d', $time ) == $today ) {
 
710
                                $relative = __( 'Today' );
 
711
                        } elseif ( date( 'Y-m-d', $time ) == $tomorrow ) {
 
712
                                $relative = __( 'Tomorrow' );
 
713
                        } else {
 
714
                                /* translators: date and time format for recent posts on the dashboard, see http://php.net/date */
 
715
                                $relative = date_i18n( __( 'M jS' ), $time );
 
716
                        }
 
717
 
 
718
                        if ( current_user_can( 'edit_post', get_the_ID() ) ) {
 
719
                                /* translators: 1: relative date, 2: time, 3: post edit link, 4: post title */
 
720
                                $format = __( '<span>%1$s, %2$s</span> <a href="%3$s">%4$s</a>' );
 
721
                                printf( "<li>$format</li>", $relative, get_the_time(), get_edit_post_link(), _draft_or_post_title() );
 
722
                        } else {
 
723
                                /* translators: 1: relative date, 2: time, 3: post title */
 
724
                                $format = __( '<span>%1$s, %2$s</span> %3$s' );
 
725
                                printf( "<li>$format</li>", $relative, get_the_time(), _draft_or_post_title() );
 
726
                        }
 
727
                }
 
728
 
 
729
                echo '</ul>';
 
730
                echo '</div>';
 
731
 
 
732
        } else {
 
733
                return false;
 
734
        }
 
735
 
 
736
        wp_reset_postdata();
 
737
 
 
738
        return true;
 
739
}
 
740
 
 
741
/**
 
742
 * Show Comments section.
 
743
 *
 
744
 * @since 3.8.0
 
745
 *
 
746
 * @param int $total_items Optional. Number of comments to query. Default 5.
 
747
 * @return bool False if no comments were found. True otherwise.
 
748
 */
 
749
function wp_dashboard_recent_comments( $total_items = 5 ) {
 
750
        // Select all comment types and filter out spam later for better query performance.
 
751
        $comments = array();
 
752
 
 
753
        $comments_query = array(
 
754
                'number' => $total_items * 5,
 
755
                'offset' => 0
 
756
        );
 
757
        if ( ! current_user_can( 'edit_posts' ) )
 
758
                $comments_query['status'] = 'approve';
 
759
 
 
760
        while ( count( $comments ) < $total_items && $possible = get_comments( $comments_query ) ) {
 
761
                foreach ( $possible as $comment ) {
 
762
                        if ( ! current_user_can( 'read_post', $comment->comment_post_ID ) )
 
763
                                continue;
 
764
                        $comments[] = $comment;
 
765
                        if ( count( $comments ) == $total_items )
 
766
                                break 2;
 
767
                }
 
768
                $comments_query['offset'] += $comments_query['number'];
 
769
                $comments_query['number'] = $total_items * 10;
 
770
        }
 
771
 
 
772
        if ( $comments ) {
 
773
                echo '<div id="latest-comments" class="activity-block">';
 
774
                echo '<h4>' . __( 'Comments' ) . '</h4>';
 
775
 
 
776
                echo '<div id="the-comment-list" data-wp-lists="list:comment">';
 
777
                foreach ( $comments as $comment )
 
778
                        _wp_dashboard_recent_comments_row( $comment );
 
779
                echo '</div>';
 
780
 
 
781
                if ( current_user_can('edit_posts') )
 
782
                        _get_list_table('WP_Comments_List_Table')->views();
 
783
 
 
784
                wp_comment_reply( -1, false, 'dashboard', false );
 
785
                wp_comment_trashnotice();
 
786
 
 
787
                echo '</div>';
 
788
        } else {
 
789
                return false;
 
790
        }
 
791
        return true;
 
792
}
 
793
 
 
794
/**
 
795
 * Display generic dashboard RSS widget feed.
 
796
 *
 
797
 * @since 2.5.0
 
798
 *
 
799
 * @param string $widget_id
 
800
 */
 
801
function wp_dashboard_rss_output( $widget_id ) {
 
802
        $widgets = get_option( 'dashboard_widget_options' );
 
803
        echo '<div class="rss-widget">';
 
804
        wp_widget_rss_output( $widgets[ $widget_id ] );
 
805
        echo "</div>";
 
806
}
 
807
 
 
808
/**
 
809
 * Checks to see if all of the feed url in $check_urls are cached.
 
810
 *
 
811
 * If $check_urls is empty, look for the rss feed url found in the dashboard
 
812
 * widget options of $widget_id. If cached, call $callback, a function that
 
813
 * echoes out output for this widget. If not cache, echo a "Loading..." stub
 
814
 * which is later replaced by AJAX call (see top of /wp-admin/index.php)
 
815
 *
 
816
 * @since 2.5.0
 
817
 *
 
818
 * @param string $widget_id
 
819
 * @param callback $callback
 
820
 * @param array $check_urls RSS feeds
 
821
 * @return bool False on failure. True on success.
 
822
 */
 
823
function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array() ) {
 
824
        $loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading&#8230;' ) . '</p><p class="hide-if-js">' . __( 'This widget requires JavaScript.' ) . '</p>';
 
825
        $doing_ajax = ( defined('DOING_AJAX') && DOING_AJAX );
 
826
 
 
827
        if ( empty($check_urls) ) {
 
828
                $widgets = get_option( 'dashboard_widget_options' );
 
829
                if ( empty($widgets[$widget_id]['url']) && ! $doing_ajax ) {
 
830
                        echo $loading;
 
831
                        return false;
 
832
                }
 
833
                $check_urls = array( $widgets[$widget_id]['url'] );
 
834
        }
 
835
 
 
836
        $cache_key = 'dash_' . md5( $widget_id );
 
837
        if ( false !== ( $output = get_transient( $cache_key ) ) ) {
 
838
                echo $output;
 
839
                return true;
 
840
        }
 
841
 
 
842
        if ( ! $doing_ajax ) {
 
843
                echo $loading;
 
844
                return false;
 
845
        }
 
846
 
 
847
        if ( $callback && is_callable( $callback ) ) {
 
848
                $args = array_slice( func_get_args(), 3 );
 
849
                array_unshift( $args, $widget_id, $check_urls );
 
850
                ob_start();
 
851
                call_user_func_array( $callback, $args );
 
852
                set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS ); // Default lifetime in cache of 12 hours (same as the feeds)
 
853
        }
 
854
 
 
855
        return true;
 
856
}
 
857
 
 
858
/* Dashboard Widgets Controls */
 
859
 
 
860
// Calls widget_control callback
 
861
/**
 
862
 * Calls widget control callback.
 
863
 *
 
864
 * @since 2.5.0
 
865
 *
 
866
 * @param int $widget_control_id Registered Widget ID.
 
867
 */
 
868
function wp_dashboard_trigger_widget_control( $widget_control_id = false ) {
 
869
        global $wp_dashboard_control_callbacks;
 
870
 
 
871
        if ( is_scalar($widget_control_id) && $widget_control_id && isset($wp_dashboard_control_callbacks[$widget_control_id]) && is_callable($wp_dashboard_control_callbacks[$widget_control_id]) ) {
 
872
                call_user_func( $wp_dashboard_control_callbacks[$widget_control_id], '', array( 'id' => $widget_control_id, 'callback' => $wp_dashboard_control_callbacks[$widget_control_id] ) );
 
873
        }
 
874
}
 
875
 
 
876
/**
 
877
 * The RSS dashboard widget control.
 
878
 *
 
879
 * Sets up $args to be used as input to wp_widget_rss_form(). Handles POST data
 
880
 * from RSS-type widgets.
 
881
 *
 
882
 * @since 2.5.0
 
883
 *
 
884
 * @param string $widget_id
 
885
 * @param array $form_inputs
 
886
 */
 
887
function wp_dashboard_rss_control( $widget_id, $form_inputs = array() ) {
 
888
        if ( !$widget_options = get_option( 'dashboard_widget_options' ) )
 
889
                $widget_options = array();
 
890
 
 
891
        if ( !isset($widget_options[$widget_id]) )
 
892
                $widget_options[$widget_id] = array();
 
893
 
 
894
        $number = 1; // Hack to use wp_widget_rss_form()
 
895
        $widget_options[$widget_id]['number'] = $number;
 
896
 
 
897
        if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['widget-rss'][$number]) ) {
 
898
                $_POST['widget-rss'][$number] = wp_unslash( $_POST['widget-rss'][$number] );
 
899
                $widget_options[$widget_id] = wp_widget_rss_process( $_POST['widget-rss'][$number] );
 
900
                $widget_options[$widget_id]['number'] = $number;
 
901
 
 
902
                // Title is optional. If black, fill it if possible.
 
903
                if ( !$widget_options[$widget_id]['title'] && isset($_POST['widget-rss'][$number]['title']) ) {
 
904
                        $rss = fetch_feed($widget_options[$widget_id]['url']);
 
905
                        if ( is_wp_error($rss) ) {
 
906
                                $widget_options[$widget_id]['title'] = htmlentities(__('Unknown Feed'));
 
907
                        } else {
 
908
                                $widget_options[$widget_id]['title'] = htmlentities(strip_tags($rss->get_title()));
 
909
                                $rss->__destruct();
 
910
                                unset($rss);
 
911
                        }
 
912
                }
 
913
                update_option( 'dashboard_widget_options', $widget_options );
 
914
                $cache_key = 'dash_' . md5( $widget_id );
 
915
                delete_transient( $cache_key );
 
916
        }
 
917
 
 
918
        wp_widget_rss_form( $widget_options[$widget_id], $form_inputs );
 
919
}
 
920
 
 
921
/**
 
922
 * WordPress News dashboard widget.
 
923
 *
 
924
 * @since 2.7.0
 
925
 */
 
926
function wp_dashboard_primary() {
 
927
        $feeds = array(
 
928
                'news' => array(
 
929
 
 
930
                        /**
 
931
                         * Filter the primary link URL for the 'WordPress News' dashboard widget.
 
932
                         *
 
933
                         * @since 2.5.0
 
934
                         *
 
935
                         * @param string $link The widget's primary link URL.
 
936
                         */
 
937
                        'link' => apply_filters( 'dashboard_primary_link', __( 'http://wordpress.org/news/' ) ),
 
938
 
 
939
                        /**
 
940
                         * Filter the primary feed URL for the 'WordPress News' dashboard widget.
 
941
                         *
 
942
                         * @since 2.3.0
 
943
                         *
 
944
                         * @param string $url The widget's primary feed URL.
 
945
                         */
 
946
                        'url' => apply_filters( 'dashboard_primary_feed', __( 'http://wordpress.org/news/feed/' ) ),
 
947
 
 
948
                        /**
 
949
                         * Filter the primary link title for the 'WordPress News' dashboard widget.
 
950
                         *
 
951
                         * @since 2.3.0
 
952
                         *
 
953
                         * @param string $title Title attribute for the widget's primary link.
 
954
                         */
 
955
                        'title'        => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ),
 
956
                        'items'        => 1,
 
957
                        'show_summary' => 1,
 
958
                        'show_author'  => 0,
 
959
                        'show_date'    => 1,
 
960
                ),
 
961
                'planet' => array(
 
962
 
 
963
                        /**
 
964
                         * Filter the secondary link URL for the 'WordPress News' dashboard widget.
 
965
                         *
 
966
                         * @since 2.3.0
 
967
                         *
 
968
                         * @param string $link The widget's secondary link URL.
 
969
                         */
 
970
                        'link' => apply_filters( 'dashboard_secondary_link', __( 'http://planet.wordpress.org/' ) ),
 
971
 
 
972
                        /**
 
973
                         * Filter the secondary feed URL for the 'WordPress News' dashboard widget.
 
974
                         *
 
975
                         * @since 2.3.0
 
976
                         *
 
977
                         * @param string $url The widget's secondary feed URL.
 
978
                         */
 
979
                        'url' => apply_filters( 'dashboard_secondary_feed', __( 'http://planet.wordpress.org/feed/' ) ),
 
980
 
 
981
                        /**
 
982
                         * Filter the secondary link title for the 'WordPress News' dashboard widget.
 
983
                         *
 
984
                         * @since 2.3.0
 
985
                         *
 
986
                         * @param string $title Title attribute for the widget's secondary link.
 
987
                         */
 
988
                        'title'        => apply_filters( 'dashboard_secondary_title', __( 'Other WordPress News' ) ),
 
989
                        'items'        => 3,
 
990
                        'show_summary' => 0,
 
991
                        'show_author'  => 0,
 
992
                        'show_date'    => 0,
 
993
                )
 
994
        );
 
995
 
 
996
        if ( ( ! is_multisite() && is_blog_admin() && current_user_can( 'install_plugins' ) ) || ( is_network_admin() && current_user_can( 'manage_network_plugins' ) && current_user_can( 'install_plugins' ) ) ) {
 
997
                $feeds['plugins'] = array(
 
998
                        'link'         => '',
 
999
                        'url'          => array(
 
1000
                                'popular' => 'http://wordpress.org/plugins/rss/browse/popular/',
 
1001
                        ),
 
1002
                        'title'        => '',
 
1003
                        'items'        => 1,
 
1004
                        'show_summary' => 0,
 
1005
                        'show_author'  => 0,
 
1006
                        'show_date'    => 0,
 
1007
                );
 
1008
        }
 
1009
 
 
1010
        wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds );
 
1011
}
 
1012
 
 
1013
/**
 
1014
 * Display the WordPress news feeds.
 
1015
 *
 
1016
 * @since 3.8.0
 
1017
 *
 
1018
 * @param string $widget_id Widget ID.
 
1019
 * @param array  $feeds     Array of RSS feeds.
 
1020
 */
 
1021
function wp_dashboard_primary_output( $widget_id, $feeds ) {
 
1022
        foreach( $feeds as $type => $args ) {
 
1023
                $args['type'] = $type;
 
1024
                echo '<div class="rss-widget">';
 
1025
                if ( $type === 'plugins' ) {
 
1026
                        wp_dashboard_plugins_output( $args['url'], $args );
 
1027
                } else {
 
1028
                        wp_widget_rss_output( $args['url'], $args );
 
1029
                }
 
1030
                echo "</div>";
 
1031
        }
 
1032
}
 
1033
 
 
1034
/**
 
1035
 * Display plugins text for the WordPress news widget.
 
1036
 *
 
1037
 * @since 2.5.0
 
1038
 */
 
1039
function wp_dashboard_plugins_output( $rss, $args = array() ) {
 
1040
        // Plugin feeds plus link to install them
 
1041
        $popular = fetch_feed( $args['url']['popular'] );
 
1042
 
 
1043
        if ( false === $plugin_slugs = get_transient( 'plugin_slugs' ) ) {
 
1044
                $plugin_slugs = array_keys( get_plugins() );
 
1045
                set_transient( 'plugin_slugs', $plugin_slugs, DAY_IN_SECONDS );
 
1046
        }
 
1047
 
 
1048
        echo '<ul>';
 
1049
 
 
1050
        foreach ( array( $popular ) as $feed ) {
 
1051
                if ( is_wp_error( $feed ) || ! $feed->get_item_quantity() )
 
1052
                        continue;
 
1053
 
 
1054
                $items = $feed->get_items(0, 5);
 
1055
 
 
1056
                // Pick a random, non-installed plugin
 
1057
                while ( true ) {
 
1058
                        // Abort this foreach loop iteration if there's no plugins left of this type
 
1059
                        if ( 0 == count($items) )
 
1060
                                continue 2;
 
1061
 
 
1062
                        $item_key = array_rand($items);
 
1063
                        $item = $items[$item_key];
 
1064
 
 
1065
                        list($link, $frag) = explode( '#', $item->get_link() );
 
1066
 
 
1067
                        $link = esc_url($link);
 
1068
                        if ( preg_match( '|/([^/]+?)/?$|', $link, $matches ) )
 
1069
                                $slug = $matches[1];
 
1070
                        else {
 
1071
                                unset( $items[$item_key] );
 
1072
                                continue;
 
1073
                        }
 
1074
 
 
1075
                        // Is this random plugin's slug already installed? If so, try again.
 
1076
                        reset( $plugin_slugs );
 
1077
                        foreach ( $plugin_slugs as $plugin_slug ) {
 
1078
                                if ( $slug == substr( $plugin_slug, 0, strlen( $slug ) ) ) {
 
1079
                                        unset( $items[$item_key] );
 
1080
                                        continue 2;
 
1081
                                }
 
1082
                        }
 
1083
 
 
1084
                        // If we get to this point, then the random plugin isn't installed and we can stop the while().
 
1085
                        break;
 
1086
                }
 
1087
 
 
1088
                // Eliminate some common badly formed plugin descriptions
 
1089
                while ( ( null !== $item_key = array_rand($items) ) && false !== strpos( $items[$item_key]->get_description(), 'Plugin Name:' ) )
 
1090
                        unset($items[$item_key]);
 
1091
 
 
1092
                if ( !isset($items[$item_key]) )
 
1093
                        continue;
 
1094
 
 
1095
                $title = esc_html( $item->get_title() );
 
1096
 
 
1097
                $ilink = wp_nonce_url('plugin-install.php?tab=plugin-information&plugin=' . $slug, 'install-plugin_' . $slug) . '&amp;TB_iframe=true&amp;width=600&amp;height=800';
 
1098
                echo "<li class='dashboard-news-plugin'><span>" . __( 'Popular Plugin' ) . ":</span> <a href='$link' class='dashboard-news-plugin-link'>$title</a>&nbsp;<span>(<a href='$ilink' class='thickbox' title='$title'>" . __( 'Install' ) . "</a>)</span></li>";
 
1099
 
 
1100
                $feed->__destruct();
 
1101
                unset( $feed );
 
1102
        }
 
1103
 
 
1104
        echo '</ul>';
 
1105
}
 
1106
 
 
1107
/**
 
1108
 * Display file upload quota on dashboard.
 
1109
 *
 
1110
 * Runs on the activity_box_end hook in wp_dashboard_right_now().
 
1111
 *
 
1112
 * @since 3.0.0
 
1113
 *
 
1114
 * @return bool True if not multisite, user can't upload files, or the space check option is disabled.
 
1115
*/
 
1116
function wp_dashboard_quota() {
 
1117
        if ( !is_multisite() || !current_user_can( 'upload_files' ) || get_site_option( 'upload_space_check_disabled' ) )
 
1118
                return true;
 
1119
 
 
1120
        $quota = get_space_allowed();
 
1121
        $used = get_space_used();
 
1122
 
 
1123
        if ( $used > $quota )
 
1124
                $percentused = '100';
 
1125
        else
 
1126
                $percentused = ( $used / $quota ) * 100;
 
1127
        $used_class = ( $percentused >= 70 ) ? ' warning' : '';
 
1128
        $used = round( $used, 2 );
 
1129
        $percentused = number_format( $percentused );
 
1130
 
 
1131
        ?>
 
1132
        <h4 class="mu-storage"><?php _e( 'Storage Space' ); ?></h4>
 
1133
        <div class="mu-storage">
 
1134
        <ul>
 
1135
                <li class="storage-count">
 
1136
                        <?php $text = sprintf(
 
1137
                                /* translators: number of megabytes */
 
1138
                                __( '%s MB Space Allowed' ),
 
1139
                                number_format_i18n( $quota )
 
1140
                        );
 
1141
                        printf(
 
1142
                                '<a href="%1$s" title="%2$s">%3$s</a>',
 
1143
                                esc_url( admin_url( 'upload.php' ) ),
 
1144
                                __( 'Manage Uploads' ),
 
1145
                                $text
 
1146
                        ); ?>
 
1147
                </li><li class="storage-count <?php echo $used_class; ?>">
 
1148
                        <?php $text = sprintf(
 
1149
                                /* translators: 1: number of megabytes, 2: percentage */
 
1150
                                __( '%1$s MB (%2$s%%) Space Used' ),
 
1151
                                number_format_i18n( $used, 2 ),
 
1152
                                $percentused
 
1153
                        );
 
1154
                        printf(
 
1155
                                '<a href="%1$s" title="%2$s" class="musublink">%3$s</a>',
 
1156
                                esc_url( admin_url( 'upload.php' ) ),
 
1157
                                __( 'Manage Uploads' ),
 
1158
                                $text
 
1159
                        ); ?>
 
1160
                </li>
 
1161
        </ul>
 
1162
        </div>
 
1163
        <?php
 
1164
}
 
1165
add_action( 'activity_box_end', 'wp_dashboard_quota' );
 
1166
 
 
1167
// Display Browser Nag Meta Box
 
1168
function wp_dashboard_browser_nag() {
 
1169
        $notice = '';
 
1170
        $response = wp_check_browser_version();
 
1171
 
 
1172
        if ( $response ) {
 
1173
                if ( $response['insecure'] ) {
 
1174
                        $msg = sprintf( __( "It looks like you're using an insecure version of <a href='%s'>%s</a>. Using an outdated browser makes your computer unsafe. For the best WordPress experience, please update your browser." ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ) );
 
1175
                } else {
 
1176
                        $msg = sprintf( __( "It looks like you're using an old version of <a href='%s'>%s</a>. For the best WordPress experience, please update your browser." ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ) );
 
1177
                }
 
1178
 
 
1179
                $browser_nag_class = '';
 
1180
                if ( !empty( $response['img_src'] ) ) {
 
1181
                        $img_src = ( is_ssl() && ! empty( $response['img_src_ssl'] ) )? $response['img_src_ssl'] : $response['img_src'];
 
1182
 
 
1183
                        $notice .= '<div class="alignright browser-icon"><a href="' . esc_attr($response['update_url']) . '"><img src="' . esc_attr( $img_src ) . '" alt="" /></a></div>';
 
1184
                        $browser_nag_class = ' has-browser-icon';
 
1185
                }
 
1186
                $notice .= "<p class='browser-update-nag{$browser_nag_class}'>{$msg}</p>";
 
1187
 
 
1188
                $browsehappy = 'http://browsehappy.com/';
 
1189
                $locale = get_locale();
 
1190
                if ( 'en_US' !== $locale )
 
1191
                        $browsehappy = add_query_arg( 'locale', $locale, $browsehappy );
 
1192
 
 
1193
                $notice .= '<p>' . sprintf( __( '<a href="%1$s" class="update-browser-link">Update %2$s</a> or learn how to <a href="%3$s" class="browse-happy-link">browse happy</a>' ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ), esc_url( $browsehappy ) ) . '</p>';
 
1194
                $notice .= '<p class="hide-if-no-js"><a href="" class="dismiss">' . __( 'Dismiss' ) . '</a></p>';
 
1195
                $notice .= '<div class="clear"></div>';
 
1196
        }
 
1197
 
 
1198
        /**
 
1199
        * Filter the notice output for the 'Browse Happy' nag meta box.
 
1200
        *
 
1201
        * @since 3.2.0
 
1202
        *
 
1203
        * @param string $notice   The notice content.
 
1204
        * @param array  $response An array containing web browser information.
 
1205
        */
 
1206
        echo apply_filters( 'browse-happy-notice', $notice, $response );
 
1207
}
 
1208
 
 
1209
function dashboard_browser_nag_class( $classes ) {
 
1210
        $response = wp_check_browser_version();
 
1211
 
 
1212
        if ( $response && $response['insecure'] )
 
1213
                $classes[] = 'browser-insecure';
 
1214
 
 
1215
        return $classes;
 
1216
}
 
1217
 
 
1218
/**
 
1219
 * Check if the user needs a browser update
 
1220
 *
 
1221
 * @since 3.2.0
 
1222
 *
 
1223
 * @return array|bool False on failure, array of browser data on success.
 
1224
 */
 
1225
function wp_check_browser_version() {
 
1226
        if ( empty( $_SERVER['HTTP_USER_AGENT'] ) )
 
1227
                return false;
 
1228
 
 
1229
        $key = md5( $_SERVER['HTTP_USER_AGENT'] );
 
1230
 
 
1231
        if ( false === ($response = get_site_transient('browser_' . $key) ) ) {
 
1232
                global $wp_version;
 
1233
 
 
1234
                $options = array(
 
1235
                        'body'                  => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ),
 
1236
                        'user-agent'    => 'WordPress/' . $wp_version . '; ' . home_url()
 
1237
                );
 
1238
 
 
1239
                $response = wp_remote_post( 'http://api.wordpress.org/core/browse-happy/1.1/', $options );
 
1240
 
 
1241
                if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) )
 
1242
                        return false;
 
1243
 
 
1244
                /**
 
1245
                 * Response should be an array with:
 
1246
                 *  'name' - string - A user friendly browser name
 
1247
                 *  'version' - string - The most recent version of the browser
 
1248
                 *  'current_version' - string - The version of the browser the user is using
 
1249
                 *  'upgrade' - boolean - Whether the browser needs an upgrade
 
1250
                 *  'insecure' - boolean - Whether the browser is deemed insecure
 
1251
                 *  'upgrade_url' - string - The url to visit to upgrade
 
1252
                 *  'img_src' - string - An image representing the browser
 
1253
                 *  'img_src_ssl' - string - An image (over SSL) representing the browser
 
1254
                 */
 
1255
                $response = json_decode( wp_remote_retrieve_body( $response ), true );
 
1256
 
 
1257
                if ( ! is_array( $response ) )
 
1258
                        return false;
 
1259
 
 
1260
                set_site_transient( 'browser_' . $key, $response, WEEK_IN_SECONDS );
 
1261
        }
 
1262
 
 
1263
        return $response;
 
1264
}
 
1265
 
 
1266
/**
 
1267
 * Empty function usable by plugins to output empty dashboard widget (to be populated later by JS).
 
1268
 */
 
1269
function wp_dashboard_empty() {}
 
1270
 
 
1271
/**
 
1272
 * Displays a welcome panel to introduce users to WordPress.
 
1273
 *
 
1274
 * @since 3.3.0
 
1275
 */
 
1276
function wp_welcome_panel() {
 
1277
        ?>
 
1278
        <div class="welcome-panel-content">
 
1279
        <h3><?php _e( 'Welcome to WordPress!' ); ?></h3>
 
1280
        <p class="about-description"><?php _e( 'We&#8217;ve assembled some links to get you started:' ); ?></p>
 
1281
        <div class="welcome-panel-column-container">
 
1282
        <div class="welcome-panel-column">
 
1283
                <?php if ( current_user_can( 'customize' ) ): ?>
 
1284
                        <h4><?php _e( 'Get Started' ); ?></h4>
 
1285
                        <a class="button button-primary button-hero load-customize hide-if-no-customize" href="<?php echo wp_customize_url(); ?>"><?php _e( 'Customize Your Site' ); ?></a>
 
1286
                <?php endif; ?>
 
1287
                <a class="button button-primary button-hero hide-if-customize" href="<?php echo admin_url( 'themes.php' ); ?>"><?php _e( 'Customize Your Site' ); ?></a>
 
1288
                <?php if ( current_user_can( 'install_themes' ) || ( current_user_can( 'switch_themes' ) && count( wp_get_themes( array( 'allowed' => true ) ) ) > 1 ) ) : ?>
 
1289
                        <p class="hide-if-no-customize"><?php printf( __( 'or, <a href="%s">change your theme completely</a>' ), admin_url( 'themes.php' ) ); ?></p>
 
1290
                <?php endif; ?>
 
1291
        </div>
 
1292
        <div class="welcome-panel-column">
 
1293
                <h4><?php _e( 'Next Steps' ); ?></h4>
 
1294
                <ul>
 
1295
                <?php if ( 'page' == get_option( 'show_on_front' ) && ! get_option( 'page_for_posts' ) ) : ?>
 
1296
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-edit-page">' . __( 'Edit your front page' ) . '</a>', get_edit_post_link( get_option( 'page_on_front' ) ) ); ?></li>
 
1297
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-add-page">' . __( 'Add additional pages' ) . '</a>', admin_url( 'post-new.php?post_type=page' ) ); ?></li>
 
1298
                <?php elseif ( 'page' == get_option( 'show_on_front' ) ) : ?>
 
1299
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-edit-page">' . __( 'Edit your front page' ) . '</a>', get_edit_post_link( get_option( 'page_on_front' ) ) ); ?></li>
 
1300
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-add-page">' . __( 'Add additional pages' ) . '</a>', admin_url( 'post-new.php?post_type=page' ) ); ?></li>
 
1301
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-write-blog">' . __( 'Add a blog post' ) . '</a>', admin_url( 'post-new.php' ) ); ?></li>
 
1302
                <?php else : ?>
 
1303
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-write-blog">' . __( 'Write your first blog post' ) . '</a>', admin_url( 'post-new.php' ) ); ?></li>
 
1304
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-add-page">' . __( 'Add an About page' ) . '</a>', admin_url( 'post-new.php?post_type=page' ) ); ?></li>
 
1305
                <?php endif; ?>
 
1306
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-view-site">' . __( 'View your site' ) . '</a>', home_url( '/' ) ); ?></li>
 
1307
                </ul>
 
1308
        </div>
 
1309
        <div class="welcome-panel-column welcome-panel-last">
 
1310
                <h4><?php _e( 'More Actions' ); ?></h4>
 
1311
                <ul>
 
1312
                <?php if ( current_theme_supports( 'widgets' ) || current_theme_supports( 'menus' ) ) : ?>
 
1313
                        <li><div class="welcome-icon welcome-widgets-menus"><?php
 
1314
                                if ( current_theme_supports( 'widgets' ) && current_theme_supports( 'menus' ) ) {
 
1315
                                        printf( __( 'Manage <a href="%1$s">widgets</a> or <a href="%2$s">menus</a>' ),
 
1316
                                                admin_url( 'widgets.php' ), admin_url( 'nav-menus.php' ) );
 
1317
                                } elseif ( current_theme_supports( 'widgets' ) ) {
 
1318
                                        echo '<a href="' . admin_url( 'widgets.php' ) . '">' . __( 'Manage widgets' ) . '</a>';
 
1319
                                } else {
 
1320
                                        echo '<a href="' . admin_url( 'nav-menus.php' ) . '">' . __( 'Manage menus' ) . '</a>';
 
1321
                                }
 
1322
                        ?></div></li>
 
1323
                <?php endif; ?>
 
1324
                <?php if ( current_user_can( 'manage_options' ) ) : ?>
 
1325
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-comments">' . __( 'Turn comments on or off' ) . '</a>', admin_url( 'options-discussion.php' ) ); ?></li>
 
1326
                <?php endif; ?>
 
1327
                        <li><?php printf( '<a href="%s" class="welcome-icon welcome-learn-more">' . __( 'Learn more about getting started' ) . '</a>', __( 'http://codex.wordpress.org/First_Steps_With_WordPress' ) ); ?></li>
 
1328
                </ul>
 
1329
        </div>
 
1330
        </div>
 
1331
        </div>
 
1332
        <?php
 
1333
}