Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms into sprint/272
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* The ticket entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class ticketEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $ticketID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function get($ticketID)
|
||||
{
|
||||
$control = $this->loadController('ticket', 'view');
|
||||
$control->view($ticketID);
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
$ticket = $data->data->ticket;
|
||||
|
||||
$ticket->productName = $data->data->product;
|
||||
$ticket->moduleName = isset($data->data->modulePath[0]->name) ? $data->data->modulePath[0]->name : '/';
|
||||
|
||||
if(!$data or !isset($data->status)) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$ticket->actions = $this->loadModel('action')->processActionForAPI($data->data->actions, $data->data->users, $this->lang->ticket);
|
||||
return $this->send(200, $this->format($ticket, 'activatedDate:time,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,mailto:userList,resolvedBy:user,resolvedDate:time,closedBy:user,closedDate:time,lastEditedBy:user,lastEditedDate:time,deadline:date,deleted:bool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT method.
|
||||
*
|
||||
* @param int $ticketID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function put($ticketID)
|
||||
{
|
||||
$oldTicket = $this->loadModel('ticket')->getById($ticketID);
|
||||
|
||||
$fields = 'module,product,type,openedBuild,assignedTo,deadline,title,desc,status,notify,uid';
|
||||
$this->batchSetPost($fields, $oldTicket);
|
||||
|
||||
$control = $this->loadController('ticket', 'edit');
|
||||
$control->edit($ticketID);
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
if(!isset($data->result)) return $this->sendError(400, 'error');
|
||||
|
||||
$ticket = $this->ticket->getByID($ticketID);
|
||||
|
||||
return $this->send(200, $this->format($ticket, 'openedBy:user,openedDate:time,activatedBy:user,activatedDate:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,editedBy:user,editedDate:time,deadline:time,assignedTo:user,mailto:userList,deleted:bool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE method.
|
||||
*
|
||||
* @param int $ticketID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function delete($ticketID)
|
||||
{
|
||||
$control = $this->loadController('ticket', 'delete');
|
||||
$control->delete($ticketID, 'yes');
|
||||
|
||||
$this->getData();
|
||||
return $this->sendSuccess(200, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* The ticketsEntry entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class ticketsEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
if(strpos(strtolower($this->param('fields')), 'moduleandproduct') !== false) return $this->getModuleAndProduct();
|
||||
|
||||
$control = $this->loadController('ticket', 'browse');
|
||||
$control->browse($this->param('status', 'wait'), 0, $this->param('orderBy', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$tickets = $data->data->tickets;
|
||||
$pager = $data->data->pager;
|
||||
|
||||
$result = array();
|
||||
foreach($tickets as $ticket)
|
||||
{
|
||||
$result[] = $this->format($ticket, 'openedBy:user,openedDate:time,activatedBy:user,activatedDate:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,editedBy:user,editedDate:time,deadline:time,assignedTo:user,mailto:userList,deleted:bool');
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['page'] = $pager->pageID;
|
||||
$data['total'] = $pager->recTotal;
|
||||
$data['limit'] = $pager->recPerPage;
|
||||
$data['tickets'] = $result;
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST method.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function post()
|
||||
{
|
||||
$fields = 'module,product,type,openedBuild,assignedTo,deadline,title,desc,status,notify,uid';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$control = $this->loadController('ticket', 'create');
|
||||
$this->requireFields('title,product,module');
|
||||
$control->create();
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$ticket = $this->loadModel('ticket')->getByID($data->id);
|
||||
|
||||
return $this->send(201, $this->format($ticket, 'openedBy:user,openedDate:time,activatedBy:user,activatedDate:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,editedBy:user,editedDate:time,deadline:time,assignedTo:user,mailto:userList,deleted:bool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModuleAndProduct()
|
||||
{
|
||||
$control = $this->loadController('ticket', 'create');
|
||||
$control->create();
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
$modules = $data->data->modules;
|
||||
$products = $data->data->products;
|
||||
|
||||
$data = array();
|
||||
$data['modules'] = $modules;
|
||||
$data['products'] = $products;
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,11 @@ $routes['/feedbacks/:id'] = 'feedback';
|
||||
$routes['/feedbacks/:id/assign'] = 'feedbackAssignto';
|
||||
$routes['/feedbacks/:id/close'] = 'feedbackClose';
|
||||
|
||||
$routes['/tickets'] = 'tickets';
|
||||
$routes['/tickets/:id'] = 'ticket';
|
||||
$routes['/tickets/:id/assign'] = 'ticketAssignto';
|
||||
$routes['/tickets/:id/close'] = 'ticketClose';
|
||||
|
||||
$routes['/options/:type'] = 'options';
|
||||
|
||||
$routes['/configurations'] = 'configs';
|
||||
|
||||
+304
-77
File diff suppressed because one or more lines are too long
+1068
-10
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
a:1:{i:0;O:8:"stdClass":12:{s:2:"id";s:2:"14";s:3:"lib";s:2:"24";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:7:"version";s:1:"2";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"addedBy";s:5:"admin";s:9:"addedDate";s:19:"2022-06-28 09:51:30";s:8:"editedBy";s:5:"admin";s:10:"editedDate";s:19:"2022-06-28 09:51:30";s:7:"deleted";s:1:"0";}}
|
||||
a:1:{i:0;O:8:"stdClass":12:{s:2:"id";s:1:"1";s:3:"lib";s:3:"833";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:7:"version";s:1:"2";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";s:8:"editedBy";s:6:"wwccss";s:10:"editedDate";s:19:"2022-12-22 10:35:42";s:7:"deleted";s:1:"0";}}
|
||||
@@ -1 +1 @@
|
||||
a:3:{i:0;O:8:"stdClass":8:{s:2:"id";s:2:"21";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:5:"admin";s:9:"addedDate";s:19:"2022-06-28 09:51:30";}i:1;O:8:"stdClass":8:{s:2:"id";s:2:"20";s:4:"name";s:3:"aaa";s:4:"type";s:8:"formData";s:4:"desc";s:0:"";s:9:"attribute";s:137:"[{"field":"aaa","paramsType":"object","required":"","desc":"","structType":"formData","sub":1,"key":"l11gznwwu2chih9mc28","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:5:"admin";s:9:"addedDate";s:19:"2022-06-28 09:51:30";}i:2;O:8:"stdClass":8:{s:2:"id";s:2:"19";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"2";s:7:"addedBy";s:5:"admin";s:9:"addedDate";s:19:"2022-06-28 09:51:30";}}
|
||||
a:3:{i:0;O:8:"stdClass":8:{s:2:"id";s:1:"1";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}i:1;O:8:"stdClass":8:{s:2:"id";s:1:"2";s:4:"name";s:3:"aaa";s:4:"type";s:8:"formData";s:4:"desc";s:0:"";s:9:"attribute";s:137:"[{"field":"aaa","paramsType":"object","required":"","desc":"","structType":"formData","sub":1,"key":"l11gznwwu2chih9mc28","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}i:2;O:8:"stdClass":8:{s:2:"id";s:1:"3";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"2";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+3758
File diff suppressed because one or more lines are too long
+1476
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
a:1:{i:0;O:8:"stdClass":12:{s:2:"id";s:1:"1";s:3:"lib";s:3:"833";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:7:"version";s:1:"2";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";s:8:"editedBy";s:6:"wwccss";s:10:"editedDate";s:19:"2022-12-22 10:35:42";s:7:"deleted";s:1:"0";}}
|
||||
@@ -0,0 +1 @@
|
||||
a:3:{i:0;O:8:"stdClass":8:{s:2:"id";s:1:"1";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}i:1;O:8:"stdClass":8:{s:2:"id";s:1:"2";s:4:"name";s:3:"aaa";s:4:"type";s:8:"formData";s:4:"desc";s:0:"";s:9:"attribute";s:137:"[{"field":"aaa","paramsType":"object","required":"","desc":"","structType":"formData","sub":1,"key":"l11gznwwu2chih9mc28","children":[]}]";s:7:"version";s:1:"1";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}i:2;O:8:"stdClass":8:{s:2:"id";s:1:"3";s:4:"name";s:4:"user";s:4:"type";s:4:"json";s:4:"desc";s:0:"";s:9:"attribute";s:537:"[{"field":"id","paramsType":"int","required":"","desc":"","structType":"json","sub":1,"key":"kwoq04srulxevt93a3h","children":[]},{"field":"account","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0a387on1wpuwntd","children":[]},{"field":"realname","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0i4g80qyu8lqp28","children":[]},{"field":"avatar","paramsType":"string","required":"","desc":"","structType":"json","sub":1,"key":"kwoq0le57o2p6vxc8ov","children":[]}]";s:7:"version";s:1:"2";s:7:"addedBy";s:6:"wwccss";s:9:"addedDate";s:19:"2022-12-22 10:35:42";}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -53,6 +53,9 @@ else
|
||||
$lang->task->common = 'Task';
|
||||
$lang->dashboard = isset($lang->dashboard->common) ? $lang->dashboard->common : $lang->dashboard;
|
||||
|
||||
/* Ticket. */
|
||||
if($config->edition != 'open') $lang->feedback->menu->ticket = array('link' => 'Ticket|ticket|browse|browseType=unclosed');
|
||||
|
||||
/* My menu. */
|
||||
$lang->my->menu = new stdclass();
|
||||
$lang->my->menu->index = array('link' => "$lang->dashboard|my|index");
|
||||
|
||||
@@ -53,6 +53,9 @@ else
|
||||
$lang->task->common = 'Task';
|
||||
$lang->dashboard = isset($lang->dashboard->common) ? $lang->dashboard->common : $lang->dashboard;
|
||||
|
||||
/* Ticket. */
|
||||
if($config->edition != 'open') $lang->feedback->menu->ticket = array('link' => 'Ticket|ticket|browse|browseType=unclosed');
|
||||
|
||||
/* My menu. */
|
||||
$lang->my->menu = new stdclass();
|
||||
$lang->my->menu->index = array('link' => "$lang->dashboard|my|index");
|
||||
|
||||
@@ -53,6 +53,9 @@ else
|
||||
$lang->task->common = 'Task';
|
||||
$lang->dashboard = isset($lang->dashboard->common) ? $lang->dashboard->common : $lang->dashboard;
|
||||
|
||||
/* Ticket. */
|
||||
if($config->edition != 'open') $lang->feedback->menu->ticket = array('link' => 'Ticket|ticket|browse|browseType=unclosed');
|
||||
|
||||
/* My menu. */
|
||||
$lang->my->menu = new stdclass();
|
||||
$lang->my->menu->index = array('link' => "$lang->dashboard|my|index");
|
||||
|
||||
@@ -53,6 +53,10 @@ else
|
||||
$lang->task->common = '任务';
|
||||
$lang->dashboard = isset($lang->dashboard->common) ? $lang->dashboard->common : $lang->dashboard;
|
||||
|
||||
|
||||
/* Ticket. */
|
||||
if($config->edition != 'open') $lang->feedback->menu->ticket = array('link' => '工单|ticket|browse|browseType=unclosed');
|
||||
|
||||
/* My menu. */
|
||||
$lang->my->menu = new stdclass();
|
||||
$lang->my->menu->index = array('link' => "$lang->dashboard|my|index");
|
||||
|
||||
@@ -984,6 +984,21 @@ if($config->edition == 'biz' or $config->edition == 'max')
|
||||
$lang->resource->faq = new stdclass();
|
||||
$lang->resource->faq->browse = 'browse';
|
||||
|
||||
/* Ticket */
|
||||
$lang->resource->ticket = new stdclass();
|
||||
$lang->resource->ticket->index = 'index';
|
||||
$lang->resource->ticket->create = 'create';
|
||||
$lang->resource->ticket->edit = 'edit';
|
||||
$lang->resource->ticket->view = 'view';
|
||||
$lang->resource->ticket->browse = 'browse';
|
||||
$lang->resource->ticket->assignTo = 'assign';
|
||||
$lang->resource->ticket->close = 'close';
|
||||
$lang->resource->ticket->activate = 'activate';
|
||||
$lang->resource->ticket->delete = 'delete';
|
||||
$lang->resource->ticket->exportTemplate = 'exportTemplate';
|
||||
$lang->resource->ticket->import = 'import';
|
||||
$lang->resource->ticket->export = 'exportAction';
|
||||
|
||||
/* Attend */
|
||||
$lang->resource->attend = new stdclass();
|
||||
$lang->resource->attend->department = 'department';
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* Build ticket browse menu.
|
||||
*
|
||||
* @param object $ticketID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function buildOperateBrowseMenu($ticketID)
|
||||
{
|
||||
$ticket = $this->getByID($ticketID);
|
||||
$menu = '';
|
||||
$params = "ticket=$ticketID";
|
||||
$disabled = '';
|
||||
|
||||
if($ticket->status == 'done')
|
||||
{
|
||||
$menu .= $this->buildMenu('ticket', 'close', $params, $ticket, 'browse', 'off', 'hiddenwin', '', '', '', $this->lang->ticket->close);
|
||||
}
|
||||
else
|
||||
{
|
||||
$menu .= $this->buildMenu('ticket', 'close', $params, $ticket, 'browse', 'off', '', 'iframe', true, '', $this->lang->ticket->close);
|
||||
}
|
||||
|
||||
$menu .= $this->buildMenu('ticket', 'edit', $params, $ticket, 'browse', 'edit', '', '', '', '', $this->lang->ticket->edit);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ticket view menu.
|
||||
*
|
||||
* @param int $ticketID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function buildOperateViewMenu($ticketID)
|
||||
{
|
||||
$ticket = $this->getByID($ticketID);
|
||||
if($ticket->deleted) return '';
|
||||
|
||||
$menu = '';
|
||||
$params = "ticket=$ticket->id";
|
||||
|
||||
if($ticket->status != 'closed') $menu .= $this->loadModel('effort')->createAppendLink('ticket', $ticketID);
|
||||
|
||||
if($ticket->status == 'done')
|
||||
{
|
||||
$menu .= $this->buildMenu('ticket', 'close', $params, $ticket, 'view', 'off', 'hiddenwin', '', '', '', $this->lang->ticket->close);
|
||||
}
|
||||
else
|
||||
{
|
||||
$menu .= $this->buildMenu('ticket', 'close', $params, $ticket, 'view', 'off', '', 'iframe', true, '', $this->lang->ticket->close);
|
||||
}
|
||||
|
||||
$menu .= $this->buildMenu('ticket', 'activate', $params, $ticket, 'view', 'magic', '', "iframe", true, '', $this->lang->ticket->activate);
|
||||
$menu .= $this->buildMenu('ticket', 'edit', $params, $ticket, 'view', 'edit', '', '', '', '', $this->lang->ticket->edit);
|
||||
$menu .= $this->buildMenu('ticket', 'delete', $params, $ticket, 'view', 'trash', 'hiddenwin');
|
||||
|
||||
return $menu;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<script>
|
||||
$('#sidebar .cell > div').addClass('hidden')
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script>
|
||||
/**
|
||||
* Load modules by product.
|
||||
*
|
||||
* @param int productID
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadModules(productID)
|
||||
{
|
||||
link = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=ticket&branch=all&rootModuleID=0&returnType=html&fieldID=&needManage=false');
|
||||
$('#moduleBox').load(link, function(data)
|
||||
{
|
||||
var $inputGroup = $(this);
|
||||
$inputGroup.find('select').chosen()
|
||||
$inputGroup.prepend("<span class='input-group-addon'>" + moduleLang + "</span>");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -49,6 +49,7 @@ html[lang='de'] #keywordsAddonLabel, .task {width: 73px;}
|
||||
#osBox {width: 190px;}
|
||||
#projectBox .required:after {right: 1px;}
|
||||
#branch_chosen {min-width: 90px;}
|
||||
#branch_chosen.chosen-container-active .chosen-single{border-left-color: #2e7fff !important;}
|
||||
#notifyEmailTd .input-group-addon, #deadlineTd .input-group-addon {border-radius: 2px 0px 2px 0px !important;}
|
||||
|
||||
#feedback.input-group + #notifyEmailLabel {position: relative; width: 50%; left: 48%;}
|
||||
|
||||
@@ -3713,8 +3713,13 @@ class bugModel extends model
|
||||
/* Get related objects title or names. */
|
||||
$table = $this->config->objectTables[$object];
|
||||
if($table) $relatedObjects = $this->dao->select($pairs)->from($table)->where('id')->in($relatedObjectIdList)->fetchPairs();
|
||||
if($object == 'branch' and $this->session->currentProductType != 'normal')
|
||||
{
|
||||
$productID = current($bugs)->product;
|
||||
$relatedObjects = $this->dao->select($pairs)->from($table)->where('product')->eq($productID)->fetchPairs();
|
||||
}
|
||||
|
||||
if(in_array($object, array('build','resolvedBuild'))) $relatedObjects= array('trunk' => $this->lang->trunk) + $relatedObjects;
|
||||
if(in_array($object, array('build','resolvedBuild','branch'))) $relatedObjects = array('trunk' => $this->lang->trunk) + $relatedObjects;
|
||||
return array('' => '', 0 => '') + $relatedObjects;
|
||||
}
|
||||
|
||||
|
||||
@@ -781,11 +781,11 @@ class build extends control
|
||||
*/
|
||||
protected function unlinkOldBranch($changes)
|
||||
{
|
||||
foreach($changes as $buildID => $changes)
|
||||
foreach($changes as $buildID => $changeList)
|
||||
{
|
||||
$oldBranch = '';
|
||||
$newBranch = '';
|
||||
foreach($changes as $changeId => $change)
|
||||
foreach($changeList as $changeId => $change)
|
||||
{
|
||||
if($change['field'] == 'branch')
|
||||
{
|
||||
@@ -795,8 +795,8 @@ class build extends control
|
||||
}
|
||||
}
|
||||
$build = $this->build->getByID($buildID);
|
||||
$planStories = $this->loadModel('story')->getByList($build->allStories);
|
||||
$planBugs = $this->loadModel('bug')->getByList($build->allBugs);
|
||||
$planStories = $build->allStories ? $this->loadModel('story')->getByList($build->allStories) : '';
|
||||
$planBugs = $build->allBugs ? $this->loadModel('bug')->getByList($build->allBugs) : '';
|
||||
if($oldBranch)
|
||||
{
|
||||
foreach($planStories as $storyID => $story)
|
||||
|
||||
@@ -2029,7 +2029,7 @@ class execution extends control
|
||||
$productPlans = array(0 => '');
|
||||
$linkedBranches = array();
|
||||
$linkedBranchList = array();
|
||||
$linkedProducts = $this->product->getProducts($executionID, 'all', '', true, $linkedProductIdList);
|
||||
$linkedProducts = $this->product->getProducts($executionID, 'all', '', true, $linkedProductIdList, false);
|
||||
$plans = $this->productplan->getGroupByProduct(array_keys($linkedProducts), 'skipParent|unexpired');
|
||||
$executionStories = $this->project->getStoriesByProject($executionID);
|
||||
|
||||
@@ -2039,7 +2039,7 @@ class execution extends control
|
||||
$linkedStoryIDList = array();
|
||||
foreach($linkedProducts as $productID => $linkedProduct)
|
||||
{
|
||||
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name;
|
||||
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->deleted ? $linkedProduct->name . "({$this->lang->product->deleted})" : $linkedProduct->name;
|
||||
$productPlans[$productID] = array();
|
||||
|
||||
foreach($branches[$productID] as $branchID => $branch)
|
||||
|
||||
@@ -810,7 +810,7 @@ class kanbanModel extends model
|
||||
}
|
||||
elseif($branchID == 'all')
|
||||
{
|
||||
$branches = $this->branch->getPairs($product->id);
|
||||
$branches = $this->branch->getPairs($product->id, 'active');
|
||||
}
|
||||
elseif($branchID == BRANCH_MAIN)
|
||||
{
|
||||
|
||||
@@ -382,16 +382,17 @@ class productModel extends model
|
||||
* @param int $projectID
|
||||
* @param string $status all|noclosed
|
||||
* @param string|array $append
|
||||
* @param bool $noDeleted
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProductPairsByProject($projectID = 0, $status = 'all', $append = '')
|
||||
public function getProductPairsByProject($projectID = 0, $status = 'all', $append = '', $noDeleted = true)
|
||||
{
|
||||
$products = empty($projectID) ? $this->getList(0, 'all', 0, 0, 'all') : $this->getProducts($projectID, $status, '', true, $append);
|
||||
$products = empty($projectID) ? $this->getList(0, 'all', 0, 0, 'all') : $this->getProducts($projectID, $status, '', true, $append, $noDeleted);
|
||||
$pairs = array();
|
||||
if(!empty($products))
|
||||
{
|
||||
foreach($products as $product) $pairs[$product->id] = $product->name;
|
||||
foreach($products as $product) $pairs[$product->id] = $product->deleted ? $product->name . "({$this->lang->product->deleted})" : $product->name;
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
@@ -424,10 +425,11 @@ class productModel extends model
|
||||
* @param string $orderBy
|
||||
* @param bool $withBranch
|
||||
* @param string|array $append
|
||||
* @param bool $noDeleted
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProducts($projectID = 0, $status = 'all', $orderBy = '', $withBranch = true, $append = '')
|
||||
public function getProducts($projectID = 0, $status = 'all', $orderBy = '', $withBranch = true, $append = '', $noDeleted = true)
|
||||
{
|
||||
if(defined('TUTORIAL'))
|
||||
{
|
||||
@@ -441,7 +443,8 @@ class productModel extends model
|
||||
$projectProducts = $this->dao->select("t1.branch, t1.plan, t2.*")
|
||||
->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->where('1=1')
|
||||
->beginIF($noDeleted)->andWhere('t2.deleted')->eq(0)->fi()
|
||||
->beginIF(!empty($projectID))->andWhere('t1.project')->in($projectID)->fi()
|
||||
->beginIF(!$this->app->user->admin and $this->config->vision == 'rnd')->andWhere('t2.id')->in($views)->fi()
|
||||
->andWhere('t2.vision')->eq($this->config->vision)
|
||||
|
||||
@@ -358,7 +358,7 @@ class productplan extends control
|
||||
else
|
||||
{
|
||||
$planGroup = $this->productplan->getGroupByProduct($product->id, 'skipParent', '', $orderBy);
|
||||
$branches = $this->branch->getPairs($product->id);
|
||||
$branches = $this->branch->getPairs($product->id, 'active');
|
||||
|
||||
foreach($branches as $id => $name)
|
||||
{
|
||||
@@ -383,6 +383,7 @@ class productplan extends control
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->viewType = $viewType;
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
|
||||
$this->view->plans = $this->productplan->getList($productID, $branch, $browseType, $pager, $sort, "", $queryID);
|
||||
$this->view->pager = $pager;
|
||||
$this->view->projects = $this->product->getProjectPairsByProduct($productID, $branch, '', $status = 'closed', 'multiple');
|
||||
|
||||
@@ -36,8 +36,24 @@ td.c-branch {overflow: hidden; text-align: left !important; white-space: nowrap;
|
||||
|
||||
.switchButton {background: #fff !important;}
|
||||
.panel-actions {position: relative; padding: 0 0; padding-top: 1px;}
|
||||
#kanbanContainer {background: #efefef; box-shadow: none;}
|
||||
#kanbanContainer {padding-bottom: 0; margin-bottom: 0; border: unset;}
|
||||
#kanbanContainer.fullscreen {overflow: auto;}
|
||||
.icon-list {padding-left: 7px;}
|
||||
.titleBox .icon-delay {padding-right: 8px;}
|
||||
|
||||
.kanban-card .productplanInfo .user {float: right;display: block;}
|
||||
.kanban-card a[disabled] {color: #313c52;}
|
||||
.kanban-card .header .actions {position: absolute;top: 4px;right: 4px;opacity: 0;}
|
||||
.kanban-card .productplanDesc {min-width: 100%;color: #838a9d;overflow: hidden;white-space: nowrap;text-overflow: clip;}
|
||||
.kanban-card {height: auto !important;padding: 8px 14px !important;min-height: 60px;}
|
||||
.icon-kanban {padding-left: 7px; font-size: 16px;}
|
||||
.label-wait {background: #EFEFEF !important;color: #838A9D;}
|
||||
.label-future {background: #EFEFEF !important;color: #838A9D;border-radius: 9px;}
|
||||
.label-doing {background: #f8d2d2 !important;color: #e64b4c;}
|
||||
.label-done {background: #dfe9d8 !important;color: #429b16;}
|
||||
.label-closed, .label-terminate {background: #e5e5e5 !important;color: #b8b8b8;}
|
||||
|
||||
#productplanList thead th.c-title {width: 100%;}
|
||||
#productplanList .c-actions {width: 265px;}
|
||||
#productplanList .c-actions .btn+.btn {margin-left: -1px;}
|
||||
|
||||
@@ -365,8 +365,39 @@ function renderKanbanItem(item, $item)
|
||||
if(privs.includes('view')) $title = $('<a class="title"></a>').appendTo($titleBox).attr('href', createLink(rawModule, 'view', 'cardID=' + item.id));
|
||||
if(!privs.includes('view')) $title = $('<a class="title"></a>').appendTo($titleBox);
|
||||
}
|
||||
if(!$title.children('i').length)
|
||||
{
|
||||
$(function() {$title.prepend('<i class="icon icon-delay"></i>');})
|
||||
}
|
||||
$title.text(item.title).attr('title', item.title);
|
||||
|
||||
var $info = $item.children('.productplanInfo');
|
||||
if(!$info.length) $info = $(
|
||||
[
|
||||
'<div class="productplanInfo">',
|
||||
'</div>'
|
||||
].join('')).appendTo($item);
|
||||
|
||||
/* Output plan desc information. */
|
||||
var $descBox = $item.children('.productplanDesc');
|
||||
if(!$descBox.length)
|
||||
{
|
||||
$descBox = $('<div class="productplanDesc c-name cardDesc" title="' + item.desc + '">' + item.desc + '</div>').appendTo($info);
|
||||
}
|
||||
|
||||
var $statusBox = $item.children('.productplanStatus');
|
||||
if(!$statusBox.length)
|
||||
{
|
||||
if(item.deleted == '0')
|
||||
{
|
||||
$statusBox = $('<span class="productplanStatus label label-' + item.status + '">' + productplanLang.statusList[item.status] + '</span>').appendTo($info);
|
||||
}
|
||||
else
|
||||
{
|
||||
$statusBox = $('<span class="productplanStatus label label-deleted">' + productplanLang.deleted + '</span>').appendTo($info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine whether to print an expired label. */
|
||||
var today = new Date();
|
||||
var begin = $.zui.createDate(item.begin);
|
||||
@@ -392,54 +423,71 @@ function renderKanbanItem(item, $item)
|
||||
].join('')).appendTo($header);
|
||||
}
|
||||
|
||||
/* Output plan date information. */
|
||||
var $dateBox = $item.children('.dateBox');
|
||||
if(!$dateBox.length) $dateBox = $(
|
||||
[
|
||||
'<div class="dateBox">',
|
||||
'<span class="time label label-outline"></span>',
|
||||
'</div>'
|
||||
].join('')).appendTo($item);
|
||||
/* Display date of product plan. */
|
||||
var $date = $info.children('.date');
|
||||
var begin = $.zui.createDate(item.begin);
|
||||
var end = $.zui.createDate(item.end);
|
||||
var today = new Date();
|
||||
var labelType = (item.begin <= $.zui.formatDate(today, 'yyyy-MM-dd') && item.end >= $.zui.formatDate(today, 'yyyy-MM-dd')) ? 'danger' : 'wait';
|
||||
var labelTitle = $.zui.formatDate(begin, 'MM-dd') + ' ' + productplanLang.to + ' ' + $.zui.formatDate(end, 'MM-dd');
|
||||
|
||||
var $time = $dateBox.children('.time');
|
||||
var beginTimeShort = $.zui.formatDate(begin, 'MM-dd');
|
||||
var beginTimeLong = $.zui.formatDate(begin, 'yyyy-MM-dd');
|
||||
var endTimeShort = $.zui.formatDate(end, 'MM-dd');
|
||||
var endTimeLong = $.zui.formatDate(end, 'yyyy-MM-dd')
|
||||
var to = productplanLang.to;
|
||||
var undetermined = productplanLang.future;
|
||||
var undetermindeDate = '2030-01-01';
|
||||
if(item.begin != undetermindeDate && item.end != undetermindeDate)
|
||||
if((item.begin == '2030-01-01' || item.end == '2030-01-01'))
|
||||
{
|
||||
$time.text(beginTimeShort + ' ' + to + ' ' + endTimeShort).attr('title', beginTimeLong + to + endTimeLong).show();
|
||||
labelType = 'future';
|
||||
labelTitle = productplanLang.future;
|
||||
}
|
||||
else if(item.begin != undetermindeDate && item.end == undetermindeDate)
|
||||
if(!$date.length) $date = $('<span class="date label label-' + labelType + '"></span>').appendTo($info);
|
||||
$date.text(labelTitle).attr('title', labelTitle).show();
|
||||
|
||||
/* Display avatars of creator. */
|
||||
var $user = $info.children('.user');
|
||||
var user = [item.createdBy];
|
||||
if(users[item.createdBy])
|
||||
{
|
||||
$time.text(beginTimeShort + ' ' + to + ' ' + undetermined).attr('title', beginTimeLong + to).show();
|
||||
if(!$user.length) $user = $('<div class="user"></div>').appendTo($info);
|
||||
$user.html(renderUsersAvatar(user, item.id)).attr('title', users[item.createdBy]);
|
||||
}
|
||||
else if(item.begin == undetermindeDate && item.end != undetermindeDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render avatars of user.
|
||||
* @param {String|{account: string, avatar: string}} user User account or user object
|
||||
* @returns {string}
|
||||
*/
|
||||
function renderUsersAvatar(users, itemID, size)
|
||||
{
|
||||
var avatarSizeClass = 'avatar-' + (size || 'md');
|
||||
|
||||
if(users.length == 0 || (users.length == 1 && users[0] == ''))
|
||||
{
|
||||
$time.text(undetermined + ' ' + to + ' ' + endTimeShort).attr('title', to + endTimeLong).show();
|
||||
}
|
||||
else if(item.begin != '2030-01-01' && item.end == '2030-01-01')
|
||||
{
|
||||
$time.text($.zui.formatDate(begin, 'MM-dd') + ' ' + productplanLang.to + ' ' + productplanLang.future).attr('title', $.zui.formatDate(begin, 'yyyy-MM-dd') + productplanLang.to).show();
|
||||
}
|
||||
else if(item.begin == '2030-01-01' && item.end != '2030-01-01')
|
||||
{
|
||||
$time.text(productplanLang.future + ' ' + productplanLang.to + ' ' + $.zui.formatDate(end, 'MM-dd')).attr('title', $.zui.formatDate(end, 'yyyy-MM-dd') + productplanLang.to).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$time.text(undetermined).attr('title', undetermined).show();
|
||||
return $('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + noAssigned + '" style="background: #ccc"><i class="icon icon-person"></i></div>');
|
||||
}
|
||||
|
||||
/* Output plan desc information. */
|
||||
var $desc = $item.children('.desc');
|
||||
if(!$desc.length)
|
||||
var assignees = [];
|
||||
for(var user of users)
|
||||
{
|
||||
$("<div class='desc c-name'"+ " title='" + item.desc + "'>" + item.desc + '</div>').appendTo($item);
|
||||
var $noPrivAndNoAssigned = $('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle" title="' + noAssigned + '" style="background: #ccc"><i class="icon icon-person"></i></div>');
|
||||
if(!priv.canAssignCard && !user)
|
||||
{
|
||||
assignees.push($noPrivAndNoAssigned);
|
||||
continue;
|
||||
}
|
||||
if(!user)
|
||||
{
|
||||
assignees.push($('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + noAssigned + '" style="background: #ccc"><i class="icon icon-person"></i></div>'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(typeof user === 'string') user = {account: user};
|
||||
if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account];
|
||||
if(!user.name && window.users && window.users[user.account]) user.name = window.users[user.account];
|
||||
|
||||
assignees.push($('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe"></div>').avatar({user: user}));
|
||||
}
|
||||
|
||||
if(assignees.length > 3) assignees.splice(3, assignees.length - 3, '<span>...</span>');
|
||||
|
||||
return assignees;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Mehrere Verknpfungen aufheben";
|
||||
$lang->productplan->linkedBugs = 'Verknüpfte Bugs';
|
||||
$lang->productplan->unlinkedBugs = 'Unverknüpfte Bugs';
|
||||
$lang->productplan->unexpired = 'Unexpired Plans';
|
||||
$lang->productplan->noAssigned = 'No Assigned';
|
||||
$lang->productplan->all = 'All Plans';
|
||||
$lang->productplan->setDate = "Set Start and end Date";
|
||||
$lang->productplan->expired = "Expired";
|
||||
|
||||
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Batch Unlink Bugs";
|
||||
$lang->productplan->linkedBugs = 'Linked Bugs';
|
||||
$lang->productplan->unlinkedBugs = 'Unlinked Bugs';
|
||||
$lang->productplan->unexpired = 'Unexpired';
|
||||
$lang->productplan->noAssigned = 'No Assigned';
|
||||
$lang->productplan->all = 'All Plans';
|
||||
$lang->productplan->setDate = "Set Start and end Date";
|
||||
$lang->productplan->expired = "Expired";
|
||||
|
||||
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Retirer Bugs par Lot";
|
||||
$lang->productplan->linkedBugs = 'Bugs Planifiés';
|
||||
$lang->productplan->unlinkedBugs = 'Bugs non Planifiés';
|
||||
$lang->productplan->unexpired = 'Plans non échus';
|
||||
$lang->productplan->noAssigned = 'No Assigned';
|
||||
$lang->productplan->all = 'Tous les Plans';
|
||||
$lang->productplan->setDate = "Set Start and end Date";
|
||||
$lang->productplan->expired = "Expired";
|
||||
|
||||
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "批量移除Bug";
|
||||
$lang->productplan->linkedBugs = 'Bug';
|
||||
$lang->productplan->unlinkedBugs = '未关联Bug';
|
||||
$lang->productplan->unexpired = "未过期";
|
||||
$lang->productplan->noAssigned = '未指派';
|
||||
$lang->productplan->all = "所有计划";
|
||||
$lang->productplan->setDate = "设置计划起止时间";
|
||||
$lang->productplan->expired = "已过期";
|
||||
|
||||
@@ -26,14 +26,16 @@
|
||||
.kanban-card:hover > .header > .actions {opacity: 1;}
|
||||
.kanban-card .title {white-space: nowrap; overflow: hidden;}
|
||||
.kanban-card .expired {margin-left: 2px;}
|
||||
.kanban-card .dateBox {padding-top: 22px;}
|
||||
.kanban-card .desc {overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #838a9d; padding-top: 5px;}
|
||||
.kanban-card .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
|
||||
.dropdown-menu > li > .disabled {pointer-events: none; color: #838a9d;}
|
||||
</style>
|
||||
<?php js::set('kanbanData', $kanbanData);?>
|
||||
<?php js::set('rawModule', $app->rawModule);?>
|
||||
<?php js::set('users', $users);?>
|
||||
<?php js::set('noAssigned', $lang->productplan->noAssigned);?>
|
||||
<?php js::set('productplanLang', $lang->productplan);?>
|
||||
<?php js::set('priv', array('canAssignCard' => common::hasPriv('kanban', 'assigncard')));?>
|
||||
<div id="mainMenu" class="clearfix">
|
||||
<div class="btn-toolbar pull-left">
|
||||
<?php if($product->type == 'normal'):?>
|
||||
|
||||
@@ -64,7 +64,7 @@ class programplan extends control
|
||||
if(!defined('RUN_MODE') || RUN_MODE != 'api') $projectID = $this->project->saveState((int)$projectID, $this->project->getPairsByProgram());
|
||||
|
||||
$products = $this->loadModel('product')->getProducts($projectID);
|
||||
if($this->session->hasProduct) $this->lang->modulePageNav = $this->product->select($products, $this->productID, 'programplan', 'browse', $type, 0, 0, '', false);
|
||||
if($this->session->hasProduct) $this->lang->modulePageNav = $this->product->select($products, $productID, 'programplan', 'browse', $type, 0, 0, '', false);
|
||||
|
||||
$selectCustom = 0; // Display date and task settings.
|
||||
$dateDetails = 1; // Gantt chart detail date display.
|
||||
@@ -81,7 +81,7 @@ class programplan extends control
|
||||
|
||||
if(strpos($selectCustom, 'date') !== false) $dateDetails = 0;
|
||||
|
||||
$plans = $this->programplan->getDataForGantt($projectID, $this->productID, $baselineID, $selectCustom, false);
|
||||
$plans = $this->programplan->getDataForGantt($projectID, $productID, $baselineID, $selectCustom, false);
|
||||
|
||||
/* Set Custom. */
|
||||
foreach(explode(',', $this->config->programplan->custom->customGanttFields) as $field) $customFields[$field] = $this->lang->programplan->ganttCustom[$field];
|
||||
@@ -98,7 +98,7 @@ class programplan extends control
|
||||
$selectCustom = $this->loadModel('setting')->getItem("owner={$owner}&module={$module}§ion={$section}&key={$object}");
|
||||
if(strpos($selectCustom, 'date') !== false) $dateDetails = 0;
|
||||
|
||||
$plans = $this->programplan->getDataForGanttGroupByAssignedTo($projectID, $this->productID, $baselineID, $selectCustom, false);
|
||||
$plans = $this->programplan->getDataForGanttGroupByAssignedTo($projectID, $productID, $baselineID, $selectCustom, false);
|
||||
|
||||
/* Set Custom. */
|
||||
foreach(explode(',', $this->config->programplan->custom->customGanttFields) as $field) $customFields[$field] = $this->lang->programplan->ganttCustom[$field];
|
||||
@@ -110,7 +110,7 @@ class programplan extends control
|
||||
{
|
||||
$sort = common::appendOrder($orderBy);
|
||||
$this->loadModel('datatable');
|
||||
$plans = $this->programplan->getPlans($projectID, $this->productID, $sort);
|
||||
$plans = $this->programplan->getPlans($projectID, $productID, $sort);
|
||||
}
|
||||
|
||||
$zooming = !empty($this->config->programplan->ganttCustom->zooming) ? $this->config->programplan->ganttCustom->zooming : 'day';
|
||||
@@ -118,8 +118,9 @@ class programplan extends control
|
||||
$this->view->position[] = $this->lang->programplan->browse;
|
||||
$this->view->projectID = $projectID;
|
||||
$this->view->project = $this->project->getByID($projectID);
|
||||
$this->view->productID = $this->productID;
|
||||
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID);
|
||||
$this->view->productID = $productID;
|
||||
$this->view->product = $this->product->getByID($productID);
|
||||
$this->view->productList = $this->product->getProductPairsByProject($projectID, 'all', '', false);
|
||||
$this->view->type = $type;
|
||||
$this->view->plans = $plans;
|
||||
$this->view->orderBy = $orderBy;
|
||||
|
||||
@@ -52,12 +52,12 @@ class programplanModel extends model
|
||||
*/
|
||||
public function getStage($executionID = 0, $productID = 0, $browseType = 'all', $orderBy = 'id_asc')
|
||||
{
|
||||
if(empty($executionID) || empty($productID)) return array();
|
||||
if(empty($executionID)) return array();
|
||||
|
||||
$plans = $this->dao->select('t2.*')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
|
||||
->where('t2.type')->eq('stage')
|
||||
->andWhere('t1.product')->eq($productID)
|
||||
->beginIF($productID)->andWhere('t1.product')->eq($productID)->fi()
|
||||
->beginIF($browseType == 'all')->andWhere('t2.project')->eq($executionID)->fi()
|
||||
->beginIF($browseType == 'parent')->andWhere('t2.parent')->eq($executionID)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi()
|
||||
|
||||
@@ -71,6 +71,8 @@ form {display: block; margin-top: 0em; margin-block-end: 1em;}
|
||||
.switchBtn > i {padding-left: 7px;}
|
||||
#mainContent > .pull-left > .btn-group > .text{display: block;margin-top: 7px;}
|
||||
#mainContent > .pull-left > .btn-group > a > .text{overflow: hidden;display: block;}
|
||||
#mainContent > .pull-right > .button-group .text{margin-left: 0px;}
|
||||
.pull-right .icon-plus.icon-sm:before{vertical-align: 4%;}
|
||||
</style>
|
||||
<?php js::set('customUrl', $this->createLink('programplan', 'ajaxCustom'));?>
|
||||
<?php js::set('dateDetails', $dateDetails);?>
|
||||
@@ -91,10 +93,10 @@ form {display: block; margin-top: 0em; margin-block-end: 1em;}
|
||||
<?php
|
||||
$class = '';
|
||||
if($productID == 0) $class = 'class="active"';
|
||||
foreach($productList as $key => $product)
|
||||
foreach($productList as $key => $productName)
|
||||
{
|
||||
$class = $productID == $key ? 'class="active"' : '';
|
||||
echo "<li $class>" . html::a($this->createLink('programplan', 'browse', "projectID=$projectID&productID=$key&type=gantt"), $product) . "</li>";
|
||||
echo "<li $class>" . html::a($this->createLink('programplan', 'browse', "projectID=$projectID&productID=$key&type=gantt"), $productName) . "</li>";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
@@ -118,7 +120,7 @@ form {display: block; margin-top: 0em; margin-block-end: 1em;}
|
||||
</div>
|
||||
<?php
|
||||
echo html::a(helper::createLink('programplan', 'ajaxcustom', '', '', true), '<i class="icon icon-cog-outline"></i> ' . $lang->settings, '', "class='iframe btn btn-link' data-width='45%'");
|
||||
if(common::hasPriv('programplan', 'create')) echo html::a($this->createLink('programplan', 'create', "projectID=$projectID"), "<i class='icon icon-sm icon-plus'> </i> " . $this->lang->programplan->create, '', "class='btn btn-primary'");
|
||||
if(common::hasPriv('programplan', 'create') and empty($product->deleted)) echo html::a($this->createLink('programplan', 'create', "projectID=$projectID"), "<i class='icon icon-sm icon-plus'> </i> " . $this->lang->programplan->create, '', "class='btn btn-primary'");
|
||||
?>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
|
||||
@@ -1116,8 +1116,9 @@ class project extends control
|
||||
|
||||
$this->view->executionStats = $executionStats;
|
||||
$this->view->showToggleIcon = $showToggleIcon;
|
||||
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID);
|
||||
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID, 'all', '', false);
|
||||
$this->view->productID = $productID;
|
||||
$this->view->product = $this->product->getByID($productID);
|
||||
$this->view->projectID = $projectID;
|
||||
$this->view->project = $project;
|
||||
$this->view->projects = $projects;
|
||||
|
||||
@@ -18,3 +18,4 @@ td.c-name > .project-type-label {flex: 0 0 36px; padding-right: 2px;}
|
||||
span.table-nest-icon.table-nest-toggle {min-width: 22px; max-width: 22px;}
|
||||
#projectForm .c-name, #executionTableList .c-name, #programTableList .c-name {padding-right:47px;}
|
||||
#mainMenu .pull-left > .btn-group > a > .text{overflow: hidden;display: block;}
|
||||
#mainMenu .pull-right .icon-list{padding-left: 7px;}
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
<div class='btn-toolbar pull-left'>
|
||||
<?php if($project->division and $project->hasProduct):?>
|
||||
<div class='btn-group'>
|
||||
<?php $viewName = $productID != 0 ? zget($productList,$productID) : $lang->product->allProduct;?>
|
||||
<?php $viewName = $productID != 0 ? zget($productList, $productID) : $lang->product->allProduct;?>
|
||||
<a href='javascript:;' class='btn btn-link btn-limit' data-toggle='dropdown'><span class='text' title='<?php echo $viewName;?>'><?php echo $viewName;?></span> <span class='caret'></span></a>
|
||||
<ul class='dropdown-menu' style='max-height:240px; max-width: 300px; overflow-y:auto'>
|
||||
<?php
|
||||
$class = '';
|
||||
if($productID == 0) $class = 'class="active"';
|
||||
echo "<li $class>" . html::a($this->createLink('project', 'execution', "status=$status&projectID=$projectID&orderby=$orderBy"), $lang->product->allProduct) . "</li>";
|
||||
foreach($productList as $key => $product)
|
||||
foreach($productList as $key => $productName)
|
||||
{
|
||||
$class = $productID == $key ? 'class="active"' : '';
|
||||
echo "<li $class>" . html::a($this->createLink('project', 'execution', "status=$status&projectID=$projectID&orderby=$orderBy&productID=$key"), $product) . "</li>";
|
||||
echo "<li $class>" . html::a($this->createLink('project', 'execution', "status=$status&projectID=$projectID&orderby=$orderBy&productID=$key"), $productName) . "</li>";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
@@ -50,10 +50,10 @@
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php common::printLink('execution', 'export', "status=$status&productID=$productID&orderBy=$orderBy&from=project", "<i class='icon-export muted'> </i> " . $lang->export, '', "class='btn btn-link export'")?>
|
||||
<?php if(common::hasPriv('programplan', 'create') and $isStage):?>
|
||||
<?php echo html::a($this->createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), "<i class='icon icon-plus'></i> " . $lang->programplan->create, '', "class='btn btn-primary'");?>
|
||||
<?php if(common::hasPriv('programplan', 'create') and $isStage and empty($product->deleted)):?>
|
||||
<?php echo html::a($this->createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), "<i class='icon icon-plus'></i> " . $lang->programplan->create, '', "class='btn btn-primary'");?>
|
||||
<?php else: ?>
|
||||
<?php if(common::hasPriv('execution', 'create')) echo html::a($this->createLink('execution', 'create', "projectID=$projectID"), "<i class='icon icon-sm icon-plus'></i> " . $lang->execution->create, '', "class='btn btn-primary create-execution-btn' data-app='execution' onclick='$(this).removeAttr(\"data-toggle\")'");?>
|
||||
<?php if(common::hasPriv('execution', 'create') and !$isStage) echo html::a($this->createLink('execution', 'create', "projectID=$projectID"), "<i class='icon icon-sm icon-plus'></i> " . $lang->execution->create, '', "class='btn btn-primary create-execution-btn' data-app='execution' onclick='$(this).removeAttr(\"data-toggle\")'");?>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+52
-6
@@ -662,10 +662,26 @@ class searchModel extends model
|
||||
{
|
||||
list($words, $againstCond, $likeCondition, $allowedObject) = $this->getSqlParams($keywords, $type);
|
||||
|
||||
$filterObject = array();
|
||||
foreach($allowedObject as $index => $object)
|
||||
{
|
||||
if(strpos(',feedback,ticket,', ",$object,") !== false)
|
||||
{
|
||||
unset($allowedObject[$index]);
|
||||
$filterObject[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
$typeCount = $this->dao->select("objectType, count(*) as objectCount")
|
||||
->from(TABLE_SEARCHINDEX)
|
||||
->where('vision')->eq($this->config->vision)
|
||||
->andWhere('objectType')->in($allowedObject)
|
||||
->where('((vision')->eq($this->config->vision)
|
||||
->beginIF(!empty($allowedObject))->andWhere('objectType')->in($allowedObject)->fi()
|
||||
->markRight(1)
|
||||
->beginIF(!empty($filterObject))
|
||||
->orWhere('(objectType')->in($filterObject)
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->markRight(1)
|
||||
->andWhere('addedDate')->le(helper::now())
|
||||
->groupBy('objectType')
|
||||
->fetchPairs('objectType', 'objectCount');
|
||||
@@ -686,12 +702,28 @@ class searchModel extends model
|
||||
{
|
||||
list($words, $againstCond, $likeCondition, $allowedObject) = $this->getSqlParams($keywords, $type);
|
||||
|
||||
$filterObject = array();
|
||||
foreach($allowedObject as $index => $object)
|
||||
{
|
||||
if(strpos(',feedback,ticket,', ",$object,") !== false)
|
||||
{
|
||||
unset($allowedObject[$index]);
|
||||
$filterObject[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
$scoreColumn = "(MATCH(title, content) AGAINST('{$againstCond}' IN BOOLEAN MODE))";
|
||||
$stmt = $this->dao->select("*, {$scoreColumn} as score")
|
||||
->from(TABLE_SEARCHINDEX)
|
||||
->where("(MATCH(title,content) AGAINST('{$againstCond}' IN BOOLEAN MODE) >= 1 {$likeCondition})")
|
||||
->andWhere('vision')->eq($this->config->vision)
|
||||
->andWhere('objectType')->in($allowedObject)
|
||||
->andWhere('((vision')->eq($this->config->vision)
|
||||
->beginIF(!empty($allowedObject))->andWhere('objectType')->in($allowedObject)->fi()
|
||||
->markRight(1)
|
||||
->beginIF(!empty($filterObject))
|
||||
->orWhere('(objectType')->in($filterObject)
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->markRight(1)
|
||||
->andWhere('addedDate')->le(helper::now())
|
||||
->orderBy('score_desc, editedDate_desc')
|
||||
->query();
|
||||
@@ -978,6 +1010,7 @@ class searchModel extends model
|
||||
$programs = $this->app->user->view->programs;
|
||||
$projects = $this->app->user->view->projects;
|
||||
$executions = $this->app->user->view->sprints;
|
||||
$grantProducts = $this->loadModel('feedback')->getGrantProducts();
|
||||
|
||||
$objectPairs = array();
|
||||
$total = count($results);
|
||||
@@ -992,7 +1025,7 @@ class searchModel extends model
|
||||
$objectExecutions = array();
|
||||
if(!isset($this->config->objectTables[$objectType])) continue;
|
||||
$table = $this->config->objectTables[$objectType];
|
||||
if(strpos(',bug,case,testcase,productplan,release,story,testtask,', ",$objectType,") !== false)
|
||||
if(strpos(',bug,case,testcase,productplan,release,story,testtask,ticket,', ",$objectType,") !== false)
|
||||
{
|
||||
$objectProducts = $this->dao->select('id,product')->from($table)->where('id')->in(array_keys($objectIdList))->fetchGroup('product', 'id');
|
||||
}
|
||||
@@ -1099,11 +1132,24 @@ class searchModel extends model
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($objectType == 'feedback')
|
||||
{
|
||||
$objectFeedbacks = $this->dao->select('*')->from($table)->where('id')->in(array_keys($objectIdList))->fetchAll('id');
|
||||
foreach($objectFeedbacks as $feedbackID => $feedback)
|
||||
{
|
||||
if($feedback->openedBy == $this->app->user->account or isset($grantProducts[$feedback->product])) continue;
|
||||
if(isset($objectIdList[$feedbackID]))
|
||||
{
|
||||
$recordID = $objectIdList[$feedbackID];
|
||||
unset($results[$recordID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($objectProducts as $productID => $idList)
|
||||
{
|
||||
if(empty($productID)) continue;
|
||||
if(strpos(",$products,", ",$productID,") === false)
|
||||
if(strpos(",$products,", ",$productID,") === false or ($objectType == 'ticket' and !isset($grantProducts[$productID])))
|
||||
{
|
||||
foreach($idList as $object)
|
||||
{
|
||||
|
||||
@@ -1910,7 +1910,6 @@ class story extends control
|
||||
|
||||
/* Get edited stories. */
|
||||
$stories = $this->story->getByList($storyIdList);
|
||||
$productStoryList = array();
|
||||
$productList = array();
|
||||
$ignoreTwins = array();
|
||||
$twinsCount = array();
|
||||
@@ -1935,7 +1934,6 @@ class story extends control
|
||||
|
||||
$storyProduct = isset($productList[$story->product]) ? $productList[$story->product] : $this->product->getByID($story->product);
|
||||
$branch = $storyProduct->type == 'branch' ? ($story->branch > 0 ? $story->branch : '0') : 'all';
|
||||
if(!isset($productStoryList[$story->product][$story->branch])) $productStoryList[$story->product][$story->branch] = $this->story->getProductStoryPairs($story->product, $branch, 0, 'all', 'id_desc', 0, '', $story->type);
|
||||
|
||||
if(!empty($story->twins))
|
||||
{
|
||||
@@ -2031,7 +2029,6 @@ class story extends control
|
||||
$this->view->storyIdList = $storyIdList;
|
||||
$this->view->storyType = $storyType;
|
||||
$this->view->reasonList = $this->lang->story->reasonList;
|
||||
$this->view->productStoryList = $productStoryList;
|
||||
$this->view->twinsCount = $twinsCount;
|
||||
|
||||
$this->display();
|
||||
@@ -3066,4 +3063,20 @@ class story extends control
|
||||
if(!dao::isError()) $this->loadModel('action')->create('story', $twinID, 'relieved');
|
||||
return $this->send(array('result' => 'success', 'silbingsCount' => count($twins)-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax get story pairs.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetStoryPairs($storyID)
|
||||
{
|
||||
$this->app->loadLang('bug');
|
||||
$story = $this->story->getByID($storyID);
|
||||
$stories = $this->story->getProductStoryPairs($story->product, $story->branch, 0, 'all', 'id_desc', 0, '', $story->type);
|
||||
|
||||
return print html::select("duplicateStoryIDList[$storyID]", $stories, '', "class='form-control' placeholder='{$this->lang->bug->duplicateTip}'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,6 @@
|
||||
|
||||
.btn.btn-action, .c-actions .btn {color: grey}
|
||||
#storyNoticeBranch {font-size: 13px; color: #5e626d;}
|
||||
#storyNoticeBranch i {font-size: 14px; color: #0075ff; margin-right: 3px;}
|
||||
#storyNoticeBranch i {font-size: 14px; color: #0075ff; margin-right: 3px;}
|
||||
|
||||
.moduleBox .required:after {right: 25px;}
|
||||
@@ -27,14 +27,30 @@ function setDuplicateAndChild(resolution, storyID)
|
||||
|
||||
$(function()
|
||||
{
|
||||
$('select[id^="duplicateStoryIDList"]').picker(
|
||||
$('td[id^="duplicateStoryBox"]').on('mouseenter', 'select[id^="duplicateStoryIDList"]', function()
|
||||
{
|
||||
disableEmptySearch : true,
|
||||
dropWidth : 'auto',
|
||||
onReady: function(event)
|
||||
var options = $(this).find('option').length;
|
||||
if(options <= 1)
|
||||
{
|
||||
$(event.picker.$container).addClass('required');
|
||||
}
|
||||
var id = $(this).attr('id');
|
||||
var storyID = id.replace('duplicateStoryIDList', '');
|
||||
var link = createLink('story', 'ajaxGetStoryPairs', 'storyID=' + storyID);
|
||||
var that = $(this);
|
||||
|
||||
});
|
||||
$.get(link, function(data)
|
||||
{
|
||||
that.replaceWith(data);
|
||||
$("#duplicateStoryIDList" + storyID).picker(
|
||||
{
|
||||
disableEmptySearch : true,
|
||||
dropWidth : 'auto',
|
||||
onReady: function(event)
|
||||
{
|
||||
$(event.picker.$container).addClass('required');
|
||||
}
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -176,7 +176,9 @@ $(window).unload(function(){
|
||||
$('.table-col .icon-plus').parent().addClass('disabled')
|
||||
}
|
||||
|
||||
itemIndex ++;
|
||||
if(requiredFields.indexOf('module') > 0) $('#modules' + itemIndex + '_chosen').addClass('required')
|
||||
|
||||
itemIndex ++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,6 +307,8 @@ function loadModuleForTwins(productID, branch, branchIndex)
|
||||
|
||||
$moduleIDBox.fixInputGroup();
|
||||
});
|
||||
|
||||
if(requiredFields.indexOf('module') > 0) $('#moduleIdBox #modules' + branchIndex + '_chosen').addClass('required')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-48
@@ -941,7 +941,7 @@ class storyModel extends model
|
||||
->join('linkStories', ',')
|
||||
->join('linkRequirements', ',')
|
||||
->join('childStories', ',')
|
||||
->remove('files,labels,comment,contactListMenu,stages,reviewer,needNotReview')
|
||||
->remove('files,labels,comment,contactListMenu,reviewer,needNotReview')
|
||||
->get();
|
||||
|
||||
/* Relieve twins when change product. */
|
||||
@@ -958,35 +958,6 @@ class storyModel extends model
|
||||
|
||||
if(isset($story->plan) and is_array($story->plan)) $story->plan = trim(join(',', $story->plan), ',');
|
||||
if(isset($_POST['branch']) and $_POST['branch'] == 0) $story->branch = 0;
|
||||
if(!empty($_POST['stages']))
|
||||
{
|
||||
$oldStages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('story')->eq($storyID)->fetchAll('branch');
|
||||
$this->dao->delete()->from(TABLE_STORYSTAGE)->where('story')->eq($storyID)->exec();
|
||||
|
||||
$stageList = join(',', array_keys($this->lang->story->stageList));
|
||||
$minStagePos = strlen($stageList);
|
||||
$minStage = '';
|
||||
foreach($this->post->stages as $branch => $stage)
|
||||
{
|
||||
$newStage = new stdclass();
|
||||
$newStage->story = $storyID;
|
||||
$newStage->branch = $branch;
|
||||
$newStage->stage = $stage;
|
||||
if(isset($oldStages[$branch]))
|
||||
{
|
||||
$oldStage = $oldStages[$branch];
|
||||
$newStage->stagedBy = $oldStage->stagedBy;
|
||||
if($stage != $oldStage->stage) $newStage->stagedBy = (strpos('tested|verified|released|closed', $stage) !== false) ? $this->app->user->account : '';
|
||||
}
|
||||
if($story->branch == 0) $this->dao->insert(TABLE_STORYSTAGE)->data($newStage)->exec();
|
||||
if(strpos($stageList, $stage) !== false and strpos($stageList, $stage) < $minStagePos)
|
||||
{
|
||||
$minStage = $stage;
|
||||
$minStagePos = strpos($stageList, $stage);
|
||||
}
|
||||
}
|
||||
$story->stage = $minStage;
|
||||
}
|
||||
|
||||
if(isset($story->stage) and $oldStory->stage != $story->stage) $story->stagedBy = (strpos('tested|verified|released|closed', $story->stage) !== false) ? $this->app->user->account : '';
|
||||
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->edit['id'], $this->post->uid);
|
||||
@@ -5109,13 +5080,22 @@ class storyModel extends model
|
||||
elseif($id == 'stage')
|
||||
{
|
||||
$style .= 'overflow: visible;';
|
||||
|
||||
$maxStage = $story->stage;
|
||||
$stageList = join(',', array_keys($this->lang->story->stageList));
|
||||
$maxStagePos = strpos($stageList, $maxStage);
|
||||
if(isset($storyStages[$story->id]))
|
||||
{
|
||||
foreach($storyStages[$story->id] as $storyBranch => $storyStage)
|
||||
{
|
||||
if(isset($branches[$storyBranch])) $title .= $branches[$storyBranch] . ": " . $this->lang->story->stageList[$storyStage->stage] . "\n";
|
||||
if(strpos($stageList, $storyStage->stage) !== false and strpos($stageList, $storyStage->stage) > $maxStagePos)
|
||||
{
|
||||
$maxStage = $storyStage->stage;
|
||||
$maxStagePos = strpos($stageList, $storyStage->stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
$title .= $this->lang->story->stageList[$maxStage];
|
||||
}
|
||||
elseif($id == 'feedbackBy')
|
||||
{
|
||||
@@ -5210,23 +5190,7 @@ class storyModel extends model
|
||||
echo $story->estimate . $this->config->hourUnit;
|
||||
break;
|
||||
case 'stage':
|
||||
if(isset($storyStages[$story->id]) and !empty($branches))
|
||||
{
|
||||
echo "<div class='dropdown dropdown-hover'>";
|
||||
echo $this->lang->story->stageList[$story->stage];
|
||||
echo "<span class='caret'></span>";
|
||||
echo "<ul class='dropdown-menu pull-right'>";
|
||||
foreach($storyStages[$story->id] as $storyBranch => $storyStage)
|
||||
{
|
||||
if(isset($branches[$storyBranch])) echo '<li class="text-ellipsis">' . $branches[$storyBranch] . ": " . $this->lang->story->stageList[$storyStage->stage] . '</li>';
|
||||
}
|
||||
echo "</ul>";
|
||||
echo '</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo $this->lang->story->stageList[$story->stage];
|
||||
}
|
||||
echo $this->lang->story->stageList[$maxStage];
|
||||
break;
|
||||
case 'taskCount':
|
||||
$tasksLink = helper::createLink('story', 'tasks', "storyID=$story->id", '', 'class="iframe"');
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<?php echo html::select("closedReasons[$storyID]", $reasonList, 'done', "class=form-control onchange=setDuplicateAndChild(this.value,$storyID) style='min-width: 80px'");?>
|
||||
</td>
|
||||
<td class='pd-0 w-p60 text-left' id='<?php echo 'duplicateStoryBox' . $storyID;?>' <?php if($story->closedReason != 'duplicate') echo "style='display:none'";?>>
|
||||
<?php echo html::select("duplicateStoryIDList[$storyID]", array('' => '') + $productStoryList[$story->product][$story->branch], $story->duplicateStory ? $story->duplicateStory : '', "class='form-control' placeholder='{$lang->bug->duplicateTip}'");?>
|
||||
<?php echo html::select("duplicateStoryIDList[$storyID]", '', '', "class='form-control' placeholder='{$lang->bug->duplicateTip}'");?>
|
||||
</td>
|
||||
<td class='pd-0' id='<?php echo 'childStoryBox' . $storyID;?>' <?php if($story->closedReason != 'subdivided') echo "style='display:none'";?>>
|
||||
<?php echo html::input("childStoriesIDList[$storyID]", '', "class='form-control' placeholder='{$lang->idAB}'");?>
|
||||
|
||||
@@ -97,10 +97,11 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
<?php echo html::select('branches[0]', $branches, $branch, "onchange='loadBranchRelation(this.value, 0);' class='form-control chosen control-branch'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='moduleIdBox'>
|
||||
<div class='table-col moduleBox' id='moduleIdBox'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->module?></span>
|
||||
<?php echo html::select('modules[0]', $moduleOptionMenu, $moduleID, "class='form-control chosen'");?>
|
||||
<?php $required = strpos($config->story->create->requiredFields, 'module') === false ? '' : 'required';?>
|
||||
<?php echo html::select('modules[0]', $moduleOptionMenu, $moduleID, "class='form-control chosen' $required");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='planIdBox'>
|
||||
@@ -379,7 +380,7 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
<?php echo html::select("branches[$i]", $branches, $branch, "onchange='loadBranchRelation(this.value, $i);' class='form-control chosen control-branch'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='moduleIdBox' style='flex: 1 0 160px'>
|
||||
<div class='table-col moduleBox' id='moduleIdBox' style='flex: 1 0 160px'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->module?></span>
|
||||
<?php echo html::select("modules[$i]", $moduleOptionMenu, $moduleID, "class='form-control chosen'");?>
|
||||
|
||||
@@ -227,17 +227,21 @@
|
||||
<th><?php echo $lang->story->stage;?></th>
|
||||
<td>
|
||||
<?php
|
||||
$maxStage = $story->stage;
|
||||
$stageList = join(',', array_keys($this->lang->story->stageList));
|
||||
$maxStagePos = strpos($stageList, $maxStage);
|
||||
if($story->stages and $branchTagOption)
|
||||
{
|
||||
foreach($story->stages as $branch => $stage)
|
||||
{
|
||||
if(isset($branchTagOption[$branch])) echo '<p>' . $branchTagOption[$branch] . html::select("stages[$branch]", $lang->story->stageList, $stage, "class='form-control chosen'") . '</p>';
|
||||
if(strpos($stageList, $stage) !== false and strpos($stageList, $stage) > $maxStagePos)
|
||||
{
|
||||
$maxStage = $stage;
|
||||
$maxStagePos = strpos($stageList, $stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo html::select('stage', $lang->story->stageList, $story->stage, "class='form-control chosen'");
|
||||
}
|
||||
echo html::select('stage', $lang->story->stageList, $maxStage, "class='form-control chosen'");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -302,14 +302,21 @@
|
||||
<th><?php echo $lang->story->stage;?></th>
|
||||
<td>
|
||||
<?php
|
||||
$maxStage = $story->stage;
|
||||
$stageList = join(',', array_keys($this->lang->story->stageList));
|
||||
$maxStagePos = strpos($stageList, $maxStage);
|
||||
if($story->stages and $branches)
|
||||
{
|
||||
foreach($story->stages as $branch => $stage) if(isset($branches[$branch])) echo $branches[$branch] . ' : ' . $lang->story->stageList[$stage] . '<br />';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo $lang->story->stageList[$story->stage];
|
||||
foreach($story->stages as $branch => $stage)
|
||||
{
|
||||
if(strpos($stageList, $stage) !== false and strpos($stageList, $stage) > $maxStagePos)
|
||||
{
|
||||
$maxStage = $stage;
|
||||
$maxStagePos = strpos($stageList, $stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
echo $lang->story->stageList[$maxStage];
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -74,7 +74,14 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
|
||||
<td>
|
||||
<div class="input-group" id="dataPlanGroup">
|
||||
<?php echo html::select('assignedTo[]', $members, $task->assignedTo, "class='form-control chosen'");?>
|
||||
<?php echo html::input('teamMember', '', "class='form-control team-group fix-border hidden' readonly='readonly'");?>
|
||||
<?php
|
||||
$teamMember = '';
|
||||
if(!empty($task->team))
|
||||
{
|
||||
foreach($task->team as $team) $teamMember .= ' ' . zget($members, $team->account);
|
||||
}
|
||||
?>
|
||||
<?php echo html::input('teamMember', $teamMember, "class='form-control team-group fix-border hidden' readonly='readonly'");?>
|
||||
<span class="input-group-btn team-group hidden"><a class="btn br-0" href="#modalTeam" data-toggle="modal"><?php echo $lang->task->team;?></a></span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -115,7 +122,7 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
|
||||
<div class='input-group <?php if(empty($stories)) echo "hidden";?>'>
|
||||
<?php echo html::select('story', $stories, $task->story, "class='form-control chosen' onchange='setStoryRelated();'");?>
|
||||
<?php if(common::hasPriv('execution', 'storyView')):?>
|
||||
<span class='input-group-btn' id='preview'><a href='#' class='btn iframe'><?php echo $lang->preview;?></a></span>
|
||||
<span class='input-group-btn' id='preview'><a href='#' class='btn iframe' data-width="85%" data-height="300px"><?php echo $lang->preview;?></a></span>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user