Skip to content

Commit c624436

Browse files
committed
Dashboard widgets caching improvements from mdawaffe. see WordPress#5750
git-svn-id: https://develop.svn.wordpress.org/trunk@6958 602fd350-edb4-49c9-b593-d223f7449a82
1 parent aaf69b4 commit c624436

4 files changed

Lines changed: 232 additions & 152 deletions

File tree

wp-admin/css/dashboard.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,6 @@ div.dashboard-widget-content .comment-meta {
254254
#dashboard_plugins p {
255255
margin: 0 0 1em;
256256
}
257+
258+
.widget-loading {
259+
}

wp-admin/includes/dashboard.php

Lines changed: 216 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ function wp_dashboard_setup() {
3232
array( 'all_link' => 'edit-comments.php', 'notice' => $notice, 'width' => 'half' )
3333
);
3434

35-
3635
// Incoming Links Widget
3736
if ( !isset( $widget_options['dashboard_incoming_links'] ) ) {
3837
$update = true;
@@ -44,7 +43,8 @@ function wp_dashboard_setup() {
4443
);
4544
}
4645
wp_register_sidebar_widget( 'dashboard_incoming_links', __( 'Incoming Links' ), 'wp_dashboard_empty',
47-
array( 'all_link' => $widget_options['dashboard_incoming_links']['link'], 'feed_link' => $widget_options['dashboard_incoming_links']['url'], 'width' => 'half' )
46+
array( 'all_link' => $widget_options['dashboard_incoming_links']['link'], 'feed_link' => $widget_options['dashboard_incoming_links']['url'], 'width' => 'half' ),
47+
'wp_dashboard_cached_rss_widget', 'wp_dashboard_incoming_links_output'
4848
);
4949
wp_register_widget_control( 'dashboard_incoming_links', __( 'Incoming Links' ), 'wp_dashboard_rss_control', array(),
5050
array( 'widget_id' => 'dashboard_incoming_links', 'form_inputs' => array( 'title' => false, 'show_summary' => false, 'show_author' => false ) )
@@ -53,13 +53,11 @@ function wp_dashboard_setup() {
5353

5454
// WP Plugins Widget
5555
wp_register_sidebar_widget( 'dashboard_plugins', __( 'Plugins' ), 'wp_dashboard_empty',
56-
array( 'all_link' => 'http://wordpress.org/extend/plugins/', 'feed_link' => 'http://wordpress.org/extend/plugins/rss/', 'width' => 'half' )
57-
);
58-
wp_register_widget_control( 'dashboard_plugins', __( 'Plugins' ), 'wp_dashboard_empty', array(),
59-
array( 'widget_id' => 'dashboard_plugins' )
56+
array( 'all_link' => 'http://wordpress.org/extend/plugins/', 'feed_link' => 'http://wordpress.org/extend/plugins/rss/', 'width' => 'half' ),
57+
'wp_dashboard_cached_rss_widget', 'wp_dashboard_plugins_output',
58+
array( 'http://wordpress.org/extend/plugins/rss/browse/popular/', 'http://wordpress.org/extend/plugins/rss/browse/new/', 'http://wordpress.org/extend/plugins/rss/browse/updated/' )
6059
);
6160

62-
6361
// Primary feed (Dev Blog) Widget
6462
if ( !isset( $widget_options['dashboard_primary'] ) ) {
6563
$update = true;
@@ -74,7 +72,8 @@ function wp_dashboard_setup() {
7472
);
7573
}
7674
wp_register_sidebar_widget( 'dashboard_primary', $widget_options['dashboard_primary']['title'], 'wp_dashboard_empty',
77-
array( 'all_link' => $widget_options['dashboard_primary']['link'], 'feed_link' => $widget_options['dashboard_primary']['url'], 'width' => 'half', 'class' => 'widget_rss' )
75+
array( 'all_link' => $widget_options['dashboard_primary']['link'], 'feed_link' => $widget_options['dashboard_primary']['url'], 'width' => 'half', 'class' => 'widget_rss' ),
76+
'wp_dashboard_cached_rss_widget', 'wp_dashboard_rss_output'
7877
);
7978
wp_register_widget_control( 'dashboard_primary', __( 'Primary Feed' ), 'wp_dashboard_rss_control', array(),
8079
array( 'widget_id' => 'dashboard_primary' )
@@ -92,13 +91,38 @@ function wp_dashboard_setup() {
9291
);
9392
}
9493
wp_register_sidebar_widget( 'dashboard_secondary', $widget_options['dashboard_secondary']['title'], 'wp_dashboard_empty',
95-
array( 'all_link' => $widget_options['dashboard_secondary']['link'], 'feed_link' => $widget_options['dashboard_secondary']['url'], 'width' => 'full' )
94+
array( 'all_link' => $widget_options['dashboard_secondary']['link'], 'feed_link' => $widget_options['dashboard_secondary']['url'], 'width' => 'full' ),
95+
'wp_dashboard_cached_rss_widget', 'wp_dashboard_secondary_output'
9696
);
9797
wp_register_widget_control( 'dashboard_secondary', __( 'Secondary Feed' ), 'wp_dashboard_rss_control', array(),
9898
array( 'widget_id' => 'dashboard_secondary', 'form_inputs' => array( 'show_summary' => false, 'show_author' => false, 'show_date' => false ) )
9999
);
100100

101101

102+
/* Dashboard Widget Template
103+
wp_register_sidebar_widget( $widget_id (unique slug) , $widget_title, $output_callback,
104+
array(
105+
'all_link' => full url for "See All" link,
106+
'feed_link' => full url for "RSS" link,
107+
'width' => 'fourth', 'third', 'half', 'full' (defaults to 'half'),
108+
'height' => 'single', 'double' (defaults to 'single'),
109+
),
110+
$wp_dashboard_empty_callback (only needed if using 'wp_dashboard_empty' as your $output_callback),
111+
$arg, $arg, $arg... (further args passed to callbacks)
112+
);
113+
114+
// optional: if you want users to be able to edit the settings of your widget, you need to register a widget_control
115+
wp_register_widget_control( $widget_id, $widget_control_title, $control_output_callback,
116+
array(), // leave an empty array here: oddity in widget code
117+
array(
118+
'widget_id' => $widget_id, // Yes - again. This is required: oddity in widget code
119+
'arg' => an arg to pass to the $control_output_callback,
120+
'another' => another arg to pass to the $control_output_callback,
121+
...
122+
)
123+
);
124+
*/
125+
102126
// Hook to register new widgets
103127
do_action( 'wp_dashboard_setup' );
104128

@@ -174,7 +198,7 @@ function wp_dashboard_dynamic_sidebar_params( $params ) {
174198
$content_class .= ' dashboard-widget-control';
175199
$wp_registered_widgets[$widget_id]['callback'] = 'wp_dashboard_empty';
176200
$sidebar_widget_name = $wp_registered_widget_controls[$widget_id]['name'];
177-
$params[1] = $widget_id;
201+
$params[1] = 'wp_dashbaord_trigger_widget_control';
178202
$sidebar_before_widget .= '<form action="' . remove_query_arg( 'edit' ) . '" method="post">';
179203
$sidebar_after_widget = "<div class='dashboard-widget-submit'><input type='hidden' name='sidebar' value='wp_dashboard' /><input type='hidden' name='widget_id' value='$widget_id' /><input type='submit' value='" . __( 'Save' ) . "' /></div></form>$sidebar_after_widget";
180204
$links[] = '<a href="' . remove_query_arg( 'edit' ) . '">' . __( 'Cancel' ) . '</a>';
@@ -218,7 +242,6 @@ function wp_dashboard_dynamic_sidebar_params( $params ) {
218242

219243
function wp_dashboard_recent_comments( $sidebar_args ) {
220244
global $comment;
221-
222245
extract( $sidebar_args, EXTR_SKIP );
223246

224247
echo $before_widget;
@@ -268,8 +291,182 @@ function wp_dashboard_recent_comments( $sidebar_args ) {
268291
echo $after_widget;
269292
}
270293

271-
// Empty widget used for JS/AJAX created output. Also used when widget is in edit mode.
272-
function wp_dashboard_empty( $sidebar_args, $widget_control_id = false ) {
294+
// $sidebar_args are handled by wp_dashboard_empty()
295+
function wp_dashboard_incoming_links_output() {
296+
$widgets = get_option( 'dashboard_widget_options' );
297+
@extract( @$widgets['dashboard_incoming_links'], EXTR_SKIP );
298+
$rss = @fetch_rss( $url );
299+
if ( isset($rss->items) && 1 < count($rss->items) ) {// Technorati returns a 1-item feed when it has no results
300+
301+
echo "<ul>\n";
302+
303+
$rss->items = array_slice($rss->items, 0, $items);
304+
foreach ( $rss->items as $item ) {
305+
$publisher = '';
306+
$site_link = '';
307+
$link = '';
308+
$content = '';
309+
$date = '';
310+
$link = clean_url( strip_tags( $item['link'] ) );
311+
312+
if ( isset( $item['author_uri'] ) )
313+
$site_link = clean_url( strip_tags( $item['author_uri'] ) );
314+
315+
if ( !$publisher = wp_specialchars( strip_tags( isset($item['dc']['publisher']) ? $item['dc']['publisher'] : $item['author_name'] ) ) )
316+
$publisher = __( 'Somebody' );
317+
if ( $site_link )
318+
$publisher = "<a href='$site_link'>$publisher</a>";
319+
else
320+
$publisher = "<strong>$publisher</strong>";
321+
322+
if ( isset($item['description']) )
323+
$content = $item['description'];
324+
elseif ( isset($item['summary']) )
325+
$content = $item['summary'];
326+
elseif ( isset($item['atom_content']) )
327+
$content = $item['atom_content'];
328+
else
329+
$content = __( 'something' );
330+
$content = strip_tags( $content );
331+
if ( 50 < strlen($content) )
332+
$content = substr($content, 0, 50) . ' ...';
333+
$content = wp_specialchars( $content );
334+
if ( $link )
335+
$text = _c( '%1$s linked here <a href="%2$s">saying</a>, "%3$s"|feed_display' );
336+
else
337+
$text = _c( '%1$s linked here saying, "%3$s"|feed_display' );
338+
339+
if ( $show_date ) {
340+
if ( $show_author || $show_summary )
341+
$text .= _c( ' on %4$s|feed_display' );
342+
$date = wp_specialchars( strip_tags( isset($item['pubdate']) ? $item['pubdate'] : $item['published'] ) );
343+
$date = strtotime( $date );
344+
$date = gmdate( get_option( 'date_format' ), $date );
345+
}
346+
347+
echo "\t<li>" . sprintf( _c( "$text|feed_display" ), $publisher, $link, $content, $date ) . "</li>\n";
348+
}
349+
350+
echo "</ul>\n";
351+
352+
} else {
353+
echo '<p>' . __('No incoming links found... yet.') . "</p>\n";
354+
}
355+
}
356+
357+
// $sidebar_args are handled by wp_dashboard_empty()
358+
function wp_dashboard_rss_output( $widget_id ) {
359+
$widgets = get_option( 'dashboard_widget_options' );
360+
wp_widget_rss_output( $widgets[$widget_id] );
361+
}
362+
363+
// $sidebar_args are handled by wp_dashboard_empty()
364+
function wp_dashboard_secondary_output() {
365+
$widgets = get_option( 'dashboard_widget_options' );
366+
@extract( @$widgets['dashboard_secondary'], EXTR_SKIP );
367+
$rss = @fetch_rss( $url );
368+
if ( !isset($rss->items) || 0 == count($rss->items) )
369+
return false;
370+
371+
echo "<ul>\n";
372+
373+
$rss->items = array_slice($rss->items, 0, $items);
374+
foreach ($rss->items as $item ) {
375+
$title = wp_specialchars($item['title']);
376+
$author = preg_replace( '|(.+?):.+|s', '$1', $item['title'] );
377+
$post = preg_replace( '|.+?:(.+)|s', '$1', $item['title'] );
378+
$link = clean_url($item['link']);
379+
380+
echo "\t<li><a href='$link'><span class='post'>$post</span><span class='hidden'> - </span><cite>$author</cite></a></li>\n";
381+
}
382+
383+
echo "</ul>\n<br class='clear' />\n";
384+
}
385+
386+
// $sidebar_args are handled by wp_dashboard_empty()
387+
function wp_dashboard_plugins_output() {
388+
$popular = @fetch_rss( 'http://wordpress.org/extend/plugins/rss/browse/popular/' );
389+
$new = @fetch_rss( 'http://wordpress.org/extend/plugins/rss/browse/new/' );
390+
$updated = @fetch_rss( 'http://wordpress.org/extend/plugins/rss/browse/updated/' );
391+
392+
foreach ( array( 'popular' => __('Most Popular'), 'new' => __('Newest Plugins'), 'updated' => __('Recently Updated') ) as $feed => $label ) {
393+
if ( !isset($$feed->items) || 0 == count($$feed->items) )
394+
continue;
395+
396+
$$feed->items = array_slice($$feed->items, 0, 5);
397+
$item_key = array_rand($$feed->items);
398+
399+
// Eliminate some common badly formed plugin descriptions
400+
while ( ( null !== $item_key = array_rand($$feed->items) ) && false !== strpos( $$feed->items[$item_key]['description'], 'Plugin Name:' ) )
401+
unset($$feed->items[$item_key]);
402+
403+
if ( !isset($$feed->items[$item_key]) )
404+
continue;
405+
406+
$item = $$feed->items[$item_key];
407+
408+
// current bbPress feed item titles are: user on "topic title"
409+
if ( preg_match( '/"(.*)"/s', $item['title'], $matches ) )
410+
$title = $matches[1];
411+
else // but let's make it forward compatible if things change
412+
$title = $item['title'];
413+
$title = wp_specialchars( $title );
414+
415+
$description = wp_specialchars( strip_tags(html_entity_decode($item['description'], ENT_QUOTES)) );
416+
417+
list($link, $frag) = explode( '#', $item['link'] );
418+
419+
$link = clean_url($link);
420+
$dlink = rtrim($link, '/') . '/download/';
421+
422+
echo "<h4>$label</h4>\n";
423+
echo "<h5><a href='$link'>$title</a></h5> <span>(<a href='$dlink'>" . __( 'Download' ) . "</a>)</span>\n";
424+
echo "<p>$description</p>\n";
425+
}
426+
}
427+
428+
// Checks to see if all of the feed url in $check_urls are cached.
429+
// If $check_urls is empty, look for the rss feed url found in the dashboard widget optios of $widget_id.
430+
// If cached, call $callback, a function that echoes out output for this widget.
431+
// If not cache, echo a "Loading..." stub which is later replaced by AJAX call (see top of /wp-admin/index.php)
432+
function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array() ) {
433+
$loading = '<p class="widget-loading">' . __( 'Loading&#8230;' ) . '</p>';
434+
435+
if ( empty($check_urls) ) {
436+
$widgets = get_option( 'dashboard_widget_options' );
437+
if ( empty($widgets[$widget_id]['url']) ) {
438+
echo $loading;
439+
return false;
440+
}
441+
$check_urls = array( $widgets[$widget_id]['url'] );
442+
}
443+
444+
445+
require_once( ABSPATH . WPINC . '/rss.php' );
446+
init(); // initialize rss constants
447+
448+
$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
449+
450+
foreach ( $check_urls as $check_url ) {
451+
$status = $cache->check_cache( $check_url );
452+
if ( 'HIT' !== $status ) {
453+
echo $loading;
454+
return false;
455+
}
456+
}
457+
458+
if ( $callback && is_callable( $callback ) ) {
459+
$args = array_slice( func_get_args(), 2 );
460+
array_unshift( $args, $widget_id );
461+
call_user_func_array( $callback, $args );
462+
}
463+
464+
return true;
465+
}
466+
467+
// Empty widget used for JS/AJAX created output.
468+
// Callback inserts content between before_widget and after_widget. Used when widget is in edit mode. Can also be used for custom widgets.
469+
function wp_dashboard_empty( $sidebar_args, $callback = false ) {
273470
extract( $sidebar_args, EXTR_SKIP );
274471

275472
echo $before_widget;
@@ -278,8 +475,12 @@ function wp_dashboard_empty( $sidebar_args, $widget_control_id = false ) {
278475
echo $widget_name;
279476
echo $after_title;
280477

281-
if ( $widget_control_id ) // If in edit mode
282-
wp_dashbaord_trigger_widget_control( $widget_control_id );
478+
// When in edit mode, the callback passed to this function is the widget_control callback
479+
if ( $callback && is_callable( $callback ) ) {
480+
$args = array_slice( func_get_args(), 2 );
481+
array_unshift( $args, $widget_id );
482+
call_user_func_array( $callback, $args );
483+
}
283484

284485
echo $after_widget;
285486
}

0 commit comments

Comments
 (0)