Ticket #6775: 6775.diff

File 6775.diff, 91.2 KB (added by mdawaffe, 4 years ago)
  • wp-includes/default-filters.php

     
    178178add_action('init', 'smilies_init', 5); 
    179179add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 ); 
    180180add_action( 'shutdown', 'wp_ob_end_flush_all', 1); 
     181add_action( 'pre_post_update', 'wp_save_revision' ); 
    181182add_action('publish_post', '_publish_post_hook', 5, 1); 
    182183add_action('future_post', '_future_post_hook', 5, 2); 
    183184add_action('future_page', '_future_post_hook', 5, 2); 
  • wp-includes/post-template.php

     
    564564        return false; 
    565565} 
    566566 
    567 ?> 
     567/** 
     568 * wp_post_revision_time() - returns formatted datetimestamp of a revision 
     569 * 
     570 * @package WordPress 
     571 * @subpackage Post Revisions 
     572 * @since 2.6 
     573 * 
     574 * @uses wp_get_revision() 
     575 * @uses date_i18n() 
     576 * 
     577 * @param int|object $revision revision ID or revision object 
     578 * @return string i18n formatted datetimestamp or localized 'Corrent Revision' 
     579 */ 
     580function wp_post_revision_time( $revision ) { 
     581        if ( !$revision = wp_get_revision( $revision ) ) { 
     582                if ( $revision = get_post( $revision ) ) 
     583                        return __( 'Current Revision' ); 
     584                return $revision; 
     585        } 
     586 
     587        $datef  = _c( 'j F, Y @ G:i|revision date format'); 
     588        return date_i18n( $datef, strtotime( $revision->post_date_gmt . ' +0000' ) ); 
     589} 
     590 
     591/** 
     592 * wp_list_post_revisions() - echoes list of a post's revisions 
     593 * 
     594 * Can output either a UL with edit links or a TABLE with diff interface, and restore action links 
     595 * 
     596 * Second argument controls parameters: 
     597 *   (bool)   parent : include the parent (the "Current Revision") in the list 
     598 *   (string) format : 'list' or 'form-table'.  'list' outputs UL, 'form-table' outputs TABLE with UI 
     599 *   (int)    right  : what revision is currently being viewed - used in form-table format 
     600 *   (int)    left   : what revision is currently being diffed against right - used in form-table format 
     601 * 
     602 * @package WordPress 
     603 * @subpackage Post Revisions 
     604 * @since 2.6 
     605 * 
     606 * @uses wp_get_post_revisions() 
     607 * @uses wp_post_revision_time() 
     608 * @uses get_edit_post_link() 
     609 * @uses get_author_name() 
     610 * 
     611 * @param int|object $post_id post ID or post object 
     612 * @param string|array $args see description @see wp_parse_args() 
     613 */ 
     614function wp_list_post_revisions( $post_id = 0, $args = null ) { // TODO? split into two functions (list, form-table) ? 
     615        if ( !$post = get_post( $post_id ) ) 
     616                return; 
     617 
     618        if ( !$revisions = wp_get_post_revisions( $post->ID ) ) 
     619                return; 
     620 
     621        $defaults = array( 'parent' => false, 'right' => false, 'left' => false, 'format' => 'list' ); 
     622        extract( wp_parse_args( $args, $defaults ), EXTR_SKIP ); 
     623 
     624        $titlef = _c( '%1$s by %2$s|post revision 1:datetime, 2:name' ); 
     625 
     626        if ( $parent ) 
     627                array_unshift( $revisions, $post ); 
     628 
     629        $rows = ''; 
     630        $class = false; 
     631        foreach ( $revisions as $revision ) { 
     632                $date = wp_post_revision_time( $revision ); 
     633                if ( $link = get_edit_post_link( $revision->ID ) ) 
     634                        $date = "<a href='$link'>$date</a>"; 
     635                $name = get_author_name( $revision->post_author ); 
     636 
     637                if ( 'form-table' == $format ) { 
     638                        if ( $left ) 
     639                                $old_checked = $left == $revision->ID ? ' checked="checked"' : ''; 
     640                        else 
     641                                $old_checked = $new_checked ? ' checked="checked"' : ''; 
     642                        $new_checked = $right == $revision->ID ? ' checked="checked"' : ''; 
     643 
     644                        $class = $class ? '' : " class='alternate'"; 
     645 
     646                        if ( $post->ID != $revision->ID && current_user_can( 'edit_post', $post->ID ) ) 
     647                                $actions = '<a href="' . wp_nonce_url( add_query_arg( array( 'revision' => $revision->ID, 'diff' => false, 'restore' => 'restore' ) ), "restore-post_$post->ID|$revision->ID" ) . '">' . __( 'Restore' ) . '</a>'; 
     648                        else 
     649                                $actions = ''; 
     650 
     651                        $rows .= "<tr$class>\n"; 
     652                        $rows .= "\t<th style='white-space: nowrap' scope='row'><input type='radio' name='diff' value='$revision->ID'$old_checked /><input type='radio' name='revision' value='$revision->ID'$new_checked />\n"; 
     653                        $rows .= "\t<td>$date</td>\n"; 
     654                        $rows .= "\t<td>$name</td>\n"; 
     655                        $rows .= "\t<td class='action-links'>$actions</td>\n"; 
     656                        $rows .= "</tr>\n"; 
     657                } else { 
     658                        $rows .= "\t<li>" . sprintf( $titlef, $date, $name ). "</li>\n"; 
     659                } 
     660        } 
     661 
     662        if ( 'form-table' == $format ) : ?> 
     663 
     664<form action="revision.php" method="get"> 
     665 
     666<div class="tablenav"> 
     667        <div class="alignleft"> 
     668                <input type="submit" class="button-secondary" value="<?php _e( 'Compare Revisions' ); ?>" /> 
     669        </div> 
     670</div> 
     671 
     672<br class="clear" /> 
     673 
     674<table class="widefat post-revisions"> 
     675        <col /> 
     676        <col style="width: 33%" /> 
     677        <col style="width: 33%" /> 
     678        <col style="width: 33%" /> 
     679<thead> 
     680        <th scope="col"></th> 
     681        <th scope="col"><?php _e( 'Date Created' ); ?></th> 
     682        <th scope="col"><?php _e( 'Author' ); ?></th> 
     683        <th scope="col" class="action-links"><?php _e( 'Actions' ); ?></th> 
     684</thead> 
     685<tbody> 
     686 
     687<?php echo $rows; ?> 
     688 
     689</tbody> 
     690</table> 
     691 
     692<?php 
     693        else : 
     694                echo "<ul class='post-revisions'>\n"; 
     695                echo $rows; 
     696                echo "</ul>"; 
     697        endif; 
     698 
     699} 
  • wp-includes/post.php

     
    29902990        } 
    29912991} 
    29922992 
     2993/* Post Revisions */ 
     2994 
     2995/** 
     2996 * _wp_revision_fields() - determines which fields of posts are to be saved in revisions 
     2997 * 
     2998 * Does two things. If passed a postn *array*, it will return a post array ready to be 
     2999 * insterted into the posts table as a post revision. 
     3000 * Otherwise, returns an array whose keys are the post fields to be saved post revisions. 
     3001 * 
     3002 * @package WordPress 
     3003 * @subpackage Post Revisions 
     3004 * @since 2.6 
     3005 * 
     3006 * @param array $post optional a post array to be processed for insertion as a post revision 
     3007 * @return array post array ready to be inserted as a post revision or array of fields that can be versioned 
     3008 */ 
     3009function _wp_revision_fields( $post = null ) { 
     3010        static $fields = false; 
     3011 
     3012        if ( !$fields ) { 
     3013                // Allow these to be versioned 
     3014                $fields = array( 
     3015                        'post_title' => __( 'Title' ), 
     3016                        'post_author' => __( 'Author' ), 
     3017                        'post_content' => __( 'Content' ), 
     3018                        'post_excerpt' => __( 'Excerpt' ), 
     3019                ); 
     3020 
     3021                // WP uses these internally either in versioning or elsewhere - they cannot be versioned 
     3022                foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count' ) as $protect ) 
     3023                        unset( $fields[$protect] ); 
     3024        } 
     3025 
     3026        if ( !is_array($post) ) 
     3027                return $fields; 
     3028 
     3029        $return = array(); 
     3030        foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field ) 
     3031                $return[$field] = $post[$field]; 
     3032 
     3033        $return['post_parent']   = $post['ID']; 
     3034        $return['post_status']   = 'inherit'; 
     3035        $return['post_type']     = 'revision'; 
     3036        $return['post_name']     = "$post[ID]-revision"; 
     3037        $return['post_date']     = $post['post_modified']; 
     3038        $return['post_date_gmt'] = $post['post_modified_gmt']; 
     3039 
     3040        return $return; 
     3041} 
     3042 
     3043/** 
     3044 * wp_save_revision() - Saves an already existing post as a post revision.  Typically used immediately prior to post updates. 
     3045 * 
     3046 * @package WordPress 
     3047 * @subpackage Post Revisions 
     3048 * @since 2.6 
     3049 * 
     3050 * @uses _wp_put_revision() 
     3051 * 
     3052 * @param int $post_id The ID of the post to save as a revision 
     3053 * @return mixed null or 0 if error, new revision ID if success 
     3054 */ 
     3055function wp_save_revision( $post_id ) { 
     3056        // TODO: rework autosave to use special type of post revision 
     3057        if ( @constant( 'DOING_AUTOSAVE' ) ) 
     3058                return; 
     3059 
     3060        if ( !$post = get_post( $post_id, ARRAY_A ) ) 
     3061                return; 
     3062 
     3063        // TODO: open this up for pages also 
     3064        if ( 'post' != $post->post_type ) 
     3065                retun; 
     3066 
     3067        return _wp_put_revision( $post ); 
     3068} 
     3069 
     3070/** 
     3071 * _wp_put_revision() - Inserts post data into the posts table as a post revision 
     3072 * 
     3073 * @package WordPress 
     3074 * @subpackage Post Revisions 
     3075 * @since 2.6 
     3076 * 
     3077 * @uses wp_insert_post() 
     3078 * 
     3079 * @param int|object|array $post post ID, post object OR post array 
     3080 * @return mixed null or 0 if error, new revision ID if success 
     3081 */ 
     3082function _wp_put_revision( $post = null ) { 
     3083        if ( is_object($post) ) 
     3084                $post = get_object_vars( $post ); 
     3085        elseif ( !is_array($post) ) 
     3086                $post = get_post($post, ARRAY_A); 
     3087 
     3088        if ( !$post || empty($post['ID']) ) 
     3089                return; 
     3090 
     3091        if ( isset($post['post_type']) && 'revision' == $post_post['type'] ) 
     3092                return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) ); 
     3093 
     3094        $post = _wp_revision_fields( $post ); 
     3095 
     3096        if ( $revision_id = wp_insert_post( $post ) ) 
     3097                do_action( '_wp_put_revision', $revision_id ); 
     3098 
     3099        return $revision_id; 
     3100} 
     3101 
     3102/** 
     3103 * wp_get_revision() - Gets a post revision 
     3104 * 
     3105 * @package WordPress 
     3106 * @subpackage Post Revisions 
     3107 * @since 2.6 
     3108 * 
     3109 * @uses get_post() 
     3110 * 
     3111 * @param int|object $post post ID or post object 
     3112 * @param $output optional OBJECT, ARRAY_A, or ARRAY_N 
     3113 * @param string $filter optional sanitation filter.  @see sanitize_post() 
     3114 * @return mixed null if error or post object if success 
     3115 */ 
     3116function &wp_get_revision(&$post, $output = OBJECT, $filter = 'raw') { 
     3117        $null = null; 
     3118        if ( !$revision = get_post( $post, OBJECT, $filter ) ) 
     3119                return $revision; 
     3120        if ( 'revision' !== $revision->post_type ) 
     3121                return $null; 
     3122 
     3123        if ( $output == OBJECT ) { 
     3124                return $revision; 
     3125        } elseif ( $output == ARRAY_A ) { 
     3126                $_revision = get_object_vars($revision); 
     3127                return $_revision; 
     3128        } elseif ( $output == ARRAY_N ) { 
     3129                $_revision = array_values(get_object_vars($revision)); 
     3130                return $_revision; 
     3131        } 
     3132 
     3133        return $revision; 
     3134} 
     3135 
     3136/** 
     3137 * wp_restore_revision() - Restores a post to the specified revision 
     3138 * 
     3139 * Can restore a past using all fields of the post revision, or only selected fields. 
     3140 * 
     3141 * @package WordPress 
     3142 * @subpackage Post Revisions 
     3143 * @since 2.6 
     3144 * 
     3145 * @uses wp_get_revision() 
     3146 * @uses wp_update_post() 
     3147 * 
     3148 * @param int|object $revision_id revision ID or revision object 
     3149 * @param array $fields optional What fields to restore from.  Defaults to all. 
     3150 * @return mixed null if error, false if no fields to restore, (int) post ID if success 
     3151 */ 
     3152function wp_restore_revision( $revision_id, $fields = null ) { 
     3153        if ( !$revision = wp_get_revision( $revision_id, ARRAY_A ) ) 
     3154                return $revision; 
     3155 
     3156        if ( !is_array( $fields ) ) 
     3157                $fields = array_keys( _wp_revision_fields() ); 
     3158 
     3159        $update = array(); 
     3160        foreach( array_intersect( array_keys( $revision ), $fields ) as $field ) 
     3161                $update[$field] = $revision[$field]; 
     3162 
     3163        if ( !$update ) 
     3164                return false; 
     3165 
     3166        $update['ID'] = $revision['post_parent']; 
     3167 
     3168        if ( $post_id = wp_update_post( $update ) ) 
     3169                do_action( 'wp_restore_revision', $post_id, $revision['ID'] ); 
     3170 
     3171        return $post_id; 
     3172} 
     3173 
     3174/** 
     3175 * wp_delete_revision() - Deletes a revision. 
     3176 * 
     3177 * Deletes the row from the posts table corresponding to the specified revision 
     3178 * 
     3179 * @package WordPress 
     3180 * @subpackage Post Revisions 
     3181 * @since 2.6 
     3182 * 
     3183 * @uses wp_get_revision() 
     3184 * @uses wp_delete_post() 
     3185 * 
     3186 * @param int|object $revision_id revision ID or revision object 
     3187 * @param array $fields optional What fields to restore from.  Defaults to all. 
     3188 * @return mixed null if error, false if no fields to restore, (int) post ID if success 
     3189 */ 
     3190function wp_delete_revision( $revision_id ) { 
     3191        if ( !$revision = wp_get_revision( $revision_id ) ) 
     3192                return $revision; 
     3193 
     3194        if ( $delete = wp_delete_post( $revision->ID ) ) 
     3195                do_action( 'wp_delete_revision', $revision->ID, $revision ); 
     3196 
     3197        return $delete; 
     3198} 
     3199 
     3200/** 
     3201 * wp_get_post_revisions() - Returns all revisions of specified post 
     3202 * 
     3203 * @package WordPress 
     3204 * @subpackage Post Revisions 
     3205 * @since 2.6 
     3206 * 
     3207 * @uses get_children() 
     3208 * 
     3209 * @param int|object $post_id post ID or post object 
     3210 * @return array empty if no revisions 
     3211 */ 
     3212function wp_get_post_revisions( $post_id = 0 ) { 
     3213        if ( ( !$post = get_post( $post_id ) ) || empty( $post->ID ) ) 
     3214                return array(); 
     3215 
     3216        if ( !$revisions = get_children( array( 'post_parent' => $post->ID, 'post_type' => 'revision' ) ) ) 
     3217                return array(); 
     3218        return $revisions; 
     3219} 
     3220 
    29933221?> 
  • wp-includes/wp-diff.php

     
     1<?php 
     2 
     3if ( !class_exists( 'Text_Diff' ) ) { 
     4        require( 'Text/Diff.php' ); 
     5        require( 'Text/Diff/Renderer.php' ); 
     6        require( 'Text/Diff/Renderer/inline.php' ); 
     7} 
     8 
     9 
     10/* Descendent of a bastard child of piece of an old MediaWiki Diff Formatter 
     11 * 
     12 * Basically all that remains is the table structure and some method names. 
     13 */ 
     14 
     15class WP_Text_Diff_Renderer_Table extends Text_Diff_Renderer { 
     16        var $_leading_context_lines  = 10000; 
     17        var $_trailing_context_lines = 10000; 
     18        var $_diff_threshold = 0.6; 
     19 
     20        var $inline_diff_renderer = 'WP_Text_Diff_Renderer_inline'; 
     21 
     22        function Text_Diff_Renderer_Table( $params = array() ) { 
     23                $parent = get_parent_class($this); 
     24                $this->$parent( $params ); 
     25        } 
     26 
     27        function _startBlock( $header ) { 
     28                return ''; 
     29        } 
     30 
     31        function _lines( $lines, $prefix=' ' ) { 
     32        } 
     33 
     34        // HTML-escape parameter before calling this 
     35        function addedLine( $line ) { 
     36                return "<td>+</td><td class='diff-addedline'>{$line}</td>"; 
     37        } 
     38 
     39        // HTML-escape parameter before calling this 
     40        function deletedLine( $line ) { 
     41                return "<td>-</td><td class='diff-deletedline'>{$line}</td>"; 
     42        } 
     43 
     44        // HTML-escape parameter before calling this 
     45        function contextLine( $line ) { 
     46                return "<td> </td><td class='diff-context'>{$line}</td>"; 
     47        } 
     48 
     49        function emptyLine() { 
     50                return '<td colspan="2">&nbsp;</td>'; 
     51        } 
     52 
     53        function _added( $lines, $encode = true ) { 
     54                $r = ''; 
     55                foreach ($lines as $line) { 
     56                        if ( $encode ) 
     57                                $line = htmlspecialchars( $line ); 
     58                        $r .= '<tr>' . $this->emptyLine() . $this->addedLine( $line ) . "</tr>\n"; 
     59                } 
     60                return $r; 
     61        } 
     62 
     63        function _deleted( $lines, $encode = true ) { 
     64                $r = ''; 
     65                foreach ($lines as $line) { 
     66                        if ( $encode ) 
     67                                $line = htmlspecialchars( $line ); 
     68                        $r .= '<tr>' . $this->deletedLine( $line ) . $this->emptyLine() . "</tr>\n"; 
     69                } 
     70                return $r; 
     71        } 
     72 
     73        function _context( $lines, $encode = true ) { 
     74                $r = ''; 
     75                foreach ($lines as $line) { 
     76                        if ( $encode ) 
     77                                $line = htmlspecialchars( $line ); 
     78                        $r .= '<tr>' . 
     79                                $this->contextLine( $line ) . $this->contextLine( $line ) . "</tr>\n"; 
     80                } 
     81                return $r; 
     82        } 
     83 
     84        // Process changed lines to do word-by-word diffs for extra highlighting (TRAC style) 
     85        // sometimes these lines can actually be deleted or added rows - we do additional processing 
     86        // to figure that out 
     87        function _changed( $orig, $final ) { 
     88                $r = ''; 
     89 
     90                // Does the aforementioned additional processing 
     91                // *_matches tell what rows are "the same" in orig and final.  Those pairs will be diffed to get word changes 
     92                //      match is numeric: an index in other column 
     93                //      match is 'X': no match.  It is a new row 
     94                // *_rows are column vectors for the orig column and the final column. 
     95                //      row >= 0: an indix of the $orig or $final array 
     96                //      row  < 0: a blank row for that column 
     97                list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final ); 
     98 
     99 
     100                // These will hold the word changes as determined by an inline diff 
     101                $orig_diffs  = array(); 
     102                $final_diffs = array(); 
     103 
     104                // Compute word diffs for each matched pair using the inline diff 
     105                foreach ( $orig_matches as $o => $f ) { 
     106                        if ( is_numeric($o) && is_numeric($f) ) { 
     107                                $text_diff = new Text_Diff( 'auto', array( array($orig[$o]), array($final[$f]) ) ); 
     108                                $renderer = new $this->inline_diff_renderer; 
     109                                $diff = $renderer->render( $text_diff ); 
     110 
     111                                // If they're too different, don't include any <ins> or <dels> 
     112                                if ( $diff_count = preg_match_all( '!(<ins>.*?</ins>|<del>.*?</del>)!', $diff, $diff_matches ) ) { 
     113                                        // length of all text between <ins> or <del> 
     114                                        $stripped_matches = strlen(strip_tags( join(' ', $diff_matches[0]) )); 
     115                                        // since we count lengith of text between <ins> or <del> (instead of picking just one), 
     116                                        //      we double the length of chars not in those tags. 
     117                                        $stripped_diff = strlen(strip_tags( $diff )) * 2 - $stripped_matches; 
     118                                        $diff_ratio = $stripped_matches / $stripped_diff; 
     119                                        if ( $diff_ratio > $this->_diff_threshold ) 
     120                                                continue; // Too different.  Don't save diffs. 
     121                                } 
     122 
     123                                // Un-inline the diffs by removing del or ins 
     124                                $orig_diffs[$o]  = preg_replace( '|<ins>.*?</ins>|', '', $diff ); 
     125                                $final_diffs[$f] = preg_replace( '|<del>.*?</del>|', '', $diff ); 
     126                        } 
     127                } 
     128 
     129                foreach ( array_keys($orig_rows) as $row ) { 
     130                        // Both columns have blanks.  Ignore them. 
     131                        if ( $orig_rows[$row] < 0 && $final_rows[$row] < 0 ) 
     132                                continue; 
     133 
     134                        // If we have a word based diff, use it.  Otherwise, use the normal line. 
     135                        $orig_line  = isset($orig_diffs[$orig_rows[$row]]) 
     136                                ? $orig_diffs[$orig_rows[$row]] 
     137                                : htmlspecialchars($orig[$orig_rows[$row]]); 
     138                        $final_line = isset($final_diffs[$final_rows[$row]]) 
     139                                ? $final_diffs[$final_rows[$row]] 
     140                                : htmlspecialchars($final[$final_rows[$row]]); 
     141 
     142                        if ( $orig_rows[$row] < 0 ) { // Orig is blank.  This is really an added row. 
     143                                $r .= $this->_added( array($final_line), false ); 
     144                        } elseif ( $final_rows[$row] < 0 ) { // Final is blank.  This is really a deleted row. 
     145                                $r .= $this->_deleted( array($orig_line), false ); 
     146                        } else { // A true changed row. 
     147                                $r .= '<tr>' . $this->deletedLine( $orig_line ) . $this->addedLine( $final_line ) . "</tr>\n"; 
     148                        } 
     149                } 
     150 
     151                return $r; 
     152        } 
     153 
     154        // Takes changed blocks and matches which rows in orig turned into which rows in final. 
     155        // Returns 
     156        //      *_matches ( which rows match with which ) 
     157        //      *_rows ( order of rows in each column interleaved with blank rows as necessary ) 
     158        function interleave_changed_lines( $orig, $final ) { 
     159 
     160                // Contains all pairwise string comparisons.  Keys are such that this need only be a one dimensional array. 
     161                $matches = array(); 
     162                foreach ( array_keys($orig) as $o ) { 
     163                        foreach ( array_keys($final) as $f ) { 
     164                                $matches["$o,$f"] = $this->compute_string_distance( $orig[$o], $final[$f] ); 
     165                        } 
     166                } 
     167                asort($matches); // Order by string distance. 
     168 
     169                $orig_matches  = array(); 
     170                $final_matches = array(); 
     171 
     172                foreach ( $matches as $keys => $difference ) { 
     173                        list($o, $f) = explode(',', $keys); 
     174                        $o = (int) $o; 
     175                        $f = (int) $f; 
     176 
     177                        // Already have better matches for these guys 
     178                        if ( isset($orig_matches[$o]) && isset($final_matches[$f]) ) 
     179                                continue; 
     180 
     181                        // First match for these guys.  Must be best match 
     182                        if ( !isset($orig_matches[$o]) && !isset($final_matches[$f]) ) { 
     183                                $orig_matches[$o] = $f; 
     184                                $final_matches[$f] = $o; 
     185                                continue; 
     186                        } 
     187 
     188                        // Best match of this final is already taken?  Must mean this final is a new row. 
     189                        if ( isset($orig_matches[$o]) ) 
     190                                $final_matches[$f] = 'x'; 
     191 
     192                        // Best match of this orig is already taken?  Must mean this orig is a deleted row. 
     193                        elseif ( isset($final_matches[$f]) ) 
     194                                $orig_matches[$o] = 'x'; 
     195                } 
     196 
     197                // We read the text in this order 
     198                ksort($orig_matches); 
     199                ksort($final_matches); 
     200 
     201 
     202                // Stores rows and blanks for each column. 
     203                $orig_rows = $orig_rows_copy = array_keys($orig_matches); 
     204                $final_rows = array_keys($final_matches); 
     205 
     206                // Interleaves rows with blanks to keep matches aligned. 
     207                // We may end up with some extraneous blank rows, but we'll just ignore them later. 
     208                foreach ( $orig_rows_copy as $orig_row ) { 
     209                        $final_pos = array_search($orig_matches[$orig_row], $final_rows, true); 
     210                        $orig_pos = (int) array_search($orig_row, $orig_rows, true); 
     211 
     212                        if ( false === $final_pos ) { // This orig is paired with a blank final. 
     213                                array_splice( $final_rows, $orig_pos, 0, -1 ); 
     214                        } elseif ( $final_pos < $orig_pos ) { // This orig's match is up a ways.  Pad final with blank rows. 
     215                                $diff_pos = $final_pos - $orig_pos; 
     216                                while ( $diff_pos < 0 ) 
     217                                        array_splice( $final_rows, $orig_pos, 0, $diff_pos++ ); 
     218                        } elseif ( $final_pos > $orig_pos ) { // This orig's match is down a ways.  Pad orig with blank rows. 
     219                                $diff_pos = $orig_pos - $final_pos; 
     220                                while ( $diff_pos < 0 ) 
     221                                        array_splice( $orig_rows, $orig_pos, 0, $diff_pos++ ); 
     222                        } 
     223                } 
     224 
     225 
     226                // Pad the ends with blank rows if the columns aren't the same length 
     227                $diff_count = count($orig_rows) - count($final_rows); 
     228                if ( $diff_count < 0 ) { 
     229                        while ( $diff_count < 0 ) 
     230                                array_push($orig_rows, $diff_count++); 
     231                } elseif ( $diff_count > 0 ) { 
     232                        $diff_count = -1 * $diff_count; 
     233                        while ( $diff_count < 0 ) 
     234                                array_push($final_rows, $diff_count++); 
     235                } 
     236 
     237                return array($orig_matches, $final_matches, $orig_rows, $final_rows); 
     238 
     239/* 
     240                // Debug 
     241                echo "\n\n\n\n\n"; 
     242 
     243                echo "-- DEBUG Matches: Orig -> Final --"; 
     244 
     245                foreach ( $orig_matches as $o => $f ) { 
     246                        echo "\n\n\n\n\n"; 
     247                        echo "ORIG: $o, FINAL: $f\n"; 
     248                        var_dump($orig[$o],$final[$f]); 
     249                } 
     250                echo "\n\n\n\n\n"; 
     251 
     252                echo "-- DEBUG Matches: Final -> Orig --"; 
     253 
     254                foreach ( $final_matches as $f => $o ) { 
     255                        echo "\n\n\n\n\n"; 
     256                        echo "FINAL: $f, ORIG: $o\n"; 
     257                        var_dump($final[$f],$orig[$o]); 
     258                } 
     259                echo "\n\n\n\n\n"; 
     260 
     261                echo "-- DEBUG Rows: Orig -- Final --"; 
     262 
     263                echo "\n\n\n\n\n"; 
     264                foreach ( $orig_rows as $row => $o ) { 
     265                        if ( $o < 0 ) 
     266                                $o = 'X'; 
     267                        $f = $final_rows[$row]; 
     268                        if ( $f < 0 ) 
     269                                $f = 'X'; 
     270                        echo "$o -- $f\n"; 
     271                } 
     272                echo "\n\n\n\n\n"; 
     273 
     274                echo "-- END DEBUG --"; 
     275 
     276                echo "\n\n\n\n\n"; 
     277 
     278                return array($orig_matches, $final_matches, $orig_rows, $final_rows); 
     279*/ 
     280        } 
     281 
     282 
     283        // Computes a number that is intended to reflect the "distance" between two strings. 
     284        function compute_string_distance( $string1, $string2 ) { 
     285                // Vectors containing character frequency for all chars in each string 
     286                $chars1 = count_chars($string1); 
     287                $chars2 = count_chars($string2); 
     288 
     289                // L1-norm of difference vector. 
     290                $difference = array_sum( array_map( array(&$this, 'difference'), $chars1, $chars2 ) ); 
     291 
     292                // $string1 has zero length? Odd.  Give huge penalty by not dividing. 
     293                if ( !$string1 ) 
     294                        return $difference; 
     295 
     296                // Return distance per charcter (of string1) 
     297                return $difference / strlen($string1); 
     298        } 
     299 
     300        function difference( $a, $b ) { 
     301                return abs( $a - $b ); 
     302        } 
     303 
     304} 
     305 
     306// Better word splitting than the PEAR package provides 
     307class WP_Text_Diff_Renderer_inline extends Text_Diff_Renderer_inline { 
     308 
     309        function _splitOnWords($string, $newlineEscape = "\n") { 
     310                $string = str_replace("\0", '', $string); 
     311                $words  = preg_split( '/([^\w])/', $string, -1, PREG_SPLIT_DELIM_CAPTURE ); 
     312                $words  = str_replace( "\n", $newlineEscape, $words ); 
     313                return $words; 
     314        } 
     315 
     316} 
     317 
     318?> 
  • wp-includes/link-template.php

     
    442442        return $link; 
    443443} 
    444444 
    445 function get_edit_post_link( $id = 0 ) { 
     445function get_edit_post_link( $id = 0, $context = 'display' ) { 
    446446        if ( !$post = &get_post( $id ) ) 
    447447                return; 
    448448 
     449        if ( 'display' == $context ) 
     450                $action = 'action=edit&amp;'; 
     451        else 
     452                $action = 'action=edit&'; 
     453 
    449454        switch ( $post->post_type ) : 
    450455        case 'page' : 
    451456                if ( !current_user_can( 'edit_page', $post->ID ) ) 
     
    459464                $file = 'media'; 
    460465                $var  = 'attachment_id'; 
    461466                break; 
     467        case 'revision' : 
     468                if ( !current_user_can( 'edit_post', $post->ID ) ) 
     469                        return; 
     470                $file = 'revision'; 
     471                $var  = 'revision'; 
     472                $action = ''; 
     473                break; 
    462474        default : 
    463475                if ( !current_user_can( 'edit_post', $post->ID ) ) 
    464476                        return; 
     
    467479                break; 
    468480        endswitch; 
    469481         
    470         return apply_filters( 'get_edit_post_link', get_bloginfo( 'wpurl' ) . "/wp-admin/$file.php?action=edit&amp;$var=$post->ID", $post->ID ); 
     482        return apply_filters( 'get_edit_post_link', get_bloginfo( 'wpurl' ) . "/wp-admin/$file.php?{$action}$var=$post->ID", $post->ID ); 
    471483} 
    472484 
    473485function edit_post_link( $link = 'Edit This', $before = '', $after = '' ) { 
  • wp-includes/Text/Diff/Engine/xdiff.php

     
     1<?php 
     2/** 
     3 * Class used internally by Diff to actually compute the diffs. 
     4 * 
     5 * This class uses the xdiff PECL package (http://pecl.php.net/package/xdiff) 
     6 * to compute the differences between the two input arrays. 
     7 * 
     8 * $Horde: framework/Text_Diff/Diff/Engine/xdiff.php,v 1.6 2008/01/04 10:07:50 jan Exp $ 
     9 * 
     10 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 
     11 * 
     12 * See the enclosed file COPYING for license information (LGPL). If you did 
     13 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     14 * 
     15 * @author  Jon Parise <jon@horde.org> 
     16 * @package Text_Diff 
     17 */ 
     18class Text_Diff_Engine_xdiff { 
     19 
     20    /** 
     21     */ 
     22    function diff($from_lines, $to_lines) 
     23    { 
     24        array_walk($from_lines, array('Text_Diff', 'trimNewlines')); 
     25        array_walk($to_lines, array('Text_Diff', 'trimNewlines')); 
     26 
     27        /* Convert the two input arrays into strings for xdiff processing. */ 
     28        $from_string = implode("\n", $from_lines); 
     29        $to_string = implode("\n", $to_lines); 
     30 
     31        /* Diff the two strings and convert the result to an array. */ 
     32        $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); 
     33        $diff = explode("\n", $diff); 
     34 
     35        /* Walk through the diff one line at a time.  We build the $edits 
     36         * array of diff operations by reading the first character of the 
     37         * xdiff output (which is in the "unified diff" format). 
     38         * 
     39         * Note that we don't have enough information to detect "changed" 
     40         * lines using this approach, so we can't add Text_Diff_Op_changed 
     41         * instances to the $edits array.  The result is still perfectly 
     42         * valid, albeit a little less descriptive and efficient. */ 
     43        $edits = array(); 
     44        foreach ($diff as $line) { 
     45            switch ($line[0]) { 
     46            case ' ': 
     47                $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1))); 
     48                break; 
     49 
     50            case '+': 
     51                $edits[] = &new Text_Diff_Op_add(array(substr($line, 1))); 
     52                break; 
     53 
     54            case '-': 
     55                $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1))); 
     56                break; 
     57            } 
     58        } 
     59 
     60        return $edits; 
     61    } 
     62 
     63} 
  • wp-includes/Text/Diff/Engine/native.php

     
     1<?php 
     2/** 
     3 * $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.10 2008/01/04 10:27:53 jan Exp $ 
     4 * 
     5 * Class used internally by Text_Diff to actually compute the diffs. This 
     6 * class is implemented using native PHP code. 
     7 * 
     8 * The algorithm used here is mostly lifted from the perl module 
     9 * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: 
     10 * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip 
     11 * 
     12 * More ideas are taken from: http://www.ics.uci.edu/~eppstein/161/960229.html 
     13 * 
     14 * Some ideas (and a bit of code) are taken from analyze.c, of GNU 
     15 * diffutils-2.7, which can be found at: 
     16 * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz 
     17 * 
     18 * Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from 
     19 * Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this 
     20 * code was written by him, and is used/adapted with his permission. 
     21 * 
     22 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 
     23 * 
     24 * See the enclosed file COPYING for license information (LGPL). If you did 
     25 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     26 * 
     27 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     28 * @package Text_Diff 
     29 */ 
     30class Text_Diff_Engine_native { 
     31 
     32    function diff($from_lines, $to_lines) 
     33    { 
     34        array_walk($from_lines, array('Text_Diff', 'trimNewlines')); 
     35        array_walk($to_lines, array('Text_Diff', 'trimNewlines')); 
     36 
     37        $n_from = count($from_lines); 
     38        $n_to = count($to_lines); 
     39 
     40        $this->xchanged = $this->ychanged = array(); 
     41        $this->xv = $this->yv = array(); 
     42        $this->xind = $this->yind = array(); 
     43        unset($this->seq); 
     44        unset($this->in_seq); 
     45        unset($this->lcs); 
     46 
     47        // Skip leading common lines. 
     48        for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { 
     49            if ($from_lines[$skip] !== $to_lines[$skip]) { 
     50                break; 
     51            } 
     52            $this->xchanged[$skip] = $this->ychanged[$skip] = false; 
     53        } 
     54 
     55        // Skip trailing common lines. 
     56        $xi = $n_from; $yi = $n_to; 
     57        for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { 
     58            if ($from_lines[$xi] !== $to_lines[$yi]) { 
     59                break; 
     60            } 
     61            $this->xchanged[$xi] = $this->ychanged[$yi] = false; 
     62        } 
     63 
     64        // Ignore lines which do not exist in both files. 
     65        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { 
     66            $xhash[$from_lines[$xi]] = 1; 
     67        } 
     68        for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { 
     69            $line = $to_lines[$yi]; 
     70            if (($this->ychanged[$yi] = empty($xhash[$line]))) { 
     71                continue; 
     72            } 
     73            $yhash[$line] = 1; 
     74            $this->yv[] = $line; 
     75            $this->yind[] = $yi; 
     76        } 
     77        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { 
     78            $line = $from_lines[$xi]; 
     79            if (($this->xchanged[$xi] = empty($yhash[$line]))) { 
     80                continue; 
     81            } 
     82            $this->xv[] = $line; 
     83            $this->xind[] = $xi; 
     84        } 
     85 
     86        // Find the LCS. 
     87        $this->_compareseq(0, count($this->xv), 0, count($this->yv)); 
     88 
     89        // Merge edits when possible. 
     90        $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); 
     91        $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); 
     92 
     93        // Compute the edit operations. 
     94        $edits = array(); 
     95        $xi = $yi = 0; 
     96        while ($xi < $n_from || $yi < $n_to) { 
     97            assert($yi < $n_to || $this->xchanged[$xi]); 
     98            assert($xi < $n_from || $this->ychanged[$yi]); 
     99 
     100            // Skip matching "snake". 
     101            $copy = array(); 
     102            while ($xi < $n_from && $yi < $n_to 
     103                   && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { 
     104                $copy[] = $from_lines[$xi++]; 
     105                ++$yi; 
     106            } 
     107            if ($copy) { 
     108                $edits[] = &new Text_Diff_Op_copy($copy); 
     109            } 
     110 
     111            // Find deletes & adds. 
     112            $delete = array(); 
     113            while ($xi < $n_from && $this->xchanged[$xi]) { 
     114                $delete[] = $from_lines[$xi++]; 
     115            } 
     116 
     117            $add = array(); 
     118            while ($yi < $n_to && $this->ychanged[$yi]) { 
     119                $add[] = $to_lines[$yi++]; 
     120            } 
     121 
     122            if ($delete && $add) { 
     123                $edits[] = &new Text_Diff_Op_change($delete, $add); 
     124            } elseif ($delete) { 
     125                $edits[] = &new Text_Diff_Op_delete($delete); 
     126            } elseif ($add) { 
     127                $edits[] = &new Text_Diff_Op_add($add); 
     128            } 
     129        } 
     130 
     131        return $edits; 
     132    } 
     133 
     134    /** 
     135     * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, 
     136     * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized 
     137     * segments. 
     138     * 
     139     * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of 
     140     * NCHUNKS+1 (X, Y) indexes giving the diving points between sub 
     141     * sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1), 
     142     * the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) == 
     143     * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). 
     144     * 
     145     * This function assumes that the first lines of the specified portions of 
     146     * the two files do not match, and likewise that the last lines do not 
     147     * match.  The caller must trim matching lines from the beginning and end 
     148     * of the portions it is going to specify. 
     149     */ 
     150    function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) 
     151    { 
     152        $flip = false; 
     153 
     154        if ($xlim - $xoff > $ylim - $yoff) { 
     155            /* Things seems faster (I'm not sure I understand why) when the 
     156             * shortest sequence is in X. */ 
     157            $flip = true; 
     158            list ($xoff, $xlim, $yoff, $ylim) 
     159                = array($yoff, $ylim, $xoff, $xlim); 
     160        } 
     161 
     162        if ($flip) { 
     163            for ($i = $ylim - 1; $i >= $yoff; $i--) { 
     164                $ymatches[$this->xv[$i]][] = $i; 
     165            } 
     166        } else { 
     167            for ($i = $ylim - 1; $i >= $yoff; $i--) { 
     168                $ymatches[$this->yv[$i]][] = $i; 
     169            } 
     170        } 
     171 
     172        $this->lcs = 0; 
     173        $this->seq[0]= $yoff - 1; 
     174        $this->in_seq = array(); 
     175        $ymids[0] = array(); 
     176 
     177        $numer = $xlim - $xoff + $nchunks - 1; 
     178        $x = $xoff; 
     179        for ($chunk = 0; $chunk < $nchunks; $chunk++) { 
     180            if ($chunk > 0) { 
     181                for ($i = 0; $i <= $this->lcs; $i++) { 
     182                    $ymids[$i][$chunk - 1] = $this->seq[$i]; 
     183                } 
     184            } 
     185 
     186            $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); 
     187            for (; $x < $x1; $x++) { 
     188                $line = $flip ? $this->yv[$x] : $this->xv[$x]; 
     189                if (empty($ymatches[$line])) { 
     190                    continue; 
     191                } 
     192                $matches = $ymatches[$line]; 
     193                reset($matches); 
     194                while (list(, $y) = each($matches)) { 
     195                    if (empty($this->in_seq[$y])) { 
     196                        $k = $this->_lcsPos($y); 
     197                        assert($k > 0); 
     198                        $ymids[$k] = $ymids[$k - 1]; 
     199                        break; 
     200                    } 
     201                } 
     202                while (list(, $y) = each($matches)) { 
     203                    if ($y > $this->seq[$k - 1]) { 
     204                        assert($y <= $this->seq[$k]); 
     205                        /* Optimization: this is a common case: next match is 
     206                         * just replacing previous match. */ 
     207                        $this->in_seq[$this->seq[$k]] = false; 
     208                        $this->seq[$k] = $y; 
     209                        $this->in_seq[$y] = 1; 
     210                    } elseif (empty($this->in_seq[$y])) { 
     211                        $k = $this->_lcsPos($y); 
     212                        assert($k > 0); 
     213                        $ymids[$k] = $ymids[$k - 1]; 
     214                    } 
     215                } 
     216            } 
     217        } 
     218 
     219        $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); 
     220        $ymid = $ymids[$this->lcs]; 
     221        for ($n = 0; $n < $nchunks - 1; $n++) { 
     222            $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); 
     223            $y1 = $ymid[$n] + 1; 
     224            $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); 
     225        } 
     226        $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); 
     227 
     228        return array($this->lcs, $seps); 
     229    } 
     230 
     231    function _lcsPos($ypos) 
     232    { 
     233        $end = $this->lcs; 
     234        if ($end == 0 || $ypos > $this->seq[$end]) { 
     235            $this->seq[++$this->lcs] = $ypos; 
     236            $this->in_seq[$ypos] = 1; 
     237            return $this->lcs; 
     238        } 
     239 
     240        $beg = 1; 
     241        while ($beg < $end) { 
     242            $mid = (int)(($beg + $end) / 2); 
     243            if ($ypos > $this->seq[$mid]) { 
     244                $beg = $mid + 1; 
     245            } else { 
     246                $end = $mid; 
     247            } 
     248        } 
     249 
     250        assert($ypos != $this->seq[$end]); 
     251 
     252        $this->in_seq[$this->seq[$end]] = false; 
     253        $this->seq[$end] = $ypos; 
     254        $this->in_seq[$ypos] = 1; 
     255        return $end; 
     256    } 
     257 
     258    /** 
     259     * Finds LCS of two sequences. 
     260     * 
     261     * The results are recorded in the vectors $this->{x,y}changed[], by 
     262     * storing a 1 in the element for each line that is an insertion or 
     263     * deletion (ie. is not in the LCS). 
     264     * 
     265     * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. 
     266     * 
     267     * Note that XLIM, YLIM are exclusive bounds.  All line numbers are 
     268     * origin-0 and discarded lines are not counted. 
     269     */ 
     270    function _compareseq ($xoff, $xlim, $yoff, $ylim) 
     271    { 
     272        /* Slide down the bottom initial diagonal. */ 
     273        while ($xoff < $xlim && $yoff < $ylim 
     274               && $this->xv[$xoff] == $this->yv[$yoff]) { 
     275            ++$xoff; 
     276            ++$yoff; 
     277        } 
     278 
     279        /* Slide up the top initial diagonal. */ 
     280        while ($xlim > $xoff && $ylim > $yoff 
     281               && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { 
     282            --$xlim; 
     283            --$ylim; 
     284        } 
     285 
     286        if ($xoff == $xlim || $yoff == $ylim) { 
     287            $lcs = 0; 
     288        } else { 
     289            /* This is ad hoc but seems to work well.  $nchunks = 
     290             * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = 
     291             * max(2,min(8,(int)$nchunks)); */ 
     292            $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; 
     293            list($lcs, $seps) 
     294                = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); 
     295        } 
     296 
     297        if ($lcs == 0) { 
     298            /* X and Y sequences have no common subsequence: mark all 
     299             * changed. */ 
     300            while ($yoff < $ylim) { 
     301                $this->ychanged[$this->yind[$yoff++]] = 1; 
     302            } 
     303            while ($xoff < $xlim) { 
     304                $this->xchanged[$this->xind[$xoff++]] = 1; 
     305            } 
     306        } else { 
     307            /* Use the partitions to split this problem into subproblems. */ 
     308            reset($seps); 
     309            $pt1 = $seps[0]; 
     310            while ($pt2 = next($seps)) { 
     311                $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); 
     312                $pt1 = $pt2; 
     313            } 
     314        } 
     315    } 
     316 
     317    /** 
     318     * Adjusts inserts/deletes of identical lines to join changes as much as 
     319     * possible. 
     320     * 
     321     * We do something when a run of changed lines include a line at one end 
     322     * and has an excluded, identical line at the other.  We are free to 
     323     * choose which identical line is included.  `compareseq' usually chooses 
     324     * the one at the beginning, but usually it is cleaner to consider the 
     325     * following identical line to be the "change". 
     326     * 
     327     * This is extracted verbatim from analyze.c (GNU diffutils-2.7). 
     328     */ 
     329    function _shiftBoundaries($lines, &$changed, $other_changed) 
     330    { 
     331        $i = 0; 
     332        $j = 0; 
     333 
     334        assert('count($lines) == count($changed)'); 
     335        $len = count($lines); 
     336        $other_len = count($other_changed); 
     337 
     338        while (1) { 
     339            /* Scan forward to find the beginning of another run of 
     340             * changes. Also keep track of the corresponding point in the 
     341             * other file. 
     342             * 
     343             * Throughout this code, $i and $j are adjusted together so that 
     344             * the first $i elements of $changed and the first $j elements of 
     345             * $other_changed both contain the same number of zeros (unchanged 
     346             * lines). 
     347             * 
     348             * Furthermore, $j is always kept so that $j == $other_len or 
     349             * $other_changed[$j] == false. */ 
     350            while ($j < $other_len && $other_changed[$j]) { 
     351                $j++; 
     352            } 
     353 
     354            while ($i < $len && ! $changed[$i]) { 
     355                assert('$j < $other_len && ! $other_changed[$j]'); 
     356                $i++; $j++; 
     357                while ($j < $other_len && $other_changed[$j]) { 
     358                    $j++; 
     359                } 
     360            } 
     361 
     362            if ($i == $len) { 
     363                break; 
     364            } 
     365 
     366            $start = $i; 
     367 
     368            /* Find the end of this run of changes. */ 
     369            while (++$i < $len && $changed[$i]) { 
     370                continue; 
     371            } 
     372 
     373            do { 
     374                /* Record the length of this run of changes, so that we can 
     375                 * later determine whether the run has grown. */ 
     376                $runlength = $i - $start; 
     377 
     378                /* Move the changed region back, so long as the previous 
     379                 * unchanged line matches the last changed one.  This merges 
     380                 * with previous changed regions. */ 
     381                while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { 
     382                    $changed[--$start] = 1; 
     383                    $changed[--$i] = false; 
     384                    while ($start > 0 && $changed[$start - 1]) { 
     385                        $start--; 
     386                    } 
     387                    assert('$j > 0'); 
     388                    while ($other_changed[--$j]) { 
     389                        continue; 
     390                    } 
     391                    assert('$j >= 0 && !$other_changed[$j]'); 
     392                } 
     393 
     394                /* Set CORRESPONDING to the end of the changed run, at the 
     395                 * last point where it corresponds to a changed run in the 
     396                 * other file. CORRESPONDING == LEN means no such point has 
     397                 * been found. */ 
     398                $corresponding = $j < $other_len ? $i : $len; 
     399 
     400                /* Move the changed region forward, so long as the first 
     401                 * changed line matches the following unchanged one.  This 
     402                 * merges with following changed regions.  Do this second, so 
     403                 * that if there are no merges, the changed region is moved 
     404                 * forward as far as possible. */ 
     405                while ($i < $len && $lines[$start] == $lines[$i]) { 
     406                    $changed[$start++] = false; 
     407                    $changed[$i++] = 1; 
     408                    while ($i < $len && $changed[$i]) { 
     409                        $i++; 
     410                    } 
     411 
     412                    assert('$j < $other_len && ! $other_changed[$j]'); 
     413                    $j++; 
     414                    if ($j < $other_len && $other_changed[$j]) { 
     415                        $corresponding = $i; 
     416                        while ($j < $other_len && $other_changed[$j]) { 
     417                            $j++; 
     418                        } 
     419                    } 
     420                } 
     421            } while ($runlength != $i - $start); 
     422 
     423            /* If possible, move the fully-merged run of changes back to a 
     424             * corresponding run in the other file. */ 
     425            while ($corresponding < $i) { 
     426                $changed[--$start] = 1; 
     427                $changed[--$i] = 0; 
     428                assert('$j > 0'); 
     429                while ($other_changed[--$j]) { 
     430                    continue; 
     431                } 
     432                assert('$j >= 0 && !$other_changed[$j]'); 
     433            } 
     434        } 
     435    } 
     436 
     437} 
  • wp-includes/Text/Diff/Engine/string.php

     
     1<?php 
     2/** 
     3 * Parses unified or context diffs output from eg. the diff utility. 
     4 * 
     5 * Example: 
     6 * <code> 
     7 * $patch = file_get_contents('example.patch'); 
     8 * $diff = new Text_Diff('string', array($patch)); 
     9 * $renderer = new Text_Diff_Renderer_inline(); 
     10 * echo $renderer->render($diff); 
     11 * </code> 
     12 * 
     13 * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.7 2008/01/04 10:07:50 jan Exp $ 
     14 * 
     15 * Copyright 2005 Örjan Persson <o@42mm.org> 
     16 * Copyright 2005-2008 The Horde Project (http://www.horde.org/) 
     17 * 
     18 * See the enclosed file COPYING for license information (LGPL). If you did 
     19 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     20 * 
     21 * @author  Örjan Persson <o@42mm.org> 
     22 * @package Text_Diff 
     23 * @since   0.2.0 
     24 */ 
     25class Text_Diff_Engine_string { 
     26 
     27    /** 
     28     * Parses a unified or context diff. 
     29     * 
     30     * First param contains the whole diff and the second can be used to force 
     31     * a specific diff type. If the second parameter is 'autodetect', the 
     32     * diff will be examined to find out which type of diff this is. 
     33     * 
     34     * @param string $diff  The diff content. 
     35     * @param string $mode  The diff mode of the content in $diff. One of 
     36     *                      'context', 'unified', or 'autodetect'. 
     37     * 
     38     * @return array  List of all diff operations. 
     39     */ 
     40    function diff($diff, $mode = 'autodetect') 
     41    { 
     42        if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { 
     43            return PEAR::raiseError('Type of diff is unsupported'); 
     44        } 
     45 
     46        if ($mode == 'autodetect') { 
     47            $context = strpos($diff, '***'); 
     48            $unified = strpos($diff, '---'); 
     49            if ($context === $unified) { 
     50                return PEAR::raiseError('Type of diff could not be detected'); 
     51            } elseif ($context === false || $context === false) { 
     52                $mode = $context !== false ? 'context' : 'unified'; 
     53            } else { 
     54                $mode = $context < $unified ? 'context' : 'unified'; 
     55            } 
     56        } 
     57 
     58        // split by new line and remove the diff header 
     59        $diff = explode("\n", $diff); 
     60        array_shift($diff); 
     61        array_shift($diff); 
     62 
     63        if ($mode == 'context') { 
     64            return $this->parseContextDiff($diff); 
     65        } else { 
     66            return $this->parseUnifiedDiff($diff); 
     67        } 
     68    } 
     69 
     70    /** 
     71     * Parses an array containing the unified diff. 
     72     * 
     73     * @param array $diff  Array of lines. 
     74     * 
     75     * @return array  List of all diff operations. 
     76     */ 
     77    function parseUnifiedDiff($diff) 
     78    { 
     79        $edits = array(); 
     80        $end = count($diff) - 1; 
     81        for ($i = 0; $i < $end;) { 
     82            $diff1 = array(); 
     83            switch (substr($diff[$i], 0, 1)) { 
     84            case ' ': 
     85                do { 
     86                    $diff1[] = substr($diff[$i], 1); 
     87                } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); 
     88                $edits[] = &new Text_Diff_Op_copy($diff1); 
     89                break; 
     90 
     91            case '+': 
     92                // get all new lines 
     93                do { 
     94                    $diff1[] = substr($diff[$i], 1); 
     95                } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); 
     96                $edits[] = &new Text_Diff_Op_add($diff1); 
     97                break; 
     98 
     99            case '-': 
     100                // get changed or removed lines 
     101                $diff2 = array(); 
     102                do { 
     103                    $diff1[] = substr($diff[$i], 1); 
     104                } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); 
     105 
     106                while ($i < $end && substr($diff[$i], 0, 1) == '+') { 
     107                    $diff2[] = substr($diff[$i++], 1); 
     108                } 
     109                if (count($diff2) == 0) { 
     110                    $edits[] = &new Text_Diff_Op_delete($diff1); 
     111                } else { 
     112                    $edits[] = &new Text_Diff_Op_change($diff1, $diff2); 
     113                } 
     114                break; 
     115 
     116            default: 
     117                $i++; 
     118                break; 
     119            } 
     120        } 
     121 
     122        return $edits; 
     123    } 
     124 
     125    /** 
     126     * Parses an array containing the context diff. 
     127     * 
     128     * @param array $diff  Array of lines. 
     129     * 
     130     * @return array  List of all diff operations. 
     131     */ 
     132    function parseContextDiff(&$diff) 
     133    { 
     134        $edits = array(); 
     135        $i = $max_i = $j = $max_j = 0; 
     136        $end = count($diff) - 1; 
     137        while ($i < $end && $j < $end) { 
     138            while ($i >= $max_i && $j >= $max_j) { 
     139                // Find the boundaries of the diff output of the two files 
     140                for ($i = $j; 
     141                     $i < $end && substr($diff[$i], 0, 3) == '***'; 
     142                     $i++); 
     143                for ($max_i = $i; 
     144                     $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; 
     145                     $max_i++); 
     146                for ($j = $max_i; 
     147                     $j < $end && substr($diff[$j], 0, 3) == '---'; 
     148                     $j++); 
     149                for ($max_j = $j; 
     150                     $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; 
     151                     $max_j++); 
     152            } 
     153 
     154            // find what hasn't been changed 
     155            $array = array(); 
     156            while ($i < $max_i && 
     157                   $j < $max_j && 
     158                   strcmp($diff[$i], $diff[$j]) == 0) { 
     159                $array[] = substr($diff[$i], 2); 
     160                $i++; 
     161                $j++; 
     162            } 
     163 
     164            while ($i < $max_i && ($max_j-$j) <= 1) { 
     165                if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { 
     166                    break; 
     167                } 
     168                $array[] = substr($diff[$i++], 2); 
     169            } 
     170 
     171            while ($j < $max_j && ($max_i-$i) <= 1) { 
     172                if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { 
     173                    break; 
     174                } 
     175                $array[] = substr($diff[$j++], 2); 
     176            } 
     177            if (count($array) > 0) { 
     178                $edits[] = &new Text_Diff_Op_copy($array); 
     179            } 
     180 
     181            if ($i < $max_i) { 
     182                $diff1 = array(); 
     183                switch (substr($diff[$i], 0, 1)) { 
     184                case '!': 
     185                    $diff2 = array(); 
     186                    do { 
     187                        $diff1[] = substr($diff[$i], 2); 
     188                        if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { 
     189                            $diff2[] = substr($diff[$j++], 2); 
     190                        } 
     191                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); 
     192                    $edits[] = &new Text_Diff_Op_change($diff1, $diff2); 
     193                    break; 
     194 
     195                case '+': 
     196                    do { 
     197                        $diff1[] = substr($diff[$i], 2); 
     198                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); 
     199                    $edits[] = &new Text_Diff_Op_add($diff1); 
     200                    break; 
     201 
     202                case '-': 
     203                    do { 
     204                        $diff1[] = substr($diff[$i], 2); 
     205                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); 
     206                    $edits[] = &new Text_Diff_Op_delete($diff1); 
     207                    break; 
     208                } 
     209            } 
     210 
     211            if ($j < $max_j) { 
     212                $diff2 = array(); 
     213                switch (substr($diff[$j], 0, 1)) { 
     214                case '+': 
     215                    do { 
     216                        $diff2[] = substr($diff[$j++], 2); 
     217                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); 
     218                    $edits[] = &new Text_Diff_Op_add($diff2); 
     219                    break; 
     220 
     221                case '-': 
     222                    do { 
     223                        $diff2[] = substr($diff[$j++], 2); 
     224                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); 
     225                    $edits[] = &new Text_Diff_Op_delete($diff2); 
     226                    break; 
     227                } 
     228            } 
     229        } 
     230 
     231        return $edits; 
     232    } 
     233 
     234} 
  • wp-includes/Text/Diff/Engine/shell.php

     
     1<?php 
     2/** 
     3 * Class used internally by Diff to actually compute the diffs. 
     4 * 
     5 * This class uses the Unix `diff` program via shell_exec to compute the 
     6 * differences between the two input arrays. 
     7 * 
     8 * $Horde: framework/Text_Diff/Diff/Engine/shell.php,v 1.8 2008/01/04 10:07:50 jan Exp $ 
     9 * 
     10 * Copyright 2007-2008 The Horde Project (http://www.horde.org/) 
     11 * 
     12 * See the enclosed file COPYING for license information (LGPL). If you did 
     13 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     14 * 
     15 * @author  Milian Wolff <mail@milianw.de> 
     16 * @package Text_Diff 
     17 * @since   0.3.0 
     18 */ 
     19class Text_Diff_Engine_shell { 
     20 
     21    /** 
     22     * Path to the diff executable 
     23     * 
     24     * @var string 
     25     */ 
     26    var $_diffCommand = 'diff'; 
     27 
     28    /** 
     29     * Returns the array of differences. 
     30     * 
     31     * @param array $from_lines lines of text from old file 
     32     * @param array $to_lines   lines of text from new file 
     33     * 
     34     * @return array all changes made (array with Text_Diff_Op_* objects) 
     35     */ 
     36    function diff($from_lines, $to_lines) 
     37    { 
     38        array_walk($from_lines, array('Text_Diff', 'trimNewlines')); 
     39        array_walk($to_lines, array('Text_Diff', 'trimNewlines')); 
     40 
     41        $temp_dir = Text_Diff::_getTempDir(); 
     42 
     43        // Execute gnu diff or similar to get a standard diff file. 
     44        $from_file = tempnam($temp_dir, 'Text_Diff'); 
     45        $to_file = tempnam($temp_dir, 'Text_Diff'); 
     46        $fp = fopen($from_file, 'w'); 
     47        fwrite($fp, implode("\n", $from_lines)); 
     48        fclose($fp); 
     49        $fp = fopen($to_file, 'w'); 
     50        fwrite($fp, implode("\n", $to_lines)); 
     51        fclose($fp); 
     52        $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file); 
     53        unlink($from_file); 
     54        unlink($to_file); 
     55 
     56        if (is_null($diff)) { 
     57            // No changes were made 
     58            return array(new Text_Diff_Op_copy($from_lines)); 
     59        } 
     60 
     61        $from_line_no = 1; 
     62        $to_line_no = 1; 
     63        $edits = array(); 
     64 
     65        // Get changed lines by parsing something like: 
     66        // 0a1,2 
     67        // 1,2c4,6 
     68        // 1,5d6 
     69        preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff, 
     70            $matches, PREG_SET_ORDER); 
     71 
     72        foreach ($matches as $match) { 
     73            if (!isset($match[5])) { 
     74                // This paren is not set every time (see regex). 
     75                $match[5] = false; 
     76            } 
     77 
     78            if ($match[3] == 'a') { 
     79                $from_line_no--; 
     80            } 
     81 
     82            if ($match[3] == 'd') { 
     83                $to_line_no--; 
     84            } 
     85 
     86            if ($from_line_no < $match[1] || $to_line_no < $match[4]) { 
     87                // copied lines 
     88                assert('$match[1] - $from_line_no == $match[4] - $to_line_no'); 
     89                array_push($edits, 
     90                    new Text_Diff_Op_copy( 
     91                        $this->_getLines($from_lines, $from_line_no, $match[1] - 1), 
     92                        $this->_getLines($to_lines, $to_line_no, $match[4] - 1))); 
     93            } 
     94 
     95            switch ($match[3]) { 
     96            case 'd': 
     97                // deleted lines 
     98                array_push($edits, 
     99                    new Text_Diff_Op_delete( 
     100                        $this->_getLines($from_lines, $from_line_no, $match[2]))); 
     101                $to_line_no++; 
     102                break; 
     103 
     104            case 'c': 
     105                // changed lines 
     106                array_push($edits, 
     107                    new Text_Diff_Op_change( 
     108                        $this->_getLines($from_lines, $from_line_no, $match[2]), 
     109                        $this->_getLines($to_lines, $to_line_no, $match[5]))); 
     110                break; 
     111 
     112            case 'a': 
     113                // added lines 
     114                array_push($edits, 
     115                    new Text_Diff_Op_add( 
     116                        $this->_getLines($to_lines, $to_line_no, $match[5]))); 
     117                $from_line_no++; 
     118                break; 
     119            } 
     120        } 
     121 
     122        if (!empty($from_lines)) { 
     123            // Some lines might still be pending. Add them as copied 
     124            array_push($edits, 
     125                new Text_Diff_Op_copy( 
     126                    $this->_getLines($from_lines, $from_line_no, 
     127                                     $from_line_no + count($from_lines) - 1), 
     128                    $this->_getLines($to_lines, $to_line_no, 
     129                                     $to_line_no + count($to_lines) - 1))); 
     130        } 
     131 
     132        return $edits; 
     133    } 
     134 
     135    /** 
     136     * Get lines from either the old or new text 
     137     * 
     138     * @access private 
     139     * 
     140     * @param array &$text_lines Either $from_lines or $to_lines 
     141     * @param int   &$line_no    Current line number 
     142     * @param int   $end         Optional end line, when we want to chop more 
     143     *                           than one line. 
     144     * 
     145     * @return array The chopped lines 
     146     */ 
     147    function _getLines(&$text_lines, &$line_no, $end = false) 
     148    { 
     149        if (!empty($end)) { 
     150            $lines = array(); 
     151            // We can shift even more 
     152            while ($line_no <= $end) { 
     153                array_push($lines, array_shift($text_lines)); 
     154                $line_no++; 
     155            } 
     156        } else { 
     157            $lines = array(array_shift($text_lines)); 
     158            $line_no++; 
     159        } 
     160 
     161        return $lines; 
     162    } 
     163 
     164} 
  • wp-includes/Text/Diff/Renderer/inline.php

     
     1<?php 
     2/** 
     3 * "Inline" diff renderer. 
     4 * 
     5 * $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.21 2008/01/04 10:07:51 jan Exp $ 
     6 * 
     7 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 
     8 * 
     9 * See the enclosed file COPYING for license information (LGPL). If you did 
     10 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     11 * 
     12 * @author  Ciprian Popovici 
     13 * @package Text_Diff 
     14 */ 
     15 
     16/** Text_Diff_Renderer */ 
     17require_once 'Text/Diff/Renderer.php'; 
     18 
     19/** 
     20 * "Inline" diff renderer. 
     21 * 
     22 * This class renders diffs in the Wiki-style "inline" format. 
     23 * 
     24 * @author  Ciprian Popovici 
     25 * @package Text_Diff 
     26 */ 
     27class Text_Diff_Renderer_inline extends Text_Diff_Renderer { 
     28 
     29    /** 
     30     * Number of leading context "lines" to preserve. 
     31     */ 
     32    var $_leading_context_lines = 10000; 
     33 
     34    /** 
     35     * Number of trailing context "lines" to preserve. 
     36     */ 
     37    var $_trailing_context_lines = 10000; 
     38 
     39    /** 
     40     * Prefix for inserted text. 
     41     */ 
     42    var $_ins_prefix = '<ins>'; 
     43 
     44    /** 
     45     * Suffix for inserted text. 
     46     */ 
     47    var $_ins_suffix = '</ins>'; 
     48 
     49    /** 
     50     * Prefix for deleted text. 
     51     */ 
     52    var $_del_prefix = '<del>'; 
     53 
     54    /** 
     55     * Suffix for deleted text. 
     56     */ 
     57    var $_del_suffix = '</del>'; 
     58 
     59    /** 
     60     * Header for each change block. 
     61     */ 
     62    var $_block_header = ''; 
     63 
     64    /** 
     65     * What are we currently splitting on? Used to recurse to show word-level 
     66     * changes. 
     67     */ 
     68    var $_split_level = 'lines'; 
     69 
     70    function _blockHeader($xbeg, $xlen, $ybeg, $ylen) 
     71    { 
     72        return $this->_block_header; 
     73    } 
     74 
     75    function _startBlock($header) 
     76    { 
     77        return $header; 
     78    } 
     79 
     80    function _lines($lines, $prefix = ' ', $encode = true) 
     81    { 
     82        if ($encode) { 
     83            array_walk($lines, array(&$this, '_encode')); 
     84        } 
     85 
     86        if ($this->_split_level == 'words') { 
     87            return implode('', $lines); 
     88        } else { 
     89            return implode("\n", $lines) . "\n"; 
     90        } 
     91    } 
     92 
     93    function _added($lines) 
     94    { 
     95        array_walk($lines, array(&$this, '_encode')); 
     96        $lines[0] = $this->_ins_prefix . $lines[0]; 
     97        $lines[count($lines) - 1] .= $this->_ins_suffix; 
     98        return $this->_lines($lines, ' ', false); 
     99    } 
     100 
     101    function _deleted($lines, $words = false) 
     102    { 
     103        array_walk($lines, array(&$this, '_encode')); 
     104        $lines[0] = $this->_del_prefix . $lines[0]; 
     105        $lines[count($lines) - 1] .= $this->_del_suffix; 
     106        return $this->_lines($lines, ' ', false); 
     107    } 
     108 
     109    function _changed($orig, $final) 
     110    { 
     111        /* If we've already split on words, don't try to do so again - just 
     112         * display. */ 
     113        if ($this->_split_level == 'words') { 
     114            $prefix = ''; 
     115            while ($orig[0] !== false && $final[0] !== false && 
     116                   substr($orig[0], 0, 1) == ' ' && 
     117                   substr($final[0], 0, 1) == ' ') { 
     118                $prefix .= substr($orig[0], 0, 1); 
     119                $orig[0] = substr($orig[0], 1); 
     120                $final[0] = substr($final[0], 1); 
     121            } 
     122            return $prefix . $this->_deleted($orig) . $this->_added($final); 
     123        } 
     124 
     125        $text1 = implode("\n", $orig); 
     126        $text2 = implode("\n", $final); 
     127 
     128        /* Non-printing newline marker. */ 
     129        $nl = "\0"; 
     130 
     131        /* We want to split on word boundaries, but we need to 
     132         * preserve whitespace as well. Therefore we split on words, 
     133         * but include all blocks of whitespace in the wordlist. */ 
     134        $diff = new Text_Diff($this->_splitOnWords($text1, $nl), 
     135                              $this->_splitOnWords($text2, $nl)); 
     136 
     137        /* Get the diff in inline format. */ 
     138        $renderer = new Text_Diff_Renderer_inline(array_merge($this->getParams(), 
     139                                                              array('split_level' => 'words'))); 
     140 
     141        /* Run the diff and get the output. */ 
     142        return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; 
     143    } 
     144 
     145    function _splitOnWords($string, $newlineEscape = "\n") 
     146    { 
     147        // Ignore \0; otherwise the while loop will never finish. 
     148        $string = str_replace("\0", '', $string); 
     149 
     150        $words = array(); 
     151        $length = strlen($string); 
     152        $pos = 0; 
     153 
     154        while ($pos < $length) { 
     155            // Eat a word with any preceding whitespace. 
     156            $spaces = strspn(substr($string, $pos), " \n"); 
     157            $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); 
     158            $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); 
     159            $pos += $spaces + $nextpos; 
     160        } 
     161 
     162        return $words; 
     163    } 
     164 
     165    function _encode(&$string) 
     166    { 
     167        $string = htmlspecialchars($string); 
     168    } 
     169 
     170} 
  • wp-includes/Text/Diff/Renderer.php

     
     1<?php 
     2/** 
     3 * A class to render Diffs in different formats. 
     4 * 
     5 * This class renders the diff in classic diff format. It is intended that 
     6 * this class be customized via inheritance, to obtain fancier outputs. 
     7 * 
     8 * $Horde: framework/Text_Diff/Diff/Renderer.php,v 1.21 2008/01/04 10:07:50 jan Exp $ 
     9 * 
     10 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 
     11 * 
     12 * See the enclosed file COPYING for license information (LGPL). If you did 
     13 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     14 * 
     15 * @package Text_Diff 
     16 */ 
     17class Text_Diff_Renderer { 
     18 
     19    /** 
     20     * Number of leading context "lines" to preserve. 
     21     * 
     22     * This should be left at zero for this class, but subclasses may want to 
     23     * set this to other values. 
     24     */ 
     25    var $_leading_context_lines = 0; 
     26 
     27    /** 
     28     * Number of trailing context "lines" to preserve. 
     29     * 
     30     * This should be left at zero for this class, but subclasses may want to 
     31     * set this to other values. 
     32     */ 
     33    var $_trailing_context_lines = 0; 
     34 
     35    /** 
     36     * Constructor. 
     37     */ 
     38    function Text_Diff_Renderer($params = array()) 
     39    { 
     40        foreach ($params as $param => $value) { 
     41            $v = '_' . $param; 
     42            if (isset($this->$v)) { 
     43                $this->$v = $value; 
     44            } 
     45        } 
     46    } 
     47 
     48    /** 
     49     * Get any renderer parameters. 
     50     * 
     51     * @return array  All parameters of this renderer object. 
     52     */ 
     53    function getParams() 
     54    { 
     55        $params = array(); 
     56        foreach (get_object_vars($this) as $k => $v) { 
     57            if ($k[0] == '_') { 
     58                $params[substr($k, 1)] = $v; 
     59            } 
     60        } 
     61 
     62        return $params; 
     63    } 
     64 
     65    /** 
     66     * Renders a diff. 
     67     * 
     68     * @param Text_Diff $diff  A Text_Diff object. 
     69     * 
     70     * @return string  The formatted output. 
     71     */ 
     72    function render($diff) 
     73    { 
     74        $xi = $yi = 1; 
     75        $block = false; 
     76        $context = array(); 
     77 
     78        $nlead = $this->_leading_context_lines; 
     79        $ntrail = $this->_trailing_context_lines; 
     80 
     81        $output = $this->_startDiff(); 
     82 
     83        $diffs = $diff->getDiff(); 
     84        foreach ($diffs as $i => $edit) { 
     85            /* If these are unchanged (copied) lines, and we want to keep 
     86             * leading or trailing context lines, extract them from the copy 
     87             * block. */ 
     88            if (is_a($edit, 'Text_Diff_Op_copy')) { 
     89                /* Do we have any diff blocks yet? */ 
     90                if (is_array($block)) { 
     91                    /* How many lines to keep as context from the copy 
     92                     * block. */ 
     93                    $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; 
     94                    if (count($edit->orig) <= $keep) { 
     95                        /* We have less lines in the block than we want for 
     96                         * context => keep the whole block. */ 
     97                        $block[] = $edit; 
     98                    } else { 
     99                        if ($ntrail) { 
     100                            /* Create a new block with as many lines as we need 
     101                             * for the trailing context. */ 
     102                            $context = array_slice($edit->orig, 0, $ntrail); 
     103                            $block[] = &new Text_Diff_Op_copy($context); 
     104                        } 
     105                        /* @todo */ 
     106                        $output .= $this->_block($x0, $ntrail + $xi - $x0, 
     107                                                 $y0, $ntrail + $yi - $y0, 
     108                                                 $block); 
     109                        $block = false; 
     110                    } 
     111                } 
     112                /* Keep the copy block as the context for the next block. */ 
     113                $context = $edit->orig; 
     114            } else { 
     115                /* Don't we have any diff blocks yet? */ 
     116                if (!is_array($block)) { 
     117                    /* Extract context lines from the preceding copy block. */ 
     118                    $context = array_slice($context, count($context) - $nlead); 
     119                    $x0 = $xi - count($context); 
     120                    $y0 = $yi - count($context); 
     121                    $block = array(); 
     122                    if ($context) { 
     123                        $block[] = &new Text_Diff_Op_copy($context); 
     124                    } 
     125                } 
     126                $block[] = $edit; 
     127            } 
     128 
     129            if ($edit->orig) { 
     130                $xi += count($edit->orig); 
     131            } 
     132            if ($edit->final) { 
     133                $yi += count($edit->final); 
     134            } 
     135        } 
     136 
     137        if (is_array($block)) { 
     138            $output .= $this->_block($x0, $xi - $x0, 
     139                                     $y0, $yi - $y0, 
     140                                     $block); 
     141        } 
     142 
     143        return $output . $this->_endDiff(); 
     144    } 
     145 
     146    function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) 
     147    { 
     148        $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); 
     149 
     150        foreach ($edits as $edit) { 
     151            switch (strtolower(get_class($edit))) { 
     152            case 'text_diff_op_copy': 
     153                $output .= $this->_context($edit->orig); 
     154                break; 
     155 
     156            case 'text_diff_op_add': 
     157                $output .= $this->_added($edit->final); 
     158                break; 
     159 
     160            case 'text_diff_op_delete': 
     161                $output .= $this->_deleted($edit->orig); 
     162                break; 
     163 
     164            case 'text_diff_op_change': 
     165                $output .= $this->_changed($edit->orig, $edit->final); 
     166                break; 
     167            } 
     168        } 
     169 
     170        return $output . $this->_endBlock(); 
     171    } 
     172 
     173    function _startDiff() 
     174    { 
     175        return ''; 
     176    } 
     177 
     178    function _endDiff() 
     179    { 
     180        return ''; 
     181    } 
     182 
     183    function _blockHeader($xbeg, $xlen, $ybeg, $ylen) 
     184    { 
     185        if ($xlen > 1) { 
     186            $xbeg .= ',' . ($xbeg + $xlen - 1); 
     187        } 
     188        if ($ylen > 1) { 
     189            $ybeg .= ',' . ($ybeg + $ylen - 1); 
     190        } 
     191 
     192        // this matches the GNU Diff behaviour 
     193        if ($xlen && !$ylen) { 
     194            $ybeg--; 
     195        } elseif (!$xlen) { 
     196            $xbeg--; 
     197        } 
     198 
     199        return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; 
     200    } 
     201 
     202    function _startBlock($header) 
     203    { 
     204        return $header . "\n"; 
     205    } 
     206 
     207    function _endBlock() 
     208    { 
     209        return ''; 
     210    } 
     211 
     212    function _lines($lines, $prefix = ' ') 
     213    { 
     214        return $prefix . implode("\n$prefix", $lines) . "\n"; 
     215    } 
     216 
     217    function _context($lines) 
     218    { 
     219        return $this->_lines($lines, '  '); 
     220    } 
     221 
     222    function _added($lines) 
     223    { 
     224        return $this->_lines($lines, '> '); 
     225    } 
     226 
     227    function _deleted($lines) 
     228    { 
     229        return $this->_lines($lines, '< '); 
     230    } 
     231 
     232    function _changed($orig, $final) 
     233    { 
     234        return $this->_deleted($orig) . "---\n" . $this->_added($final); 
     235    } 
     236 
     237} 
  • wp-includes/Text/Diff.php

     
     1<?php 
     2/** 
     3 * General API for generating and formatting diffs - the differences between 
     4 * two sequences of strings. 
     5 * 
     6 * The original PHP version of this code was written by Geoffrey T. Dairiki 
     7 * <dairiki@dairiki.org>, and is used/adapted with his permission. 
     8 * 
     9 * $Horde: framework/Text_Diff/Diff.php,v 1.26 2008/01/04 10:07:49 jan Exp $ 
     10 * 
     11 * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org> 
     12 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 
     13 * 
     14 * See the enclosed file COPYING for license information (LGPL). If you did 
     15 * not receive this file, see http://opensource.org/licenses/lgpl-license.php. 
     16 * 
     17 * @package Text_Diff 
     18 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     19 */ 
     20class Text_Diff { 
     21 
     22    /** 
     23     * Array of changes. 
     24     * 
     25     * @var array 
     26     */ 
     27    var $_edits; 
     28 
     29    /** 
     30     * Computes diffs between sequences of strings. 
     31     * 
     32     * @param string $engine     Name of the diffing engine to use.  'auto' 
     33     *                           will automatically select the best. 
     34     * @param array $params      Parameters to pass to the diffing engine. 
     35     *                           Normally an array of two arrays, each 
     36     *                           containing the lines from a file. 
     37     */ 
     38    function Text_Diff($engine, $params) 
     39    { 
     40        // Backward compatibility workaround. 
     41        if (!is_string($engine)) { 
     42            $params = array($engine, $params); 
     43            $engine = 'auto'; 
     44        } 
     45 
     46        if ($engine == 'auto') { 
     47            $engine = extension_loaded('xdiff') ? 'xdiff' : 'native'; 
     48        } else { 
     49            $engine = basename($engine); 
     50        } 
     51 
     52        require_once 'Text/Diff/Engine/' . $engine . '.php'; 
     53        $class = 'Text_Diff_Engine_' . $engine; 
     54        $diff_engine = new $class(); 
     55 
     56        $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); 
     57    } 
     58 
     59    /** 
     60     * Returns the array of differences. 
     61     */ 
     62    function getDiff() 
     63    { 
     64        return $this->_edits; 
     65    } 
     66 
     67    /** 
     68     * Computes a reversed diff. 
     69     * 
     70     * Example: 
     71     * <code> 
     72     * $diff = new Text_Diff($lines1, $lines2); 
     73     * $rev = $diff->reverse(); 
     74     * </code> 
     75     * 
     76     * @return Text_Diff  A Diff object representing the inverse of the 
     77     *                    original diff.  Note that we purposely don't return a 
     78     *                    reference here, since this essentially is a clone() 
     79     *                    method. 
     80     */ 
     81    function reverse() 
     82    { 
     83        if (version_compare(zend_version(), '2', '>')) { 
     84            $rev = clone($this); 
     85        } else { 
     86            $rev = $this; 
     87        } 
     88        $rev->_edits = array(); 
     89        foreach ($this->_edits as $edit) { 
     90            $rev->_edits[] = $edit->reverse(); 
     91        } 
     92        return $rev; 
     93    } 
     94 
     95    /** 
     96     * Checks for an empty diff. 
     97     * 
     98     * @return boolean  True if two sequences were identical. 
     99     */ 
     100    function isEmpty() 
     101    { 
     102        foreach ($this->_edits as $edit) { 
     103            if (!is_a($edit, 'Text_Diff_Op_copy')) { 
     104                return false; 
     105            } 
     106        } 
     107        return true; 
     108    } 
     109 
     110    /** 
     111     * Computes the length of the Longest Common Subsequence (LCS). 
     112     * 
     113     * This is mostly for diagnostic purposes. 
     114     * 
     115     * @return integer  The length of the LCS. 
     116     */ 
     117    function lcs() 
     118    { 
     119        $lcs = 0; 
     120        foreach ($this->_edits as $edit) { 
     121            if (is_a($edit, 'Text_Diff_Op_copy')) { 
     122                $lcs += count($edit->orig); 
     123            } 
     124        } 
     125        return $lcs; 
     126    } 
     127 
     128    /** 
     129     * Gets the original set of lines. 
     130     * 
     131     * This reconstructs the $from_lines parameter passed to the constructor. 
     132     * 
     133     * @return array  The original sequence of strings. 
     134     */ 
     135    function getOriginal() 
     136    { 
     137        $lines = array(); 
     138        foreach ($this->_edits as $edit) { 
     139            if ($edit->orig) { 
     140                array_splice($lines, count($lines), 0, $edit->orig); 
     141            } 
     142        } 
     143        return $lines; 
     144    } 
     145 
     146    /** 
     147     * Gets the final set of lines. 
     148     * 
     149     * This reconstructs the $to_lines parameter passed to the constructor. 
     150     * 
     151     * @return array  The sequence of strings. 
     152     */ 
     153    function getFinal() 
     154    { 
     155        $lines = array(); 
     156        foreach ($this->_edits as $edit) { 
     157            if ($edit->final) { 
     158                array_splice($lines, count($lines), 0, $edit->final); 
     159            } 
     160        } 
     161        return $lines; 
     162    } 
     163 
     164    /** 
     165     * Removes trailing newlines from a line of text. This is meant to be used 
     166     * with array_walk(). 
     167     * 
     168     * @param string $line  The line to trim. 
     169     * @param integer $key  The index of the line in the array. Not used. 
     170     */ 
     171    function trimNewlines(&$line, $key) 
     172    { 
     173        $line = str_replace(array("\n", "\r"), '', $line); 
     174    } 
     175 
     176    /** 
     177     * Determines the location of the system temporary directory. 
     178     * 
     179     * @static 
     180     * 
     181     * @access protected 
     182     * 
     183     * @return string  A directory name which can be used for temp files. 
     184     *                 Returns false if one could not be found. 
     185     */ 
     186    function _getTempDir() 
     187    { 
     188        $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', 
     189                               'c:\windows\temp', 'c:\winnt\temp'); 
     190 
     191        /* Try PHP's upload_tmp_dir directive. */ 
     192        $tmp = ini_get('upload_tmp_dir'); 
     193 
     194        /* Otherwise, try to determine the TMPDIR environment variable. */ 
     195        if (!strlen($tmp)) { 
     196            $tmp = getenv('TMPDIR'); 
     197        } 
     198 
     199        /* If we still cannot determine a value, then cycle through a list of 
     200         * preset possibilities. */ 
     201        while (!strlen($tmp) && count($tmp_locations)) { 
     202            $tmp_check = array_shift($tmp_locations); 
     203            if (@is_dir($tmp_check)) { 
     204                $tmp = $tmp_check; 
     205            } 
     206        } 
     207 
     208        /* If it is still empty, we have failed, so return false; otherwise 
     209         * return the directory determined. */ 
     210        return strlen($tmp) ? $tmp : false; 
     211    } 
     212 
     213    /** 
     214     * Checks a diff for validity. 
     215     * 
     216     * This is here only for debugging purposes. 
     217     */ 
     218    function _check($from_lines, $to_lines) 
     219    { 
     220        if (serialize($from_lines) != serialize($this->getOriginal())) { 
     221            trigger_error("Reconstructed original doesn't match", E_USER_ERROR); 
     222        } 
     223        if (serialize($to_lines) != serialize($this->getFinal())) { 
     224            trigger_error("Reconstructed final doesn't match", E_USER_ERROR); 
     225        } 
     226 
     227        $rev = $this->reverse(); 
     228        if (serialize($to_lines) != serialize($rev->getOriginal())) { 
     229            trigger_error("Reversed original doesn't match", E_USER_ERROR); 
     230        } 
     231        if (serialize($from_lines) != serialize($rev->getFinal())) { 
     232            trigger_error("Reversed final doesn't match", E_USER_ERROR); 
     233        } 
     234 
     235        $prevtype = null; 
     236        foreach ($this->_edits as $edit) { 
     237            if ($prevtype == get_class($edit)) { 
     238                trigger_error("Edit sequence is non-optimal", E_USER_ERROR); 
     239            } 
     240            $prevtype = get_class($edit); 
     241        } 
     242 
     243        return true; 
     244    } 
     245 
     246} 
     247 
     248/** 
     249 * @package Text_Diff 
     250 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     251 */ 
     252class Text_MappedDiff extends Text_Diff { 
     253 
     254    /** 
     255     * Computes a diff between sequences of strings. 
     256     * 
     257     * This can be used to compute things like case-insensitve diffs, or diffs 
     258     * which ignore changes in white-space. 
     259     * 
     260     * @param array $from_lines         An array of strings. 
     261     * @param array $to_lines           An array of strings. 
     262     * @param array $mapped_from_lines  This array should have the same size 
     263     *                                  number of elements as $from_lines.  The 
     264     *                                  elements in $mapped_from_lines and 
     265     *                                  $mapped_to_lines are what is actually 
     266     *                                  compared when computing the diff. 
     267     * @param array $mapped_to_lines    This array should have the same number 
     268     *                                  of elements as $to_lines. 
     269     */ 
     270    function Text_MappedDiff($from_lines, $to_lines, 
     271                             $mapped_from_lines, $mapped_to_lines) 
     272    { 
     273        assert(count($from_lines) == count($mapped_from_lines)); 
     274        assert(count($to_lines) == count($mapped_to_lines)); 
     275 
     276        parent::Text_Diff($mapped_from_lines, $mapped_to_lines); 
     277 
     278        $xi = $yi = 0; 
     279        for ($i = 0; $i < count($this->_edits); $i++) { 
     280            $orig = &$this->_edits[$i]->orig; 
     281            if (is_array($orig)) { 
     282                $orig = array_slice($from_lines, $xi, count($orig)); 
     283                $xi += count($orig); 
     284            } 
     285 
     286            $final = &$this->_edits[$i]->final; 
     287            if (is_array($final)) { 
     288                $final = array_slice($to_lines, $yi, count($final)); 
     289                $yi += count($final); 
     290            } 
     291        } 
     292    } 
     293 
     294} 
     295 
     296/** 
     297 * @package Text_Diff 
     298 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     299 * 
     300 * @access private 
     301 */ 
     302class Text_Diff_Op { 
     303 
     304    var $orig; 
     305    var $final; 
     306 
     307    function &reverse() 
     308    { 
     309        trigger_error('Abstract method', E_USER_ERROR); 
     310    } 
     311 
     312    function norig() 
     313    { 
     314        return $this->orig ? count($this->orig) : 0; 
     315    } 
     316 
     317    function nfinal() 
     318    { 
     319        return $this->final ? count($this->final) : 0; 
     320    } 
     321 
     322} 
     323 
     324/** 
     325 * @package Text_Diff 
     326 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     327 * 
     328 * @access private 
     329 */ 
     330class Text_Diff_Op_copy extends Text_Diff_Op { 
     331 
     332    function Text_Diff_Op_copy($orig, $final = false) 
     333    { 
     334        if (!is_array($final)) { 
     335            $final = $orig; 
     336        } 
     337        $this->orig = $orig; 
     338        $this->final = $final; 
     339    } 
     340 
     341    function &reverse() 
     342    { 
     343        $reverse = &new Text_Diff_Op_copy($this->final, $this->orig); 
     344        return $reverse; 
     345    } 
     346 
     347} 
     348 
     349/** 
     350 * @package Text_Diff 
     351 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     352 * 
     353 * @access private 
     354 */ 
     355class Text_Diff_Op_delete extends Text_Diff_Op { 
     356 
     357    function Text_Diff_Op_delete($lines) 
     358    { 
     359        $this->orig = $lines; 
     360        $this->final = false; 
     361    } 
     362 
     363    function &reverse() 
     364    { 
     365        $reverse = &new Text_Diff_Op_add($this->orig); 
     366        return $reverse; 
     367    } 
     368 
     369} 
     370 
     371/** 
     372 * @package Text_Diff 
     373 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     374 * 
     375 * @access private 
     376 */ 
     377class Text_Diff_Op_add extends Text_Diff_Op { 
     378 
     379    function Text_Diff_Op_add($lines) 
     380    { 
     381        $this->final = $lines; 
     382        $this->orig = false; 
     383    } 
     384 
     385    function &reverse() 
     386    { 
     387        $reverse = &new Text_Diff_Op_delete($this->final); 
     388        return $reverse; 
     389    } 
     390 
     391} 
     392 
     393/** 
     394 * @package Text_Diff 
     395 * @author  Geoffrey T. Dairiki <dairiki@dairiki.org> 
     396 * 
     397 * @access private 
     398 */ 
     399class Text_Diff_Op_change extends Text_Diff_Op { 
     400 
     401    function Text_Diff_Op_change($orig, $final) 
     402    { 
     403        $this->orig = $orig; 
     404        $this->final = $final; 
     405    } 
     406 
     407    function &reverse() 
     408    { 
     409        $reverse = &new Text_Diff_Op_change($this->final, $this->orig); 
     410        return $reverse; 
     411    } 
     412 
     413} 
  • wp-includes/pluggable.php

     
    13501350} 
    13511351endif; 
    13521352 
     1353if ( !function_exists( 'wp_text_diff' ) ) : 
     1354/** 
     1355 * wp_text_diff() - compares two strings and outputs a human readable HTML representation of their difference 
     1356 * 
     1357 * Basically a wrapper for man diff(1) 
     1358 * 
     1359 * Must accept an optional third parameter, $args @see wp_parse_args() 
     1360 *    (string) title: optional.  If present, titles the diff in a manner compatible with the output 
     1361 * 
     1362 * Must return the empty string if the two compared strings are found to be equivalent according to whatever metric 
     1363 * 
     1364 * @since 2.6 
     1365 * @uses Text_Diff 
     1366 * @uses WP_Text_Diff_Renderer_Table 
     1367 * 
     1368 * @param string $left_string "old" (left) version of string 
     1369 * @param string $right_string "new" (right) version of string 
     1370 * @param string|array $args @see wp_parse_args() 
     1371 * @return string human readable HTML of string differences.  Empty string if strings are equivalent 
     1372 */ 
     1373function wp_text_diff( $left_string, $right_string, $args = null ) { 
     1374        $defaults = array( 'title' => '' ); 
     1375        $args = wp_parse_args( $args, $defaults ); 
     1376 
     1377        // PEAR Text_Diff is lame; it includes things from include_path rather than it's own path. 
     1378        // Not sure of the ramifications of disttributing modified code. 
     1379        ini_set('include_path', '.' . PATH_SEPARATOR . ABSPATH . WPINC ); 
     1380 
     1381        if ( !class_exists( 'WP_Text_Diff_Renderer_Table' ) ) 
     1382                require( ABSPATH . WPINC . '/wp-diff.php' ); 
     1383 
     1384        // Normalize whitespace 
     1385        $left_string  = trim($left_string); 
     1386        $right_string = trim($right_string); 
     1387        $left_string  = str_replace("\r", "\n", $left_string); 
     1388        $right_string = str_replace("\r", "\n", $right_string); 
     1389        $left_string  = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $left_string ); 
     1390        $right_string = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $right_string ); 
     1391         
     1392        $left_lines  = split("\n", $left_string); 
     1393        $right_lines = split("\n", $right_string); 
     1394 
     1395        $text_diff = new Text_Diff($left_lines, $right_lines); 
     1396        $renderer  = new WP_Text_Diff_Renderer_Table(); 
     1397        $diff = $renderer->render($text_diff); 
     1398 
     1399        ini_restore('include_path'); 
     1400 
     1401        if ( !$diff ) 
     1402                return ''; 
     1403 
     1404        $r  = "<table class='diff'>\n"; 
     1405        $r .= "<col class='ltype' /><col class='content' /><col class='ltype' /><col class='content' />"; 
     1406 
     1407        if ( $args['title'] ) 
     1408                $r .= "<thead><tr><th colspan='4'>$args[title]</th></tr></thead>\n"; 
     1409 
     1410        $r .= "<tbody>\n$diff\n</tbody>\n"; 
     1411        $r .= "</table>"; 
     1412 
     1413        return $r; 
     1414} 
     1415endif; 
     1416 
    13531417?> 
  • wp-admin/admin-ajax.php

     
    460460        $x->send(); 
    461461        break; 
    462462case 'autosave' : // The name of this action is hardcoded in edit_post() 
     463        define( 'DOING_AUTOSAVE', true ); 
     464 
    463465        $nonce_age = check_ajax_referer( 'autosave', 'autosavenonce'); 
    464466        global $current_user; 
    465467 
  • wp-admin/wp-admin.css

     
    894894        margin-right: 5px 
    895895} 
    896896 
     897.form-table pre { 
     898        padding: 8px; 
     899        margin: 0; 
     900        /* http://www.longren.org/2006/09/27/wrapping-text-inside-pre-tags/ */ 
     901        white-space: pre-wrap; /* css-3 */ 
     902        white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ 
     903        white-space: -pre-wrap; /* Opera 4-6 */ 
     904        white-space: -o-pre-wrap; /* Opera 7 */ 
     905        word-wrap: break-word; /* Internet Explorer 5.5+ */ 
     906} 
     907 
     908table.form-table td .updated { 
     909        font-size: 13px; 
     910} 
     911 
    897912/* Post Screen */ 
    898913 
    899914#tagsdiv #newtag { 
     
    14641479.hide-if-no-js { 
    14651480        display: none; 
    14661481} 
     1482 
     1483/* Diff */ 
     1484 
     1485table.diff { 
     1486        width: 100%; 
     1487} 
     1488 
     1489table.diff col.content { 
     1490        width: 50%; 
     1491} 
     1492 
     1493table.diff tr { 
     1494        background-color: transparent; 
     1495} 
     1496 
     1497table.diff td, table.diff th { 
     1498        padding: .5em; 
     1499        font-family: monospace; 
     1500        border: none; 
     1501} 
     1502 
     1503table.diff .diff-deletedline del, table.diff .diff-addedline ins { 
     1504        text-decoration: none; 
     1505} 
  • wp-admin/post.php

     
    7777 
    7878        if ( empty($post->ID) ) wp_die( __("You attempted to edit a post that doesn't exist. Perhaps it was deleted?") ); 
    7979 
    80         if ( 'page' == $post->post_type ) { 
    81                 wp_redirect("page.php?action=edit&post=$post_ID"); 
     80        if ( 'post' != $post->post_type ) { 
     81                wp_redirect( get_edit_post_link( $post->ID, 'url' ) ); 
    8282                exit(); 
    8383        } 
    8484 
  • wp-admin/revision.php

     
     1<?php 
     2 
     3require_once('admin.php'); 
     4 
     5$parent_file = 'edit.php'; 
     6$submenu_file = 'edit.php'; 
     7 
     8wp_reset_vars(array('revision', 'diff', 'restore')); 
     9 
     10$revision_id = absint($revision); 
     11$diff        = absint($diff); 
     12 
     13if ( $diff ) { 
     14        $restore = false; 
     15        $revision = get_post( $revision_id ); 
     16        $post = 'revision' == $revision->post_type ? get_post( $revision->post_parent ) : get_post( $revision_id ); 
     17        $left_revision = get_post( $diff ); 
     18 
     19        // Don't allow reverse diffs? 
     20        if ( strtotime($revision->post_modified_gmt) < strtotime($left_revision->post_modified_gmt) ) { 
     21                wp_redirect( add_query_arg( array( 'diff' => $revision->ID, 'revision' => $diff ) ) ); 
     22                exit; 
     23        } 
     24 
     25        $h2 = __( 'Compare Revisions of &#8220;%1$s&#8221;' ); 
     26        $right = $revision->ID; 
     27        $left  = $left_revision->ID; 
     28 
     29        if ( 
     30                // They're the same 
     31                $left_revision->ID == $revision->ID 
     32        || 
     33                // They don't have a comment parent (and we're not comparing a revision to it's post) 
     34                ( $left_revision->ID != $revision->post_parent && $left_revision->post_parent != $revision->ID && $left_revision->post_parent != $revision->post_parent ) 
     35        || 
     36                // Neither is a revision 
     37                ( !wp_get_revision( $left_revision->ID ) && !wp_get_revision( $revision->ID ) ) 
     38        ) { 
     39                wp_redirect( get_edit_post_link( $revision->ID, 'url' ) ); 
     40                exit(); 
     41        } 
     42} else { 
     43        $revision = wp_get_revision( $revision_id ); 
     44        $post = get_post( $revision->post_parent ); 
     45        $h2 = __( 'Post Revision for &#8220;%1$s&#8221; created on %2$s' ); 
     46        $right = $post->ID; 
     47        $left  = $revision->ID; 
     48} 
     49 
     50if ( !$revision || !$post ) { 
     51        wp_redirect("edit.php"); 
     52        exit(); 
     53} 
     54 
     55if ( $restore && current_user_can( 'edit_post', $revision->post_parent ) ) { 
     56        check_admin_referer( "restore-post_$post->ID|$revision->ID" ); 
     57        wp_restore_revision( $revision->ID ); 
     58        wp_redirect( add_query_arg( array( 'message' => 5, 'revision' => $revision->ID ), get_edit_post_link( $post->ID, 'url' ) ) ); 
     59        exit; 
     60} 
     61 
     62add_filter( '_wp_revision_field_post_author', 'get_author_name' ); 
     63 
     64$title = __( 'Post Revision' ); 
     65 
     66require_once( 'admin-header.php' ); 
     67 
     68$post_title = '<a href="' . get_edit_post_link() . '">' . get_the_title() . '</a>'; 
     69$revision_time = wp_post_revision_time( $revision ); 
     70?> 
     71 
     72<div class="wrap"> 
     73 
     74<h2 style="padding-right: 0"><?php printf( $h2, $post_title, $revision_time ); ?></h2> 
     75 
     76<table class="form-table ie-fixed"> 
     77        <col class="th" /> 
     78<?php if ( $diff ) : ?> 
     79 
     80<tr id="revision"> 
     81        <th scope="row"></th> 
     82        <th scope="col" class="th-full"><?php printf( __('Older: %s'), wp_post_revision_time( $left_revision ) ); ?></td> 
     83        <th scope="col" class="th-full"><?php printf( __('Newer: %s'), wp_post_revision_time( $revision ) ); ?></td> 
     84</tr> 
     85 
     86<?php endif; 
     87 
     88// use get_post_to_edit ?  
     89$identical = true; 
     90foreach ( _wp_revision_fields() as $field => $field_title ) : 
     91        if ( !$diff ) 
     92                add_filter( "_wp_revision_field_$field", 'htmlspecialchars' ); 
     93        $content = apply_filters( "_wp_revision_field_$field", $revision->$field, $field ); 
     94        if ( $diff ) { 
     95                $left_content = apply_filters( "_wp_revision_field_$field", $left_revision->$field, $field ); 
     96                if ( !$content = wp_text_diff( $left_content, $content ) ) 
     97                        continue; 
     98        } 
     99        $identical = false; 
     100        ?> 
     101 
     102        <tr id="revision-field-<?php echo $field; ?>"?> 
     103                <th scope="row"><?php echo wp_specialchars( $field_title ); ?></th> 
     104                <td colspan="2"><pre><?php echo $content; ?></pre></td> 
     105        </tr> 
     106 
     107        <?php 
     108 
     109endforeach; 
     110 
     111if ( $diff && $identical ) : 
     112 
     113        ?> 
     114 
     115        <tr><td colspan="3"><div class="updated"><p><?php _e( 'These revisions are identical' ); ?></p></div></td></tr> 
     116 
     117        <?php 
     118 
     119endif; 
     120 
     121?> 
     122 
     123</table> 
     124 
     125<br class="clear" /> 
     126 
     127<h2><?php _e( 'Post Revisions' ); ?></h2> 
     128 
     129<?php 
     130        wp_list_post_revisions( $post, array( 'format' => 'form-table', 'exclude' => $revision->ID, 'parent' => true, 'right' => $right, 'left' => $left ) ); 
     131 
     132        require_once( 'admin-footer.php' ); 
  • wp-admin/edit-form-advanced.php

     
    66$messages[2] = __('Custom field updated.'); 
    77$messages[3] = __('Custom field deleted.'); 
    88$messages[4] = __('Post updated.'); 
     9$messages[5] = sprintf( __('Post restored to revision from %s'), wp_post_revision_time( $_GET['revision'] ) ); 
    910?> 
    1011<?php if (isset($_GET['message'])) : ?> 
    1112<div id="message" class="updated fade"><p><?php echo $messages[$_GET['message']]; ?></p></div> 
     
    336337</div> 
    337338<?php endif; ?> 
    338339 
     340<?php if ( isset($post_ID) && 0 < $post_ID && wp_get_post_revisions( $post_ID ) ) : ?> 
     341<div id="revisionsdiv" class="postbox <?php echo postbox_classes('revisionsdiv', 'post'); ?>"> 
     342<h3><?php _e('Post Revisions'); ?></h3> 
     343<div class="inside"> 
     344<?php wp_list_post_revisions(); ?> 
     345</div> 
     346</div> 
     347<?php endif; ?> 
     348 
    339349<?php do_meta_boxes('post', 'advanced', $post); ?> 
    340350 
    341351<?php do_action('dbx_post_sidebar'); ?> 
  • wp-admin/css/ie.css

     
    111111.tablenav-pages { 
    112112        display: block; 
    113113        margin-top: -3px; 
     114} 
    114115 
     116table.ie-fixed { 
     117        table-layout: fixed; 
    115118} 
    116119 
    117120#post-search .button, #widget-search .button { 
  • wp-admin/css/colors-fresh.css

     
    22        border-color: #999; 
    33} 
    44 
    5 body    { 
     5body, .form-table pre { 
    66        background-color: #fff; 
    77        color: #333; 
    88} 
     
    684684        background-color: #ddd; 
    685685        color: #333; 
    686686} 
     687 
     688/* Diff */ 
     689 
     690table.diff .diff-deletedline { 
     691        background-color: #ffdddd; 
     692} 
     693table.diff .diff-deletedline del { 
     694        background-color: #ff9999; 
     695} 
     696table.diff .diff-addedline { 
     697        background-color: #ddffdd; 
     698} 
     699table.diff .diff-addedline ins { 
     700        background-color: #99ff99; 
     701} 
  • wp-admin/css/colors-classic.css

     
    22        border-color: #999; 
    33} 
    44 
    5 body    { 
     5body, .form-table pre { 
    66        background-color: #fff; 
    77        color: #333; 
    88} 
     
    713713        background-color: #ddd; 
    714714        color: #333; 
    715715} 
     716 
     717/* Diff */ 
     718 
     719table.diff .diff-deletedline { 
     720        background-color: #ffdddd; 
     721} 
     722table.diff .diff-deletedline del { 
     723        background-color: #ff9999; 
     724} 
     725table.diff .diff-addedline { 
     726        background-color: #ddffdd; 
     727} 
     728table.diff .diff-addedline ins { 
     729        background-color: #99ff99; 
     730} 
  • wp-admin/page.php

     
    7070 
    7171        if ( empty($post->ID) ) wp_die( __("You attempted to edit a page that doesn't exist. Perhaps it was deleted?") ); 
    7272 
    73         if ( 'post' == $post->post_type ) { 
    74                 wp_redirect("post.php?action=edit&post=$post_ID"); 
     73        if ( 'page' != $post->post_type ) { 
     74                wp_redirect( get_edit_post_link( $post_ID, 'url' ) ); 
    7575                exit(); 
    7676        } 
    7777