Make WordPress Core

Ticket #6775: 6775.diff

File 6775.diff, 91.2 KB (added by mdawaffe, 17 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