znt/module/todo/ui/view.html.php

441 lines
16 KiB
PHP

<?php
declare(strict_types=1);
/**
* The view view file of todo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Mengyi Liu <liumengyi@easycorp.ltd>
* @package todo
* @link https://www.zentao.net
*/
namespace zin;
jsVar('selectProduct', $lang->todo->selectProduct);
jsVar('selectExecution', $lang->execution->selectExecution);
jsVar('todoID', $todo->id);
jsVar('vision', $config->vision);
$isInModal = isAjaxRequest('modal');
/* Generate title suffix for bug,task,story type. */
$fnGenerateTitleSuffix = function() use($todo)
{
if($todo->type == 'bug') return btn(set::url(createLink('bug', 'view', "id={$todo->objectID}")), set::text(' BUG#' . $todo->objectID), setClass('ghost'));
if($todo->type == 'task') return btn(set::url(createLink('task', 'view', "id={$todo->objectID}")), set::text(' TASK#' . $todo->objectID), setClass('ghost'));
if($todo->type == 'story') return btn(set::url(createLink('story', 'view', "id={$todo->objectID}")), set::text(' STORY#' . $todo->objectID), setClass('ghost'));
};
/* Render modal for creating story. */
if(hasPriv('story', 'create') && $config->vision != 'lite')
{
modal
(
setID('productModal'),
set::modalProps(array('title' => $lang->product->select)),
empty($products) ? div
(
setClass('text-center pb-8 mt-2'),
span($lang->product->noProduct),
btn
(
setID('createProductBtn'),
setClass('secondary-pale'),
set::icon('plus'),
set::url($this->createLink('product', 'create')),
setData(array('toggle' => 'modal', 'size' => 'lg')),
$lang->product->create
)
) : form
(
setClass('mt-2'),
set::actions(array()),
formGroup
(
inputGroup
(
picker
(
set::name('product'),
set::items($products),
set::required(true)
),
btn
(
setID('toStoryButton'),
setClass('primary'),
$lang->todo->reasonList['story'],
on::click('toStory')
)
)
)
)
);
}
if(hasPriv('story', 'create') && $config->vision == 'lite')
{
modal
(
setID('projectModal'),
set::modalProps(array('title' => $lang->project->select)),
form
(
setClass('mt-2'),
set::actions(array()),
formGroup
(
inputGroup
(
picker
(
set::name('projectToStory'),
set::items($projects),
set::required(true)
),
btn
(
setID('toStoryButton'),
setClass('primary'),
$lang->todo->reasonList['story'],
on::click('toStory')
)
)
)
)
);
}
/* Render modal for creating task. */
if(hasPriv('task', 'create'))
{
modal
(
setID('executionModal'),
set::modalProps(array('title' => $lang->execution->selectExecution)),
to::footer
(
div
(
setClass('toolbar gap-4 w-full justify-center'),
btn($lang->todo->reasonList['task'], setID('toTaskButton'), setClass('primary')),
on::click('toTask')
)
),
form
(
setClass('mt-2'),
set::actions(array()),
formGroup
(
set::label($lang->todo->project),
picker
(
on::change('getExecutionByProject'),
set::name('project'),
set::items($projects),
set::required(true)
)
),
formGroup
(
set::label($lang->todo->execution),
picker
(
set::name('execution'),
set::items($executions),
set::required(true)
)
)
)
);
}
/* Render modal for creating bug. */
if(hasPriv('bug', 'create'))
{
modal
(
setID('projectProductModal'),
set::modalProps(array('title' => $lang->product->select)),
to::footer
(
div
(
setClass('toolbar gap-4 w-full justify-center'),
btn($lang->todo->reasonList['bug'], setID('toBugButton'), setClass('primary')),
on::click('toBug')
)
),
form
(
setClass('mt-2'),
set::actions(array()),
formGroup
(
set::label($lang->todo->project),
picker
(
set::name('bugProject'),
set::items($projects),
set::required(true),
on::change('getProductByProject')
)
),
formGroup
(
set::label($lang->todo->product),
picker
(
set::name('bugProduct'),
set::items($projectProducts),
set::required(true)
)
)
)
);
}
/* Generate goback url. */
$fnGenerateGoBackUrl = function() use ($app, $todo, $user)
{
if($this->session->todoList)
{
$browseLink = empty($todo->deleted) ? $this->session->todoList : $this->createLink('action', 'trash');
}
elseif($todo->account == $app->user->account)
{
$browseLink = $this->createLink('my', 'todo');
}
else
{
$browseLink = $this->createLink('user', 'todo', "userID=$user->id");
}
return $browseLink;
};
/* Generate action buttons and related menus within float toolbar. */
$fnGenerateFloatToolbarBtns = function() use ($lang, $config, $todo, $projects, $isInModal, $fnGenerateGoBackUrl)
{
/* Deleted item without action buttons. */
if($todo->deleted) return array();
/* Verify privilege of current account. */
if(!$this->app->user->admin && $this->app->user->account != $todo->account && $this->app->user->account != $todo->assignedTo) return array();
/* Prepare variables for verifying. */
$status = $todo->status;
$canStart = hasPriv('todo', 'start');
$canActivate = hasPriv('todo', 'activate');
$canClose = hasPriv('todo', 'close');
$canEdit = hasPriv('todo', 'edit');
$canDelete = hasPriv('todo', 'delete');
$actionList = array('prefix' => array(), 'main' => array(), 'suffix' => array());
!$isInModal && $actionList['prefix'][] = array('icon' => 'back', 'url' => $fnGenerateGoBackUrl(), 'hint' => $lang->goback . $lang->backShortcutKey, 'text' => $lang->goback);
/* Common action buttons. */
$canStart && $status == 'wait' ? $actionList['main'][] = array('icon' => 'play', 'url' => createLink('todo', 'start', "todoID={$todo->id}"), 'text' => $lang->todo->abbr->start, 'class' => 'ajax-submit') : null;
$canActivate && ($status == 'done' || $status == 'closed') ? $actionList['main'][] = array('icon' => 'magic', 'url' => createLink('todo', 'activate', "todoID={$todo->id}"), 'text' => $lang->activate, 'class' => 'ajax-submit') : null;
$canClose && $status == 'done' ? $actionList['main'][] = array('icon' => 'off', 'url' => createLink('todo', 'close', "todoID={$todo->id}"), 'text' => $lang->close) : null;
$canEdit ? $actionList['main'][] = array('icon' => 'edit', 'url' => createLink('todo', 'edit', "todoID={$todo->id}"), 'data-load' => $isInModal ? 'modal' : null, 'text' => $lang->edit) : null;
$canDelete ? $actionList['main'][] = array('icon' => 'trash', 'url' => createLink('todo', 'delete', "todoID={$todo->id}&confirm=yes"), 'text' => $lang->delete, 'class' => 'ajax-submit', 'data-confirm' => $lang->todo->confirmDelete) : null;
/* The status is 'done' or 'closed' without more action buttons. */
if($status == 'done' || $status == 'closed') return $actionList;
$actionList['main'][] = array('icon' => 'checked', 'url' => createLink('todo', 'finish', "todoID={$todo->id}"), 'text' => $lang->todo->abbr->finish, 'class' => 'ajax-submit');
$canCreateStory = hasPriv('story', 'create');
$canCreateTask = hasPriv('task', 'create');
$canCreateBug = hasPriv('bug', 'create') && $config->vision != 'lite';
$printBtn = $config->vision == 'lite' && empty($projects) ? false : true;
/* Render more button. */
if($printBtn && ($canCreateStory || $canCreateTask || $canCreateBug))
{
$actionList['suffix'][] = array('url' => '#navActions', 'text' => $lang->todo->transform, 'data-toggle' => 'dropdown', 'data-placement' => 'top-end', 'caret' => 'up');
}
/* Popup menu of more button. */
$storyTarget = $canCreateStory && $config->vision == 'lite' ? '#projectModal' : '#productModal';
$suffixItems = array();
if($canCreateStory) $suffixItems[] = array('text' => $lang->todo->reasonList['story'], 'id' => 'toStoryLink', 'data-url' => '###', 'data-toggle' => 'modal', 'data-target' => $storyTarget, 'data-backdrop' => false, 'data-moveable' => true, 'data-position' => 'center', 'data-size' => 'sm');
if($canCreateTask) $suffixItems[] = array('text' => $lang->todo->reasonList['task'], 'id' => 'toTaskLink', 'data-url' => '###', 'data-toggle' => 'modal', 'data-target' => '#executionModal', 'data-backdrop' => false, 'data-moveable' => true, 'data-position' => 'center', 'data-size' => 'sm');
if($canCreateBug) $suffixItems[] = array('text' => $lang->todo->reasonList['bug'], 'id' => 'toBugLink', 'data-url' => '###', 'data-toggle' => 'modal', 'data-target' => '#projectProductModal', 'data-backdrop' => false, 'data-moveable' => true, 'data-position' => 'center', 'data-size' => 'sm');
menu
(
setID('navActions'),
setClass('menu dropdown-menu'),
set::items($suffixItems)
);
return $actionList;
};
$actionList = $fnGenerateFloatToolbarBtns();
/* Generate from data and item. */
$fnGenerateFrom = function() use ($app, $lang, $config, $todo)
{
if(!in_array($todo->type, array('story', 'task', 'bug')) || empty($todo->object)) return array(null, null);
/* Generate from data. */
$app->loadLang($todo->type);
$objectData = array();
foreach($config->todo->related[$todo->type]['title'] as $index => $relatedTitle)
{
$content = zget($todo->object, $config->todo->related[$todo->type]['content'][$index], '');
$objectData[] = item
(
set::title($lang->{$todo->type}->{$relatedTitle}),
empty($content) ? $lang->noData : html($content)
);
}
$fromItemData = section
(
set::title(zget($lang->todo->fromList, $todo->type)),
sectionCard
(
entityLabel
(
set::entityID($todo->objectID),
set::text($todo->name)
),
$objectData
)
);
/* Generate from item. */
$fromItem = item
(
set::name(zget($lang->todo->fromList, $todo->type)),
a
(
set::href(createLink($todo->type, 'view', "id={$todo->objectID}", '', false)),
setData
(
array
(
'toggle' => 'modal',
'data-type' => 'html',
'type' => 'ajax'
)
),
$todo->name
)
);
return array($fromItem, $fromItemData);
};
list($fromItem, $fromItemData) = $fnGenerateFrom();
/* Generate cycle configuration information. */
$todo->config = json_decode($todo->config);
$fnGenerateCycleCfg = function() use ($lang, $todo)
{
$cfg = '';
if($todo->config->type == 'day')
{
if(isset($todo->config->day)) $cfg .= $lang->todo->every . $todo->config->day . $lang->day;
if(isset($todo->config->specifiedDate))
{
$specifiedNotes = $lang->todo->specify;
if(isset($todo->config->cycleYear)) $specifiedNotes .= $lang->todo->everyYear;
$specifiedNotes .= zget($lang->datepicker->monthNames, $todo->config->specify->month) . $todo->config->specify->day . $lang->todo->day;
$cfg .= $specifiedNotes;
}
}
elseif($todo->config->type == 'week')
{
foreach(explode(',', $todo->config->week) as $week) $cfg .= $lang->todo->dayNames[$week] . ' ';
}
elseif($todo->config->type == 'month')
{
foreach(explode(',', $todo->config->month) as $month) $cfg .= $month . ' ';
}
$cfg .= '<br />';
if($todo->config->beforeDays) $cfg .= sprintf($lang->todo->lblBeforeDays, $todo->config->beforeDays);
return $cfg;
};
/* ZIN: layout. */
detailHeader
(
$isInModal ? to::prefix('') : '',
to::title
(
entityLabel
(
set::entityID($todo->id),
set::level(1),
set::text($todo->name)
),
$todo->deleted ? span(setClass('label danger circle'), $lang->todo->deleted) : null
)
);
detailBody
(
sectionList
(
section
(
set::title($lang->todo->desc),
set::content(nl2br($todo->desc)),
set::useHtml(true),
to::actions($fnGenerateTitleSuffix())
),
$fromItemData,
history(),
/* Render float toolbar. */
$actionList ? center(floatToolbar(set($actionList))) : null
),
detailSide
(
/* Basic information. */
tabs(set::collapse(true), tabPane
(
set::key('legendBasic'),
set::title($lang->todo->legendBasic),
set::active(true),
tableData
(
item(set::name($lang->todo->pri), priLabel($todo->pri, set::text($lang->todo->priList))),
item(set::name($lang->todo->status), zget($lang->todo->statusList, $todo->status)),
item(set::name($lang->todo->type), zget($lang->todo->typeList, $todo->type)),
$fromItem,
item(set::name($lang->todo->account), zget($users, $todo->account)),
item(set::name($lang->todo->date), formatTime($todo->date, DT_DATE1)),
item(set::name($lang->todo->beginAndEnd), isset($times[$todo->begin]) ? $times[$todo->begin] : '', isset($times[$todo->end]) ? ' ~ ' . $times[$todo->end] : ''),
!isset($todo->assignedTo) ? null : item(set::name($lang->todo->assignedTo), zget($users, $todo->assignedTo)),
!isset($todo->assignedTo) ? null : item(set::name($lang->todo->assignedBy), zget($users, $todo->assignedBy)),
!isset($todo->assignedTo) ? null : item(set::name($lang->todo->assignedDate), formatTime($todo->assignedDate, DT_DATE1))
)
)),
/* Cycle information. */
$todo->cycle ? tabs(set::collapse(true), tabPane
(
set::key('cycle'),
set::title($lang->todo->cycle),
set::active(true),
tableData
(
item(set::name($lang->todo->beginAndEnd), $todo->config->begin . " ~ " . zget($todo->config, 'end', '')),
item(set::name($lang->todo->cycleConfig), html($fnGenerateCycleCfg()))
)
)) : null
)
);
render();