<!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>[2102] 2013/ahoereth/trunk/inc/editors.php: Notify user when file changed since last editor visit.</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://gsoc.trac.wordpress.org/changeset/2102">2102</a></dd>
<dt>Author</dt> <dd>a.hoereth</dd>
<dt>Date</dt> <dd>2013-07-10 14:00:33 +0000 (Wed, 10 Jul 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Notify user when file changed since last editor visit. See <a href="http://gsoc.trac.wordpress.org/ticket/303">#303</a>
This uses md5 checksums instead of file last modified timestamps because we can generate the md5 before the changes are actually written to the file. This is important because atm no core changes are applied.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#2013ahoerethtrunkinceditorsphp">2013/ahoereth/trunk/inc/editors.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="2013ahoerethtrunkinceditorsphp"></a>
<div class="modfile"><h4>Modified: 2013/ahoereth/trunk/inc/editors.php (2101 => 2102)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/ahoereth/trunk/inc/editors.php      2013-07-10 13:45:38 UTC (rev 2101)
+++ 2013/ahoereth/trunk/inc/editors.php 2013-07-10 14:00:33 UTC (rev 2102)
</span><span class="lines">@@ -64,6 +64,8 @@
</span><span class="cx">          // file update process
</span><span class="cx">          if ( isset( $_POST['action'] ) && 'update' == $_POST['action'] )
</span><span class="cx">                  $this->handle_file_update();
</span><ins>+               else
+                       $this->handle_direct_changes();
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="lines">@@ -146,24 +148,22 @@
</span><span class="cx">          // save meta array
</span><span class="cx">          $this->meta['package'] = $package;
</span><span class="cx">          $this->meta['file'   ] = $file;
</span><ins>+               $this->meta['checksum'] = md5_file( $this->get_abs() );
</ins><span class="cx"> 
</span><span class="cx">          // build post title
</span><span class="cx">          $this->title = "$p: $f";
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><del>-        * Gets the current file contents.
</del><ins>+         * Gets the file's absolute path.
</ins><span class="cx">    *
</span><del>-        * @since 0.1
</del><ins>+         * @since 0.4
</ins><span class="cx">    *
</span><del>-        * @return string/boolean string with file contents of success : false on failure
</del><ins>+         * @return string absolute file path
</ins><span class="cx">    */
</span><del>-       private function get_old_file() {
</del><ins>+        private function get_abs() {
</ins><span class="cx">           extract( $this->meta );
</span><del>-               if ( 'theme' == $type )
-                       return @file_get_contents( get_theme_root() . "/$package/$file" );
-               else
-                       return @file_get_contents( WP_PLUGIN_DIR    . "/$file"          );
</del><ins>+                return 'theme' == $type ? get_theme_root() . "/$package/$file" : WP_PLUGIN_DIR . "/$file";
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="lines">@@ -175,7 +175,7 @@
</span><span class="cx">   * @return boolean success/failure
</span><span class="cx">   */
</span><span class="cx">  private function handle_file_update() {
</span><del>-               $old = $this->get_old_file();
</del><ins>+                $old = @file_get_contents( $this->get_abs() );
</ins><span class="cx"> 
</span><span class="cx">          // Did we successfully get the file?
</span><span class="cx">          if ( false === $old )
</span><span class="lines">@@ -188,6 +188,9 @@
</span><span class="cx">          if ( $old === $new )
</span><span class="cx">                  return false;
</span><span class="cx"> 
</span><ins>+               // update checksum
+               $this->meta['checksum'] = md5( $new );
+
</ins><span class="cx">           // file wasn't saved to the database yet: save with old content
</span><span class="cx">          if ( ! $this->id && ! ( $this->id = $this->insert( $old ) ) )
</span><span class="cx">                          return false;
</span><span class="lines">@@ -197,6 +200,38 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * Checks if the file changed since last visit.
+        *
+        * @since  0.4
+        */
+       private function handle_direct_changes() {
+               $meta = get_post_meta( $this->id, $this->metakey, true );
+
+               if ( empty( $meta['checksum'] ) || $meta['checksum'] == $this->meta['checksum'] )
+                       return;
+
+               // add admin notice
+               add_action( 'admin_notices', array( &$this, 'notice' ) );
+
+               // update checksum in database
+               update_post_meta( $this->id, $this->metakey, $this->meta );
+       }
+
+       /**
+        * Used for displaying an admin notice above the editor which notifies the user
+        * that the file changed since his last visit.
+        */
+       public function notice() { ?>
+    <div class="updated">
+        <p>
+               <?php _e('This file has changed since the last time you viewed it in the editor.','code-revisions'); ?>
+               <a href="#"><?php _e('View changes','code-revisions'); ?></a>
+        </p>
+    </div>
+<?php
+       }
+
+       /**
</ins><span class="cx">    * Save file contents into new post. Used on the first edit of a file.
</span><span class="cx">   *
</span><span class="cx">   * @since 0.1
</span><span class="lines">@@ -240,7 +275,12 @@
</span><span class="cx">                  'post_content' => $content,
</span><span class="cx">                  'post_status'  => 'private',
</span><span class="cx">          );
</span><del>-               return wp_update_post( $args );
</del><ins>+                wp_update_post( $args );
+
+               if ( ! update_post_meta( $this->id, $this->metakey, $this->meta ) )
+                       return 0;
+
+               return $this->id;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="lines">@@ -278,6 +318,9 @@
</span><span class="cx">   * @return string     Revisions list ready for printing.
</span><span class="cx">   */
</span><span class="cx">  function get_revisions_list( $id ) {
</span><ins>+               if ( ! $id )
+                       return;
+
</ins><span class="cx">           ob_start();
</span><span class="cx"> 
</span><span class="cx">          // requires $id to be set
</span></span></pre>
</div>
</div>

</body>
</html>