diff --git a/module/doc/control.php b/module/doc/control.php
index ad45009561..a1855adcdf 100644
--- a/module/doc/control.php
+++ b/module/doc/control.php
@@ -423,6 +423,79 @@ class doc extends control
$this->display();
}
+ /**
+ * Diff doc.
+ *
+ * @param int $docID
+ * @param int $newVersion
+ * @param int $oldVersion
+ * @access public
+ * @return void
+ */
+ public function diff($docID, $newVersion, $oldVersion = '')
+ {
+ if(empty($oldVersion)) $oldVersion = $newVersion - 1;
+ if(empty($oldVersion) or empty($newVersion)) die(js::error($this->lang->doc->versionNotFound) . js::locate('back'));
+
+ $newDoc = $this->doc->getById($docID, $newVersion, true);
+ $oldDoc = $this->doc->getById($docID, $oldVersion, true);
+ if(!$newDoc) die(js::error($this->lang->notFound) . js::locate('back'));
+
+ /* Merge files in doc. */
+ $dbOldDocFiles = $this->dao->select('files')->from(TABLE_DOCCONTENT)->where('doc')->eq($docID)->andWhere('version')->eq($oldVersion)->fetch('files');
+ $dbOldDocFiles = explode(',', $dbOldDocFiles);
+ sort($dbOldDocFiles);
+ $oldDocFiles = array();
+ foreach($dbOldDocFiles as $fileID)
+ {
+ $file = isset($oldDoc->files[$fileID]) ? $oldDoc->files[$fileID] : '';
+ $oldDocFiles[$fileID] = 'File #' . $fileID;
+ if($file) $oldDocFiles[$fileID] .= ' ' . $file->title . '.' . $file->extension;
+ }
+ $oldDoc->files = join("\n", $oldDocFiles);
+
+ $dbNewDocFiles = $this->dao->select('files')->from(TABLE_DOCCONTENT)->where('doc')->eq($docID)->andWhere('version')->eq($newVersion)->fetch('files');
+ $dbNewDocFiles = explode(',', $dbNewDocFiles);
+ sort($dbNewDocFiles);
+ $newDocFiles = array();
+ foreach($dbNewDocFiles as $fileID)
+ {
+ $file = isset($newDoc->files[$fileID]) ? $newDoc->files[$fileID] : '';
+ $newDocFiles[$fileID] = 'File #' . $fileID;
+ if($file) $newDocFiles[$fileID] .= ' ' . $file->title . '.' . $file->extension;
+ }
+ $newDoc->files = join("\n", $newDocFiles);
+
+ /* Get diff. */
+ $diff = new stdclass();
+ $diff->content = $this->doc->diff($oldDoc->content, $newDoc->content);
+ $diff->digest = $this->doc->diff($oldDoc->digest, $newDoc->digest);
+ $diff->files = $this->doc->diff($oldDoc->files, $newDoc->files);
+
+ /* Check priv when lib is product or project. */
+ $lib = $this->doc->getLibByID($newDoc->lib);
+ $type = $lib->product ? 'product' : ($lib->project ? 'project' : 'custom');
+ $this->libs = $this->doc->getLibs($type);
+
+ /* Set menu. */
+ $this->doc->setMenu($this->libs, $newDoc->lib, $type);
+
+ $this->view->title = "DOC #$newDoc->id $newDoc->title - " . $this->libs[$newDoc->lib];
+ $this->view->position[] = html::a($this->createLink('doc', 'browse', "libID=$newDoc->lib"), $this->libs[$newDoc->lib]);
+ $this->view->position[] = $this->lang->doc->view;
+
+ $this->view->newDoc = $newDoc;
+ $this->view->oldDoc = $oldDoc;
+ $this->view->docID = $docID;
+ $this->view->lib = $lib;
+ $this->view->type = $type;
+ $this->view->newVersion = $newVersion;
+ $this->view->oldVersion = $oldVersion;
+ $this->view->diff = $diff;
+
+ $this->display();
+ }
+
/**
* Delete a doc.
*
diff --git a/module/doc/css/diff.css b/module/doc/css/diff.css
new file mode 100644
index 0000000000..67d6c0f687
--- /dev/null
+++ b/module/doc/css/diff.css
@@ -0,0 +1,11 @@
+table.alldiff th{width:70px;border-right:1px solid #ddd;}
+table.table.alldiff td:last-child{padding:0px;}
+table.table.diff {border:none}
+table.alldiff table.table.diff td{border:none;}
+table.alldiff table.table.diff .num{border-right:1px solid #ddd; text-align:right; width:20px;}
+table.alldiff table.table.diff .oldLine{border-right:1px solid #ddd;}
+table.table.diff td.newLine{padding:3px;}
+table.table.diff td.oldLine{padding:3px;}
+table.table.diff td.line{padding-left:20px;}
+table.table.diff td.diffline.newLine{background: #8eff8e}
+table.table.diff td.diffline.oldLine{background: #f6b2b2}
diff --git a/module/doc/lang/en.php b/module/doc/lang/en.php
index 1316eb4e3d..f8c05453b4 100644
--- a/module/doc/lang/en.php
+++ b/module/doc/lang/en.php
@@ -28,12 +28,17 @@ $lang->doc->addedBy = 'Added by';
$lang->doc->addedDate = 'Added date';
$lang->doc->editedBy = 'Edited by';
$lang->doc->editedDate = 'Edited date';
+$lang->doc->version = 'Version';
$lang->doc->basicInfo = 'Basic Info';
$lang->doc->deleted = 'Deleted';
+$lang->doc->fileObject = 'File object';
+$lang->doc->whiteList = 'White list';
+$lang->doc->contentType = 'Doc type';
$lang->doc->moduleDoc = 'By module';
$lang->doc->searchDoc = 'By search';
-//$lang->doc->allDoc = 'All document';
+$lang->doc->allDoc = 'All doc';
+$lang->doc->openedByMe = 'Added by me';
/* Actions. */
$lang->doc->index = 'Index';
@@ -42,33 +47,60 @@ $lang->doc->edit = 'Edit doc';
$lang->doc->delete = 'Delete doc';
$lang->doc->browse = 'Browse doc';
$lang->doc->view = 'View doc';
+$lang->doc->diff = 'Diff';
$lang->doc->manageType = 'Manage type';
$lang->doc->addType = 'Add type';
$lang->doc->libName = 'Library name';
+$lang->doc->libType = 'Library type';
+$lang->doc->custom = 'Custom library';
$lang->doc->createLib = 'Create library';
$lang->doc->editLib = 'Edit library';
$lang->doc->deleteLib = 'Delete library';
+$lang->doc->fixedMenu = 'Fixed in Menu';
+$lang->doc->removeMenu = 'Move from menu';
/* Browse tabs. */
$lang->doc->allProduct = "All {$lang->productCommon}s";
$lang->doc->allProject = "All {$lang->projectCommon}s";
+$lang->doc->libTypeList['product'] = "{$lang->productCommon} doc";
+$lang->doc->libTypeList['project'] = "{$lang->projectCommon} doc";
+$lang->doc->libTypeList['custom'] = 'Custom library';
+
$lang->doc->systemLibs['product'] = "{$lang->productCommon} doc";
$lang->doc->systemLibs['project'] = "{$lang->projectCommon} doc";
-$lang->doc->types['file'] = 'File';
-$lang->doc->types['url'] = 'Link';
-$lang->doc->types['text'] = 'Html';
+$lang->doc->aclList['public'] = 'Public';
+$lang->doc->aclList['custom'] = 'Custom';
+$lang->doc->aclList['private'] = 'Private';
+
+$lang->doc->contentTypeList['html'] = 'HTML';
+$lang->doc->contentTypeList['markdown'] = 'MarkDown';
+
+$lang->doc->browseType = 'Browse type';
+$lang->doc->browseTypeList['list'] = 'List';
+$lang->doc->browseTypeList['menu'] = 'Menu';
+$lang->doc->browseTypeList['tree'] = 'Tree';
$lang->doc->confirmDelete = "Are you sure to delete this doc?";
$lang->doc->confirmDeleteLib = " Are you sure to delete this doc library?";
$lang->doc->errorEditSystemDoc = "System doc library needn't edit";
$lang->doc->errorEmptyProduct = "{$lang->productCommon} is empty, can not create doc.";
$lang->doc->errorEmptyProject = "{$lang->projectCommon} is empty, can not create doc.";
+$lang->doc->errorEmptySysLib = "Do not delete all library in system library";
+$lang->doc->accessDenied = "Access to this library denied.";
+$lang->doc->noMatched = "No matched library including '%s'";
+$lang->doc->versionNotFount = 'Do not found doc in this version';
$lang->doc->placeholder = new stdclass();
$lang->doc->placeholder->url = 'url';
$lang->doclib = new stdclass();
-$lang->doclib->name = 'Library Name';
+$lang->doclib->name = 'Library Name';
+$lang->doclib->control = 'Control';
+$lang->doclib->acl = 'Power';
+$lang->doclib->group = 'Group';
+$lang->doclib->user = 'User';
+$lang->doclib->files = 'Files';
+$lang->doclib->all = 'All library';
diff --git a/module/doc/lang/zh-cn.php b/module/doc/lang/zh-cn.php
index 5f37ec26be..9df288c47e 100644
--- a/module/doc/lang/zh-cn.php
+++ b/module/doc/lang/zh-cn.php
@@ -47,6 +47,7 @@ $lang->doc->edit = '编辑文档';
$lang->doc->delete = '删除文档';
$lang->doc->browse = '文档列表';
$lang->doc->view = '文档详情';
+$lang->doc->diff = '对比';
$lang->doc->manageType = '维护分类';
$lang->doc->addType = '增加分类';
@@ -90,6 +91,7 @@ $lang->doc->errorEmptyProject = "没有{$lang->projectCommon},无法创建文
$lang->doc->errorEmptySysLib = "系统文档库不能全部删除!";
$lang->doc->accessDenied = "您没有权限访问该文档库!";
$lang->doc->noMatched = '找不到包含"%s"的文档库';
+$lang->doc->versionNotFount = '该版本文档不存在';
$lang->doc->placeholder = new stdclass();
$lang->doc->placeholder->url = '相应的链接地址';
diff --git a/module/doc/model.php b/module/doc/model.php
index 8c287f2cbd..0b6cc7f3d6 100644
--- a/module/doc/model.php
+++ b/module/doc/model.php
@@ -279,6 +279,7 @@ class docModel extends model
$this->dao->update(TABLE_DOC)->set('version')->eq($doc->version)->where('id')->eq($doc->id)->exec();
}
+ $doc->title = isset($docContent->title) ? $docContent->title : '';
$doc->digest = isset($docContent->digest) ? $docContent->digest : '';
$doc->content = isset($docContent->content) ? $docContent->content : '';
$doc->type = isset($docContent->type) ? $docContent->type : '';
@@ -893,4 +894,164 @@ class docModel extends model
$node->actions = false;
return $node;
}
+
+ /**
+ * Diff doc.
+ *
+ * @param string $text1
+ * @param string $text2
+ * @access public
+ * @return array
+ */
+ public function diff($text1, $text2)
+ {
+ $text1 = explode("\n", trim($text1));
+ $text2 = explode("\n", trim($text2));
+ $text1Len = count($text1);
+ $text2Len = count($text2);
+ $trace = array();
+ $trace[-1] = array_fill(-1, $text2Len + 1, +1);
+ $lcs0 = array_fill(-1, $text2Len + 1, 0);
+ $lcs1[-1] = 0;
+
+ foreach($text1 as $line)
+ {
+ $temp = array();
+ $temp[-1] = -1;
+ for($i = 0; $i < $text2Len; $i++)
+ {
+ if($line == $text2[$i])
+ {
+ $lcs1[$i] = $lcs0[$i - 1] + 1;
+ $temp[$i] = 0;
+ }
+ elseif($lcs0[$i] > $lcs1[$i - 1])
+ {
+ $lcs1[$i] = $lcs0[$i];
+ $temp[$i] = -1;
+ }
+ else
+ {
+ $lcs1[$i] = $lcs1[$i - 1];
+ $temp[$i] = +1;
+ }
+ }
+ array_push($trace, $temp);
+ $temp = $lcs1;
+ $lcs1 = $lcs0;
+ $lcs0 = $temp;
+ }
+
+ $same = 0;
+ $out = false;
+ $ret = array();
+ for($i = $text1Len - 1, $j = $text2Len - 1; $i != -1 || $j != -1;)
+ {
+ if($trace[$i][$j] == 0)
+ {
+ $same++;
+ if($out && $same > 0)
+ {
+ $outbegina = $i + $same;
+ $outbeginb = $j + $same;
+ for($k = $i + $same - 1; $k >= $outbegina; $k--) array_push($ret, array('cmd' => ' ', 'dat' => $text1[$k]));
+ $dat = array($outbegina, $outenda - $outbegina + 1, $outbeginb, $outendb - $outbeginb + 1);
+ array_push($ret, array('cmd' => '@', 'dat'=> $dat));
+ $out = false;
+ }
+ $i--;
+ $j--;
+ }
+ else
+ {
+ if(!$out)
+ {
+ $same = min($same, 0);
+ $out = true;
+ $outenda = $i + $same;
+ $outendb = $j + $same;
+ }
+ for($k = $i + $same; $k > $i; $k--) array_push($ret, array('cmd' => ' ', 'dat' => $text1[$k]));
+ $same = 0;
+ if($trace[$i][$j] == -1)
+ {
+ array_push($ret, array('cmd' => '-', 'dat' => $text1[$i]));
+ $i--;
+ }
+ else
+ {
+ array_push($ret, array('cmd' => '+', 'dat' => $text2[$j]));
+ $j--;
+ }
+ }
+ }
+
+ if($out)
+ {
+ $outbegina = max(0, $same);
+ $outbeginb = max(0, $same);
+ for($k = $same - 1; $k >= $outbegina; $k--) array_push($ret, array('cmd' => ' ', 'dat' => $text1[$k]));
+ $dat = array($outbegina, $outenda - $outbegina + 1, $outbeginb, $outendb - $outbeginb + 1);
+ array_push($ret, array('cmd' => '@', 'dat'=> $dat));
+ }
+
+ $diffs = array_reverse($ret);
+ if(empty($diffs)) return array();
+
+ $processedDiff = array();
+ foreach($diffs as $i => $diff)
+ {
+ if($diff['cmd'] == '@')
+ {
+ $max = max($diff['dat'][1], $diff['dat'][3]);
+ for($number = 0; $number < $max; $number++)
+ {
+ $oldLineNO = $diff['dat'][0] + $number;
+ $newLineNO = $diff['dat'][2] + $number;
+ $oldLine = '';
+ $newLine = '';
+ if($diff['dat'][1] > 0 and $diff['dat'][1] >= $number + 1) $oldLine = $diffs[$i + $number + 1]['dat'];
+ if($diff['dat'][3] > 0 and $diff['dat'][3] >= $number + 1) $newLine = $diffs[$i + $number + 1 + $diff['dat'][1]]['dat'];
+ $processedDiff['old'][$oldLineNO] = $oldLine;
+ $processedDiff['new'][$newLineNO] = $newLine;
+ }
+
+ }
+ }
+ return $processedDiff;
+ }
+
+ /**
+ * Get line number by diff.
+ *
+ * @param array $diff
+ * @param int $i
+ * @param int $lineNO
+ * @access public
+ * @return array
+ */
+ public function getLineNumber($diff, $i, $lineNO)
+ {
+ $action = '';
+ if(isset($diff[$i]))
+ {
+ if(empty($diff[$i]))
+ {
+ $showNumber = '';
+ $action = '';
+ }
+ else
+ {
+ $showNumber = $lineNO + 1;
+ $action = 'diff';
+ }
+ }
+ else
+ {
+ $showNumber = $lineNO + 1;
+ $action = '';
+ }
+
+ return array($showNumber, $action, 'number' => $showNumber, 'action' => $action);
+ }
}
diff --git a/module/doc/view/diff.html.php b/module/doc/view/diff.html.php
new file mode 100644
index 0000000000..c3d145daf3
--- /dev/null
+++ b/module/doc/view/diff.html.php
@@ -0,0 +1,94 @@
+
+ * @package doc
+ * @version $Id: view.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
+ * @link http://www.zentao.net
+ */
+?>
+
+
+
+ icons['doc']);?>
+ title;?>
+
+ deleted):?>
+ doc->deleted;?>
+
+
+
+
+
+
+ title != $newDoc->title):?>
+
+ | doc->title?> |
+ title?> |
+ title?> |
+
+
+ $diffLines):?>
+
+
+ | doc->$field?> |
+
+
+ $field)));
+ $newLines = explode("\n", htmlspecialchars(trim($newDoc->$field)));
+ $lines = max(count($oldLines), count($newLines));
+ $oldLineNO = 0;
+ $newLineNO = 0;
+ ?>
+
+
+ doc->getLineNumber($diffLines['old'], $i, $oldLineNO);
+ $showLine = '';
+ if($showNumber)
+ {
+ $showLine = $oldLines[$oldLineNO];
+ $oldLineNO ++;
+ }
+ elseif(isset($oldLines[$oldLineNO]) and empty($oldLines[$oldLineNO]))
+ {
+ $showNumber = $oldLineNO + 1;
+ $oldLineNO ++;
+ if(isset($diffLines['old'][$i])) $action = 'diff';
+ }
+ if($action) $showLine = " " . $showLine;
+ ?>
+ |
+ |
+ doc->getLineNumber($diffLines['new'], $i, $newLineNO);
+ $showLine = '';
+ if($showNumber)
+ {
+ $showLine = $newLines[$newLineNO];
+ $newLineNO ++;
+ }
+ elseif(isset($newLines[$newLineNO]) and empty($newLines[$newLineNO]))
+ {
+ $showNumber = $newLineNO + 1;
+ $newLineNO ++;
+ if(isset($diffLines['new'][$i])) $action = 'diff';
+ }
+ if($action) $showLine = " " . $showLine;
+ ?>
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
diff --git a/module/doc/view/view.html.php b/module/doc/view/view.html.php
index 4d0cb7b259..f31e2c4e45 100644
--- a/module/doc/view/view.html.php
+++ b/module/doc/view/view.html.php
@@ -41,6 +41,19 @@
if(!$doc->deleted)
{
ob_start();
+ if($doc->version > 1 and common::hasPriv('doc', 'diff'))
+ {
+ echo "";
+ echo "";
+ echo "";
+ echo "
";
+ }
echo "";
common::printIcon('doc', 'edit', $params);
common::printIcon('doc', 'delete', $params, '', 'button', '', 'hiddenwin');