Ticket #6775: 6775.diff
File 6775.diff, 91.2 KB (added by , 17 years ago) |
---|
-
wp-includes/default-filters.php
178 178 add_action('init', 'smilies_init', 5); 179 179 add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 ); 180 180 add_action( 'shutdown', 'wp_ob_end_flush_all', 1); 181 add_action( 'pre_post_update', 'wp_save_revision' ); 181 182 add_action('publish_post', '_publish_post_hook', 5, 1); 182 183 add_action('future_post', '_future_post_hook', 5, 2); 183 184 add_action('future_page', '_future_post_hook', 5, 2); -
wp-includes/post-template.php
564 564 return false; 565 565 } 566 566 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 */ 580 function 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 */ 614 function 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
2990 2990 } 2991 2991 } 2992 2992 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 */ 3009 function _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 */ 3055 function 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 */ 3082 function _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 */ 3116 function &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 */ 3152 function 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 */ 3190 function 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 */ 3212 function 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 2993 3221 ?> -
wp-includes/wp-diff.php
1 <?php 2 3 if ( !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 15 class 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"> </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 307 class 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
442 442 return $link; 443 443 } 444 444 445 function get_edit_post_link( $id = 0 ) {445 function get_edit_post_link( $id = 0, $context = 'display' ) { 446 446 if ( !$post = &get_post( $id ) ) 447 447 return; 448 448 449 if ( 'display' == $context ) 450 $action = 'action=edit&'; 451 else 452 $action = 'action=edit&'; 453 449 454 switch ( $post->post_type ) : 450 455 case 'page' : 451 456 if ( !current_user_can( 'edit_page', $post->ID ) ) … … 459 464 $file = 'media'; 460 465 $var = 'attachment_id'; 461 466 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; 462 474 default : 463 475 if ( !current_user_can( 'edit_post', $post->ID ) ) 464 476 return; … … 467 479 break; 468 480 endswitch; 469 481 470 return apply_filters( 'get_edit_post_link', get_bloginfo( 'wpurl' ) . "/wp-admin/$file.php? action=edit&$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 ); 471 483 } 472 484 473 485 function 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 */ 18 class 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 */ 30 class 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 */ 25 class 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 */ 19 class 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 */ 17 require_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 */ 27 class 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 */ 17 class 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 */ 20 class 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 */ 252 class 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 */ 302 class 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 */ 330 class 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 */ 355 class 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 */ 377 class 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 */ 399 class 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
1350 1350 } 1351 1351 endif; 1352 1352 1353 if ( !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 */ 1373 function 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 } 1415 endif; 1416 1353 1417 ?> -
wp-admin/admin-ajax.php
460 460 $x->send(); 461 461 break; 462 462 case 'autosave' : // The name of this action is hardcoded in edit_post() 463 define( 'DOING_AUTOSAVE', true ); 464 463 465 $nonce_age = check_ajax_referer( 'autosave', 'autosavenonce'); 464 466 global $current_user; 465 467 -
wp-admin/wp-admin.css
894 894 margin-right: 5px 895 895 } 896 896 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 908 table.form-table td .updated { 909 font-size: 13px; 910 } 911 897 912 /* Post Screen */ 898 913 899 914 #tagsdiv #newtag { … … 1464 1479 .hide-if-no-js { 1465 1480 display: none; 1466 1481 } 1482 1483 /* Diff */ 1484 1485 table.diff { 1486 width: 100%; 1487 } 1488 1489 table.diff col.content { 1490 width: 50%; 1491 } 1492 1493 table.diff tr { 1494 background-color: transparent; 1495 } 1496 1497 table.diff td, table.diff th { 1498 padding: .5em; 1499 font-family: monospace; 1500 border: none; 1501 } 1502 1503 table.diff .diff-deletedline del, table.diff .diff-addedline ins { 1504 text-decoration: none; 1505 } -
wp-admin/post.php
77 77 78 78 if ( empty($post->ID) ) wp_die( __("You attempted to edit a post that doesn't exist. Perhaps it was deleted?") ); 79 79 80 if ( 'p age' == $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' ) ); 82 82 exit(); 83 83 } 84 84 -
wp-admin/revision.php
1 <?php 2 3 require_once('admin.php'); 4 5 $parent_file = 'edit.php'; 6 $submenu_file = 'edit.php'; 7 8 wp_reset_vars(array('revision', 'diff', 'restore')); 9 10 $revision_id = absint($revision); 11 $diff = absint($diff); 12 13 if ( $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 “%1$s”' ); 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 “%1$s” created on %2$s' ); 46 $right = $post->ID; 47 $left = $revision->ID; 48 } 49 50 if ( !$revision || !$post ) { 51 wp_redirect("edit.php"); 52 exit(); 53 } 54 55 if ( $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 62 add_filter( '_wp_revision_field_post_author', 'get_author_name' ); 63 64 $title = __( 'Post Revision' ); 65 66 require_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; 90 foreach ( _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 109 endforeach; 110 111 if ( $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 119 endif; 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
6 6 $messages[2] = __('Custom field updated.'); 7 7 $messages[3] = __('Custom field deleted.'); 8 8 $messages[4] = __('Post updated.'); 9 $messages[5] = sprintf( __('Post restored to revision from %s'), wp_post_revision_time( $_GET['revision'] ) ); 9 10 ?> 10 11 <?php if (isset($_GET['message'])) : ?> 11 12 <div id="message" class="updated fade"><p><?php echo $messages[$_GET['message']]; ?></p></div> … … 336 337 </div> 337 338 <?php endif; ?> 338 339 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 339 349 <?php do_meta_boxes('post', 'advanced', $post); ?> 340 350 341 351 <?php do_action('dbx_post_sidebar'); ?> -
wp-admin/css/ie.css
111 111 .tablenav-pages { 112 112 display: block; 113 113 margin-top: -3px; 114 } 114 115 116 table.ie-fixed { 117 table-layout: fixed; 115 118 } 116 119 117 120 #post-search .button, #widget-search .button { -
wp-admin/css/colors-fresh.css
2 2 border-color: #999; 3 3 } 4 4 5 body 5 body, .form-table pre { 6 6 background-color: #fff; 7 7 color: #333; 8 8 } … … 684 684 background-color: #ddd; 685 685 color: #333; 686 686 } 687 688 /* Diff */ 689 690 table.diff .diff-deletedline { 691 background-color: #ffdddd; 692 } 693 table.diff .diff-deletedline del { 694 background-color: #ff9999; 695 } 696 table.diff .diff-addedline { 697 background-color: #ddffdd; 698 } 699 table.diff .diff-addedline ins { 700 background-color: #99ff99; 701 } -
wp-admin/css/colors-classic.css
2 2 border-color: #999; 3 3 } 4 4 5 body 5 body, .form-table pre { 6 6 background-color: #fff; 7 7 color: #333; 8 8 } … … 713 713 background-color: #ddd; 714 714 color: #333; 715 715 } 716 717 /* Diff */ 718 719 table.diff .diff-deletedline { 720 background-color: #ffdddd; 721 } 722 table.diff .diff-deletedline del { 723 background-color: #ff9999; 724 } 725 table.diff .diff-addedline { 726 background-color: #ddffdd; 727 } 728 table.diff .diff-addedline ins { 729 background-color: #99ff99; 730 } -
wp-admin/page.php
70 70 71 71 if ( empty($post->ID) ) wp_die( __("You attempted to edit a page that doesn't exist. Perhaps it was deleted?") ); 72 72 73 if ( 'p ost' == $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' ) ); 75 75 exit(); 76 76 } 77 77