Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
#cards .project-detail .progress-text-left .progress-text {width: 50px; left: -50px;}
|
||||
#cards .panel-heading {cursor: pointer;}
|
||||
#cards .project-stages-container {margin: 0 0 -16px 0; padding: 0 4px; height: 46px; overflow-x: auto; position: relative;}
|
||||
#cards .project-stages:after {content: ' '; width: 30px; display: block; right: -16px; top: 16px; bottom: -6px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;}
|
||||
#cards .project-stages:after {content: ' '; width: 30px; display: block; right: -5px; top: 16px; bottom: -5px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;}
|
||||
#cards .project-stages-row {position: relative; height: 30px; z-index: 0;}
|
||||
#cards .project-stage-item {white-space: nowrap; position: absolute; top: 0; min-width: 48px; padding-top: 13px; color: #838A9D;}
|
||||
#cards .project-stage-item > div {white-space: nowrap; overflow: visible; text-align: center; text-overflow: ellipsis;}
|
||||
@@ -73,7 +73,7 @@
|
||||
<span><i class='icon icon-clock'></i> <?php printf($lang->project->hoursUnit, $project->estimate); ?></span>
|
||||
</div>
|
||||
<?php if($config->systemMode == 'new'):?>
|
||||
<?php if($project->model === 'waterfall'): ?>
|
||||
<?php if($project->model !== 'waterfall'): ?>
|
||||
<div class='project-detail project-stages'>
|
||||
<?php
|
||||
$projectProjects = array();
|
||||
|
||||
@@ -2901,77 +2901,6 @@ class bugModel extends model
|
||||
echo !common::hasPriv('bug', 'assignTo', $bug) ? "<span style='padding-left: 21px' class='{$btnTextClass}'>{$assignedToText}</span>" : $assignToHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param int $bugID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($bugID, $actionID)
|
||||
{
|
||||
$this->loadModel('mail');
|
||||
$bug = $this->getByID($bugID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* Get action info. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
$action->appendLink = '';
|
||||
if(strpos($action->extra, ':') !== false)
|
||||
{
|
||||
list($extra, $id) = explode(':', $action->extra);
|
||||
if($id and is_numeric($id))
|
||||
{
|
||||
$action->extra = $extra;
|
||||
|
||||
$name = $this->dao->select('title')->from(TABLE_BUG)->where('id')->eq($id)->fetch('title');
|
||||
if($name) $action->appendLink = html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink($action->objectType, 'view', "id=$id", 'html'), "#$id " . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'bug');
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
$sendUsers = $this->getToAndCcList($bug);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = $this->getSubject($bug);
|
||||
|
||||
/* Send it. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subject.
|
||||
*
|
||||
* @param object $bug
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($bug)
|
||||
{
|
||||
$productName = $this->loadModel('product')->getById($bug->product)->name;
|
||||
return 'BUG #'. $bug->id . ' ' . $bug->title . ' - ' . $productName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = 'BUG #' . $bug->id . ' ' . $bug->title;?>
|
||||
<?php $mailTitle = 'BUG #' . $object->id . ' ' . $object->title;?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
<td>
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php $color = empty($bug->color) ? '#333' : $bug->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('bug', 'view', "bugID=$bug->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
<?php $color = empty($object->color) ? '#333' : $object->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('bug', 'view', "bugID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->bug->legendSteps;?></legend>
|
||||
<div style='padding:5px;'><?php echo $bug->steps;?></div>
|
||||
<div style='padding:5px;'><?php echo $object->steps;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -281,9 +281,7 @@ class commonModel extends model
|
||||
$isGuest = $app->user->account == 'guest';
|
||||
|
||||
echo "<a class='dropdown-toggle' data-toggle='dropdown'>";
|
||||
echo "<div id='main-avatar' class='avatar avatar bg-secondary avatar-circle'>";
|
||||
echo !empty($app->user->avatar) ? html::image($app->user->avatar) : strtoupper($app->user->account[0]);
|
||||
echo "</div>\n";
|
||||
echo html::avatar($app->user);
|
||||
echo '</a>';
|
||||
echo "<ul class='dropdown-menu pull-right'>";
|
||||
if(!$isGuest)
|
||||
@@ -291,9 +289,7 @@ class commonModel extends model
|
||||
$noRole = (!empty($app->user->role) && isset($lang->user->roleList[$app->user->role])) ? '' : ' no-role';
|
||||
echo '<li class="user-profile-item">';
|
||||
echo "<a href='" . helper::createLink('my', 'profile', '', '', true) . "' data-width='600' class='iframe $noRole'" . '>';
|
||||
echo "<div id='menu-avatar' class='avatar avatar bg-secondary avatar-circle'>";
|
||||
echo $app->user->avatar ? html::image($app->user->avatar) : strtoupper($app->user->account[0]);
|
||||
echo "</div>\n";
|
||||
echo html::avatar($app->user, '', 'avatar-circle', 'id="menu-avatar"');
|
||||
echo '<div class="user-profile-name">' . (empty($app->user->realname) ? $app->user->account : $app->user->realname) . '</div>';
|
||||
if(isset($lang->user->roleList[$app->user->role])) echo '<div class="user-profile-role">' . $lang->user->roleList[$app->user->role] . '</div>';
|
||||
echo '</a></li><li class="divider"></li>';
|
||||
|
||||
+35
-6
@@ -1,9 +1,24 @@
|
||||
/**
|
||||
* Load modules by libID.
|
||||
*
|
||||
* @param int $libID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadModules(libID)
|
||||
{
|
||||
link = createLink('doc', 'ajaxGetModules', 'libID=' + libID);
|
||||
$('#moduleBox').load(link, function(){$('#moduleBox').find('select').chosen()});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle acl.
|
||||
*
|
||||
* @param string $acl
|
||||
* @param string $type
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function toggleAcl(acl, type)
|
||||
{
|
||||
if(acl == 'custom')
|
||||
@@ -27,6 +42,13 @@ function toggleAcl(acl, type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load doc module by libID.
|
||||
*
|
||||
* @param int $libID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadDocModule(libID)
|
||||
{
|
||||
link = createLink('doc', 'ajaxGetChild', 'libID=' + libID);
|
||||
@@ -38,6 +60,13 @@ function loadDocModule(libID)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookie of browse type and reload.
|
||||
*
|
||||
* @param type $type
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function setBrowseType(type)
|
||||
{
|
||||
$.cookie('browseType', type, {expires:config.cookieLife, path:config.webRoot});
|
||||
@@ -46,7 +75,7 @@ function setBrowseType(type)
|
||||
|
||||
$(document).ready(function()
|
||||
{
|
||||
/* hide #module chosen dropdown on #lib dropdown show */
|
||||
/* Hide #module chosen dropdown on #lib dropdown show. */
|
||||
$('#lib').on('chosen:showing_dropdown', function()
|
||||
{
|
||||
$('#module').trigger('chosen:close');
|
||||
@@ -83,9 +112,9 @@ $(document).ready(function()
|
||||
|
||||
'use strict';
|
||||
|
||||
var NAME = 'zui.splitRow'; // model name
|
||||
var NAME = 'zui.splitRow'; // model name.
|
||||
|
||||
/* The SplitRow model class */
|
||||
/* The SplitRow model class. */
|
||||
var SplitRow = function(element, options)
|
||||
{
|
||||
var that = this;
|
||||
@@ -181,7 +210,7 @@ $(document).ready(function()
|
||||
resizeCols();
|
||||
};
|
||||
|
||||
/* default options */
|
||||
/* default options. */
|
||||
SplitRow.DEFAULTS =
|
||||
{
|
||||
spliter: '<div class="col-spliter"></div>',
|
||||
@@ -189,7 +218,7 @@ $(document).ready(function()
|
||||
middleSize: 850
|
||||
};
|
||||
|
||||
/* Extense jquery element */
|
||||
/* Extense jquery element. */
|
||||
$.fn.splitRow = function(option)
|
||||
{
|
||||
return this.each(function()
|
||||
@@ -205,7 +234,7 @@ $(document).ready(function()
|
||||
|
||||
$.fn.splitRow.Constructor = SplitRow;
|
||||
|
||||
/* Auto call splitRow after document load complete */
|
||||
/* Auto call splitRow after document load complete. */
|
||||
$(function()
|
||||
{
|
||||
$('.split-row').splitRow();
|
||||
|
||||
@@ -21,7 +21,7 @@ $(function()
|
||||
$('.ke-toolbar .ke-outline:last').after("<span data-name='unlink' class='ke-outline' title='Markdown' onclick='toggleEditor(\"markdown\")' style='font-size: unset; line-height: unset;'>Markdown</span>");
|
||||
}
|
||||
|
||||
$(document).on("mousedown", 'span[data-name="fullscreen"]', function()
|
||||
$(document).on("mouseup", 'span[data-name="fullscreen"]', function()
|
||||
{
|
||||
if($(this).hasClass('ke-selected'))
|
||||
{
|
||||
@@ -35,7 +35,7 @@ $(function()
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("mousedown", 'a[title="Fullscreen"],.icon-columns', function()
|
||||
$(document).on("mouseup", 'a[title="Fullscreen"],.icon-columns', function()
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ $(function()
|
||||
/* Automatically save document contents. */
|
||||
setInterval("saveTempContent()", 60 * 1000);
|
||||
|
||||
$(document).on("mousedown", 'span[data-name="fullscreen"]', function()
|
||||
$(document).on("mouseup", 'span[data-name="fullscreen"]', function()
|
||||
{
|
||||
if(config.onlybody == 'no')
|
||||
{
|
||||
@@ -50,7 +50,7 @@ $(function()
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("mousedown", 'a[title="Fullscreen"],.icon-columns', function()
|
||||
$(document).on("mouseup", 'a[title="Fullscreen"],.icon-columns', function()
|
||||
{
|
||||
if(config.onlybody == 'no')
|
||||
{
|
||||
|
||||
+3
-81
@@ -1875,10 +1875,10 @@ class docModel extends model
|
||||
if($doc->type == 'project') $objectID = $doc->project;
|
||||
if($doc->type == 'execution') $objectID = $doc->execution;
|
||||
|
||||
$app = $this->app->openApp;
|
||||
if($app != 'doc') $app = $objectID ? $doc->type : 'doc';
|
||||
$tab = $this->app->openApp;
|
||||
if($tab != 'doc') $tab = $objectID ? $doc->type : 'doc';
|
||||
|
||||
$html .= '<li>' . html::a(inlink('objectLibs', "type={$doc->type}&objectID=$objectID&libID={$doc->lib}&docID={$doc->id}"), "<i class='icon icon-file-text'></i> " . $doc->title, '', "data-app='$app' title='{$doc->title}'") . '</li>';
|
||||
$html .= '<li>' . html::a(inlink('objectLibs', "type={$doc->type}&objectID=$objectID&libID={$doc->lib}&docID={$doc->id}"), "<i class='icon icon-file-text'></i> " . $doc->title, '', "data-app='$tab' title='{$doc->title}'") . '</li>';
|
||||
}
|
||||
|
||||
$collectionCount = $this->dao->select('count(id) as count')->from(TABLE_DOC)
|
||||
@@ -1935,84 +1935,6 @@ class docModel extends model
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail.
|
||||
*
|
||||
* @param int $docID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($docID, $actionID)
|
||||
{
|
||||
/* Load module and get doc and users. */
|
||||
$this->loadModel('mail');
|
||||
$doc = $this->getById($docID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* When the content type is markdown format, add attributes to the table. */
|
||||
if($doc->contentType == 'markdown')
|
||||
{
|
||||
$doc->content = $this->app->loadClass('hyperdown')->makeHtml($doc->content);
|
||||
$doc->content = str_replace("<table>", "<table style='border-collapse: collapse;'>", $doc->content);
|
||||
$doc->content = str_replace("<th>", "<th style='word-break: break-word; border:1px solid #000;'>", $doc->content);
|
||||
$doc->content = str_replace("<td>", "<td style='word-break: break-word; border:1px solid #000;'>", $doc->content);
|
||||
}
|
||||
|
||||
/* Get action info. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'doc');
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
/* Get sender and subject. */
|
||||
$sendUsers = $this->getToAndCcList($doc);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = $this->getSubject($doc, $action->action);
|
||||
|
||||
/* Send mail. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mail subject.
|
||||
*
|
||||
* @param object $doc
|
||||
* @param string $actionType created|edited
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($doc, $actionType)
|
||||
{
|
||||
/* Set email title. */
|
||||
if($actionType == 'created')
|
||||
{
|
||||
return sprintf($this->lang->doc->mail->create->title, $this->app->user->realname, $doc->id, $doc->title);
|
||||
}
|
||||
else
|
||||
{
|
||||
return sprintf($this->lang->doc->mail->edit->title, $this->app->user->realname, $doc->id, $doc->title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = $this->lang->doc->common . ' #' . $doc->id . ' ' . $doc->title;?>
|
||||
<?php $mailTitle = $this->lang->doc->common . ' #' . $object->id . ' ' . $object->title;?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
<td>
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php $color = empty($doc->color) ? '#333' : $doc->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('doc', 'view', "docID=$doc->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
<?php $color = empty($object->color) ? '#333' : $object->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('doc', 'view', "docID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->doc->content;?></legend>
|
||||
<div style='padding: 5px;'><?php echo $doc->content;?></div>
|
||||
<div style='padding: 5px;'><?php echo $object->content;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -27,3 +27,4 @@
|
||||
|
||||
#whitelistBox .checkbox-inline, #whitelistBox .radio-inline {margin-left: 10px; display: inline-block; padding-left: 0px; padding-right: 10px;}
|
||||
.main-table > .table-responsive {padding: 0 1px; background: #fff;}
|
||||
#teams_chosen ul li:first-child {max-width:256px; display: inline-block; vertical-align: middle;}
|
||||
|
||||
@@ -46,7 +46,7 @@ $(function()
|
||||
|
||||
$('#teams_chosen').click(function()
|
||||
{
|
||||
if(systemMode == 'new') $('#teams_chosen ul li:first').append(' <label class="label">' + projectCommon + '</label>');
|
||||
if(systemMode == 'new') $('#teams_chosen ul li:first').after(' <label class="label">' + projectCommon + '</label>');
|
||||
})
|
||||
|
||||
$('#teams').change(function()
|
||||
|
||||
@@ -358,7 +358,7 @@ class executionModel extends model
|
||||
->batchcheck($this->config->execution->create->requiredFields, 'notempty')
|
||||
->checkIF($sprint->begin != '', 'begin', 'date')
|
||||
->checkIF($sprint->end != '', 'end', 'date')
|
||||
->checkIF($sprint->end != '', 'end', 'gt', $sprint->begin)
|
||||
->checkIF($sprint->end != '', 'end', 'ge', $sprint->begin)
|
||||
->checkIF(!empty($sprint->code), 'code', 'unique')
|
||||
->exec();
|
||||
|
||||
@@ -2454,7 +2454,7 @@ class executionModel extends model
|
||||
{
|
||||
if(empty($projectID)) return array();
|
||||
|
||||
$teams = $this->dao->select('id,team,type')->from(TABLE_PROJECT)
|
||||
$teams = $this->dao->select('id,name,type')->from(TABLE_PROJECT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('(project')->eq($projectID)
|
||||
->orWhere('id')->eq($projectID)
|
||||
@@ -2466,10 +2466,10 @@ class executionModel extends model
|
||||
$teamPairs = array();
|
||||
foreach($teams as $id => $team)
|
||||
{
|
||||
if(empty($team->team)) continue;
|
||||
if(empty($team->name)) continue;
|
||||
|
||||
$prefix = ($team->type != 'project' and $this->config->systemMode == 'new') ? ' ' : '';
|
||||
$teamPairs[$id] = $prefix . $team->team;
|
||||
$teamPairs[$id] = $prefix . $team->name;
|
||||
}
|
||||
|
||||
return $teamPairs;
|
||||
|
||||
@@ -190,10 +190,13 @@ class gitlab extends control
|
||||
*/
|
||||
public function checkToken()
|
||||
{
|
||||
if(strpos($this->post->url, 'http') !== 0) return $this->send(array('result' => 'fail', 'message' => array('url' => array($this->lang->gitlab->hostError))));
|
||||
if(!$this->post->token) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gitlab->tokenError))));
|
||||
$gitlabURL = trim($this->post->url);
|
||||
$token = trim($this->post->token);
|
||||
|
||||
$user = $this->gitlab->apiGetCurrentUser($this->post->url, $this->post->token);
|
||||
if(strpos($gitlabURL, 'http') !== 0) return $this->send(array('result' => 'fail', 'message' => array('url' => array($this->lang->gitlab->hostError))));
|
||||
if(!$token) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gitlab->tokenError))));
|
||||
|
||||
$user = $this->gitlab->apiGetCurrentUser($gitlabURL, $token);
|
||||
|
||||
if(!is_object($user)) return $this->send(array('result' => 'fail', 'message' => array('url' => array($this->lang->gitlab->hostError))));
|
||||
if(!isset($user->is_admin) or !$user->is_admin) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gitlab->tokenError))));
|
||||
@@ -256,6 +259,11 @@ class gitlab extends control
|
||||
$gitlabID = $repo->gitlab;
|
||||
$projectID = $repo->project;
|
||||
|
||||
$gitlab = $this->gitlab->getByID($gitlabID);
|
||||
$user = $this->gitlab->apiGetCurrentUser($gitlab->url, $gitlab->token);
|
||||
if(!isset($user->is_admin) or !$user->is_admin) die(js::alert($this->lang->gitlab->tokenLimit) . js::locate($this->createLink('gitlab', 'edit', array('gitlabID' => $gitlabID))));
|
||||
|
||||
|
||||
if($_POST)
|
||||
{
|
||||
$executionList = $this->post->executionList;
|
||||
|
||||
@@ -40,6 +40,7 @@ $lang->gitlab->placeholder->token = "Please fill in the access token of an accou
|
||||
|
||||
$lang->gitlab->noImportableIssues = "There are currently no issues available for import.";
|
||||
$lang->gitlab->tokenError = "The current token is not admin rights.";
|
||||
$lang->gitlab->tokenLimit = "The current token has no admin privilege. Please regenerate one with admin user in GitLab.";
|
||||
$lang->gitlab->hostError = "Invalid GitLab service address.";
|
||||
$lang->gitlab->bindUserError = "Can not bind users repeatedly %s";
|
||||
$lang->gitlab->importIssueError = "The execution to which this issue belongs is not selected.";
|
||||
|
||||
@@ -661,4 +661,134 @@ class mailModel extends model
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail.
|
||||
*
|
||||
* @param int $objectID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($objectID, $actionID)
|
||||
{
|
||||
/* Load module and get vars. */
|
||||
$this->loadModel('action');
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
$action = $this->action->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$objectType = $action->objectType;
|
||||
$object = $this->loadModel($objectType)->getByID($objectID);
|
||||
$nameFields = $this->config->action->objectNameFields[$objectType];
|
||||
$title = zget($object, $nameFields, '');
|
||||
$subject = $this->getSubject($objectType, $object, $title, $action->action);
|
||||
|
||||
if($objectType == 'review' and empty($object->auditedBy)) return;
|
||||
|
||||
if($objectType == 'doc')
|
||||
{
|
||||
if($object->contentType == 'markdown')
|
||||
{
|
||||
$object->content = $this->app->loadClass('hyperdown')->makeHtml($object->content);
|
||||
$object->content = str_replace("<table>", "<table style='border-collapse: collapse;'>", $object->content);
|
||||
$object->content = str_replace("<th>", "<th style='word-break: break-word; border:1px solid #000;'>", $object->content);
|
||||
$object->content = str_replace("<td>", "<td style='word-break: break-word; border:1px solid #000;'>", $object->content);
|
||||
}
|
||||
}
|
||||
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
$action->appendLink = '';
|
||||
if(strpos($action->extra, ':') !== false)
|
||||
{
|
||||
list($extra, $id) = explode(':', $action->extra);
|
||||
$action->extra = $extra;
|
||||
if($title)
|
||||
{
|
||||
$action->appendLink = html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink($action->objectType, 'view', "id=$id", 'html'), "#$id " . $title);
|
||||
}
|
||||
}
|
||||
|
||||
if($objectType == 'meeting') $rooms = $this->loadmodel('meetingroom')->getpairs();
|
||||
if($objectType == 'review') $this->app->loadLang('baseline');
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', $objectType);
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
/* Get the sender. */
|
||||
if($objectType == 'story' or $objectType == 'meeting')
|
||||
{
|
||||
$sendUsers = $this->{$objectType}->getToAndCcList($object, $action->action);
|
||||
}
|
||||
elseif($objectType == 'review')
|
||||
{
|
||||
$sendUsers = array($object->auditedBy, '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sendUsers = $this->{$objectType}->getToAndCcList($object);
|
||||
}
|
||||
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
|
||||
/* Send it. */
|
||||
$this->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->isError()) error_log(join("\n", $this->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subject.
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param object $object
|
||||
* @param string $title
|
||||
* @param string $actionType
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($objectType, $object, $title, $actionType)
|
||||
{
|
||||
$suffix = '';
|
||||
$subject = '';
|
||||
$titleType = 'edit';
|
||||
|
||||
if($objectType == 'testtask')
|
||||
{
|
||||
$this->app->loadLang('testtask');
|
||||
|
||||
if($actionType == 'opened') $titleType = 'create';
|
||||
if($actionType == 'closed') $titleType = 'close';
|
||||
|
||||
$subject = sprintf($this->lang->testtask->mail->{$titleType}->title, $this->app->user->realname, $object->id, $object->name);
|
||||
}
|
||||
elseif($objectType == 'doc')
|
||||
{
|
||||
$this->app->loadLang('doc');
|
||||
|
||||
if($actionType == 'created') $titleType = 'create';
|
||||
$subject = sprintf($this->lang->doc->mail->{$titleType}->title, $this->app->user->realname, $object->id, $object->title);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($objectType == 'story' or $objectType == 'bug') $suffix = empty($object->product) ? '' : ' - ' . $this->loadModel('product')->getById($object->product)->name;
|
||||
if($objectType == 'task') $suffix = empty($object->execution) ? '' : ' - ' . $this->loadModel('execution')->getById($object->execution)->name;
|
||||
|
||||
$subject = strtoupper($objectType) . ' #' . $object->id . ' ' . $title . $suffix;
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ class messageModel extends model
|
||||
if(isset($actions[$objectType]) and in_array($actionType, $actions[$objectType]))
|
||||
{
|
||||
$moduleName = $objectType == 'case' ? 'testcase' : $objectType;
|
||||
$this->loadModel($moduleName);
|
||||
if(method_exists($this->$moduleName, 'sendmail')) $this->$moduleName->sendmail($objectID, $actionID);
|
||||
$this->loadModel('mail')->sendmail($objectID, $actionID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -529,7 +529,7 @@ class my extends control
|
||||
$this->app->loadLang('project');
|
||||
|
||||
$this->app->session->set('programList', $this->app->getURI(true), 'program');
|
||||
$this->app->session->set('projectList', $this->app->getURI(true), 'project');
|
||||
$this->app->session->set('projectList', $this->app->getURI(true), 'my');
|
||||
|
||||
/* Set the pager. */
|
||||
$this->app->loadClass('pager', $static = true);
|
||||
|
||||
@@ -5,8 +5,11 @@ table+table {border-top: 1px solid #eee;}
|
||||
.row td {width: 33%; text-align: left; font-weight: 700;}
|
||||
.main-actions {margin: 5px 0 10px 0; text-align: center;}
|
||||
.user-title {margin: 10px 10px -10px 10px; font-size: 14px; font-weight: bold; padding-left: 10px;}
|
||||
.avatar {display: inline-block; width: 50px; height: 50px; line-height: 50px;}
|
||||
#avatarForm {width: 100%; height: 100%;}
|
||||
.avatar .btn-avatar {padding: 0; vertical-align: revert; border: 0; background-color: auto; color: #fff; font-size: 20px;}
|
||||
.user-name {margin-left: 15px; font-weight: bold;}
|
||||
.user-name, .user-role {line-height: 50px; font-size: 16px; vertical-align: top;}
|
||||
|
||||
#avatarUpload {display: inline-block; width: 50px; height: 50px; position: relative;}
|
||||
#avatarForm {position: absolute; top: 0; left: 0; right: 0; bottom: 0;}
|
||||
#avatarUploadBtn {display: block; position: absolute; top: 0; left: 0; right: 0; bottom: 0;}
|
||||
#avatarUploadBtn {opacity: 0; color: #fff; border-radius: 50%; line-height: 50px;}
|
||||
#avatarUploadBtn:hover {opacity: 1; background-color: rgba(0,0,0,.5);}
|
||||
|
||||
@@ -22,6 +22,8 @@ $(document).ready(function()
|
||||
window.parent.$('#main-avatar, #menu-avatar').html('<img src="' + avatar + '"/>');
|
||||
window.parent.$('#mainContent>.cell>.main-header>.avatar').html('<img src="' + avatar + '"/>');
|
||||
window.parent.$('#mainContent .avatar-' + account).html('<img src="' + avatar + '"/>');
|
||||
|
||||
$('#avatarUploadBtn').tooltip();
|
||||
});
|
||||
|
||||
function uploadAvatar()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<form class='main-table' id='projectForm' method='post' data-ride='table' data-checkable='false'>
|
||||
<form id='projectForm' method='post' data-ride='table' data-checkable='false'>
|
||||
<table class='table table-fixed' id='docList'>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
<div id='mainContent'>
|
||||
<div class='cell'>
|
||||
<div class='main-header text-center'>
|
||||
<span class="avatar avatar bg-secondary avatar-circle">
|
||||
<div id="avatarUpload">
|
||||
<?php echo html::avatar($user, 50); ?>
|
||||
<form method='post' class='form-ajax' action=<?php echo inlink('uploadAvatar');?> id='avatarForm' enctype='multipart/form-data'>
|
||||
<input type="file" name="files" id="files" class="form-control hidden">
|
||||
<?php $avatar = $user->avatar ? html::image($user->avatar) : strtoupper($user->account[0]);?>
|
||||
<?php echo html::a('javascript:void(0);', $avatar, '', "class='btn-avatar' id='avatarUploadBtn' data-placement='right'");?>
|
||||
<?php echo html::a('javascript:void(0);', '<i class="icon icon-pencil icon-2x"></i>', '', "class='btn-avatar' id='avatarUploadBtn' data-toggle='tooltip' data-container='body' data-placement='bottom' title='{$lang->my->uploadAvatar}'");?>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
<span class='user-name'><?php echo $user->realname;?></span>
|
||||
<span class='user-role'><?php echo zget($lang->user->roleList, $user->role, '');?></span>
|
||||
</div>
|
||||
|
||||
@@ -76,6 +76,7 @@ class pipelineModel extends model
|
||||
->add('private',md5(rand(10,113450)))
|
||||
->add('createdBy', $this->app->user->account)
|
||||
->add('createdDate', helper::now())
|
||||
->trim('token')
|
||||
->skipSpecial('url,token,account,password')
|
||||
->get();
|
||||
if($type == 'gitlab') $pipeline->url = rtrim($pipeline->url, '/');
|
||||
@@ -104,6 +105,7 @@ class pipelineModel extends model
|
||||
$pipeline = fixer::input('post')
|
||||
->add('editedBy', $this->app->user->account)
|
||||
->add('editedDate', helper::now())
|
||||
->trim('token')
|
||||
->skipSpecial('url,token,account,password')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -1278,7 +1278,7 @@ class product extends control
|
||||
$product->closedBugs = (int)$product->closedBugs;
|
||||
$product->bugFixedRate = (($product->unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100) . '%';
|
||||
$product->program = $product->programName;
|
||||
|
||||
|
||||
/* get rowspan data */
|
||||
if($lastProgram == '' or $product->program != $lastProgram)
|
||||
{
|
||||
|
||||
@@ -117,7 +117,8 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo html::a($this->createLink($this->app->rawModule, $this->app->rawMethod, $projectIDParam . "productID=$productID&branch=$branch&browseType=$menuBrowseType¶m=0&storyType=$storyType"), "<span class='text'>$menuItem->text</span>" . ($menuItem->name == $this->session->storyBrowseType ? ' <span class="label label-light label-badge">' . $pager->recTotal . '</span>' : ''), '', "id='{$menuItem->name}Tab' class='btn btn-link" . ($this->session->storyBrowseType == $menuItem->name ? ' btn-active-text' : '') . "'");
|
||||
$menuItemName = strtolower($menuItem->name);
|
||||
echo html::a($this->createLink($this->app->rawModule, $this->app->rawMethod, $projectIDParam . "productID=$productID&branch=$branch&browseType=$menuBrowseType¶m=0&storyType=$storyType"), "<span class='text'>$menuItem->text</span>" . ($menuItemName == $this->session->storyBrowseType ? ' <span class="label label-light label-badge">' . $pager->recTotal . '</span>' : ''), '', "id='{$menuItem->name}Tab' class='btn btn-link" . ($this->session->storyBrowseType == $menuItemName ? ' btn-active-text' : '') . "'");
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -67,6 +67,12 @@ class program extends control
|
||||
$this->display();
|
||||
}
|
||||
|
||||
public function kanban()
|
||||
{
|
||||
$this->view->kanbanGroup = $this->program->getKanbanGroup();
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Program products list.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
.main-table .table {cursor: default;}
|
||||
.main-table .table td {background: #f5f5f5;}
|
||||
|
||||
.board-item {border: 1px solid #EBEBEB; padding: 5px 10px; cursor: default; border-radius: 2px; background-color: #fff;}
|
||||
.board-item:hover {border-color: #ccc;}
|
||||
.board-item + .board-item {margin-top: 10px;}
|
||||
|
||||
#kanban {overflow-y: auto;}
|
||||
#kanban tbody > tr > td {line-height: 24px;}
|
||||
#kanban tbody > tr > td{border-right: 3px solid #fff; border-bottom: 3px solid #fff;}
|
||||
#kanban thead > tr:first-child > th:not(:first-child) {border-right: 3px solid #fff; border-bottom: 3px solid #fff;}
|
||||
#kanban thead > tr:last-child > th {border: 3px solid #fff;}
|
||||
|
||||
.table-grouped tbody > tr > td:first-child, .table-grouped thead > tr > th:first-child {padding-left: 3px;}
|
||||
@@ -62,6 +62,9 @@ $lang->program->readjustTime = 'Change the program begin&end date.';
|
||||
$lang->program->stakeholderTypeList['inside'] = 'Inside';
|
||||
$lang->program->stakeholderTypeList['outside'] = 'Outside';
|
||||
|
||||
$lang->program->typeList['my'] = 'My Programs';
|
||||
$lang->program->typeList['others'] = 'Others';
|
||||
|
||||
$lang->program->noProgram = 'No program.';
|
||||
$lang->program->showClosed = 'Closed programs.';
|
||||
$lang->program->tips = 'If a parent item set is selected, products under that parent item set can be associated. If no item set is selected, a product with the same name as the item is created by default and associated with that item.';
|
||||
|
||||
@@ -74,6 +74,9 @@ $lang->program->isStakeholderKey = 'Key stakeholder';
|
||||
$lang->program->stakeholderTypeList['inside'] = 'Inside';
|
||||
$lang->program->stakeholderTypeList['outside'] = 'Outside';
|
||||
|
||||
$lang->program->typeList['my'] = 'My Programs';
|
||||
$lang->program->typeList['others'] = 'Others';
|
||||
|
||||
$lang->program->noProgram = 'No program.';
|
||||
$lang->program->showClosed = 'Closed programs.';
|
||||
$lang->program->tips = 'If a parent item set is selected, products under that parent item set can be associated. If no item set is selected, a product with the same name as the item is created by default and associated with that item.';
|
||||
|
||||
@@ -62,6 +62,9 @@ $lang->program->readjustTime = 'Change the program begin&end date.';
|
||||
$lang->program->stakeholderTypeList['inside'] = 'Inside';
|
||||
$lang->program->stakeholderTypeList['outside'] = 'Outside';
|
||||
|
||||
$lang->program->typeList['my'] = 'My Programs';
|
||||
$lang->program->typeList['others'] = 'Others';
|
||||
|
||||
$lang->program->noProgram = 'No program.';
|
||||
$lang->program->showClosed = 'Closed programs.';
|
||||
$lang->program->tips = 'If a parent item set is selected, products under that parent item set can be associated. If no item set is selected, a product with the same name as the item is created by default and associated with that item.';
|
||||
|
||||
@@ -62,6 +62,9 @@ $lang->program->readjustTime = 'Change the program begin&end date.';
|
||||
$lang->program->stakeholderTypeList['inside'] = 'Inside';
|
||||
$lang->program->stakeholderTypeList['outside'] = 'Outside';
|
||||
|
||||
$lang->program->typeList['my'] = 'My Programs';
|
||||
$lang->program->typeList['others'] = 'Others';
|
||||
|
||||
$lang->program->noProgram = 'No program.';
|
||||
$lang->program->showClosed = 'Closed programs.';
|
||||
$lang->program->tips = 'If a parent item set is selected, products under that parent item set can be associated. If no item set is selected, a product with the same name as the item is created by default and associated with that item.';
|
||||
|
||||
@@ -115,3 +115,16 @@ $lang->program->statusList['suspended'] = '已挂起';
|
||||
$lang->program->statusList['closed'] = '已关闭';
|
||||
|
||||
$lang->program->featureBar['all'] = '所有';
|
||||
|
||||
$lang->program->kanban = new stdclass();
|
||||
$lang->program->kanban->typeList['my'] = '我参与的项目集';
|
||||
$lang->program->kanban->typeList['others'] = '其他项目集';
|
||||
|
||||
$lang->program->kanban->activeProducts = '未关闭的产品';
|
||||
$lang->program->kanban->activePlans = '未过期的计划';
|
||||
$lang->program->kanban->waitProjects = '未开始的项目';
|
||||
$lang->program->kanban->doingProjects = '进行中的项目';
|
||||
$lang->program->kanban->doingExecutions = '进行中的执行';
|
||||
$lang->program->kanban->normalReleases = '正常的发布';
|
||||
|
||||
$lang->program->kanban->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761');
|
||||
|
||||
+266
-2
@@ -96,7 +96,7 @@ class programModel extends model
|
||||
*/
|
||||
public function getByID($programID = 0)
|
||||
{
|
||||
$program = $this->dao->select('*')->from(TABLE_PROGRAM)->where('id')->eq($programID)->andWhere('`type`')->eq('program')->fetch();
|
||||
$program = $this->dao->select('*')->from(TABLE_PROGRAM)->where('id')->eq($programID)->fetch();
|
||||
$program = $this->loadModel('file')->replaceImgURL($program, 'desc');
|
||||
return $program;
|
||||
}
|
||||
@@ -143,7 +143,271 @@ class programModel extends model
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get kanban group data.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getKanbanGroup()
|
||||
{
|
||||
$kanbanGroup = array();
|
||||
$kanbanGroup['my'] = array();
|
||||
$kanbanGroup['others'] = array();
|
||||
|
||||
/* Get all prived programs. */
|
||||
$programs = $this->dao->select('id, name')->from(TABLE_PROGRAM)
|
||||
->where('type')->eq('program')
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('grade')->eq(1)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->programs)->fi()
|
||||
->andWhere('status')->ne('closed')
|
||||
->fetchAll('id');
|
||||
|
||||
$involvedPrograms = $this->getInvolvedPrograms($this->app->user->account);
|
||||
|
||||
/* Get all products under programs. */
|
||||
$productGroup = $this->dao->select('id, program, name')->from(TABLE_PRODUCT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('program')->in(array_keys($programs))
|
||||
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi()
|
||||
->andWhere('status')->ne('closed')
|
||||
->fetchGroup('program', 'id');
|
||||
|
||||
$productPairs = array();
|
||||
foreach($productGroup as $programID => $products)
|
||||
{
|
||||
foreach($products as $productID => $product) $productPairs[$productID] = $productID;
|
||||
}
|
||||
|
||||
/* Get all plans under products. */
|
||||
$plans = $this->dao->select('id, product, title')->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productPairs)
|
||||
->andWhere('end')->gt(helper::today())
|
||||
->fetchGroup('product');
|
||||
|
||||
/* Get all products linked projects and executions. */
|
||||
$projectGroup = $this->dao->select('t1.product, t2.id, t2.name, t2.status')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')
|
||||
->on('t1.project = t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->andWhere('t1.product')->in($productPairs)
|
||||
->andWhere('t2.status')->ne('closed')
|
||||
->andWhere('t2.type')->eq('project')
|
||||
->fetchGroup('product');
|
||||
|
||||
$projectPairs = array();
|
||||
foreach($projectGroup as $projects)
|
||||
{
|
||||
foreach($projects as $project) $projectPairs[$project->id] = $project->id;
|
||||
}
|
||||
|
||||
$tasks = $this->dao->select('id, project, estimate, consumed, `left`, status, closedReason, execution')
|
||||
->from(TABLE_TASK)
|
||||
->where('project')->in($projectPairs)
|
||||
->andWhere('parent')->lt(1)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchGroup('project', 'id');
|
||||
|
||||
$projectHours = $this->computeProgress($tasks);
|
||||
|
||||
$releases = $this->dao->select('product, id, name')->from(TABLE_RELEASE)
|
||||
->where('product')->in($productPairs)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('status')->eq('normal')
|
||||
->fetchGroup('product');
|
||||
|
||||
$doingExecutions = $this->dao->select('id, project, name')->from(TABLE_EXECUTION)
|
||||
->where('type')->in('sprint,stage')
|
||||
->andWhere('status')->eq('doing')
|
||||
->andWhere('deleted')->eq(0)
|
||||
->orderBy('id_asc')
|
||||
->fetchAll('project');
|
||||
|
||||
$executionPairs = array();
|
||||
foreach($doingExecutions as $execution) $executionPairs[$execution->id] = $execution->id;
|
||||
|
||||
$tasks = $this->dao->select('id, project, estimate, consumed, `left`, status, closedReason, execution')
|
||||
->from(TABLE_TASK)
|
||||
->where('execution')->in($executionPairs)
|
||||
->andWhere('parent')->lt(1)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchGroup('execution', 'id');
|
||||
|
||||
$executionHours = $this->computeProgress($tasks);
|
||||
|
||||
foreach($productGroup as $programID => $products)
|
||||
{
|
||||
foreach($products as $productID => $product)
|
||||
{
|
||||
$product->plans = zget($plans, $productID, array());
|
||||
$product->releases = zget($releases, $productID, array());
|
||||
$projects = zget($projectGroup, $productID, array());
|
||||
foreach($projects as $project)
|
||||
{
|
||||
$status = $project->status == 'wait' ? 'wait' : 'doing';
|
||||
$execution = zget($doingExecutions, $project->id, array());
|
||||
if(!empty($execution)) $execution->hours = zget($executionHours, $execution->id, array());
|
||||
|
||||
$project->execution = $execution;
|
||||
$project->hours = zget($projectHours, $project->id, array());
|
||||
$product->projects[$status][] = $project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//区分出其他项目集
|
||||
foreach($programs as $programID => $program)
|
||||
{
|
||||
$program->products = zget($productGroup, $programID, array());
|
||||
|
||||
if(in_array($programID, $involvedPrograms))
|
||||
{
|
||||
$kanbanGroup['my'][$programID] = $program;
|
||||
}
|
||||
else
|
||||
{
|
||||
$kanbanGroup['others'][$programID] = $program;
|
||||
}
|
||||
}
|
||||
|
||||
return $kanbanGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get involved programs by user.
|
||||
*
|
||||
* @param string $account
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getInvolvedPrograms($account)
|
||||
{
|
||||
$involvedPrograms = array();
|
||||
|
||||
/* All involves in program table. */
|
||||
$objects = $this->dao->select('id, type, project')->from(TABLE_PROGRAM)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere("(openedBy = '$account' or PM = '$account')")
|
||||
->fetchAll('id');
|
||||
|
||||
foreach($objects as $id => $object)
|
||||
{
|
||||
if($object->type == 'program') $involvedPrograms[$id] = $id;
|
||||
if($object->type == 'project')
|
||||
{
|
||||
$programID = $this->getTopByID($id);
|
||||
$involvedPrograms[$programID] = $programID;
|
||||
}
|
||||
if($object->type == 'sprint' || $object->type == 'stage')
|
||||
{
|
||||
$programID = $this->getTopByID($object->project);
|
||||
$involvedPrograms[$programID] = $programID;
|
||||
}
|
||||
}
|
||||
|
||||
/* All involves in stakeholder table. */
|
||||
$stakeholders = $this->dao->select('t1.objectID, t2.type')->from(TABLE_STAKEHOLDER)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')
|
||||
->on('t1.objectID = t2.id')
|
||||
->where('t1.objectType')->in("program,project")
|
||||
->andWhere('t1.user')->eq($account)
|
||||
->fetchAll('objectID');
|
||||
|
||||
foreach($stakeholders as $objectID => $object)
|
||||
{
|
||||
if($object->type == 'program')
|
||||
{
|
||||
$involvedPrograms[$objectID] = $objectID;
|
||||
}
|
||||
if($object->type == 'project')
|
||||
{
|
||||
$programID = $this->getTopByID($objectID);
|
||||
$involvedPrograms[$programID] = $programID;
|
||||
}
|
||||
}
|
||||
|
||||
/* All involves in team table. */
|
||||
$teams = $this->dao->select('t1.root, t2.project, t2.type')->from(TABLE_TEAM)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')
|
||||
->on('t1.root = t2.id')
|
||||
->where('t1.account')->eq($account)
|
||||
->andWhere('t1.type')->in('project,execution')
|
||||
->fetchAll('root');
|
||||
|
||||
foreach($teams as $objectID => $object)
|
||||
{
|
||||
if($object->type == 'project')
|
||||
{
|
||||
$programID = $this->getTopByID($objectID);
|
||||
$involvedPrograms[$programID] = $programID;
|
||||
}
|
||||
if($object->type == 'execution')
|
||||
{
|
||||
$programID = $this->getTopByID($object->project);
|
||||
$involvedPrograms[$programID] = $programID;
|
||||
}
|
||||
}
|
||||
|
||||
/* All involves in products table. */
|
||||
$products = $this->dao->select('id, program, createdBy, PO, QD, RD')->from(TABLE_PRODUCT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere("(createdBy = '$account' or PO = '$account' or QD = '$account' or RD = '$account')")
|
||||
->fetchAll('id');
|
||||
|
||||
foreach($products as $id => $product) $involvedPrograms[$product->program] = $product->program;
|
||||
|
||||
/* Check priv. */
|
||||
$involvedPrograms = $this->dao->select('id')->from(TABLE_PROGRAM)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->programs)->fi()
|
||||
->andWhere('id')->in($involvedPrograms)
|
||||
->andWhere('grade')->eq(1)
|
||||
->fetchPairs();
|
||||
|
||||
return $involvedPrograms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute progress for project or execution.
|
||||
*
|
||||
* @param array $tasks
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function computeProgress($tasks)
|
||||
{
|
||||
$hours = array();
|
||||
foreach($tasks as $projectID => $projectTasks)
|
||||
{
|
||||
$hour = new stdclass();
|
||||
$hour->totalConsumed = 0;
|
||||
$hour->totalEstimate = 0;
|
||||
$hour->totalLeft = 0;
|
||||
|
||||
foreach($projectTasks as $task)
|
||||
{
|
||||
$hour->totalConsumed += $task->consumed;
|
||||
$hour->totalEstimate += $task->estimate;
|
||||
if($task->status != 'cancel' and $task->status != 'closed') $hour->totalLeft += $task->left;
|
||||
}
|
||||
$hours[$projectID] = $hour;
|
||||
}
|
||||
|
||||
foreach($hours as $hour)
|
||||
{
|
||||
$hour->totalEstimate = round($hour->totalEstimate, 1) ;
|
||||
$hour->totalConsumed = round($hour->totalConsumed, 1);
|
||||
$hour->totalLeft = round($hour->totalLeft, 1);
|
||||
$hour->totalReal = $hour->totalConsumed + $hour->totalLeft;
|
||||
$hour->progress = $hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 2) * 100 : 0;
|
||||
}
|
||||
|
||||
return $hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project list data.
|
||||
*
|
||||
* @param int $programID
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<th class='text-right w-100px'><?php common::printOrderLink('budget', $orderBy, $vars, $lang->project->budget);?></th>
|
||||
<th class='w-100px'><?php common::printOrderLink('begin', $orderBy, $vars, $lang->project->begin);?></th>
|
||||
<th class='w-100px'><?php common::printOrderLink('end', $orderBy, $vars, $lang->project->end);?></th>
|
||||
<th class='w-70px'><?php echo $lang->project->progress;?></th>
|
||||
<th class='w-60px'><?php echo $lang->project->progress;?></th>
|
||||
<th class='text-center w-180px'><?php echo $lang->actions;?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -62,9 +62,7 @@
|
||||
<td class='c-status'><span class="status-program status-<?php echo $program->status?>"><?php echo zget($lang->project->statusList, $program->status, '');?></span></td>
|
||||
<td>
|
||||
<?php if(!empty($program->PM)):?>
|
||||
<div class="avatar bg-secondary avatar-circle avatar-<?php echo $program->PM;?>">
|
||||
<?php echo !empty($usersAvatar[$program->PM]) ? html::image($usersAvatar[$program->PM]) : strtoupper($program->PM[0]);?>
|
||||
</div>
|
||||
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$program->PM], 'account' => $program->PM)); ?>
|
||||
<?php $userID = isset($PMList[$program->PM]) ? $PMList[$program->PM]->id : '';?>
|
||||
<?php $userName = zget($users, $program->PM);?>
|
||||
<?php echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'");?>
|
||||
@@ -76,7 +74,7 @@
|
||||
<td><?php echo $program->end == LONG_TIME ? $lang->program->longTime : $program->end;?></td>
|
||||
<td>
|
||||
<?php if(isset($progressList[$program->id])):?>
|
||||
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo $progressList[$program->id]?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
|
||||
<div class='progress-pie' data-doughnut-size='85' data-color='#00DA88' data-value='<?php echo $progressList[$program->id]?>' data-width='26' data-height='26' data-back-color='#e8edf3'>
|
||||
<div class='progress-info'><?php echo $progressList[$program->id];?></div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* The html template file of kanban method of program module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Guangming Sun<sunguangming@cnezsoft.com>
|
||||
* @package ZenTaoPMS
|
||||
* @version $Id
|
||||
*/
|
||||
?>
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
|
||||
<?php foreach($kanbanGroup as $type => $programGroup):?>
|
||||
<?php $colorIndex = 0;?>
|
||||
<div class="cell">
|
||||
<div class='detail'>
|
||||
<div class='detail-title'><?php echo $lang->program->kanban->typeList[$type];?></div>
|
||||
<div class='detail-content'>
|
||||
<table class="table no-margin table-grouped text-center" style='background: #f5f5f5;'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan='2' class='w-20px' style='background: #32C5FF; border-bottom: none'></th>
|
||||
<th rowspan='2'><?php echo $lang->program->kanban->activeProducts;?></th>
|
||||
<th rowspan='2'><?php echo $lang->program->kanban->activePlans;?></th>
|
||||
<th rowspan='2'><?php echo $lang->program->kanban->waitProjects;?></th>
|
||||
<th colspan='2'><?php echo $lang->program->statusList['doing'];?></th>
|
||||
<th rowspan='2'><?php echo $lang->program->kanban->normalReleases;?></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->program->kanban->doingProjects;?></th>
|
||||
<th><?php echo $lang->program->kanban->doingExecutions;?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($programGroup as $programID => $program):?>
|
||||
<tr>
|
||||
<td style='background: <?php echo $lang->program->kanban->laneColorList[$colorIndex];?>; color: #fff; border-right: none;' rowspan='<?php echo count($program->products);?>'><?php echo $program->name;?></td>
|
||||
<?php $i = 0;?>
|
||||
<?php if(!empty($program->products)):?>
|
||||
<?php foreach($program->products as $productID => $product):?>
|
||||
<?php if($i != 0) echo '<tr>';?>
|
||||
<td><?php echo $product->name;?></td>
|
||||
<td>
|
||||
<?php foreach($product->plans as $planID => $plan):?>
|
||||
<div class='board-item'>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php echo html::a($this->createLink('productplan', 'view', "planID=$plan->id"), $plan->title);?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if(isset($product->projects['wait'])):?>
|
||||
<?php foreach($product->projects['wait'] as $projectID => $project):?>
|
||||
<div class='board-item'>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php echo html::a($this->createLink('project', 'view', "projectID=$projectID"), $project->name);?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
<td class='doing-project'>
|
||||
<?php if(isset($product->projects['doing'])):?>
|
||||
<?php foreach($product->projects['doing'] as $projectID => $project):?>
|
||||
<div class='board-item'>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php echo html::a($this->createLink('project', 'view', "projectID=$projectID"), $project->name);?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
<td class='doing-execution'>
|
||||
<?php if(isset($product->projects['doing'])):?>
|
||||
<?php foreach($product->projects['doing'] as $projectID => $project):?>
|
||||
<div class='board-item'>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php if(!empty($project->execution)):?>
|
||||
<?php echo html::a($this->createLink('execution', 'view', "executionID={$project->execution->id}"), $project->execution->name);?>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
<td>
|
||||
<?php foreach($product->releases as $releaseID => $release):?>
|
||||
<div class='board-item'>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php echo html::a($this->createLink('release', 'view', "releaseID=$release->id"), $release->name);?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
</td>
|
||||
<?php if($i != 0) echo '</tr>';?>
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<?php endif;?>
|
||||
</tr>
|
||||
<?php $colorIndex ++;?>
|
||||
<?php if($colorIndex > 9) $colorIndex = 0;?>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
</div>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
@@ -760,7 +760,7 @@ class projectModel extends model
|
||||
$this->dao->insert(TABLE_PROJECT)->data($project)
|
||||
->autoCheck()
|
||||
->batchcheck($requiredFields, 'notempty')
|
||||
->check('name', 'unique', "type='project'")
|
||||
->check('name', 'unique', "type='project' AND deleted='0'")
|
||||
->exec();
|
||||
|
||||
/* Add the creater to the team. */
|
||||
@@ -951,7 +951,7 @@ class projectModel extends model
|
||||
->checkIF($project->begin != '', 'begin', 'date')
|
||||
->checkIF($project->end != '', 'end', 'date')
|
||||
->checkIF($project->end != '', 'end', 'gt', $project->begin)
|
||||
->check('name', 'unique', "id != $projectID AND type='project'")
|
||||
->check('name', 'unique', "id != $projectID AND type='project' AND deleted='0'")
|
||||
->where('id')->eq($projectID)
|
||||
->exec();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#cards .project-infos > span + span {margin-left: 15px;}
|
||||
#cards .project-infos > .budget {max-width: 75px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
|
||||
#cards .project-detail {position: absolute; top: 75px; left: 16px; right: 16px; font-size: 12px;}
|
||||
#cards .project-footer {position: absolute; bottom: 5px;}
|
||||
#cards .project-footer {position: absolute; bottom: 10px; right: 10px; left: 15px;}
|
||||
#cards .pager {margin: 0; float: right;}
|
||||
#cards .pager .btn {border: none}
|
||||
#cards .panel .label-wait {background: #EFEFEF !important; color: #838A9D;}
|
||||
@@ -40,16 +40,18 @@
|
||||
#cards .project-infos .text-red {color: #F85A40 !important;}
|
||||
#cards .project-detail .progress-pie {width: 24px; position: absolute; top: 18px;}
|
||||
#cards .project-detail .leftTasks, .totalLeft {display:block; margin-top: 8px;}
|
||||
#cards .project-members {float: left;}
|
||||
#cards .project-members .avatar {display: inline-block; width: 25px; height: 25px; line-height: 25px; margin-right: 1px;}
|
||||
#cards .project-members a:not(:first-child) {margin-left: -5px;}
|
||||
#cards .totalMembers{display: inline-block; margin-left: 6px; position: absolute; bottom: 8px;}
|
||||
#cards .project-actions {float: right;}
|
||||
#cards .project-actions .menu-actions {padding-right: 20px;}
|
||||
#cards .project-actions ul {min-width: 35px; padding: 5px 6px;}
|
||||
#cards .project-actions .dropdown-hover:hover>.dropdown-menu ul,
|
||||
#cards .project-actions .open>.dropdown-menu ul {display: flex;}
|
||||
#cards .menu-actions .btn.btn-action {margin-bottom: -3px !important; margin-right: 5px;}
|
||||
#cards .project-members {float: left; height: 24px; line-height: 24px;}
|
||||
#cards .project-members > a {display: inline-block; height: 24px;}
|
||||
#cards .project-members > a + a {margin-left: -5px;}
|
||||
#cards .project-members > a > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 1px;}
|
||||
#cards .project-members > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: relative; border-radius: 50%; top: 3px; margin: 0 3px;}
|
||||
#cards .project-members > span:before,
|
||||
#cards .project-members > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%}
|
||||
#cards .project-members > span:before {left: -4px;}
|
||||
#cards .project-members > span:after {right: -4px;}
|
||||
#cards .project-members-total {display: inline-block; margin-left: 6px; position: relative; top: 3px}
|
||||
#cards .project-actions {position: absolute; right: -8px; bottom: -5px; white-space: nowrap;}
|
||||
#cards .project-actions .dropdown-menu {padding: 5px 6px; top: -5px; right: 30px;}
|
||||
#cards .icon-ellipsis-v {font-size: 13px;}
|
||||
#cards .teamTitle {margin-bottom: 30px;}
|
||||
</style>
|
||||
@@ -138,41 +140,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='project-footer table-row'>
|
||||
<div class='project-footer'>
|
||||
<?php $titleClass = ($project->teamCount == 0 and !$canActions) ? 'teamTitle' : '';?>
|
||||
<?php $count = 0;?>
|
||||
<div class="<?php echo $titleClass?>"><?php echo $lang->project->teamMember;?></div>
|
||||
<?php if(!empty($project->teamMembers)):?>
|
||||
<div class='project-members table-col'>
|
||||
<?php foreach($project->teamMembers as $member):?>
|
||||
<?php
|
||||
if($count > 2) continue;
|
||||
if(!isset($users[$member]))
|
||||
{
|
||||
$project->teamCount --;
|
||||
continue;
|
||||
}
|
||||
$count ++;
|
||||
?>
|
||||
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
|
||||
<div class="avatar bg-secondary avatar-circle avatar-<?php echo $member;?>">
|
||||
<?php echo !empty($usersAvatar[$member]) ? html::image(zget($usersAvatar, $member)) : strtoupper($member[0]);?>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach;?>
|
||||
<?php if($project->teamCount > 3):?>
|
||||
<?php echo '...';?>
|
||||
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
|
||||
<div class="avatar bg-secondary avatar-circle avatar-<?php echo $member;?>">
|
||||
<?php echo !empty($usersAvatar[end($project->teamMembers)]) ? html::image(zget($usersAvatar, end($project->teamMembers))) : strtoupper($member[0]);?>
|
||||
</div>
|
||||
</a>
|
||||
<div class="clearfix">
|
||||
<?php if(!empty($project->teamMembers)):?>
|
||||
<div class='project-members pull-left'>
|
||||
<?php foreach($project->teamMembers as $member):?>
|
||||
<?php
|
||||
if($count > 2) continue;
|
||||
if(!isset($users[$member]))
|
||||
{
|
||||
$project->teamCount --;
|
||||
continue;
|
||||
}
|
||||
$count ++;
|
||||
?>
|
||||
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
|
||||
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member], 'account' => $member)); ?>
|
||||
</a>
|
||||
<?php endforeach;?>
|
||||
<?php if($project->teamCount > 3):?>
|
||||
<?php echo '<span>…</span>';?>
|
||||
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
|
||||
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[end($project->teamMembers)], 'account' => $member)); ?>
|
||||
</a>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div class='project-members-total pull-left'><?php echo html::a(helper::createLink('project', 'team', "projectID=$projectID"), sprintf($lang->project->teamSumCount, $project->teamCount));?></div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<span class='totalMembers'><?php echo html::a(helper::createLink('project', 'team', "projectID=$projectID"), sprintf($lang->project->teamSumCount, $project->teamCount));?></span>
|
||||
<div class='project-actions table-col'>
|
||||
<div class='menu-actions'>
|
||||
<div class='project-actions'>
|
||||
<div class='dropdown'>
|
||||
<?php if($canActions):?>
|
||||
<?php echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");?>
|
||||
<ul class='dropdown-menu pull-right'>
|
||||
|
||||
@@ -18,7 +18,9 @@ $lang->projectstory->batchUnlinkTip = 'Other requirements are removed. The follo
|
||||
|
||||
global $app;
|
||||
$app->loadLang('product');
|
||||
$lang->projectstory->featureBar['story']['allstory'] = $lang->product->allStory;
|
||||
$lang->projectstory->featureBar['story']['unclosed'] = $lang->product->unclosed;
|
||||
$lang->projectstory->featureBar['story']['changed'] = $lang->product->changedStory;
|
||||
$lang->projectstory->featureBar['story']['closed'] = $lang->product->closedStory;
|
||||
$lang->projectstory->featureBar['story']['allstory'] = $lang->product->allStory;
|
||||
$lang->projectstory->featureBar['story']['unclosed'] = $lang->product->unclosed;
|
||||
$lang->projectstory->featureBar['story']['changed'] = $lang->product->changedStory;
|
||||
$lang->projectstory->featureBar['story']['closed'] = $lang->product->closedStory;
|
||||
$lang->projectstory->featureBar['story']['linkedExecution'] = 'Linked ' . $lang->execution->common;
|
||||
$lang->projectstory->featureBar['story']['unlinkedExecution'] = 'Unlinked ' . $lang->execution->common;
|
||||
|
||||
@@ -18,7 +18,9 @@ $lang->projectstory->batchUnlinkTip = '其他需求已经移除,如下需求
|
||||
|
||||
global $app;
|
||||
$app->loadLang('product');
|
||||
$lang->projectstory->featureBar['story']['allstory'] = $lang->product->allStory;
|
||||
$lang->projectstory->featureBar['story']['unclosed'] = $lang->product->unclosed;
|
||||
$lang->projectstory->featureBar['story']['changed'] = $lang->product->changedStory;
|
||||
$lang->projectstory->featureBar['story']['closed'] = $lang->product->closedStory;
|
||||
$lang->projectstory->featureBar['story']['allstory'] = $lang->product->allStory;
|
||||
$lang->projectstory->featureBar['story']['unclosed'] = $lang->product->unclosed;
|
||||
$lang->projectstory->featureBar['story']['changed'] = $lang->product->changedStory;
|
||||
$lang->projectstory->featureBar['story']['closed'] = $lang->product->closedStory;
|
||||
$lang->projectstory->featureBar['story']['linkedExecution'] = '已关联' . $lang->execution->common;
|
||||
$lang->projectstory->featureBar['story']['unlinkedExecution'] = '未关联' . $lang->execution->common;
|
||||
|
||||
@@ -477,53 +477,6 @@ class releaseModel extends model
|
||||
return dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail.
|
||||
*
|
||||
* @param int $releaseID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($releaseID, $actionID)
|
||||
{
|
||||
$this->loadModel('mail');
|
||||
$release = $this->getByID($releaseID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* Get action info. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
$action->appendLink = '';
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'release');
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
$sendUsers = $this->getToAndCcList($release);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = 'RELEASE #' . $release->id . ' ' . $release->name;
|
||||
|
||||
/* Send it. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
@@ -543,7 +496,7 @@ class releaseModel extends model
|
||||
|
||||
foreach($notifyPersons as $account)
|
||||
{
|
||||
if(strpos($ccList, ",{$account},") === false) $ccList .= $account . ',';
|
||||
if(strpos($ccList, ",{$account},") === false) $ccList .= ",$account,";
|
||||
}
|
||||
|
||||
$ccList = trim($ccList, ',');
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = 'RELEASE #' . $release->id . ' ' . $release->name;?>
|
||||
<?php $mailTitle = 'RELEASE #' . $object->id . ' ' . $object->name;?>
|
||||
<?php $module = $this->app->openApp == 'product' ? 'release' : 'projectrelease';?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
@@ -18,7 +18,7 @@
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink($module, 'view', "releaseID=$release->id", 'html'), $mailTitle, '', "text-decoration: underline;'");?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink($module, 'view', "releaseID=$object->id", 'html'), $mailTitle, '', "text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->release->desc;?></legend>
|
||||
<div style='padding:5px;'><?php echo $release->desc;?></div>
|
||||
<div style='padding:5px;'><?php echo $object->desc;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+10
-70
@@ -2581,6 +2581,14 @@ class storyModel extends model
|
||||
$unclosedStatus = $this->lang->story->statusList;
|
||||
unset($unclosedStatus['closed']);
|
||||
|
||||
/* Get story id list of linked executions. */
|
||||
$storyIdList = array();
|
||||
if($type == 'linkedexecution' or $type == 'unlinkedexecution')
|
||||
{
|
||||
$executions = $this->loadModel('execution')->getPairs($executionID);
|
||||
$storyIdList = $this->dao->select('story')->from(TABLE_PROJECTSTORY)->where('project')->in(array_keys($executions))->fetchPairs();
|
||||
}
|
||||
|
||||
$stories = $this->dao->select('distinct t1.*, t2.*,t3.branch as productBranch,t4.type as productType,t2.version as version')->from(TABLE_PROJECTSTORY)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
||||
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t3')->on('t1.project = t3.project')
|
||||
@@ -2593,6 +2601,8 @@ class storyModel extends model
|
||||
->beginIF($type == 'bybranch' and strpos($branchID, ',') !== false)->andWhere('t2.branch')->eq($branchParam)->fi()
|
||||
->beginIF(strpos('changed|closed', $type) !== false)->andWhere('t2.status')->eq($type)->fi()
|
||||
->beginIF($type == 'unclosed')->andWhere('t2.status')->in(array_keys($unclosedStatus))->fi()
|
||||
->beginIF($type == 'linkedexecution')->andWhere('t2.id')->in(array_keys($storyIdList))->fi()
|
||||
->beginIF($type == 'unlinkedexecution')->andWhere('t2.id')->notIn(array_keys($storyIdList))->fi()
|
||||
->fi()
|
||||
->beginIF($execution->type != 'project')
|
||||
->beginIF(!empty($productParam))->andWhere('t1.product')->eq($productParam)->fi()
|
||||
@@ -3302,19 +3312,6 @@ class storyModel extends model
|
||||
return $storyGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mail subject.
|
||||
*
|
||||
* @param object $story
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($story)
|
||||
{
|
||||
$productName = empty($story->product) ? '' : ' - ' . $this->loadModel('product')->getById($story->product)->name;
|
||||
return 'STORY #' . $story->id . ' ' . $story->title . $productName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
@@ -3880,63 +3877,6 @@ class storyModel extends model
|
||||
return $forceReview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($storyID, $actionID)
|
||||
{
|
||||
$this->loadModel('mail');
|
||||
$story = $this->getById($storyID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* Get actions. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
$action->appendLink = '';
|
||||
if(strpos($action->extra, ':') !== false)
|
||||
{
|
||||
list($extra, $id) = explode(':', $action->extra);
|
||||
$action->extra = $extra;
|
||||
if($id)
|
||||
{
|
||||
$name = $this->dao->select('title')->from(TABLE_STORY)->where('id')->eq($id)->fetch('title');
|
||||
if($name) $action->appendLink = html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink($action->objectType, 'view', "id=$id", 'html'), "#$id " . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'story');
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
$sendUsers = $this->getToAndCcList($story, $action->action);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = $this->getSubject($story);
|
||||
|
||||
/* Send it. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tracks.
|
||||
*
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = 'STORY #' . $story->id . ' ' . $story->title;?>
|
||||
<?php $mailTitle = 'STORY #' . $object->id . ' ' . $object->title;?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
<td>
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php $color = empty($story->color) ? '#333' : $story->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('story', 'view', "storyID=$story->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
<?php $color = empty($object->color) ? '#333' : $object->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('story', 'view', "storyID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->story->legendSpec;?></legend>
|
||||
<div style='padding:5px;'><?php echo $story->spec;?></div>
|
||||
<div style='padding:5px;'><?php echo $object->spec;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -3166,68 +3166,6 @@ class taskModel extends model
|
||||
echo !common::hasPriv('task', 'assignTo', $task) ? "<span style='padding-left: 21px' class='{$btnTextClass}'>{$assignedToText}</span>" : $assignToHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail.
|
||||
*
|
||||
* @param int $taskID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($taskID, $actionID)
|
||||
{
|
||||
$this->loadModel('mail');
|
||||
$task = $this->getById($taskID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* Get action info. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
|
||||
/* Get mail content. */
|
||||
$oldcwd = getcwd();
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'task');
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
chdir($oldcwd);
|
||||
|
||||
$sendUsers = $this->getToAndCcList($task);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = $this->getSubject($task);
|
||||
|
||||
/* Send emails. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mail subject.
|
||||
*
|
||||
* @param object $task
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($task)
|
||||
{
|
||||
$executionName = $this->loadModel('execution')->getById($task->execution)->name;
|
||||
return 'TASK#' . $task->id . ' ' . $task->name . ' - ' . $executionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = 'TASK #' . $task->id . ' ' . $task->name;?>
|
||||
<?php $mailTitle = 'TASK #' . $object->id . ' ' . $object->name;?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
<td>
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php $color = empty($task->color) ? '#333' : $task->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('task', 'view', "taskID=$task->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
<?php $color = empty($object->color) ? '#333' : $object->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('task', 'view', "taskID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->task->legendDesc;?></legend>
|
||||
<div style='padding:5px;'><?php echo $task->desc;?></div>
|
||||
<div style='padding:5px;'><?php echo $object->desc;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1543,77 +1543,6 @@ class testtaskModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail.
|
||||
*
|
||||
* @param int $testtaskID
|
||||
* @param int $actionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sendmail($testtaskID, $actionID)
|
||||
{
|
||||
$this->loadModel('mail');
|
||||
$testtask = $this->getByID($testtaskID);
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
|
||||
/* Get action info. */
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
$history = $this->action->getHistory($actionID);
|
||||
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
|
||||
|
||||
/* Get mail content. */
|
||||
$modulePath = $this->app->getModulePath($appName = '', 'testtask');
|
||||
$oldcwd = getcwd();
|
||||
$viewFile = $modulePath . 'view/sendmail.html.php';
|
||||
chdir($modulePath . 'view');
|
||||
if(file_exists($modulePath . 'ext/view/sendmail.html.php'))
|
||||
{
|
||||
$viewFile = $modulePath . 'ext/view/sendmail.html.php';
|
||||
chdir($modulePath . 'ext/view');
|
||||
}
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile;
|
||||
$mailContent = ob_get_contents();
|
||||
ob_end_clean();
|
||||
chdir($oldcwd);
|
||||
|
||||
$sendUsers = $this->getToAndCcList($testtask);
|
||||
if(!$sendUsers) return;
|
||||
list($toList, $ccList) = $sendUsers;
|
||||
$subject = $this->getSubject($testtask, $action->action);
|
||||
|
||||
/* Send mail. */
|
||||
$this->mail->send($toList, $subject, $mailContent, $ccList);
|
||||
if($this->mail->isError()) error_log(join("\n", $this->mail->getError()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mail subject.
|
||||
*
|
||||
* @param object $testtask
|
||||
* @param string $actionType
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject($testtask, $actionType)
|
||||
{
|
||||
/* Set email title. */
|
||||
if($actionType == 'opened')
|
||||
{
|
||||
return sprintf($this->lang->testtask->mail->create->title, $this->app->user->realname, $testtask->id, $testtask->name);
|
||||
}
|
||||
elseif($actionType == 'closed')
|
||||
{
|
||||
return sprintf($this->lang->testtask->mail->close->title, $this->app->user->realname, $testtask->id, $testtask->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return sprintf($this->lang->testtask->mail->edit->title, $this->app->user->realname, $testtask->id, $testtask->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toList and ccList.
|
||||
*
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php $mailTitle = 'TESTTASK #' . $testtask->id . ' ' . $testtask->name;?>
|
||||
<?php $mailTitle = 'TESTTASK #' . $object->id . ' ' . $object->name;?>
|
||||
<?php include $this->app->getModuleRoot() . 'common/view/mail.header.html.php';?>
|
||||
<tr>
|
||||
<td>
|
||||
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
|
||||
<tr>
|
||||
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
|
||||
<?php $color = empty($testtask->color) ? '#333' : $testtask->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('testtask', 'view', "testtaskID=$testtask->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
<?php $color = empty($object->color) ? '#333' : $object->color;?>
|
||||
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('testtask', 'view', "testtaskID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -28,7 +28,7 @@
|
||||
<td style='padding: 10px; border: none;'>
|
||||
<fieldset style='border: 1px solid #e5e5e5'>
|
||||
<legend style='color: #114f8e'><?php echo $this->lang->testtask->desc;?></legend>
|
||||
<div style='padding:5px;'><?php echo $testtask->desc;?></div>
|
||||
<div style='padding:5px;'><?php echo $object->desc;?></div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -50,7 +50,7 @@ $lang->upgrade->sureExecute = 'Execute';
|
||||
$lang->upgrade->forbiddenExt = 'The extension is incompatible with the version. It has been deactivated:';
|
||||
$lang->upgrade->updateFile = 'File information has to be updated.';
|
||||
$lang->upgrade->noticeSQL = 'Your database is inconsistent with the standard and it failed to fix it. Please run the following SQL and refresh.';
|
||||
$lang->upgrade->afterDeleted = 'File is not deleted. Please refresh after you delete it.';
|
||||
$lang->upgrade->afterDeleted = 'Please execute commands to delete the files. Please refresh after you delete them.';
|
||||
$lang->upgrade->mergeProgram = 'Data Merge';
|
||||
$lang->upgrade->mergeTips = 'Data Migration Tips';
|
||||
$lang->upgrade->toPMS15Guide = 'ZenTao open source version 15 upgrade';
|
||||
|
||||
@@ -50,7 +50,7 @@ $lang->upgrade->sureExecute = '确认执行';
|
||||
$lang->upgrade->forbiddenExt = '以下插件与新版本不兼容,已经自动禁用:';
|
||||
$lang->upgrade->updateFile = '需要更新附件信息。';
|
||||
$lang->upgrade->noticeSQL = '检查到你的数据库跟标准不一致,尝试修复失败。请执行以下SQL语句,再刷新页面检查。';
|
||||
$lang->upgrade->afterDeleted = '以上文件未能删除, 删除后刷新!';
|
||||
$lang->upgrade->afterDeleted = '请执行上面命令删除文件, 删除后刷新!';
|
||||
$lang->upgrade->mergeProgram = '数据迁移';
|
||||
$lang->upgrade->mergeTips = '数据迁移提示';
|
||||
$lang->upgrade->toPMS15Guide = '禅道开源版15版本升级';
|
||||
|
||||
@@ -1050,10 +1050,11 @@ class upgradeModel extends model
|
||||
$fullPath = $basePath . str_replace('/', DIRECTORY_SEPARATOR, $file);
|
||||
if(file_exists($fullPath))
|
||||
{
|
||||
if((is_dir($fullPath) and !$zfile->removeDir($fullPath)) or
|
||||
(is_file($fullPath) and !$zfile->removeFile($fullPath)))
|
||||
$isDir = is_dir($fullPath);
|
||||
if(($isDir and !$zfile->removeDir($fullPath)) or
|
||||
(!$isDir and !$zfile->removeFile($fullPath)))
|
||||
{
|
||||
$result[] = $fullPath;
|
||||
$result[] = 'rm -f ' . ($isDir ? '-r ' : '') . $fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4441,6 +4442,7 @@ class upgradeModel extends model
|
||||
|
||||
$this->dao->insert(TABLE_PROJECT)->data($project)
|
||||
->batchcheck('name', 'notempty')
|
||||
->check('name', 'unique', "type='project'")
|
||||
->exec();
|
||||
if(dao::isError()) return false;
|
||||
|
||||
|
||||
@@ -942,14 +942,6 @@ class user extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!empty($this->config->global->showDemoUsers))
|
||||
{
|
||||
$demoUsers = 'productManager,projectManager,dev1,dev2,dev3,tester1,tester2,tester3,testManager';
|
||||
if($this->app->getClientLang() == 'en') $demoUsers = 'thePO,pm1,pm2,pg1,pg2,pg3,thePM,qa1,theQS';
|
||||
$demoUsers = $this->dao->select('account,password,realname')->from(TABLE_USER)->where('account')->in($demoUsers)->andWhere('deleted')->eq(0)->fetchAll('account');
|
||||
$this->view->demoUsers = $demoUsers;
|
||||
}
|
||||
|
||||
$this->loadModel('misc');
|
||||
$this->view->noGDLib = sprintf($this->lang->misc->noGDLib, common::getSysURL() . $this->config->webRoot, '', false, true);
|
||||
$this->view->title = $this->lang->user->login;
|
||||
|
||||
@@ -1234,8 +1234,7 @@ class userModel extends model
|
||||
if(!$uploadResult) return array('result' => 'fail', 'message' => $this->lang->fail);
|
||||
|
||||
$fileIdList = array_keys($uploadResult);
|
||||
$file = $this->file->getByID($fileIdList[0]);
|
||||
$this->dao->update(TABLE_USER)->set('avatar')->eq($file->webPath)->where('account')->eq($this->app->user->account)->exec();
|
||||
$file = $this->file->getByID($fileIdList[0]);
|
||||
|
||||
return array('result' => 'success', 'message' => '', 'locate' => helper::createLink('user', 'cropavatar', "image={$file->id}"));
|
||||
}
|
||||
@@ -1615,8 +1614,9 @@ class userModel extends model
|
||||
}
|
||||
|
||||
/* Compute parent stakeholders. */
|
||||
$programStakeholderGroup = $this->loadModel('stakeholder')->getParentStakeholderGroup(array_keys($allPrograms));
|
||||
$projectStakeholderGroup = $this->loadModel('stakeholder')->getParentStakeholderGroup(array_keys($allProjects));
|
||||
$this->loadModel('stakeholder');
|
||||
$programStakeholderGroup = $this->stakeholder->getParentStakeholderGroup(array_keys($allPrograms));
|
||||
$projectStakeholderGroup = $this->stakeholder->getParentStakeholderGroup(array_keys($allProjects));
|
||||
|
||||
list($productTeams, $productStakeholders) = $this->getProductMembers($allProducts);
|
||||
|
||||
|
||||
@@ -72,17 +72,23 @@ if(empty($config->notMd5Pwd))js::import($jsRoot . 'md5.js');
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php if(isset($demoUsers)):?>
|
||||
<?php if(!empty($this->config->global->showDemoUsers)):?>
|
||||
<?php
|
||||
$demoPassword = '123456';
|
||||
$md5Password = md5('123456');
|
||||
$demoUsers = 'productManager,projectManager,dev1,dev2,dev3,tester1,tester2,tester3,testManager';
|
||||
if($this->app->getClientLang() == 'en') $demoUsers = 'thePO,pm1,pm2,pg1,pg2,pg3,thePM,qa1,theQS';
|
||||
$demoUsers = $this->dao->select('account,password,realname')->from(TABLE_USER)->where('account')->in($demoUsers)->andWhere('deleted')->eq(0)->andWhere('password')->eq($md5Password)->fetchAll('account');
|
||||
?>
|
||||
<footer>
|
||||
<span><?php echo $lang->user->loginWithDemoUser;?></span>
|
||||
<?php
|
||||
$password = md5('123456');
|
||||
$link = inlink('login');
|
||||
$link .= strpos($link, '?') !== false ? '&' : '?';
|
||||
$link = inlink('login');
|
||||
$link .= strpos($link, '?') !== false ? '&' : '?';
|
||||
foreach($demoUsers as $demoAccount => $demoUser)
|
||||
{
|
||||
if($demoUser->password != $password) continue;
|
||||
echo html::a($link . "account={$demoAccount}&password=" . md5($password . $this->session->rand), $demoUser->realname);
|
||||
if($demoUser->password != $md5Password) continue;
|
||||
echo html::a($link . "account={$demoAccount}&password=" . md5($md5Password . $this->session->rand), $demoUser->realname);
|
||||
}
|
||||
?>
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user