diff --git a/module/bug/model.php b/module/bug/model.php
index b8bc244652..7405d1673e 100644
--- a/module/bug/model.php
+++ b/module/bug/model.php
@@ -2430,7 +2430,7 @@ class bugModel extends model
$canBatchResolve = common::hasPriv('bug', 'batchResolve');
$canBatchAssignTo = common::hasPriv('bug', 'batchAssignTo');
- $canBatchAction = $canBatchEdit or $canBatchConfirm or $canBatchClose or $canBatchActivate or $canBatchChangeBranch or $canBatchChangeModule or $canBatchResolve or $canBatchAssignTo or $canBatchCancel;
+ $canBatchAction = $canBatchEdit or $canBatchConfirm or $canBatchClose or $canBatchActivate or $canBatchChangeBranch or $canBatchChangeModule or $canBatchResolve or $canBatchAssignTo;
$canView = common::hasPriv('bug', 'view');
$bugLink = inlink('view', "bugID=$bug->id");
diff --git a/module/common/model.php b/module/common/model.php
index 84ccfad48a..77ba607de0 100644
--- a/module/common/model.php
+++ b/module/common/model.php
@@ -1363,7 +1363,7 @@ EOD;
if(strpos(",{$limitedProjects},", ",$objectID,") !== false) $limitedProject = true;
if(empty($app->user->rights['rights']['my']['limited']) && !$limitedProject) return true;
}
-
+ if(empty($app->user->rights['rights']['my']['limited'])) return true;
if(!is_null($method) && strpos($method, 'batch') === 0) return false;
if(!is_null($method) && strpos($method, 'link') === 0) return false;
diff --git a/module/custom/css/ajaxmenu.css b/module/custom/css/ajaxmenu.css
index 7402724b98..b9fd5075b5 100644
--- a/module/custom/css/ajaxmenu.css
+++ b/module/custom/css/ajaxmenu.css
@@ -12,4 +12,5 @@
#menuEditor li > a .item-hidden-icon {display: none; margin-left: 3px;}
#menuEditor .dropdown-menu > li > a .item-hidden-icon {margin-left: 10px;}
#menuEditor li.menu-hidden > a .item-hidden-icon {display: inline-block;}
-#menuEditor .nav > li.menu-hidden > a {opacity: .7; border: 1px dashed #ddd; background: rgba(0,0,0,.1); margin: 0 2px;}
\ No newline at end of file
+#menuEditor .nav > li.menu-hidden > a {opacity: .7; border: 1px dashed #ddd; background: rgba(0,0,0,.1); margin: 0 2px;}
+#menuEditor .nav > li.menu-hidden > a > .dropdown{display: inline-block;}
diff --git a/module/doc/model.php b/module/doc/model.php
index 3a504b49b3..33ffe6bfda 100644
--- a/module/doc/model.php
+++ b/module/doc/model.php
@@ -35,11 +35,16 @@ class docModel extends model
if($libID)
{
$lib = $this->getLibById($libID);
- if($this->checkPriv($lib))
+ if(!$this->checkPrivLib($lib))
{
- $type = $lib->product ? 'product' : 'custom';
- $type = $lib->project ? 'project' : $type;
+ echo(js::alert($this->lang->doc->accessDenied));
+ $loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login";
+ if(strpos($this->server->http_referer, $loginLink) !== false) die(js::locate(inlink('index')));
+ die(js::locate('back'));
}
+
+ $type = $lib->product ? 'product' : 'custom';
+ $type = $lib->project ? 'project' : $type;
}
$mainLib = $type == 'custom' ? $this->lang->doc->customAB : $mainLib;
@@ -191,7 +196,7 @@ class docModel extends model
* @access public
* @return array
*/
- public function getLibs($type = '')
+ public function getLibs($type = '', $extra = '')
{
if($type == 'product' or $type == 'project')
{
@@ -215,11 +220,49 @@ class docModel extends model
$libPairs = array();
while($lib = $stmt->fetch())
{
- if($this->checkPriv($lib)) $libPairs[$lib->id] = $lib->name;
+ if($this->checkPrivLib($lib, $extra)) $libPairs[$lib->id] = $lib->name;
}
return $libPairs;
}
+ /**
+ * Get grant libs by doc.
+ *
+ * @access public
+ * @return array
+ */
+ public function getPrivLibsByDoc()
+ {
+ static $libs;
+ if($libs === null)
+ {
+ $libs = array();
+ $stmt = $this->dao->select('lib,groups,users')->from(TABLE_DOC)->where('acl')->ne('open')->andWhere("(groups != '' or users != '')")->query();
+
+ $account = ",{$this->app->user->account},";
+ $userGroups = $this->app->user->groups;
+ while($lib = $stmt->fetch())
+ {
+ if(strpos(",$lib->users,", $account) !== false)
+ {
+ $libs[$lib->lib] = $lib->lib;
+ }
+ else
+ {
+ foreach($userGroups as $groupID)
+ {
+ if(strpos(",$lib->groups,", ",$groupID,") !== false)
+ {
+ $libs[$lib->lib] = $lib->lib;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return $libs;
+ }
+
/**
* Create a library.
*
@@ -377,7 +420,7 @@ class docModel extends model
->fetchAll('id');
foreach($docs as $docID => $doc)
{
- if(!$this->checkPriv($doc)) unset($docs[$docID]);
+ if(!$this->checkPrivDoc($doc)) unset($docs[$docID]);
}
$docs = $this->dao->select('*')->from(TABLE_DOC)
->where('id')->in(array_keys($docs))
@@ -481,7 +524,7 @@ class docModel extends model
$docIdList = array();
while($doc = $stmt->fetch())
{
- if($this->checkPriv($doc)) $docIdList[$doc->id] = $doc->id;
+ if($this->checkPrivDoc($doc)) $docIdList[$doc->id] = $doc->id;
}
return $docIdList;
}
@@ -498,7 +541,7 @@ class docModel extends model
{
$doc = $this->dao->select('*')->from(TABLE_DOC)->where('id')->eq((int)$docID)->fetch();
if(!$doc) return false;
- if(!$this->checkPriv($doc))
+ if(!$this->checkPrivDoc($doc))
{
echo(js::alert($this->lang->doc->accessDenied));
$loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login";
@@ -803,13 +846,14 @@ class docModel extends model
}
/**
- * Check priv.
+ * Check priv for lib.
*
* @param object $object
+ * @param string $extra
* @access public
* @return bool
*/
- public function checkPriv($object, $type = 'lib')
+ public function checkPrivLib($object, $extra = '')
{
if($this->app->user->admin) return true;
@@ -817,29 +861,26 @@ class docModel extends model
if(!empty($object->product) and !empty($acls['products']) and !in_array($object->product, $acls['products'])) return false;
if(!empty($object->project) and !empty($acls['projects']) and !in_array($object->project, $acls['projects'])) return false;
- if(isset($object->lib))
- {
- static $libs;
- if(empty($libs)) $libs = $this->getLibs('all');
- if(!isset($libs[$object->lib])) return false;
- }
-
if($object->acl == 'open') return true;
$account = ',' . $this->app->user->account . ',';
if(isset($object->addedBy) and $object->addedBy == $this->app->user->account) return true;
- if($object->acl == 'private' and strpos(",$object->users,", $account) !== false) return true;
+ if(strpos(",$object->users,", $account) !== false) return true;
if($object->acl == 'custom')
{
- if(strpos(",$object->users,", $account) !== false) return true;
-
$userGroups = $this->app->user->groups;
foreach($userGroups as $groupID)
{
if(strpos(",$object->groups,", ",$groupID,") !== false) return true;
}
}
- if(isset($object->lib)) return false;
+
+ if(strpos($extra, 'notdoc') === false)
+ {
+ static $extraDocLibs;
+ if($extraDocLibs === null) $extraDocLibs = $this->getPrivLibsByDoc();
+ if(isset($extraDocLibs[$object->id])) return true;
+ }
if($object->project)
{
@@ -858,6 +899,42 @@ class docModel extends model
return false;
}
+ /**
+ * Check priv for doc.
+ *
+ * @param object $object
+ * @access public
+ * @return bool
+ */
+ public function checkPrivDoc($object)
+ {
+ if($this->app->user->admin) return true;
+
+ static $extraDocLibs;
+ if($extraDocLibs === null) $extraDocLibs = $this->getPrivLibsByDoc();
+
+ static $libs;
+ if($libs === null) $libs = $this->getLibs('all', 'notdoc');
+ if(isset($libs[$object->lib]) and isset($extraDocLibs[$object->lib])) unset($extraDocLibs[$object->lib]);
+ if(!isset($libs[$object->lib]) and !isset($extraDocLibs[$object->lib])) return false;
+
+ if($object->acl == 'open' and !isset($extraDocLibs[$object->lib])) return true;
+
+ $account = ',' . $this->app->user->account . ',';
+ if(isset($object->addedBy) and $object->addedBy == $this->app->user->account) return true;
+ if(strpos(",$object->users,", $account) !== false) return true;
+ if($object->acl == 'custom')
+ {
+ $userGroups = $this->app->user->groups;
+ foreach($userGroups as $groupID)
+ {
+ if(strpos(",$object->groups,", ",$groupID,") !== false) return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Get all libs by type.
*
@@ -1017,7 +1094,7 @@ class docModel extends model
{
if($limit && $i > $limit) break;
$key = ($type == 'product' or $type == 'project') ? $type : 'id';
- if($this->checkPriv($docLib) and !isset($libs[$docLib->$key]))
+ if($this->checkPrivLib($docLib) and !isset($libs[$docLib->$key]))
{
$libs[$docLib->$key] = $docLib->name;
$i++;
@@ -1062,7 +1139,7 @@ class docModel extends model
{
foreach($libs as $lib)
{
- if($this->checkPriv($lib)) $buildGroups[$objectID][$lib->id] = $lib->name;
+ if($this->checkPrivLib($lib)) $buildGroups[$objectID][$lib->id] = $lib->name;
}
if($type == 'product' and isset($hasProject[$objectID]) and common::hasPriv('doc', 'allLibs')) $buildGroups[$objectID]['project'] = $this->lang->doclib->project;
if(common::hasPriv('doc', 'showFiles')) $buildGroups[$objectID]['files'] = $this->lang->doclib->files;
@@ -1104,7 +1181,7 @@ class docModel extends model
$libs = array();
foreach($objectLibs as $lib)
{
- if($this->checkPriv($lib)) $libs[$lib->id] = $lib;
+ if($this->checkPrivLib($lib)) $libs[$lib->id] = $lib;
}
$itemCounts = $this->statLibCounts(array_keys($libs));
@@ -1154,7 +1231,7 @@ class docModel extends model
$docCounts = array();
foreach($docs as $doc)
{
- if(!$this->checkPriv($doc)) continue;
+ if(!$this->checkPrivDoc($doc)) continue;
if(!isset($docCounts[$doc->lib])) $docCounts[$doc->lib] = 0;
$docCounts[$doc->lib] ++;
}
@@ -1272,7 +1349,7 @@ class docModel extends model
$docGroups = array();
foreach($docs as $doc)
{
- if($this->checkPriv($doc)) $docGroups[$doc->module][$doc->id] = $doc;
+ if($this->checkPrivDoc($doc)) $docGroups[$doc->module][$doc->id] = $doc;
}
}
@@ -1326,7 +1403,7 @@ class docModel extends model
if(empty($libID)) return '';
$lib = $this->getLibById($libID);
- if(!$this->checkPriv($lib))
+ if(!$this->checkPrivLib($lib))
{
echo(js::alert($this->lang->doc->accessDenied));
$loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login";
diff --git a/module/file/model.php b/module/file/model.php
index c96813bfdf..ab7a49ee2a 100644
--- a/module/file/model.php
+++ b/module/file/model.php
@@ -542,13 +542,13 @@ class fileModel extends model
if($pos === false)
{
$data[$row][$col] .= "\n" . $line;
- $data[$row][$col] = str_replace(',', ',', trim($data[$row][$col], '"'));
+ $data[$row][$col] = str_replace(',', ',', $data[$row][$col]);
continue;
}
else
{
- $data[$row][$col] .= "\n" . substr($line, 0, $pos + 1);
- $data[$row][$col] = trim(str_replace(',', ',', trim($data[$row][$col], '"')));
+ $data[$row][$col] .= "\n" . substr($line, 0, $pos);
+ $data[$row][$col] = trim(str_replace(',', ',', $data[$row][$col]));
$line = substr($line, $pos + 2);
$col++;
}
@@ -561,20 +561,20 @@ class fileModel extends model
/* the cell has '"', the delimiter is '",'. */
if($line{0} == '"')
{
- $pos = strpos($line, '",');
+ $pos = strpos($line, '",');
if($pos === false)
{
- $data[$row][$col] = $line;
+ $data[$row][$col] = substr($line, 1);
/* if line is not empty, then the data of cell is not end. */
if(strlen($line) >= 1) continue 2;
$line = '';
}
else
{
- $data[$row][$col] = substr($line, 0, $pos + 1);
+ $data[$row][$col] = substr($line, 1, $pos - 1);
$line = substr($line, $pos + 2);
}
- $data[$row][$col] = str_replace(',', ',', trim($data[$row][$col], '"'));
+ $data[$row][$col] = str_replace(',', ',', $data[$row][$col]);
}
else
{
@@ -593,7 +593,7 @@ class fileModel extends model
}
}
- $data[$row][$col] = trim(str_replace(',', ',', trim($data[$row][$col], '"')));
+ $data[$row][$col] = trim(str_replace(',', ',', $data[$row][$col]));
$col++;
}
}
diff --git a/module/file/view/printfiles.html.php b/module/file/view/printfiles.html.php
index 8ac6804d41..0e978e6449 100644
--- a/module/file/view/printfiles.html.php
+++ b/module/file/view/printfiles.html.php
@@ -78,10 +78,13 @@ $sessionString .= session_name() . '=' . session_id();
echo html::a($this->createLink('file', 'download', "fileID=$file->id") . $sessionString, $fileTitle . " ({$fileSize})", '_blank', "onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth)\"");
- echo " ";
- common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='edit iframe text-primary' title='{$lang->file->edit}'");
- if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
- echo '';
+ if(common::hasPriv($file->objectType, 'edit', $file->objectID))
+ {
+ echo " ";
+ common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='edit iframe text-primary' title='{$lang->file->edit}'");
+ if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
+ echo '';
+ }
echo '';
}
}
diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php
index e5b4c78c5a..51361c10c7 100644
--- a/module/group/lang/resource.php
+++ b/module/group/lang/resource.php
@@ -337,6 +337,8 @@ $lang->resource->project->updateOrder = 'updateOrder';
$lang->resource->project->kanban = 'kanban';
$lang->resource->project->printKanban = 'printKanban';
$lang->resource->project->tree = 'tree';
+$lang->resource->project->treeTask = 'treeTask';
+$lang->resource->project->treeStory = 'treeStory';
$lang->resource->project->all = 'all';
$lang->resource->project->kanbanHideCols = 'kanbanHideCols';
$lang->resource->project->kanbanColsColor = 'kanbanColsColor';
@@ -382,12 +384,14 @@ $lang->project->methodOrder[170] = 'updateOrder';
$lang->project->methodOrder[175] = 'kanban';
$lang->project->methodOrder[180] = 'printKanban';
$lang->project->methodOrder[185] = 'tree';
-$lang->project->methodOrder[190] = 'all';
-$lang->project->methodOrder[195] = 'kanbanHideCols';
-$lang->project->methodOrder[200] = 'kanbanColsColor';
-$lang->project->methodOrder[205] = 'export';
-$lang->project->methodOrder[210] = 'storyKanban';
-$lang->project->methodOrder[215] = 'storySort';
+$lang->project->methodOrder[190] = 'tree';
+$lang->project->methodOrder[195] = 'tree';
+$lang->project->methodOrder[200] = 'all';
+$lang->project->methodOrder[205] = 'kanbanHideCols';
+$lang->project->methodOrder[210] = 'kanbanColsColor';
+$lang->project->methodOrder[215] = 'export';
+$lang->project->methodOrder[220] = 'storyKanban';
+$lang->project->methodOrder[225] = 'storySort';
/* Task. */
$lang->resource->task = new stdclass();
diff --git a/module/productplan/js/create.js b/module/productplan/js/create.js
index 480f5822d2..db1aff6aca 100644
--- a/module/productplan/js/create.js
+++ b/module/productplan/js/create.js
@@ -50,6 +50,14 @@ $('#end').on('change', function()
$("input:radio[name='delta']").attr("checked", false);
});
+$('form').submit(function()
+{
+ $('#submit').click(function()
+ {
+ return false;
+ });
+})
+
$('#future').on('change', function()
{
if($(this).prop('checked'))
diff --git a/module/project/lang/en.php b/module/project/lang/en.php
index 45e311efc2..93e30b51e3 100644
--- a/module/project/lang/en.php
+++ b/module/project/lang/en.php
@@ -145,6 +145,8 @@ $lang->project->importPlanStories = 'Link Story From Plan';
$lang->project->importBug = 'Import Bugs';
$lang->project->updateOrder = 'Sort';
$lang->project->tree = 'Tree';
+$lang->project->treeTask = 'Show Task';
+$lang->project->treeStory = 'Show Story';
$lang->project->storyKanban = 'Story Kanban';
$lang->project->storySort = 'Sort Story';
$lang->project->importPlanStory = '' . $lang->projectCommon . ' is created!\nDo you want to iport stories linked to the plan?';
diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php
index 7f44be0252..73053d872b 100644
--- a/module/project/lang/zh-cn.php
+++ b/module/project/lang/zh-cn.php
@@ -145,6 +145,8 @@ $lang->project->importPlanStories = '按计划关联需求';
$lang->project->importBug = '导入Bug';
$lang->project->updateOrder = '排序';
$lang->project->tree = '树状图';
+$lang->project->treeTask = '只看任务';
+$lang->project->treeStory = '只看需求';
$lang->project->storyKanban = '需求看板';
$lang->project->storySort = '需求排序';
$lang->project->importPlanStory = '创建' . $lang->projectCommon . '成功!\n是否导入计划关联的相关需求?';
diff --git a/module/testcase/control.php b/module/testcase/control.php
index d336618f8c..fe2735701e 100644
--- a/module/testcase/control.php
+++ b/module/testcase/control.php
@@ -1112,7 +1112,7 @@ class testcase extends control
$case->stepExpect = '';
$case->real = '';
$result = isset($results[$case->id]) ? $results[$case->id] : array();
- if($case->lastRunResult == 'fail' and !isset($relatedSteps[$case->id]) and !empty($result)) $case->real = $result[0]['real'];
+ $case->real = $result[0]['real'];
if(isset($relatedSteps[$case->id]))
{
$i = $childId = 0;
@@ -1133,7 +1133,7 @@ class testcase extends control
$sign = (in_array($this->post->fileType, array('html', 'xml'))) ? '
' : "\n";
$case->stepDesc .= $stepId . ". " . $step->desc . $sign;
$case->stepExpect .= $stepId . ". " . $step->expect . $sign;
- if($case->lastRunResult == 'fail') $case->real .= $stepId . ". " . (isset($result[$step->id]) ? $result[$step->id]['real'] : '') . $sign;
+ $case->real .= $stepId . ". " . (isset($result[$step->id]) ? $result[$step->id]['real'] : '') . $sign;
$childId ++;
}
}
diff --git a/module/user/model.php b/module/user/model.php
index 14f0a372bd..f26e9727f3 100644
--- a/module/user/model.php
+++ b/module/user/model.php
@@ -767,9 +767,9 @@ class userModel extends model
if(!empty($acl['projects'])) $acls['projects'] = !empty($acls['projects']) ? array_merge($acls['projects'], $acl['projects']) : $acl['projects'];
}
- if($productAllow && empty($acls['products'])) $acls['products'] = array();
- if($projectAllow && empty($acls['projects'])) $acls['projects'] = array();
- if($viewAllow && empty($acl['views'])) $acls['views'] = array();
+ if($productAllow) $acls['products'] = array();
+ if($projectAllow) $acls['projects'] = array();
+ if($viewAllow) $acls['views'] = array();
$sql = $this->dao->select('module, method')->from(TABLE_USERGROUP)->alias('t1')->leftJoin(TABLE_GROUPPRIV)->alias('t2')
->on('t1.group = t2.group')