1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8//this script may only be included - so its better to die if called directly. 9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) { 10 header("location: index.php"); 11 exit; 12} 13 14require_once(__DIR__ . "/Diff.php"); 15require_once(__DIR__ . "/Renderer.php"); 16 17/* @brief modif tiki for the renderer lib */ 18class Tiki_Text_Diff_Renderer extends Text_Diff_Renderer 19{ 20 protected function _lines($lines, $prefix = '', $suffix = '', $type = '') 21 { 22//ADD $suffix 23 foreach ($lines as $line) { 24 echo "$prefix$line$suffix\n"; 25 } 26 } 27 public function render($diff, $singleEdit = false) 28 { 29 $x0 = $y0 = 0; 30 $xi = $yi = 1; 31 $block = false; 32 $context = []; 33 34 $nlead = $this->_leading_context_lines; 35 $ntrail = $this->_trailing_context_lines; 36 37 $this->_startDiff(); 38 39 if (! $singleEdit) { 40 $diff = $diff->getDiff(); 41 } 42 43 foreach ($diff as $edit) { 44 if (is_a($edit, 'Text_Diff_Op_copy')) { 45 if (is_array($block)) { 46 if (count($edit->orig) <= $nlead + $ntrail) { 47 $block[] = $edit; 48 } else { 49 if ($ntrail) { 50 $context = array_slice($edit->orig, 0, $ntrail); 51 $block[] = new Text_Diff_Op_copy($context); 52 } 53 $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); 54 $block = false; 55 } 56 } 57 $context = $edit->orig; 58 } else { 59 if (! is_array($block)) { 60 //BUG if compare on all the length: $context = array_slice($context, count($context) - $nlead); 61 $context = array_slice($context, -$nlead, $nlead); 62 $x0 = $xi - count($context); 63 $y0 = $yi - count($context); 64 $block = []; 65 if ($context) { 66 $block[] = new Text_Diff_Op_copy($context); 67 } 68 } 69 $block[] = $edit; 70 } 71 72 if ($edit->orig) { 73 $xi += count($edit->orig); 74 } 75 if ($edit->final) { 76 $yi += count($edit->final); 77 } 78 } 79 80 if (is_array($block)) { 81 $this->_block($x0, $xi - $x0, $y0, $yi - $y0, $block); 82 } 83 84 return $this->_endDiff(); 85 } 86} 87 88function diff2($page1, $page2, $type = 'sidediff') 89{ 90 global $tikilib, $prefs; 91 if ($type == 'htmldiff') { 92 //$search = "#(<[^>]+>|\s*[^\s<]+\s*|</[^>]+>)#"; 93 $search = "#(<[^>]+>|[,\"':\s]+|[^\s,\"':<]+|</[^>]+>)#"; 94 preg_match_all($search, $page1, $out, PREG_PATTERN_ORDER); 95 $page1 = $out[0]; 96 preg_match_all($search, $page2, $out, PREG_PATTERN_ORDER); 97 $page2 = $out[0]; 98 } else { 99 $page1 = explode("\n", $page1); 100 $page2 = explode("\n", $page2); 101 } 102 $z = new Text_Diff($page1, $page2); 103 if ($z->isEmpty()) { 104 $html = ''; 105 } else { 106 $context = 2; 107 $words = 1; 108 if (strstr($type, "-")) { 109 list($type,$opt) = explode("-", $type, 2); 110 if (strstr($opt, "full")) { 111 $context = count($page1); 112 } 113 if (strstr($opt, "char")) { 114 $words = 0; 115 } 116 } 117 118 if ($type == 'unidiff') { 119 require_once('renderer_unified.php'); 120 $renderer = new Text_Diff_Renderer_unified($context); 121 } elseif ($type == 'inlinediff') { 122 require_once('renderer_inline.php'); 123 $renderer = new Text_Diff_Renderer_inline($context, $words); 124 } elseif ($type == 'sidediff') { 125 require_once('renderer_sidebyside.php'); 126 $renderer = new Text_Diff_Renderer_sidebyside($context, $words); 127 } elseif ($type == 'bytes' && $prefs['feature_actionlog_bytes'] == 'y') { 128 require_once('renderer_bytes.php'); 129 $renderer = new Text_Diff_Renderer_bytes(); 130 } elseif ($type == 'htmldiff') { 131 require_once('renderer_htmldiff.php'); 132 $renderer = new Text_Diff_Renderer_htmldiff($context, $words); 133 } else { 134 return ""; 135 } 136 $html = $renderer->render($z); 137 } 138 return $html; 139} 140 141/* @brief compute the characters differences between a list of lines 142 * @param $orig array list lines in the original version 143 * @param $final array the same lines in the final version 144 * @param int $words 145 * @param string $function 146 * @return array 147 */ 148function diffChar($orig, $final, $words = 0, $function = 'character') 149{ 150 $glue = strpos($function, 'inline') !== false ? "<br />" : "\n"; 151 if ($words) { 152 preg_match_all("/\w+\s+(?=\w)|\w+|\W/u", implode($glue, $orig), $matches); 153 $line1 = $matches[0]; 154 preg_match_all("/\w+\s+(?=\w)|\w+|\W/u", implode($glue, $final), $matches); 155 $line2 = $matches[0]; 156 } else { 157 $line1 = preg_split('//u', implode($glue, $orig), -1, PREG_SPLIT_NO_EMPTY); 158 $line2 = preg_split('//u', implode($glue, $final), -1, PREG_SPLIT_NO_EMPTY); 159 } 160 $z = new Text_Diff($line1, $line2); 161 if ($z->isEmpty()) { 162 return [$orig[0], $final[0]]; 163 } 164//echo "<pre>";print_r($z);echo "</pre>"; 165 166 compileRendererClass($function); 167 $new = "Text_Diff_Renderer_$function"; 168 $renderer = new $new(count($line1)); 169 return $renderer->render($z); 170} 171 172function compileRendererClass($function) 173{ 174 /* 175 * The various subclasses of Text_Diff_Renderer have methods whose signatures are incompatible 176 * with those of their parents. This raises some warnings which don't matter in production settings. 177 * 178 * But when running phpunit tests, this causes some failures, because we have configured phpunit 179 * to report warnings as failures. 180 * 181 * Making the methods compatible with each other would be very involved, and might introduce some 182 * actual bugs. So instead, temporarily disable warning reporting, just for the compilation of 183 * this file. 184 */ 185 global $old_error_reporting_level; 186 if (defined('TIKI_IN_TEST')) { 187 $old_error_reporting_level = error_reporting(E_ERROR | E_PARSE); 188 } 189 190 require_once("renderer_$function.php"); 191 192 if (defined('TIKI_IN_TEST')) { 193 error_reporting($old_error_reporting_level); 194 } 195} 196 197/** 198 * Find mentions 199 * 200 * @param $lines 201 * @param $state 202 * @return array 203 */ 204function findMentions($lines, $state) 205{ 206 $allMatches = [] ; 207 208 if (isset($lines) && is_array($lines)) { 209 foreach (array_filter($lines) as $line) { 210 preg_match_all("/(?:^|\s)@(\w+)/i", $line, $matches); 211 foreach ($matches[0] as $match) { 212 $allMatches[] = [ 213 'state' => $state, 214 'mention' => trim($match) 215 ]; 216 } 217 } 218 } 219 220 return $allMatches; 221} 222 223/** 224 * Find mentions on change content 225 * 226 * @param $edit 227 * @return array 228 */ 229function findMentionsOnChange($edit) 230{ 231 $allMatches = []; 232 233 if ((isset($edit->orig) && is_array($edit->orig)) && (isset($edit->final) && is_array($edit->final))) { 234 if (empty($edit->orig[0])) { 235 $mentions = findMentions($edit->final, 'new'); 236 foreach ($mentions as $m) { 237 $allMatches[] = $m; 238 } 239 } else { 240 require_once('renderer_inline.php'); 241 $renderer = new Text_Diff_Renderer_inline(1); 242 $html = $renderer->render([$edit], true); 243 244 // remove unnecessary content 245 $html = preg_replace("#<tr class=\"diffheader\">(.*?)</tr>#", "", $html); 246 $html = preg_replace("#<span class='diffinldel'>(.*?)</span>#", "", $html); 247 $html = str_replace(["<tr class='diffbody'>", "</tr>", "<td colspan='3'>", "</td>"], "", $html); 248 $html = str_replace(["<span class='diffadded'>", "</span>"], "<ins>", $html); 249 $finalContent = explode('<ins>', $html); 250 251 $index = 0; 252 foreach ($finalContent as $key => $value) { 253 if (($index % 2) == 1) { 254 // new mention 255 $charToAdd = ''; 256 $previousMention = $finalContent[$key - 1]; 257 if (! empty($previousMention)) { 258 $lastChar = substr($previousMention, -1); 259 if ($lastChar == '@') { 260 $charToAdd = '@'; 261 } 262 } 263 264 $mentions = findMentions([$charToAdd . $value], 'new'); 265 } else { 266 // old mention 267 $mentions = findMentions([$value], 'old'); 268 } 269 270 foreach ($mentions as $m) { 271 $allMatches[] = $m; 272 } 273 $index++; 274 } 275 } 276 } 277 278 return $allMatches; 279} 280