Merge branch 'master' of github.com:easysoft/zentaopms

This commit is contained in:
wangyidong
2019-08-12 10:39:24 +08:00
16 changed files with 149 additions and 108 deletions
+5 -50
View File
@@ -21,10 +21,10 @@ include dirname(__FILE__) . '/base/control.class.php';
class control extends baseControl
{
/**
* 加载指定模块的model文件。
* Load the model file of one module.
*
* Extension: set appName as empty.
* 企业版部分功能是从然之合并过来的。然之代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。
* 调用父类的loadModel方法来避免这个错误。
* Some codes merged from ranzhi called the function loadModel with a non-empty appName which causes an error in zentao.
* Call the parent function with empty appName to avoid this error.
*
* @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
* @param string $appName The app name, if empty, use current app's name.
@@ -33,52 +33,7 @@ class control extends baseControl
*/
public function loadModel($moduleName = '', $appName = '')
{
$appName = '';
if(empty($moduleName)) $moduleName = $this->moduleName;
if(empty($appName)) $appName = $this->appName;
global $loadedModels;
if(isset($loadedModels[$appName][$moduleName]))
{
$this->$moduleName = $loadedModels[$appName][$moduleName];
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
}
$modelFile = $this->app->setModelFile($moduleName, $appName);
/**
* 如果没有model文件,尝试加载config配置信息。
* If no model file, try load config.
*/
if(!helper::import($modelFile))
{
$this->app->loadModuleConfig($moduleName, $appName);
$this->app->loadLang($moduleName, $appName);
$this->dao = new dao();
return false;
}
/**
* 如果没有扩展文件,model类名是$moduleName + 'model',如果有扩展,还需要增加ext前缀。
* If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix.
*/
$modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model';
if(!class_exists($modelClass))
{
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
}
/**
* 初始化model对象,在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量,方便引用。
* Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object.
*/
$loadedModels[$appName][$moduleName] = new $modelClass($appName);
$this->$moduleName = $loadedModels[$appName][$moduleName];
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
return parent::loadModel($moduleName);
}
/**
+5 -31
View File
@@ -21,12 +21,10 @@ include dirname(__FILE__) . '/base/model.class.php';
class model extends baseModel
{
/**
* 加载一个模块的model。加载完成后,使用$this->$moduleName来访问这个model对象。
* 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。
*
* Load the model of one module. After loaded, can use $this->$moduleName to visit the model object.
*
* Extension: set appName as empty.
* 企业版部分功能是从然之合并过来的。然之代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。
* 调用父类的loadModel方法来避免这个错误。
* Some codes merged from ranzhi called the function loadModel with a non-empty appName which causes an error in zentao.
* Call the parent function with empty appName to avoid this error.
*
* @param string $moduleName
* @access public
@@ -34,31 +32,7 @@ class model extends baseModel
*/
public function loadModel($moduleName, $appName = '')
{
$appName = '';
if(empty($moduleName)) return false;
if(empty($appName)) $appName = $this->appName;
global $loadedModels;
if(isset($loadedModels[$appName][$moduleName]))
{
$this->$moduleName = $loadedModels[$appName][$moduleName];
return $this->$moduleName;
}
$modelFile = $this->app->setModelFile($moduleName, $appName);
if(!helper::import($modelFile)) return false;
$modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model';
if(!class_exists($modelClass))
{
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
}
$loadedModels[$appName][$moduleName] = new $modelClass($appName);
$this->$moduleName = $loadedModels[$appName][$moduleName];
return $this->$moduleName;
return parent::loadModel($moduleName);
}
/**
+8 -9
View File
@@ -66,8 +66,10 @@ class router extends baseRouter
}
/**
* 加载语言文件,返回全局$lang对象。
* Load lang and return it as the global lang object.
* 企业版部分功能是从然之合并过来的。然之代码中调用loadLang方法时传递了一个非空的appName,在禅道中会导致错误。
* 把appName设置为空来避免这个错误。
* Some codes merged from ranzhi called the function loadLang with a non-empty appName which causes an error in zentao.
* Set the value of appName to empty to avoid this error.
*
* @param string $moduleName the module name
* @param string $appName the app name
@@ -176,13 +178,10 @@ class router extends baseRouter
}
/**
* 加载模块的config文件,返回全局$config对象。
* 如果该模块是common,加载$configRoot的配置文件,其他模块则加载其模块的配置文件。
*
* Load config and return it as the global config object.
* If the module is common, search in $configRoot, else in $modulePath.
*
* Extension: set appName as empty.
* 企业版部分功能是从然之合并过来的。然之代码中调用loadModuleConfig方法时传递了一个非空的appName,在禅道中会导致错误。
* 把appName设置为空来避免这个错误。
* Some codes merged from ranzhi called the function loadModuleConfig with a non-empty appName which causes an error in zentao.
* Set the value of appName to empty to avoid this error.
*
* @param string $moduleName module name
* @param string $appName app name
+1 -1
View File
@@ -53,7 +53,7 @@
$flow = $config->action->customFlows[$action->objectType];
$module = $flow->module;
}
if(strpos(',doclib,module,webhook,workflowdatasource,workflowfield,workflowlabel,workflowlayout,workflowrule,', ",{$module},") !== false)
if(strpos(',doclib,module,webhook,', ",{$module},") !== false)
{
echo $action->objectName;
}
+1 -1
View File
@@ -19,7 +19,7 @@ if(!$selfCall) die(include('./todolist.html.php'));
.block-todoes .todoes-input .form-control:-ms-input-placeholder {font-size: 12px; line-height: 20px;color: #a4a8b6;}
.block-todoes .todoes-input .form-control::placeholder {font-size: 12px; line-height: 20px; color: #a4a8b6;}
.block-todoes .todoes {padding: 0 10px 10px 10px; margin: 0 -20px; max-height: 350px; overflow: auto; overflow-x:hidden}
.block-todoes .todoes > li {position: relative; padding: 5px 10px 5px 35px; list-style: none; white-space:nowrap; overflow: auto; overflow-x:hidden;}
.block-todoes .todoes > li {position: relative; padding: 5px 10px 5px 35px; list-style: none; white-space:nowrap; overflow: auto; overflow-x:hidden; max-width: 820px;}
.block-todoes .todoes > li:hover {background-color: #e9f2fb;}
.block-todoes .todo-title {padding: 5px 15px 5px 5px;}
.block-todoes .todo-pri {margin: 0 5px;}
+1
View File
@@ -45,6 +45,7 @@ class doc extends control
$this->lang->modulePageActions = $this->doc->setFastMenu($this->lang->doc->fast);
$this->lang->modulePageActions .= common::hasPriv('doc', 'createLib') ? html::a(helper::createLink('doc', 'createLib'), "<i class='icon icon-plus'></i> " . $this->lang->doc->createLib, '', "class='btn btn-secondary iframe' data-width='70%'") : '';
$this->lang->modulePageActions .= common::hasPriv('doc', 'create') ? $this->doc->setCreateDocMenu() : '';
$actionURL = $this->createLink('doc', 'browse', "lib=0&browseType=bySearch&queryID=myQueryID");
$this->doc->buildSearchForm(0, array(), 0, $actionURL, 'index');
-1
View File
@@ -52,7 +52,6 @@ $(document).ready(function()
$('#module').trigger('chosen:close');
});
$('#pageActions ul.dropdown-menu').css('left', '67px');
$('.libs-group.sort').sortable(
{
trigger: '.lib',
+36 -3
View File
@@ -147,7 +147,13 @@ class docModel extends model
}
elseif($type == 'all')
{
$stmt = $this->dao->select('*')->from(TABLE_DOCLIB)->where('deleted')->eq(0)->orderBy('`order`, id desc')->query();
/* Associated display Settings -> shows only unclosed projects.*/
$stmt = $this->dao->select('distinct t1.*')->from(TABLE_DOCLIB)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on("t1.project = '' || t1.project = t2.id")
->where('t1.deleted')->eq(0)
->beginIF(strpos($this->config->doc->custom->showLibs,'unclosed') !== false)->andWhere('t2.status')->notin('done,closed')->fi()
->orderBy('t1.order,t1.id desc')
->query();
}
else
{
@@ -174,7 +180,7 @@ class docModel extends model
if($lib->project != 0) $lib->name = zget($projects, $lib->project, '') . '/' . $lib->name;
}
$libPairs[$lib->id] = $lib->name;
$libPairs[$lib->id] = '/' . $lib->name;
}
}
@@ -183,7 +189,7 @@ class docModel extends model
$stmt = $this->dao->select('*')->from(TABLE_DOCLIB)->where('id')->in($appendLibs)->orderBy('`order`, id desc')->query();
while($lib = $stmt->fetch())
{
if(!isset($libPairs[$lib->id]) and $this->checkPrivLib($lib, $extra)) $libPairs[$lib->id] = $lib->name;
if(!isset($libPairs[$lib->id]) and $this->checkPrivLib($lib, $extra)) $libPairs[$lib->id] = '/' . $lib->name;
}
}
@@ -1603,6 +1609,33 @@ class docModel extends model
return $title;
}
/**
* Set document module index page create document button.
*
* @access public
* @return void
*/
public function setCreateDocMenu()
{
$libID = $this->dao->select('id')->from(TABLE_DOCLIB)->where('deleted')->eq(0)->orderBy('id desc')->limit('1')->fetch('id');
$actions = "";
if($libID)
{
$actions .= "<div class='dropdown' id='createDropdown'>";
$actions .= "<button class='btn btn-primary' type='button' data-toggle='dropdown'><i class='icon icon-plus'></i>" . $this->lang->doc->create . "<span class='caret'></span></button>";
$actions .= "<ul class='dropdown-menu' style='left:0px'>";
foreach($this->lang->doc->typeList as $typeKey => $typeName)
{
$class = strpos($this->config->doc->officeTypes, $typeKey) !== false ? 'iframe' : '';
$actions .= "<li>";
$actions .= html::a(helper::createLink('doc', 'create', "libID=$libID&moduleID=0&type=$typeKey"), $typeName, '', "class='$class'");
$actions .= "</li>";
}
$actions .="</ul></div>";
}
return $actions;
}
public function setFastMenu($fastLib)
{
$actions = '';
+1 -1
View File
@@ -58,11 +58,11 @@
<th><?php echo $lang->product->status;?></th>
<td><?php echo html::select('status', $lang->product->statusList, $product->status, "class='form-control'");?></td><td></td>
</tr>
<?php $this->printExtendFields($product, 'table', 'columns=1');?>
<tr>
<th><?php echo $lang->product->desc;?></th>
<td colspan='2'><?php echo html::textarea('desc', htmlspecialchars($product->desc), "rows='8' class='form-control'");?></td>
</tr>
<?php $this->printExtendFields($product, 'table', 'columns=1');?>
<tr>
<th><?php echo $lang->product->acl;?></th>
<td colspan='2'><?php echo nl2br(html::radio('acl', $lang->product->aclList, $product->acl, "onclick='setWhite(this.value);'", 'block'));?></td>
+1 -1
View File
@@ -52,11 +52,11 @@
<td><?php echo html::input('end', $plan->end != '2030-01-01' ? formatTime($plan->end) : '', 'class="form-control form-date"');?></td>
<td colspan='2'><?php echo html::radio('delta', $lang->productplan->endList , '', "onclick='computeEndDate(this.value)'");?></td>
</tr>
<?php $this->printExtendFields($plan, 'table', 'columns=1');?>
<tr>
<th><?php echo $lang->productplan->desc;?></th>
<td colspan='3'><?php echo html::textarea('desc', htmlspecialchars($plan->desc), "rows='10' class='form-control kindeditor' hidefocus='true'");?></td>
</tr>
<?php $this->printExtendFields($plan, 'table', 'columns=3');?>
<tr>
<td colspan='4' class='text-center form-actions'>
<?php echo html::submitButton();?>
+1 -1
View File
@@ -130,7 +130,7 @@ class releaseModel extends model
->add('builder', $this->app->user->account)
->add('branch', $branch)
->stripTags($this->config->release->editor->create['id'], $this->config->allowedTags)
->remove('marker,build,files,labels,uid')
->remove('marker,build,files,labels,uid,subStatus') // There is the subStatus field in the biz version.
->get();
$build = $this->loadModel('file')->processImgURL($build, $this->config->release->editor->create['id']);
$this->dao->insert(TABLE_BUILD)->data($build)
+1 -1
View File
@@ -47,11 +47,11 @@
<th><?php echo $lang->release->status;?></th>
<td><?php echo html::select('status', $lang->release->statusList, $release->status, "class='form-control'");?></td><td></td>
</tr>
<?php $this->printExtendFields($release, 'table', 'columns=1');?>
<tr>
<th><?php echo $lang->release->desc;?></th>
<td colspan='2'><?php echo html::textarea('desc', htmlspecialchars($release->desc), "rows=10 class='form-control kindeditor' hidefocus='true'");?></td>
</tr>
<?php $this->printExtendFields($release, 'table', 'columns=2');?>
<tr>
<th><?php echo $lang->files;?></th>
<td colspan='2'><?php echo $this->fetch('file', 'buildform');?></td>
+29 -3
View File
@@ -2511,9 +2511,7 @@ class storyModel extends model
echo substr($story->openedDate, 5, 11);
break;
case 'assignedTo':
$assignedToText = zget($users, $story->assignedTo, $story->assignedTo);
$btnTextClass = ($story->assignedTo == $this->app->user->account) ? 'text-red' : '';
echo "<span style='padding-left:10px;' class='{$btnTextClass}'>{$assignedToText}</span>";
$this->printAssignedHtml($story, $users);
break;
case 'assignedDate':
echo substr($story->assignedDate, 5, 11);
@@ -2564,6 +2562,34 @@ class storyModel extends model
}
}
/**
* Product module story page add assignment function.
*
* @param object $story
* @param array $users
* @access public
* @return void
*/
public function printAssignedHtml($story, $users)
{
$btnTextClass = '';
$assignedToText = zget($users, $story->assignedTo);
if(empty($story->assignedTo))
{
$btnTextClass = 'text-primary';
$assignedToText = $this->lang->task->noAssigned;
}
if($story->assignedTo == $this->app->user->account) $btnTextClass = 'text-red';
$btnClass = $story->assignedTo == 'closed' ? ' disabled' : '';
$btnClass = "iframe btn btn-icon-left btn-sm {$btnClass}";
$assignToLink = helper::createLink('story', 'assignTo', "storyID=$story->id", '', true);
$assignToHtml = html::a($assignToLink, "<i class='icon icon-hand-right'></i> <span class='{$btnTextClass}'>{$assignedToText}</span>", '', "class='$btnClass'");
echo !common::hasPriv('story', 'assignTo', $story) ? "<span style='padding-left: 21px' class='{$btnTextClass}'>{$assignedToText}</span>" : $assignToHtml;
}
/**
* Set report condition.
*
+23 -4
View File
@@ -315,6 +315,7 @@ class taskModel extends model
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchCreate');
if($parentID > 0 && !empty($taskID))
{
$oldParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq((int)$parentID)->fetch();
$this->updateParentStatus($taskID);
$this->computeBeginAndEnd($parentID);
@@ -324,7 +325,10 @@ class taskModel extends model
$task->lastEditedDate = $now;
$this->dao->update(TABLE_TASK)->data($task)->where('id')->eq($parentID)->exec();
$this->action->create('task', $parentID, 'createChildren', '', trim($childTasks, ','));
$newParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq((int)$parentID)->fetch();
$changes = common::createChanges($oldParentTask, $newParentTask);
$actionID = $this->action->create('task', $parentID, 'createChildren', '', trim($childTasks, ','));
if(!empty($changes)) $this->action->logHistory($actionID, $changes);
}
return $mails;
}
@@ -408,6 +412,7 @@ class taskModel extends model
if(empty($parentID)) $parentID = $childTask->parent;
if($parentID <= 0) return true;
$oldParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq($parentID)->fetch();
$this->computeWorkingHours($parentID);
$childrenStatus = $this->dao->select('id,status')->from(TABLE_TASK)->where('parent')->eq($parentID)->andWhere('deleted')->eq(0)->fetchPairs('status', 'status');
@@ -499,7 +504,8 @@ class taskModel extends model
{
if(!$createAction) return $task;
$changes = common::createChanges($parentTask, $task);
$newParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq($parentID)->fetch();
$changes = common::createChanges($oldParentTask, $newParentTask);
$action = '';
if($status == 'done') $action = 'Finished';
if($status == 'closed') $action = 'Closed';
@@ -515,6 +521,19 @@ class taskModel extends model
}
}
}
else
{
if(!dao::isError())
{
$newParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq($parentID)->fetch();
$changes = common::createChanges($oldParentTask, $newParentTask);
if($changes)
{
$actionID = $this->loadModel('action')->create('task', $parentID, 'Edited');
$this->action->logHistory($actionID, $changes);
}
}
}
}
/**
@@ -1103,7 +1122,7 @@ class taskModel extends model
->setDefault('account', $this->app->user->account)
->setDefault('task', $taskID)
->setDefault('date', $task->realStarted)
->remove('realStarted,comment')
->remove('realStarted,comment,status')
->get();
$estimate->consumed = $estimate->consumed - $oldTask->consumed;
$this->addTaskEstimate($estimate);
@@ -1329,7 +1348,7 @@ class taskModel extends model
->setDefault('date', date(DT_DATE1))
->setIF($this->post->finishedDate, 'date', $this->post->finishedDate)
->setDefault('left', 0)
->remove('finishedDate,comment,assignedTo,files,labels,consumed,currentConsumed')
->remove('finishedDate,comment,assignedTo,files,labels,consumed,currentConsumed,status')
->get();
$estimate->consumed = $consumed;
+35
View File
@@ -1,3 +1,32 @@
/**
* load builds of selected product project.
*
* @param productID $productID
* @access public
* @return void
*/
function loadProductRelated(productID)
{
projectID = $('#project').val();
loadProductProjectBuilds(productID, projectID);
}
/**
* loadProductProjectBuilds
*
* @param productID $productID
* @param projectID $projectID
* @access public
* @return void
*/
function loadProductProjectBuilds(productID, projectID)
{
selectedBuild = $('#build').val();
if(!selectedBuild) selectedBuild = 0;
link = createLink('build', 'ajaxGetProjectBuilds', 'projectID=' + projectID + '&productID=' + productID + '&varName=testTaskBuild&build=' + selectedBuild);
$('#buildBox').load(link, function(){$('#build').chosen();});
}
/**
* Load project related builds
*
@@ -62,4 +91,10 @@ function suitEndDate()
$(function()
{
adjustPriBoxWidth();
if($('#project').val())
{
productID = $('#product').val();
projectID = $('#project').val();
loadProductProjectBuilds(productID, projectID);
}
});
+1 -1
View File
@@ -24,7 +24,7 @@
<?php if(isset($projectID)):?>
<tr>
<th class='w-80px'><?php echo $lang->testtask->product;?></th>
<td class='w-p35-f'><?php echo html::select('product', $products, $productID, "class='form-control chosen'");?></td><td></td>
<td class='w-p35-f'><?php echo html::select('product', $products, $productID, "class='form-control chosen' onchange='loadProductRelated(this.value)'");?></td><td></td>
</tr>
<?php else:?>
<tr class='hide'>