RevisionInfo.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <?php
  2. namespace dokuwiki\ChangeLog;
  3. /**
  4. * Class RevisionInfo
  5. *
  6. * Provides methods to show Revision Information in DokuWiki Ui components:
  7. * - Ui\Recent
  8. * - Ui\PageRevisions
  9. * - Ui\MediaRevisions
  10. */
  11. class RevisionInfo
  12. {
  13. /* @var array */
  14. protected $info;
  15. /**
  16. * Constructor
  17. *
  18. * @param array $info Revision Information structure with entries:
  19. * - date: unix timestamp
  20. * - ip: IPv4 or IPv6 address
  21. * - type: change type (log line type)
  22. * - id: page id
  23. * - user: user name
  24. * - sum: edit summary (or action reason)
  25. * - extra: extra data (varies by line type)
  26. * - sizechange: change of filesize
  27. * additionally,
  28. * - current: (optional) whether current revision or not
  29. * - timestamp: (optional) set only when external edits occurred
  30. * - mode: (internal use) ether "media" or "page"
  31. */
  32. public function __construct($info = null)
  33. {
  34. if (is_array($info) && isset($info['id'])) {
  35. // define strategy context
  36. $info['mode'] = $info['media'] ? 'media' : 'page';
  37. } else {
  38. $info = [
  39. 'mode' => 'page',
  40. 'date' => false,
  41. ];
  42. }
  43. $this->info = $info;
  44. }
  45. /**
  46. * Set or return whether this revision is current page or media file
  47. *
  48. * This method does not check exactly whether the revision is current or not. Instead,
  49. * set value of associated "current" key for internal use. Some UI element like diff
  50. * link button depend on relation to current page or media file. A changelog line does
  51. * not indicate whether it corresponds to current page or media file.
  52. *
  53. * @param bool $value true if the revision is current, otherwise false
  54. * @return bool
  55. */
  56. public function isCurrent($value = null)
  57. {
  58. return (bool) $this->val('current', $value);
  59. }
  60. /**
  61. * Return or set a value of associated key of revision information
  62. * but does not allow to change values of existing keys
  63. *
  64. * @param string $key
  65. * @param mixed $value
  66. * @return string|null
  67. */
  68. public function val($key, $value = null)
  69. {
  70. if (isset($value) && !array_key_exists($key, $this->info)) {
  71. // setter, only for new keys
  72. $this->info[$key] = $value;
  73. }
  74. if (array_key_exists($key, $this->info)) {
  75. // getter
  76. return $this->info[$key];
  77. }
  78. return null;
  79. }
  80. /**
  81. * Set extra key-value to the revision information
  82. * but does not allow to change values of existing keys
  83. * @param array $info
  84. * @return void
  85. */
  86. public function append(array $info)
  87. {
  88. foreach ($info as $key => $value) {
  89. $this->val($key, $value);
  90. }
  91. }
  92. /**
  93. * file icon of the page or media file
  94. * used in [Ui\recent]
  95. *
  96. * @return string
  97. */
  98. public function showFileIcon()
  99. {
  100. $id = $this->val('id');
  101. switch ($this->val('mode')) {
  102. case 'media': // media file revision
  103. return media_printicon($id);
  104. case 'page': // page revision
  105. return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />';
  106. }
  107. }
  108. /**
  109. * edit date and time of the page or media file
  110. * used in [Ui\recent, Ui\Revisions]
  111. *
  112. * @param bool $checkTimestamp enable timestamp check, alter formatted string when timestamp is false
  113. * @return string
  114. */
  115. public function showEditDate($checkTimestamp = false)
  116. {
  117. $formatted = dformat($this->val('date'));
  118. if ($checkTimestamp && $this->val('timestamp') === false) {
  119. // exact date is unknown for externally deleted file
  120. // when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__"
  121. $formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted);
  122. }
  123. return '<span class="date">'. $formatted .'</span>';
  124. }
  125. /**
  126. * edit summary
  127. * used in [Ui\recent, Ui\Revisions]
  128. *
  129. * @return string
  130. */
  131. public function showEditSummary()
  132. {
  133. return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>';
  134. }
  135. /**
  136. * editor of the page or media file
  137. * used in [Ui\recent, Ui\Revisions]
  138. *
  139. * @return string
  140. */
  141. public function showEditor()
  142. {
  143. if ($this->val('user')) {
  144. $html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>';
  145. if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>';
  146. } else {
  147. $html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>';
  148. }
  149. return '<span class="user">'. $html. '</span>';
  150. }
  151. /**
  152. * name of the page or media file
  153. * used in [Ui\recent, Ui\Revisions]
  154. *
  155. * @return string
  156. */
  157. public function showFileName()
  158. {
  159. $id = $this->val('id');
  160. $rev = $this->isCurrent() ? '' : $this->val('date');
  161. switch ($this->val('mode')) {
  162. case 'media': // media file revision
  163. $params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id];
  164. if ($rev) $params += ['rev'=> $rev];
  165. $href = media_managerURL($params, '&');
  166. $display_name = $id;
  167. $exists = file_exists(mediaFN($id, $rev));
  168. break;
  169. case 'page': // page revision
  170. $params = $rev ? ['rev'=> $rev] : [];
  171. $href = wl($id, $params, false, '&');
  172. $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
  173. if (!$display_name) $display_name = $id;
  174. $exists = page_exists($id, $rev);
  175. }
  176. if($exists) {
  177. $class = 'wikilink1';
  178. } else {
  179. if($this->isCurrent()) {
  180. //show only not-existing link for current page, which allows for directly create a new page/upload
  181. $class = 'wikilink2';
  182. } else {
  183. //revision is not in attic
  184. return $display_name;
  185. }
  186. }
  187. if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
  188. $class = 'wikilink2';
  189. }
  190. return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>';
  191. }
  192. /**
  193. * Revision Title for PageDiff table headline
  194. *
  195. * @return string
  196. */
  197. public function showRevisionTitle()
  198. {
  199. global $lang;
  200. if (!$this->val('date')) return '&mdash;';
  201. $id = $this->val('id');
  202. $rev = $this->isCurrent() ? '' : $this->val('date');
  203. $params = ($rev) ? ['rev'=> $rev] : [];
  204. // revision info may have timestamp key when external edits occurred
  205. $date = ($this->val('timestamp') === false)
  206. ? $lang['unknowndate']
  207. : dformat($this->val('date'));
  208. switch ($this->val('mode')) {
  209. case 'media': // media file revision
  210. $href = ml($id, $params, false, '&');
  211. $exists = file_exists(mediaFN($id, $rev));
  212. break;
  213. case 'page': // page revision
  214. $href = wl($id, $params, false, '&');
  215. $exists = page_exists($id, $rev);
  216. }
  217. if($exists) {
  218. $class = 'wikilink1';
  219. } else {
  220. if($this->isCurrent()) {
  221. //show only not-existing link for current page, which allows for directly create a new page/upload
  222. $class = 'wikilink2';
  223. } else {
  224. //revision is not in attic
  225. return $id.' ['.$date.']';
  226. }
  227. }
  228. if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
  229. $class = 'wikilink2';
  230. }
  231. return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>';
  232. }
  233. /**
  234. * diff link icon in recent changes list, to compare (this) current revision with previous one
  235. * all items in "recent changes" are current revision of the page or media
  236. *
  237. * @return string
  238. */
  239. public function showIconCompareWithPrevious()
  240. {
  241. global $lang;
  242. $id = $this->val('id');
  243. $href = '';
  244. switch ($this->val('mode')) {
  245. case 'media': // media file revision
  246. // unlike page, media file does not copied to media_attic when uploaded.
  247. // diff icon will not be shown when external edit occurred
  248. // because no attic file to be compared with current.
  249. $revs = (new MediaChangeLog($id))->getRevisions(0, 1);
  250. $showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id)));
  251. if ($showLink) {
  252. $param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id];
  253. $href = media_managerURL($param, '&');
  254. }
  255. break;
  256. case 'page': // page revision
  257. // when a page just created anyway, it is natural to expect no older revisions
  258. // even if it had once existed but deleted before. Simply ignore to check changelog.
  259. if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) {
  260. $href = wl($id, ['do'=>'diff'], false, '&');
  261. }
  262. }
  263. if ($href) {
  264. return '<a href="'.$href.'" class="diff_link">'
  265. .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
  266. .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
  267. .'</a>';
  268. } else {
  269. return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
  270. }
  271. }
  272. /**
  273. * diff link icon in revisions list, compare this revision with current one
  274. * the icon does not displayed for the current revision
  275. *
  276. * @return string
  277. */
  278. public function showIconCompareWithCurrent()
  279. {
  280. global $lang;
  281. $id = $this->val('id');
  282. $rev = $this->isCurrent() ? '' : $this->val('date');
  283. $href = '';
  284. switch ($this->val('mode')) {
  285. case 'media': // media file revision
  286. if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) {
  287. $param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev];
  288. $href = media_managerURL($param, '&');
  289. }
  290. break;
  291. case 'page': // page revision
  292. if (!$this->isCurrent()) {
  293. $href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&');
  294. }
  295. }
  296. if ($href) {
  297. return '<a href="'.$href.'" class="diff_link">'
  298. .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
  299. .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
  300. .'</a>';
  301. } else {
  302. return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
  303. }
  304. }
  305. /**
  306. * icon for revision action
  307. * used in [Ui\recent]
  308. *
  309. * @return string
  310. */
  311. public function showIconRevisions()
  312. {
  313. global $lang;
  314. if (!actionOK('revisions')) {
  315. return '';
  316. }
  317. $id = $this->val('id');
  318. switch ($this->val('mode')) {
  319. case 'media': // media file revision
  320. $param = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id];
  321. $href = media_managerURL($param, '&');
  322. break;
  323. case 'page': // page revision
  324. $href = wl($id, ['do'=>'revisions'], false, '&');
  325. }
  326. return '<a href="'.$href.'" class="revisions_link">'
  327. . '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"'
  328. . ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />'
  329. . '</a>';
  330. }
  331. /**
  332. * size change
  333. * used in [Ui\recent, Ui\Revisions]
  334. *
  335. * @return string
  336. */
  337. public function showSizeChange()
  338. {
  339. $class = 'sizechange';
  340. $value = filesize_h(abs($this->val('sizechange')));
  341. if ($this->val('sizechange') > 0) {
  342. $class .= ' positive';
  343. $value = '+' . $value;
  344. } elseif ($this->val('sizechange') < 0) {
  345. $class .= ' negative';
  346. $value = '-' . $value;
  347. } else {
  348. $value = '±' . $value;
  349. }
  350. return '<span class="'.$class.'">'.$value.'</span>';
  351. }
  352. /**
  353. * current indicator, used in revision list
  354. * not used in Ui\Recent because recent files are always current one
  355. *
  356. * @return string
  357. */
  358. public function showCurrentIndicator()
  359. {
  360. global $lang;
  361. return $this->isCurrent() ? '('.$lang['current'].')' : '';
  362. }
  363. }