MediaDiff.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. namespace dokuwiki\Ui;
  3. use dokuwiki\ChangeLog\MediaChangeLog;
  4. use dokuwiki\ChangeLog\RevisionInfo;
  5. use dokuwiki\Form\Form;
  6. use InvalidArgumentException;
  7. use JpegMeta;
  8. /**
  9. * DokuWiki MediaDiff Interface
  10. *
  11. * @package dokuwiki\Ui
  12. */
  13. class MediaDiff extends Diff
  14. {
  15. /* @var MediaChangeLog */
  16. protected $changelog;
  17. /* @var RevisionInfo older revision */
  18. protected $RevInfo1;
  19. /* @var RevisionInfo newer revision */
  20. protected $RevInfo2;
  21. /* @var bool */
  22. protected $is_img;
  23. /**
  24. * MediaDiff Ui constructor
  25. *
  26. * @param string $id media id
  27. */
  28. public function __construct($id)
  29. {
  30. if (!isset($id)) {
  31. throw new InvalidArgumentException('media id should not be empty!');
  32. }
  33. // init preference
  34. $this->preference['fromAjax'] = false; // see dokuwiki\Ajax::callMediadiff()
  35. $this->preference['showIntro'] = false;
  36. $this->preference['difftype'] = 'both'; // diff view type: both, opacity or portions
  37. parent::__construct($id);
  38. }
  39. /** @inheritdoc */
  40. protected function setChangeLog()
  41. {
  42. $this->changelog = new MediaChangeLog($this->id);
  43. }
  44. /**
  45. * Handle requested revision(s) and diff view preferences
  46. *
  47. * @return void
  48. */
  49. protected function handle()
  50. {
  51. global $INPUT;
  52. // retrieve requested rev or rev2
  53. parent::handle();
  54. // requested diff view type
  55. if ($INPUT->has('difftype')) {
  56. $this->preference['difftype'] = $INPUT->str('difftype');
  57. }
  58. }
  59. /**
  60. * Prepare revision info of comparison pair
  61. */
  62. protected function preProcess()
  63. {
  64. $changelog =& $this->changelog;
  65. // create revision info object for older and newer sides
  66. // RevInfo1 : older, left side
  67. // RevInfo2 : newer, right side
  68. $changelogRev1 = $changelog->getRevisionInfo($this->rev1);
  69. $changelogRev2 = $changelog->getRevisionInfo($this->rev2);
  70. $changelogRev1['media'] = $changelogRev2['media'] = true;
  71. $this->RevInfo1 = new RevisionInfo($changelogRev1);
  72. $this->RevInfo2 = new RevisionInfo($changelogRev2);
  73. $this->is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
  74. foreach ([$this->RevInfo1, $this->RevInfo2] as $RevInfo) {
  75. $isCurrent = $changelog->isCurrentRevision($RevInfo->val('date'));
  76. $RevInfo->isCurrent($isCurrent);
  77. if ($this->is_img) {
  78. $rev = $isCurrent ? '' : $RevInfo->val('date');
  79. $meta = new JpegMeta(mediaFN($this->id, $rev));
  80. // get image width and height for the media manager preview panel
  81. $RevInfo->append([
  82. 'previewSize' => media_image_preview_size($this->id, $rev, $meta)
  83. ]);
  84. }
  85. }
  86. // re-check image, ensure minimum image width for showImageDiff()
  87. $this->is_img = ($this->is_img
  88. && ($this->RevInfo1->val('previewSize')[0] ?? 0) >= 30
  89. && ($this->RevInfo2->val('previewSize')[0] ?? 0) >= 30
  90. );
  91. // adjust requested diff view type
  92. if (!$this->is_img) {
  93. $this->preference['difftype'] = 'both';
  94. }
  95. }
  96. /**
  97. * Shows difference between two revisions of media
  98. *
  99. * @author Kate Arzamastseva <pshns@ukr.net>
  100. */
  101. public function show()
  102. {
  103. global $conf;
  104. $ns = getNS($this->id);
  105. $auth = auth_quickaclcheck("$ns:*");
  106. if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return;
  107. // retrieve form parameters: rev, rev2, difftype
  108. $this->handle();
  109. // prepare revision info of comparison pair
  110. $this->preProcess();
  111. // display intro
  112. if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
  113. // print form to choose diff view type
  114. if ($this->is_img && !$this->preference['fromAjax']) {
  115. $this->showDiffViewSelector();
  116. echo '<div id="mediamanager__diff" >';
  117. }
  118. switch ($this->preference['difftype']) {
  119. case 'opacity':
  120. case 'portions':
  121. $this->showImageDiff();
  122. break;
  123. case 'both':
  124. default:
  125. $this->showFileDiff();
  126. break;
  127. }
  128. if ($this->is_img && !$this->preference['fromAjax']) {
  129. echo '</div>';
  130. }
  131. }
  132. /**
  133. * Print form to choose diff view type
  134. * the dropdown is to be added through JavaScript, see lib/scripts/media.js
  135. */
  136. protected function showDiffViewSelector()
  137. {
  138. // use timestamp for current revision, date may be false when revisions < 2
  139. [$rev1, $rev2] = [(int)$this->RevInfo1->val('date'), (int)$this->RevInfo2->val('date')];
  140. echo '<div class="diffoptions group">';
  141. $form = new Form([
  142. 'id' => 'mediamanager__form_diffview',
  143. 'action' => media_managerURL([], '&'),
  144. 'method' => 'get',
  145. 'class' => 'diffView',
  146. ]);
  147. $form->addTagOpen('div')->addClass('no');
  148. $form->setHiddenField('sectok', null);
  149. $form->setHiddenField('mediado', 'diff');
  150. $form->setHiddenField('rev2[0]', $rev1);
  151. $form->setHiddenField('rev2[1]', $rev2);
  152. $form->addTagClose('div');
  153. echo $form->toHTML();
  154. echo '</div>'; // .diffoptions
  155. }
  156. /**
  157. * Prints two images side by side
  158. * and slider
  159. *
  160. * @author Kate Arzamastseva <pshns@ukr.net>
  161. */
  162. protected function showImageDiff()
  163. {
  164. $rev1 = $this->RevInfo1->isCurrent() ? '' : $this->RevInfo1->val('date');
  165. $rev2 = $this->RevInfo2->isCurrent() ? '' : $this->RevInfo2->val('date');
  166. // diff view type: opacity or portions
  167. $type = $this->preference['difftype'];
  168. // adjust image width, right side (newer) has priority
  169. $rev1Size = $this->RevInfo1->val('previewSize');
  170. $rev2Size = $this->RevInfo2->val('previewSize');
  171. if ($rev1Size != $rev2Size) {
  172. if ($rev2Size[0] > $rev1Size[0]) {
  173. $rev1Size = $rev2Size;
  174. }
  175. }
  176. $rev1Src = ml($this->id, ['rev' => $rev1, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
  177. $rev2Src = ml($this->id, ['rev' => $rev2, 'h' => $rev1Size[1], 'w' => $rev1Size[0]]);
  178. // slider
  179. echo '<div class="slider" style="max-width: '.($rev1Size[0]-20).'px;" ></div>';
  180. // two images in divs
  181. echo '<div class="imageDiff '.$type.'">';
  182. echo '<div class="image1" style="max-width: '.$rev1Size[0].'px;">';
  183. echo '<img src="'.$rev1Src.'" alt="" />';
  184. echo '</div>';
  185. echo '<div class="image2" style="max-width: '.$rev1Size[0].'px;">';
  186. echo '<img src="'.$rev2Src.'" alt="" />';
  187. echo '</div>';
  188. echo '</div>';
  189. }
  190. /**
  191. * Shows difference between two revisions of media file
  192. *
  193. * @author Kate Arzamastseva <pshns@ukr.net>
  194. */
  195. protected function showFileDiff()
  196. {
  197. global $lang;
  198. $ns = getNS($this->id);
  199. $auth = auth_quickaclcheck("$ns:*");
  200. $rev1 = $this->RevInfo1->isCurrent() ? '' : (int)$this->RevInfo1->val('date');
  201. $rev2 = $this->RevInfo2->isCurrent() ? '' : (int)$this->RevInfo2->val('date');
  202. // revision title
  203. $rev1Title = trim($this->RevInfo1->showRevisionTitle() .' '. $this->RevInfo1->showCurrentIndicator());
  204. $rev1Summary = ($this->RevInfo1->val('date'))
  205. ? $this->RevInfo1->showEditSummary() .' '. $this->RevInfo1->showEditor()
  206. : '';
  207. $rev2Title = trim($this->RevInfo2->showRevisionTitle() .' '. $this->RevInfo2->showCurrentIndicator());
  208. $rev2Summary = ($this->RevInfo2->val('date'))
  209. ? $this->RevInfo2->showEditSummary() .' '. $this->RevInfo2->showEditor()
  210. : '';
  211. $rev1Meta = new JpegMeta(mediaFN($this->id, $rev1));
  212. $rev2Meta = new JpegMeta(mediaFN($this->id, $rev2));
  213. // display diff view table
  214. echo '<div class="table">';
  215. echo '<table>';
  216. echo '<tr>';
  217. echo '<th>'. $rev1Title .' '. $rev1Summary .'</th>';
  218. echo '<th>'. $rev2Title .' '. $rev2Summary .'</th>';
  219. echo '</tr>';
  220. echo '<tr class="image">';
  221. echo '<td>';
  222. media_preview($this->id, $auth, $rev1, $rev1Meta); // $auth not used in media_preview()?
  223. echo '</td>';
  224. echo '<td>';
  225. media_preview($this->id, $auth, $rev2, $rev2Meta);
  226. echo '</td>';
  227. echo '</tr>';
  228. echo '<tr class="actions">';
  229. echo '<td>';
  230. media_preview_buttons($this->id, $auth, $rev1); // $auth used in media_preview_buttons()
  231. echo '</td>';
  232. echo '<td>';
  233. media_preview_buttons($this->id, $auth, $rev2);
  234. echo '</td>';
  235. echo '</tr>';
  236. $rev1Tags = media_file_tags($rev1Meta);
  237. $rev2Tags = media_file_tags($rev2Meta);
  238. // FIXME rev2Tags-only stuff ignored
  239. foreach ($rev1Tags as $key => $tag) {
  240. if ($tag['value'] != $rev2Tags[$key]['value']) {
  241. $rev2Tags[$key]['highlighted'] = true;
  242. $rev1Tags[$key]['highlighted'] = true;
  243. } elseif (!$tag['value'] || !$rev2Tags[$key]['value']) {
  244. unset($rev2Tags[$key]);
  245. unset($rev1Tags[$key]);
  246. }
  247. }
  248. echo '<tr>';
  249. foreach ([$rev1Tags, $rev2Tags] as $tags) {
  250. echo '<td>';
  251. echo '<dl class="img_tags">';
  252. foreach ($tags as $tag) {
  253. $value = cleanText($tag['value']);
  254. if (!$value) $value = '-';
  255. echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
  256. echo '<dd>';
  257. if (!empty($tag['highlighted'])) echo '<strong>';
  258. if ($tag['tag'][2] == 'date') {
  259. echo dformat($value);
  260. } else {
  261. echo hsc($value);
  262. }
  263. if (!empty($tag['highlighted'])) echo '</strong>';
  264. echo '</dd>';
  265. }
  266. echo '</dl>';
  267. echo '</td>';
  268. }
  269. echo '</tr>';
  270. echo '</table>';
  271. echo '</div>';
  272. }
  273. }