*
* @param string $text when non-empty: compare with this text with most current version
* @param bool $intro display the intro text
* @param string $type type of the diff (inline or sidebyside)
* @return void
*/
public function show($text = '', $intro = true, $type = null)
{
global $ID;
global $REV;
global $lang;
global $INPUT;
global $INFO;
$pagelog = new PageChangeLog($ID);
/*
* Determine diff type
*/
if (!$type) {
$type = $INPUT->str('difftype');
if (empty($type)) {
$type = get_doku_pref('difftype', $type);
if (empty($type) && $INFO['ismobile']) {
$type = 'inline';
}
}
}
if ($type != 'inline') $type = 'sidebyside';
/*
* Determine requested revision(s)
*/
// we're trying to be clever here, revisions to compare can be either
// given as rev and rev2 parameters, with rev2 being optional. Or in an
// array in rev2.
$rev1 = $REV;
$rev2 = $INPUT->ref('rev2');
if (is_array($rev2)) {
$rev1 = (int) $rev2[0];
$rev2 = (int) $rev2[1];
if (!$rev1) {
$rev1 = $rev2;
unset($rev2);
}
} else {
$rev2 = $INPUT->int('rev2');
}
/*
* Determine left and right revision, its texts and the header
*/
$r_minor = '';
$l_minor = '';
if ($text) { // compare text to the most current revision
$l_rev = '';
$l_text = rawWiki($ID, '');
$l_head = ''
. $ID .' '. dformat((int) @filemtime(wikiFN($ID))) .' '
. $lang['current'];
$r_rev = '';
$r_text = cleanText($text);
$r_head = $lang['yours'];
} else {
if ($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
// make sure order is correct (older on the left)
if ($rev1 < $rev2) {
$l_rev = $rev1;
$r_rev = $rev2;
} else {
$l_rev = $rev2;
$r_rev = $rev1;
}
} elseif ($rev1) { // single revision given, compare to current
$r_rev = '';
$l_rev = $rev1;
} else { // no revision was given, compare previous to current
$r_rev = '';
$revs = $pagelog->getRevisions(0, 1);
$l_rev = $revs[0];
$REV = $l_rev; // store revision back in $REV
}
// when both revisions are empty then the page was created just now
if (!$l_rev && !$r_rev) {
$l_text = '';
} else {
$l_text = rawWiki($ID, $l_rev);
}
$r_text = rawWiki($ID, $r_rev);
list($l_head, $r_head, $l_minor, $r_minor) = $this->diffHead(
$l_rev, $r_rev, null, false, $type == 'inline'
);
}
/*
* Build navigation
*/
$l_nav = '';
$r_nav = '';
if (!$text) {
list($l_nav, $r_nav) = $this->diffNavigation($pagelog, $type, $l_rev, $r_rev);
}
/*
* Create diff object and the formatter
*/
$diff = new \Diff(explode("\n", $l_text), explode("\n", $r_text));
if ($type == 'inline') {
$diffformatter = new \InlineDiffFormatter();
} else {
$diffformatter = new \TableDiffFormatter();
}
/*
* Display intro
*/
if ($intro) print p_locale_xhtml('diff');
/*
* Display type and exact reference
*/
if (!$text) {
print '
';
// create the form to select diff view type
$form = new Form(['action' => wl()]);
$form->setHiddenField('id', $ID);
$form->setHiddenField('rev2[0]', $l_rev);
$form->setHiddenField('rev2[1]', $r_rev);
$form->setHiddenField('do', 'diff');
$options = array(
'sidebyside' => $lang['diff_side'],
'inline' => $lang['diff_inline']
);
$input = $form->addDropdown('difftype', $options, $lang['diff_type'])
->val($type)->addClass('quickselect');
$form->addButton('do[diff]', 'Go')->attr('type','submit');
print $form->toHTML();
print '
';
// link to exactly this view FS#2835
print $this->diffNavigationlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
print '
';
print '
'; // .diffoptions
}
/*
* Display diff view table
*/
print '';
print '
';
//navigation and header
if ($type == 'inline') {
if (!$text) {
print ''
. ''
. '| '. $l_nav .' | '
. '
';
print ''
. ''
. '| '. $l_head .' | '
.'
';
}
print ''
. ''
. '| '. $r_nav .' | '
.'
';
print ''
. ''
. '| '. $r_head .' | '
. '
';
} else {
if (!$text) {
print ''
. '| '. $l_nav .' | '
. ''. $r_nav .' | '
. '
';
}
print ''
. '| '. $l_head .' | '
. ''. $r_head .' | '
. '
';
}
//diff view
print html_insert_softbreaks($diffformatter->format($diff));
print '
';
print '
'. DOKU_LF;
}
/**
* Get header of diff HTML
*
* @param string $l_rev Left revisions
* @param string $r_rev Right revision
* @param string $id Page id, if null $ID is used
* @param bool $media If it is for media files
* @param bool $inline Return the header on a single line
* @return string[] HTML snippets for diff header
*/
protected function diffHead($l_rev, $r_rev, $id = null, $media = false, $inline = false)
{
global $lang;
if ($id === null) {
global $ID;
$id = $ID;
}
$head_separator = $inline ? ' ' : '
';
$media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
$ml_or_wl = $media ? 'ml' : 'wl';
$l_minor = $r_minor = '';
if ($media) {
$changelog = new MediaChangeLog($id);
} else {
$changelog = new PageChangeLog($id);
}
if (!$l_rev) {
$l_head = '—';
} else {
$l_info = $changelog->getRevisionInfo($l_rev);
if ($l_info['user']) {
$l_user = ''.editorinfo($l_info['user']).'';
if (auth_ismanager()) $l_user .= ' ('.$l_info['ip'].')';
} else {
$l_user = ''.$l_info['ip'].'';
}
$l_user = ''.$l_user.'';
$l_sum = ($l_info['sum']) ? ''.hsc($l_info['sum']).'' : '';
if ($l_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
$l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
$l_head = ''
. $l_head_title.''.$head_separator.$l_user.' '.$l_sum;
}
if ($r_rev) {
$r_info = $changelog->getRevisionInfo($r_rev);
if ($r_info['user']) {
$r_user = ''.editorinfo($r_info['user']).'';
if (auth_ismanager()) $r_user .= ' ('.$r_info['ip'].')';
} else {
$r_user = ''.$r_info['ip'].'';
}
$r_user = ''.$r_user.'';
$r_sum = ($r_info['sum']) ? ''.hsc($r_info['sum']).'' : '';
if ($r_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
$r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
$r_head = ''
. $r_head_title.''.$head_separator.$r_user.' '.$r_sum;
} elseif ($_rev = @filemtime($media_or_wikiFN($id))) {
$_info = $changelog->getRevisionInfo($_rev);
if ($_info['user']) {
$_user = ''.editorinfo($_info['user']).'';
if (auth_ismanager()) $_user .= ' ('.$_info['ip'].')';
} else {
$_user = ''.$_info['ip'].'';
}
$_user = ''.$_user.'';
$_sum = ($_info['sum']) ? ''.hsc($_info['sum']).'' : '';
if ($_info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
$r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
$r_head = ''
. $r_head_title.' '.'('.$lang['current'].')'.$head_separator.$_user.' '.$_sum;
}else{
$r_head = '— ('.$lang['current'].')';
}
return array($l_head, $r_head, $l_minor, $r_minor);
}
/**
* Create html for revision navigation
*
* @param PageChangeLog $pagelog changelog object of current page
* @param string $type inline vs sidebyside
* @param int $l_rev left revision timestamp
* @param int $r_rev right revision timestamp
* @return string[] html of left and right navigation elements
*/
protected function diffNavigation($pagelog, $type, $l_rev, $r_rev)
{
global $INFO, $ID;
// last timestamp is not in changelog, retrieve timestamp from metadata
// note: when page is removed, the metadata timestamp is zero
if (!$r_rev) {
if (isset($INFO['meta']['last_change']['date'])) {
$r_rev = $INFO['meta']['last_change']['date'];
} else {
$r_rev = 0;
}
}
//retrieve revisions with additional info
list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
$l_revisions = array();
if (!$l_rev) {
//no left revision given, add dummy
$l_revisions[0]= array('label' => '', 'attrs' => []);
}
foreach ($l_revs as $rev) {
$info = $pagelog->getRevisionInfo($rev);
$l_revisions[$rev] = array(
'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
'attrs' => ['title' => $rev],
);
if ($r_rev ? $rev >= $r_rev : false) $l_revisions[$rev]['attrs']['disabled'] = 'disabled';
}
$r_revisions = array();
if (!$r_rev) {
//no right revision given, add dummy
$r_revisions[0] = array('label' => '', 'attrs' => []);
}
foreach ($r_revs as $rev) {
$info = $pagelog->getRevisionInfo($rev);
$r_revisions[$rev] = array(
'label' => dformat($info['date']) .' '. editorinfo($info['user'], true) .' '. $info['sum'],
'attrs' => ['title' => $rev],
);
if ($rev <= $l_rev) $r_revisions[$rev]['attrs']['disabled'] = 'disabled';
}
//determine previous/next revisions
$l_index = array_search($l_rev, $l_revs);
$l_prev = $l_revs[$l_index + 1];
$l_next = $l_revs[$l_index - 1];
if ($r_rev) {
$r_index = array_search($r_rev, $r_revs);
$r_prev = $r_revs[$r_index + 1];
$r_next = $r_revs[$r_index - 1];
} else {
//removed page
if ($l_next) {
$r_prev = $r_revs[0];
} else {
$r_prev = null;
}
$r_next = null;
}
/*
* Left side:
*/
$l_nav = '';
//move back
if ($l_prev) {
$l_nav .= $this->diffNavigationlink($type, 'diffbothprevrev', $l_prev, $r_prev);
$l_nav .= $this->diffNavigationlink($type, 'diffprevrev', $l_prev, $r_rev);
}
//dropdown
$form = new Form(['action' => wl()]);
$form->setHiddenField('id', $ID);
$form->setHiddenField('difftype', $type);
$form->setHiddenField('rev2[1]', $r_rev);
$form->setHiddenField('do', 'diff');
$input = $form->addDropdown('rev2[0]', $l_revisions, /*'LeftSide'*/)->val($l_rev)->addClass('quickselect');
$input->useInput(false); // inhibit prefillInput() during toHTML() process
$form->addButton('do[diff]', 'Go')->attr('type','submit');
$l_nav .= $form->toHTML();
//move forward
if ($l_next && ($l_next < $r_rev || !$r_rev)) {
$l_nav .= $this->diffNavigationlink($type, 'diffnextrev', $l_next, $r_rev);
}
/*
* Right side:
*/
$r_nav = '';
//move back
if ($l_rev < $r_prev) {
$r_nav .= $this->diffNavigationlink($type, 'diffprevrev', $l_rev, $r_prev);
}
//dropdown
$form = new Form(['action' => wl()]);
$form->setHiddenField('id', $ID);
$form->setHiddenField('rev2[0]', $l_rev);
$form->setHiddenField('difftype', $type);
$form->setHiddenField('do', 'diff');
$input = $form->addDropdown('rev2[1]', $r_revisions, /*'RightSide'*/)->val($r_rev)->addClass('quickselect');
$input->useInput(false); // inhibit prefillInput() during toHTML() process
$form->addButton('do[diff]', 'Go')->attr('type','submit');
$r_nav .= $form->toHTML();
//move forward
if ($r_next) {
if ($pagelog->isCurrentRevision($r_next)) {
//last revision is diff with current page
$r_nav .= $this->diffNavigationlink($type, 'difflastrev', $l_rev);
} else {
$r_nav .= $this->diffNavigationlink($type, 'diffnextrev', $l_rev, $r_next);
}
} else {
$r_nav .= $this->diffNavigationlink($type, 'diffbothnextrev', $l_next, $r_next);
}
return array($l_nav, $r_nav);
}
/**
* Create html link to a diff defined by two revisions
*
* @param string $difftype display type
* @param string $linktype
* @param int $lrev oldest revision
* @param int $rrev newest revision or null for diff with current revision
* @return string html of link to a diff
*/
protected function diffNavigationlink($difftype, $linktype, $lrev, $rrev = null)
{
global $ID, $lang;
if (!$rrev) {
$urlparam = array(
'do' => 'diff',
'rev' => $lrev,
'difftype' => $difftype,
);
} else {
$urlparam = array(
'do' => 'diff',
'rev2[0]' => $lrev,
'rev2[1]' => $rrev,
'difftype' => $difftype,
);
}
return ''
. ''. $lang[$linktype] .''
. ''. DOKU_LF;
}
}