<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[27966] trunk/src/wp-includes: WP_Widget: Introduce `is_preview()` method.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/27966">27966</a></dd>
<dt>Author</dt> <dd>ocean90</dd>
<dt>Date</dt> <dd>2014-04-06 18:47:46 +0000 (Sun, 06 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>WP_Widget: Introduce `is_preview()` method.

With the Widget Customizer it's possible that previewed widgets can leak data outside of Customizer, when the widget uses the cache API.
The Customizer calls the regular update callback which should already refresh the cache. Since cache additions aren't blocked yet the cache can be filled with preview data.
To prevent this issue `WP_Widget::is_preview()` will return true, when `$wp_customize->is_preview()` returns true. If `is_preview()` is true, cache additions are suspended via `wp_suspend_cache_addition()`. Make sure your object cache drop-in has implemented `wp_suspend_cache_addition()`.

`is_preview()` can/should also be used inside `WP_Widget::widget()`, see WP_Widget_Recent_Posts or WP_Widget_Recent_Comments for examples.

For more info see IRC logs: http://irclogs.wordpress.org/chanlog.php?channel=wordpress-dev&day=2014-04-02&sort=asc#m824279

props westonruter.
fixes <a href="http://core.trac.wordpress.org/ticket/27538">#27538</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesdefaultwidgetsphp">trunk/src/wp-includes/default-widgets.php</a></li>
<li><a href="#trunksrcwpincludeswidgetsphp">trunk/src/wp-includes/widgets.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesdefaultwidgetsphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/default-widgets.php (27965 => 27966)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/default-widgets.php        2014-04-05 22:29:41 UTC (rev 27965)
+++ trunk/src/wp-includes/default-widgets.php   2014-04-06 18:47:46 UTC (rev 27966)
</span><span class="lines">@@ -660,13 +660,18 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  function widget($args, $instance) {
</span><del>-               $cache = wp_cache_get('widget_recent_posts', 'widget');
</del><ins>+                $cache = array();
+               if ( ! $this->is_preview() ) {
+                       $cache = wp_cache_get( 'widget_recent_posts', 'widget' );
+               }
</ins><span class="cx"> 
</span><del>-               if ( !is_array($cache) )
</del><ins>+                if ( ! is_array( $cache ) ) {
</ins><span class="cx">                   $cache = array();
</span><ins>+               }
</ins><span class="cx"> 
</span><del>-               if ( ! isset( $args['widget_id'] ) )
</del><ins>+                if ( ! isset( $args['widget_id'] ) ) {
</ins><span class="cx">                   $args['widget_id'] = $this->id;
</span><ins>+               }
</ins><span class="cx"> 
</span><span class="cx">          if ( isset( $cache[ $args['widget_id'] ] ) ) {
</span><span class="cx">                  echo $cache[ $args['widget_id'] ];
</span><span class="lines">@@ -723,8 +728,12 @@
</span><span class="cx"> 
</span><span class="cx">          endif;
</span><span class="cx"> 
</span><del>-               $cache[$args['widget_id']] = ob_get_flush();
-               wp_cache_set('widget_recent_posts', $cache, 'widget');
</del><ins>+                if ( ! $this->is_preview() ) {
+                       $cache[ $args['widget_id'] ] = ob_get_flush();
+                       wp_cache_set( 'widget_recent_posts', $cache, 'widget' );
+               } else {
+                       ob_flush();
+               }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  function update( $new_instance, $old_instance ) {
</span><span class="lines">@@ -807,10 +816,13 @@
</span><span class="cx">  function widget( $args, $instance ) {
</span><span class="cx">          global $comments, $comment;
</span><span class="cx"> 
</span><del>-               $cache = wp_cache_get('widget_recent_comments', 'widget');
-
-               if ( ! is_array( $cache ) )
</del><ins>+                $cache = array();
+               if ( ! $this->is_preview() ) {
+                       $cache = wp_cache_get('widget_recent_comments', 'widget');
+               }
+               if ( ! is_array( $cache ) ) {
</ins><span class="cx">                   $cache = array();
</span><ins>+               }
</ins><span class="cx"> 
</span><span class="cx">          if ( ! isset( $args['widget_id'] ) )
</span><span class="cx">                  $args['widget_id'] = $this->id;
</span><span class="lines">@@ -865,8 +877,11 @@
</span><span class="cx">          $output .= $after_widget;
</span><span class="cx"> 
</span><span class="cx">          echo $output;
</span><del>-               $cache[$args['widget_id']] = $output;
-               wp_cache_set('widget_recent_comments', $cache, 'widget');
</del><ins>+
+               if ( ! $this->is_preview() ) {
+                       $cache[ $args['widget_id'] ] = $output;
+                       wp_cache_set( 'widget_recent_comments', $cache, 'widget' );
+               }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  function update( $new_instance, $old_instance ) {
</span></span></pre></div>
<a id="trunksrcwpincludeswidgetsphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/widgets.php (27965 => 27966)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/widgets.php        2014-04-05 22:29:41 UTC (rev 27965)
+++ trunk/src/wp-includes/widgets.php   2014-04-06 18:47:46 UTC (rev 27966)
</span><span class="lines">@@ -163,6 +163,21 @@
</span><span class="cx">          return array($this, 'form_callback');
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+       /**
+        * Determine if we're in the Customizer; if true, then the object cache gets
+        * suspended and widgets should check this to decide whether they should
+        * store anything persistently to the object cache, to transients, or
+        * anywhere else.
+        *
+        * @since 3.9.0
+        *
+        * @return bool True if Customizer is on, false if not.
+        */
+       function is_preview() {
+               global $wp_customize;
+               return ( isset( $wp_customize ) && $wp_customize->is_preview() ) ;
+       }
+
</ins><span class="cx">   /** Generate the actual widget content.
</span><span class="cx">   *      Just finds the instance and calls widget().
</span><span class="cx">   *      Do NOT over-ride this function. */
</span><span class="lines">@@ -189,8 +204,21 @@
</span><span class="cx">                   * @param array     $args     An array of default widget arguments.
</span><span class="cx">                   */
</span><span class="cx">                  $instance = apply_filters( 'widget_display_callback', $instance, $this, $args );
</span><del>-                       if ( false !== $instance )
-                               $this->widget($args, $instance);
</del><ins>+
+                       if ( false === $instance ) {
+                               return;
+                       }
+
+                       $was_cache_addition_suspended = wp_suspend_cache_addition();
+                       if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
+                               wp_suspend_cache_addition( true );
+                       }
+
+                       $this->widget( $args, $instance );
+
+                       if ( $this->is_preview() ) {
+                               wp_suspend_cache_addition( $was_cache_addition_suspended );
+                       }
</ins><span class="cx">           }
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="lines">@@ -241,8 +269,17 @@
</span><span class="cx"> 
</span><span class="cx">                          $old_instance = isset($all_instances[$number]) ? $all_instances[$number] : array();
</span><span class="cx"> 
</span><del>-                               $instance = $this->update($new_instance, $old_instance);
</del><ins>+                                $was_cache_addition_suspended = wp_suspend_cache_addition();
+                               if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
+                                       wp_suspend_cache_addition( true );
+                               }
</ins><span class="cx"> 
</span><ins>+                               $instance = $this->update( $new_instance, $old_instance );
+
+                               if ( $this->is_preview() ) {
+                                       wp_suspend_cache_addition( $was_cache_addition_suspended );
+                               }
+
</ins><span class="cx">                           /**
</span><span class="cx">                           * Filter a widget's settings before saving.
</span><span class="cx">                           *
</span><span class="lines">@@ -257,8 +294,9 @@
</span><span class="cx">                           * @param WP_Widget $this         The current widget instance.
</span><span class="cx">                           */
</span><span class="cx">                          $instance = apply_filters( 'widget_update_callback', $instance, $new_instance, $old_instance, $this );
</span><del>-                               if ( false !== $instance )
</del><ins>+                                if ( false !== $instance ) {
</ins><span class="cx">                                   $all_instances[$number] = $instance;
</span><ins>+                               }
</ins><span class="cx"> 
</span><span class="cx">                          break; // run only once
</span><span class="cx">                  }
</span></span></pre>
</div>
</div>

</body>
</html>