1<?php 2 3namespace dokuwiki\Ui; 4 5/** 6 * DokuWiki Diff Interface 7 * parent class of PageDiff and MediaDiff 8 * 9 * @package dokuwiki\Ui 10 */ 11abstract class Diff extends Ui 12{ 13 /* @var string */ 14 protected $id; // page id or media id 15 protected $item; // page or media 16 17 /* @var int */ 18 protected $old_rev; // older revision, timestamp of left side 19 protected $new_rev; // newer revision, timestamp of right side 20 protected $last_rev; // current revision, or last revision when it had removed 21 22 /* @var array */ 23 protected $preference = []; 24 25 /* @var ChangeLog */ 26 protected $changelog; // PageChangeLog or MediaChangeLog object 27 28 /** 29 * Diff Ui constructor 30 * 31 * @param string $id page id or media id 32 */ 33 public function __construct($id) 34 { 35 $this->id = $id; 36 $this->setChangeLog(); 37 } 38 39 /** 40 * set class property changelog 41 */ 42 abstract protected function setChangeLog(); 43 44 /** 45 * Set a pair of revisions to be compared 46 * 47 * @param int $old_rev 48 * @param int $new_rev 49 * @return $this 50 */ 51 public function compare($old_rev, $new_rev) 52 { 53 $this->old_rev = $old_rev; 54 $this->new_rev = $new_rev; 55 return $this; 56 } 57 58 /** 59 * Gets or Sets preference of the Ui\Diff object 60 * 61 * @param string|array $prefs a key name or key-value pair(s) 62 * @param mixed $value value used when the first args is string 63 * @return array|$this 64 */ 65 public function preference($prefs = null, $value = null) 66 { 67 // set 68 if (is_string($prefs) && isset($value)) { 69 $this->preference[$prefs] = $value; 70 return $this; 71 } elseif (is_array($prefs)) { 72 foreach ($prefs as $name => $value) { 73 $this->preference[$name] = $value; 74 } 75 return $this; 76 } 77 // get 78 return $this->preference; 79 } 80 81 /** 82 * Retrieve requested revision(s) and difftype from Ui\Revisions 83 * 84 * @return void 85 */ 86 protected function preProcess() 87 { 88 global $INPUT; 89 90 // difflink icon click, eg. ?rev=123456789&do=diff 91 if ($INPUT->has('rev')) { 92 $this->old_rev = $INPUT->int('rev'); 93 $this->new_rev = ''; // current revision 94 } 95 96 // submit button with two checked boxes 97 $rev2 = $INPUT->arr('rev2', []); 98 if (count($rev2) > 1) { 99 if ($rev2[0] == 'current') { 100 [$this->old_rev, $this->new_rev] = [$rev2[1], '']; 101 } elseif ($rev2[1] == 'current') { 102 [$this->old_rev, $this->new_rev] = [$rev2[0], '']; 103 } elseif ($rev2[0] < $rev2[1]) { 104 [$this->old_rev, $this->new_rev] = [$rev2[0], $rev2[1]]; 105 } else { 106 [$this->old_rev, $this->new_rev] = [$rev2[1], $rev2[0]]; 107 } 108 } 109 110 // diff view type 111 if ($INPUT->has('difftype')) { 112 // retrieve requested $difftype 113 $this->preference['difftype'] = $INPUT->str('difftype'); 114 } else { 115 // read preference from DokuWiki cookie. PageDiff only 116 get_doku_pref('difftype', $mode); 117 if (isset($mode)) $this->preference['difftype'] = $mode; 118 } 119 } 120 121 /** 122 * get extended revision info 123 * 124 * @param int|string $rev revision identifier, '' means current one 125 * @return array revision info structure of a page or media file 126 */ 127 protected function getExtendedRevisionInfo($rev) 128 { 129 $changelog =& $this->changelog; 130 131 if ($rev) { 132 $info = $changelog->getRevisionInfo($rev); 133 } elseif (file_exists($filename = $this->itemFN($this->id))) { 134 $rev = filemtime(fullpath($filename)); 135 $info = $changelog->getRevisionInfo($rev) + array( 136 'current' => true, 137 ); 138 } else { // once exists, but now removed 139 $info = array( 140 'current' => true, 141 ); 142 } 143 return array('item' => $this->item) + $info; 144 } 145 146 147 148 /** 149 * Build header of diff HTML 150 * 151 * @param string $l_rev Left revisions 152 * @param string $r_rev Right revision 153 * @return string[] HTML snippets for diff header 154 */ 155 public function buildDiffHead($l_rev, $r_rev) 156 { 157 global $lang; 158 159 $changelog =& $this->changelog; 160 161 switch ($this->item) { 162 case 'page': 163 $isMedia = false; 164 $ui = new PageRevisions($this->id); 165 break; 166 case 'media': 167 $isMedia = true; 168 $ui = new MediaRevisions($this->id); 169 break; 170 } 171 172 $head_separator = ($this->preference['difftype'] === 'inline') ? ' ' : '<br />'; 173 174 // assign minor edit checker to the variable 175 $isMinorEdit = function ($info) { 176 return ($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT); 177 }; 178 179 // assign title builder to the variable 180 $itemTitle = function ($id, $rev = '') use ($isMedia) { 181 return ($isMedia) ? dformat($rev) : $id.' ['.dformat($rev).']'; 182 }; 183 184 // left side 185 if (!$l_rev) { 186 $l_minor = ''; 187 $l_head = '—'; 188 } else { 189 $info = $changelog->getRevisionInfo($l_rev); 190 $objRevInfo = $ui->getObjRevInfo($info); 191 $l_minor = $isMinorEdit($info) ? ' class="minor"' : ''; 192 $l_head = '<bdi>' 193 .'<a class="wikilink1" href="'.$this->itemUrl($this->id, "rev=$l_rev").'">' 194 .$itemTitle($this->id, $l_rev).'</a></bdi>'.$head_separator 195 .$objRevInfo->editor().' '.$objRevInfo->editSummary(); 196 } 197 198 // right side 199 if ($r_rev) { 200 $info = $changelog->getRevisionInfo($r_rev); 201 $objRevInfo = $ui->getObjRevInfo($info); 202 $r_minor = $isMinorEdit($info) ? ' class="minor"' : ''; 203 $r_head = '<bdi>' 204 .'<a class="wikilink1" href="'.$this->itemUrl($this->id, "rev=$r_rev").'">' 205 .$itemTitle($this->id, $r_rev).'</a></bdi>'.$head_separator 206 .$objRevInfo->editor().' '.$objRevInfo->editSummary(); 207 } elseif ($this->last_rev) { 208 $_rev = $this->last_rev; 209 $info = $changelog->getRevisionInfo($_rev); 210 $objRevInfo = $ui->getObjRevInfo($info); 211 $r_minor = $isMinorEdit($info) ? ' class="minor"' : ''; 212 $r_head = '<bdi>' 213 .'<a class="wikilink1" href="'.$this->itemUrl($this->id).'">' 214 .$itemTitle($this->id, $_rev).'</a></bdi> '.'('.$lang['current'].')'.$head_separator 215 .$objRevInfo->editor().' '.$objRevInfo->editSummary(); 216 } else { 217 $r_minor = ''; 218 $r_head = '— ('.$lang['current'].')'; 219 } 220 221 return array($l_head, $r_head, $l_minor, $r_minor); 222 } 223 224 /** 225 * item url generator 226 * 227 * @param string $id page id or media id 228 * @param string|array $urlParameters URL parameters, associative array recommended 229 * @return string 230 */ 231 protected function itemUrl($id, $urlParameters = '') 232 { 233 switch ($this->item) { 234 case 'page': return wl($id, $urlParameters, $absolute = false, '&'); 235 case 'media': return ml($id, $urlParameters, $direct = true, '&', $absolute = false); 236 } 237 } 238 239 /** 240 * item filename resolver 241 * 242 * @param string $id page id or media id 243 * @param string|int $rev empty string or revision timestamp 244 * @return string 245 */ 246 protected function itemFN($id, $rev = '') 247 { 248 switch ($this->item) { 249 case 'page': return wikiFN($id, $rev); 250 case 'media': return mediaFN($id, $rev); 251 } 252 } 253 254} 255