3204 lines
134 KiB
PHP
3204 lines
134 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
class convertTao extends convertModel
|
||
{
|
||
/**
|
||
* 构建user数据。
|
||
* Build user data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildUserData(array $data): object
|
||
{
|
||
$user = new stdclass();
|
||
$user->id = $data['id'];
|
||
$user->account = isset($data['lowerUserName']) ? $data['lowerUserName'] : '';
|
||
$user->realname = isset($data['lowerDisplayName']) ? $data['lowerDisplayName'] : '';
|
||
$user->email = isset($data['emailAddress']) ? $data['emailAddress'] : '';
|
||
$user->join = isset($data['createdDate']) ? $data['createdDate'] : null;
|
||
|
||
return $user;
|
||
}
|
||
|
||
/**
|
||
* 构建project数据。
|
||
* Build project data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildProjectData(array $data): object
|
||
{
|
||
$project = new stdclass();
|
||
$project->id = $data['id'];
|
||
$project->pname = isset($data['name']) ? $data['name'] : '';
|
||
$project->pkey = isset($data['key']) ? $data['key'] : '';
|
||
$project->originalkey = isset($data['originalkey']) ? $data['originalkey'] : '';
|
||
$project->description = isset($data['description']) ? $data['description'] : '';
|
||
$project->lead = isset($data['lead']) ? $data['lead'] : '';
|
||
$project->pstatus = isset($data['status']) ? $data['status'] : '';
|
||
$project->created = isset($data['created']) ? $data['created'] : null;
|
||
$project->archived = isset($data['archived']) ? $data['archived'] : false;
|
||
$project->versions = isset($data['versions']) ? $data['versions'] : array();
|
||
|
||
return $project;
|
||
}
|
||
|
||
/**
|
||
* 构建issue数据。
|
||
* Build issue data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildIssueData(array $data): object
|
||
{
|
||
$issue = new stdclass();
|
||
$issue->id = $data['id'];
|
||
$issue->summary = isset($data['summary']) ? $data['summary'] : '';
|
||
$issue->priority = isset($data['priority']) ? $data['priority'] : '';
|
||
$issue->project = isset($data['project']) ? $data['project'] : 0;
|
||
$issue->issuestatus = isset($data['status']) ? $data['status'] : '';
|
||
$issue->created = isset($data['created']) ? $data['created'] : '';
|
||
$issue->creator = isset($data['creator']) ? $data['creator'] : '';
|
||
$issue->issuetype = isset($data['type']) ? $data['type'] : '';
|
||
$issue->assignee = isset($data['assignee']) ? $data['assignee'] : '';
|
||
$issue->resolution = isset($data['resolution']) ? $data['resolution'] : '';
|
||
$issue->timeoriginalestimate = isset($data['timeoriginalestimate']) ? $data['timeoriginalestimate'] : '';
|
||
$issue->timeestimate = isset($data['timeestimate']) ? $data['timeestimate'] : '';
|
||
$issue->timespent = isset($data['timespent']) ? $data['timespent'] : '';
|
||
$issue->issuenum = isset($data['number']) ? $data['number'] : '';
|
||
$issue->description = isset($data['description']) ? $data['description'] : '';
|
||
$issue->duedate = isset($data['duedate ']) ? $data['duedate'] : '';
|
||
$issue->fixVersions = isset($data['fixVersions']) ? $data['fixVersions'] : array();
|
||
$issue->comments = !empty($data['comments']) ? $data['comments'] : array();
|
||
$issue->changeGroups = !empty($data['changeGroups']) ? $data['changeGroups'] : array();
|
||
$issue->changeItems = !empty($data['changeItems']) ? $data['changeItems'] : array();
|
||
$issue->worklogs = !empty($data['worklogs']) ? $data['worklogs'] : array();
|
||
$issue->links = !empty($data['links']) ? $data['links'] : array();
|
||
$issue->files = !empty($data['files']) ? $data['files'] : array();
|
||
|
||
/* 获取自定义字段的值。 */
|
||
foreach($data as $fieldKey => $fieldValue)
|
||
{
|
||
if(strpos($fieldKey, 'customfield_') === false) continue;
|
||
$customFieldKey = str_replace('customfield_', '', $fieldKey);
|
||
if(is_array($fieldValue))
|
||
{
|
||
if(!empty($fieldValue['content']) && !empty($data['renderedFields'][$fieldKey]))
|
||
{
|
||
$issue->{$customFieldKey} = $data['renderedFields'][$fieldKey];
|
||
}
|
||
elseif(!empty($fieldValue['id']))
|
||
{
|
||
$issue->{$customFieldKey} = $fieldValue['id'];
|
||
}
|
||
elseif(!empty($fieldValue['accountId']))
|
||
{
|
||
$issue->{$customFieldKey} = $fieldValue['accountId'];
|
||
}
|
||
else
|
||
{
|
||
$issue->{$customFieldKey} = '';
|
||
foreach($fieldValue as $field)
|
||
{
|
||
if(!empty($field['id']))
|
||
{
|
||
$issue->{$customFieldKey} .= $field['id'] . ',';
|
||
}
|
||
elseif(!empty($field['accountId']))
|
||
{
|
||
$issue->{$customFieldKey} .= $field['accountId'] . ',';
|
||
}
|
||
}
|
||
$issue->{$customFieldKey} = rtrim($issue->{$customFieldKey}, ',');
|
||
}
|
||
}
|
||
else
|
||
{
|
||
$issue->{$customFieldKey} = $fieldValue;
|
||
if(!empty($fieldValue) && preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{4}$/', (string)$fieldValue)) $issue->{$customFieldKey} = date('Y-m-d H:i:s', strtotime($fieldValue));
|
||
}
|
||
}
|
||
|
||
return $issue;
|
||
}
|
||
|
||
/**
|
||
* 构建issue类型数据。
|
||
* Build issue type data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildIssueTypeData(array $data): object
|
||
{
|
||
$issueType = new stdclass();
|
||
$issueType->id = $data['id'];
|
||
$issueType->pname = $data['name'];
|
||
$issueType->description = zget($data, 'description', '');
|
||
$issueType->pstyle = zget($data, 'style', '');
|
||
$issueType->iconurl = zget($data, 'iconurl', '');
|
||
$issueType->avatar = zget($data, 'avatar', '');
|
||
|
||
return $issueType;
|
||
}
|
||
|
||
/**
|
||
* 构建issue关联关系数据。
|
||
* Build issue link type data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildIssueLinkTypeData(array $data): object
|
||
{
|
||
$issueLinkType = new stdclass();
|
||
$issueLinkType->id = $data['id'];
|
||
$issueLinkType->linkname = zget($data, 'name', zget($data, 'outward', ''));
|
||
$issueLinkType->inward = zget($data, 'inward', '');
|
||
$issueLinkType->outward = zget($data, 'outward', '');
|
||
$issueLinkType->pstyle = zget($data, 'style', '');
|
||
|
||
return $issueLinkType;
|
||
}
|
||
|
||
/**
|
||
* 构建解决方案数据。
|
||
* Build resolution data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildResolutionData(array $data): object
|
||
{
|
||
$resolution = new stdclass();
|
||
$resolution->id = $data['id'];
|
||
$resolution->sequence = zget($data, 'sequence', '');
|
||
$resolution->pname = $data['name'];
|
||
$resolution->description = zget($data, 'description', '');
|
||
|
||
return $resolution;
|
||
}
|
||
|
||
/**
|
||
* 构建issue状态数据。
|
||
* Build issue status data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildStatusData(array $data): object
|
||
{
|
||
$status = new stdclass();
|
||
$status->id = $data['id'];
|
||
$status->sequence = zget($data, 'sequence', '');
|
||
$status->pname = $data['name'];
|
||
$status->description = zget($data, 'description', '');
|
||
|
||
return $status;
|
||
}
|
||
|
||
/**
|
||
* 构建build数据。
|
||
* Build build data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildBuildData(array $data): object
|
||
{
|
||
$build = new stdclass();
|
||
$build->id = $data['id'];
|
||
$build->project = isset($data['project']) ? $data['project'] : 0;
|
||
$build->vname = isset($data['name']) ? $data['name'] : '';
|
||
$build->startdate = isset($data['startdate']) ? $data['startdate'] : null;
|
||
$build->releasedate = isset($data['releasedate']) ? $data['releasedate'] : '';
|
||
$build->released = isset($data['released']) ? $data['released'] : '';
|
||
$build->archived = isset($data['archived']) ? $data['archived'] : false;
|
||
$build->description = isset($data['description']) ? $data['description'] : '';
|
||
|
||
return $build;
|
||
}
|
||
|
||
/**
|
||
* 构建issuelink数据。
|
||
* Build issuelink data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildIssuelinkData(array $data): object
|
||
{
|
||
$issueLink = new stdclass();
|
||
$issueLink->id = $data['id'];
|
||
$issueLink->linktype = isset($data['linktype']) ? $data['linktype'] : '';
|
||
$issueLink->source = isset($data['source']) ? $data['source'] : '';
|
||
$issueLink->destination = isset($data['destination']) ? $data['destination'] : '';
|
||
|
||
return $issueLink;
|
||
}
|
||
|
||
/**
|
||
* 构建action数据。
|
||
* Build action data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildActionData(array $data): object
|
||
{
|
||
$action = new stdclass();
|
||
$action->id = $data['id'];
|
||
$action->issueid = $data['issue'];
|
||
$action->actionbody = isset($data['body']) ? $data['body'] : '';
|
||
$action->author = isset($data['author']) ? $data['author'] : '';
|
||
$action->created = isset($data['created']) ? $data['created'] : '';
|
||
|
||
return $action;
|
||
}
|
||
|
||
/**
|
||
* 构建file数据。
|
||
* Build file data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildFileData(array $data): object
|
||
{
|
||
$file = new stdclass();
|
||
$file->issueid = $data['issue'];
|
||
$file->id = $data['id'];
|
||
$file->filename = isset($data['filename']) ? $data['filename'] : '';
|
||
$file->mimetype = isset($data['mimetype']) ? $data['mimetype'] : '';
|
||
$file->filesize = isset($data['filesize']) ? $data['filesize'] : '';
|
||
$file->created = isset($data['created']) ? $data['created'] : '';
|
||
$file->author = isset($data['author']) ? $data['author'] : '';
|
||
$file->content = isset($data['content']) ? $data['content'] : '';
|
||
|
||
return $file;
|
||
}
|
||
|
||
/**
|
||
* 构建优先级数据。
|
||
* Build Priority data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildPriorityData(array $data): object
|
||
{
|
||
$priority = new stdclass();
|
||
$priority->id = $data['id'];
|
||
$priority->pname = isset($data['name']) ? $data['name'] : '';
|
||
|
||
return $priority;
|
||
}
|
||
|
||
/**
|
||
* 构建自定义字段数据。
|
||
* Build custom field data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildCustomFieldData(array $data): object
|
||
{
|
||
$field = new stdclass();
|
||
$field->id = $data['id'];
|
||
$field->cfname = $data['name'];
|
||
$field->description = isset($data['description']) ? $data['description'] : '';
|
||
$field->customfieldtypekey = isset($data['customfieldtypekey']) ? $data['customfieldtypekey'] : '';
|
||
$field->customfieldsearcherkey = isset($data['customfieldsearcherkey']) ? $data['customfieldsearcherkey'] : '';
|
||
$field->issueTypeIds = isset($data['issueTypeIds']) ? $data['issueTypeIds'] : array();
|
||
|
||
return $field;
|
||
}
|
||
|
||
/**
|
||
* 构建工作流字段值数据。
|
||
* Build custom field value data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildCustomFieldValueData(array $data): object
|
||
{
|
||
$fieldValue = new stdclass();
|
||
$fieldValue->id = $data['id'];
|
||
$fieldValue->issue = $data['issue'];
|
||
$fieldValue->customfield = $data['customfield'];
|
||
$fieldValue->stringvalue = zget($data, 'stringvalue', '');
|
||
$fieldValue->datevalue = zget($data, 'datevalue', '');
|
||
$fieldValue->numbervalue = zget($data, 'numbervalue', '');
|
||
$fieldValue->textvalue = zget($data, 'textvalue', '');
|
||
|
||
return $fieldValue;
|
||
}
|
||
|
||
/**
|
||
* 构建工作流字段配置数据。
|
||
* Build custom field option data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildCustomFieldOptionData(array $data): object
|
||
{
|
||
$fieldOption = new stdclass();
|
||
$fieldOption->id = $data['id'];
|
||
$fieldOption->customfield = $data['customfield'];
|
||
$fieldOption->customfieldconfig = $data['customfieldconfig'];
|
||
$fieldOption->customvalue = $data['value'];
|
||
$fieldOption->disabled = $data['disabled'];
|
||
|
||
return $fieldOption;
|
||
}
|
||
|
||
/**
|
||
* 构建属性条目数据。
|
||
* Build os property entry data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildOSPropertyEntryData(array $data): object
|
||
{
|
||
$property = new stdclass();
|
||
$property->id = $data['id'];
|
||
$property->entity_name = $data['entityName'];
|
||
$property->entity_id = $data['entityId'];
|
||
$property->property_key = $data['propertyKey'];
|
||
$property->propertytype = $data['type'];
|
||
|
||
return $property;
|
||
}
|
||
|
||
/**
|
||
* 构建上下文数据。
|
||
* Build configuration context data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildConfigurationcontextData(array $data): object
|
||
{
|
||
$config = new stdclass();
|
||
$config->id = $data['id'];
|
||
$config->customfield = isset($data['key']) ? $data['key'] : '';
|
||
$config->fieldconfigscheme = isset($data['fieldconfigscheme']) ? $data['fieldconfigscheme'] : '';
|
||
$config->project = isset($data['project']) ? $data['project'] : '';
|
||
|
||
return $config;
|
||
}
|
||
|
||
/**
|
||
* 构建配置选择数据。
|
||
* Build option configuration data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildOptionconfigurationData(array $data): object
|
||
{
|
||
$option = new stdclass();
|
||
$option->id = $data['id'];
|
||
$option->fieldid = isset($data['fieldid']) ? $data['fieldid'] : '';
|
||
$option->optionid = isset($data['optionid']) ? $data['optionid'] : '';
|
||
$option->fieldconfig = isset($data['fieldconfig']) ? $data['fieldconfig'] : '';
|
||
|
||
return $option;
|
||
}
|
||
|
||
/**
|
||
* 构建日志数据。
|
||
* Build audit log data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildAuditLogData(array $data): object
|
||
{
|
||
$auditLog = new stdclass();
|
||
$auditLog->id = $data['id'];
|
||
$auditLog->summary = isset($data['summary']) ? $data['summary'] : '';
|
||
$auditLog->object_type = isset($data['objectType']) ? $data['objectType'] : '';
|
||
$auditLog->object_id = isset($data['objectId']) ? $data['objectId'] : '';
|
||
|
||
return $auditLog;
|
||
}
|
||
|
||
/**
|
||
* 构建项目用户角色数据。
|
||
* Build project role actor data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildProjectRoleActorData(array $data): object
|
||
{
|
||
$projectRole = new stdclass();
|
||
$projectRole->id = $data['id'];
|
||
$projectRole->pid = isset($data['pid']) ? $data['pid'] : '';
|
||
$projectRole->roletype = $data['roletype'];
|
||
$projectRole->roletypeparameter = $data['roletypeparameter'];
|
||
|
||
return $projectRole;
|
||
}
|
||
|
||
/**
|
||
* 构建角色成员数据。
|
||
* Build member ship data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildMemberShipData(array $data): object
|
||
{
|
||
$memberShip = new stdclass();
|
||
$memberShip->id = $data['id'];
|
||
$memberShip->parent_id = $data['parentId'];
|
||
$memberShip->child_id = $data['childId'];
|
||
$memberShip->membership_type = $data['membershipType'];
|
||
$memberShip->parent_name = $data['parentName'];
|
||
$memberShip->child_name = $data['childName'];
|
||
|
||
return $memberShip;
|
||
}
|
||
|
||
/**
|
||
* 构建字段视图方案数据。
|
||
* Build field screen layout data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildFieldScreenLayoutItemData(array $data): object
|
||
{
|
||
$layout = new stdclass();
|
||
$layout->id = $data['id'];
|
||
$layout->fieldidentifier = $data['fieldidentifier'];
|
||
$layout->fieldscreentab = isset($data['fieldscreentab']) ? $data['fieldscreentab'] : '';
|
||
|
||
return $layout;
|
||
}
|
||
|
||
/**
|
||
* 构建字段配置数据。
|
||
* Build field config scheme data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildFieldConfigSchemeData(array $data): object
|
||
{
|
||
$fieldConfig = new stdclass();
|
||
$fieldConfig->id = $data['id'];
|
||
$fieldConfig->configname = $data['name'];
|
||
$fieldConfig->description = isset($data['description']) ? $data['description'] : '';
|
||
$fieldConfig->fieldid = isset($data['fieldid']) ? $data['fieldid'] : '';
|
||
|
||
return $fieldConfig;
|
||
}
|
||
|
||
/**
|
||
* 构建字段方案类型数据。
|
||
* Build field config scheme issue type data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildFieldConfigSchemeIssueTypeData(array $data): object
|
||
{
|
||
$fieldIssueType = new stdclass();
|
||
$fieldIssueType->id = $data['id'];
|
||
$fieldIssueType->issuetype = isset($data['issuetype']) ? $data['issuetype'] : '';
|
||
$fieldIssueType->fieldconfigscheme = isset($data['fieldconfigscheme']) ? $data['fieldconfigscheme'] : '';
|
||
$fieldIssueType->fieldconfiguration = isset($data['fieldconfiguration']) ? $data['fieldconfiguration'] : '';
|
||
|
||
return $fieldIssueType;
|
||
}
|
||
|
||
/**
|
||
* 构建工作流数据。
|
||
* Build workflow data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildWorkflowData(array $data): object
|
||
{
|
||
$workflow = new stdclass();
|
||
$workflow->id = $data['id'];
|
||
$workflow->workflowname = $data['name'];
|
||
$workflow->descriptor = $data['descriptor'];
|
||
|
||
return $workflow;
|
||
}
|
||
|
||
/**
|
||
* 构建工作流方案数据。
|
||
* Build workflow scheme data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildWorkflowSchemeData(array $data): object
|
||
{
|
||
$workflow = new stdclass();
|
||
$workflow->id = $data['id'];
|
||
$workflow->name = $data['name'];
|
||
$workflow->description = zget($data, 'description', '');
|
||
|
||
return $workflow;
|
||
}
|
||
|
||
/**
|
||
* 构建工作日志数据。
|
||
* Build work log data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildWorklogData(array $data): object
|
||
{
|
||
$worklog = new stdclass();
|
||
$worklog->id = $data['id'];
|
||
$worklog->issueid = $data['issue'];
|
||
$worklog->author = zget($data, 'author', '');
|
||
$worklog->worklogbody = zget($data, 'body', '');
|
||
$worklog->timeworked = zget($data, 'timeworked', 0);
|
||
$worklog->created = zget($data, 'created', null);
|
||
|
||
return $worklog;
|
||
}
|
||
|
||
/**
|
||
* 构建变更记录数据。
|
||
* Build change item data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildChangeItemData(array $data): object
|
||
{
|
||
$changeItem = new stdclass();
|
||
$changeItem->id = $data['id'];
|
||
$changeItem->groupid = $data['group'];
|
||
$changeItem->fieldtype = $data['fieldtype'];
|
||
$changeItem->field = $data['field'];
|
||
$changeItem->oldvalue = zget($data, 'oldvalue', '');
|
||
$changeItem->oldstring = zget($data, 'oldstring', '');
|
||
$changeItem->newvalue = zget($data, 'newvalue', '');
|
||
$changeItem->newstring = zget($data, 'newstring', '');
|
||
|
||
return $changeItem;
|
||
}
|
||
|
||
/**
|
||
* 构建变更记录分组数据。
|
||
* Build change item group data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildChangeGroupData(array $data): object
|
||
{
|
||
$changeGroup = new stdclass();
|
||
$changeGroup->id = $data['id'];
|
||
$changeGroup->issueid = $data['issue'];
|
||
$changeGroup->author = $data['author'];
|
||
$changeGroup->created = $data['created'];
|
||
|
||
return $changeGroup;
|
||
}
|
||
|
||
/**
|
||
* 构建节点关联数据。
|
||
* Build node association data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildNodeAssociationData(array $data): object
|
||
{
|
||
$association = new stdclass();
|
||
$association->source_node_id = $data['sourceNodeId'];
|
||
$association->source_node_entity = $data['sourceNodeEntity'];
|
||
$association->sink_node_id = $data['sinkNodeId'];
|
||
$association->sink_node_entity = $data['sinkNodeEntity'];
|
||
$association->association_type = $data['associationType'];
|
||
|
||
return $association;
|
||
}
|
||
|
||
/**
|
||
* 构建版本发布数据。
|
||
* Build fix version data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildFixVersionData(array $data): object
|
||
{
|
||
$fixVersion = new stdclass();
|
||
$fixVersion->issue = $data['issue'];
|
||
$fixVersion->version = $data['version'];
|
||
|
||
return $fixVersion;
|
||
}
|
||
|
||
/**
|
||
* 构建影响版本数据。
|
||
* Build affects version data.
|
||
*
|
||
* @param array $data
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function buildAffectsVersionData(array $data): object
|
||
{
|
||
$affectsVersion = new stdclass();
|
||
$affectsVersion->issue = $data['issue'];
|
||
$affectsVersion->version = $data['version'];
|
||
|
||
return $affectsVersion;
|
||
}
|
||
|
||
/**
|
||
* 获取Jira事务与禅道数据的关联关系。
|
||
* Get Jira issue and ZenTao object links.
|
||
*
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function getIssueData(): array
|
||
{
|
||
return $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('BID')->ne('')->andWhere('extra')->eq('issue')->fetchAll('AID');
|
||
}
|
||
|
||
/**
|
||
* 获取Jira用户与禅道用户的关联关系。
|
||
* Get Jira users.
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function getJiraUser(): array
|
||
{
|
||
return $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('juser')->fetchPairs('AID', 'BID');
|
||
}
|
||
|
||
/**
|
||
* 导入user数据。
|
||
* Import jira user.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraUser(array $dataList): bool
|
||
{
|
||
$this->loadModel('admin');
|
||
|
||
$localUsers = $this->dao->dbh($this->dbh)->select('account')->from(TABLE_USER)->where('deleted')->eq('0')->fetchPairs();
|
||
$userConfig = $this->session->jiraUser;
|
||
$jiraUserRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('juser')->fetchPairs();
|
||
foreach($dataList as $data)
|
||
{
|
||
if(!empty($jiraUserRelation[$data->account])) continue;
|
||
|
||
/* 如果是atlassian内部帐号,则不导入。 */
|
||
if(isset($data->email) && strpos($data->email, '@connect.atlassian.com') !== false) continue;
|
||
|
||
$user = new stdclass();
|
||
$user->account = $this->processJiraUser($data->account, isset($data->email) ? $data->email : '');
|
||
if(!isset($localUsers[$user->account]))
|
||
{
|
||
$user->realname = isset($data->realname) ? $data->realname : '';
|
||
$user->password = $userConfig['password'];
|
||
$user->email = isset($data->email) ? $data->email : '';
|
||
$user->gender = 'm';
|
||
$user->type = 'inside';
|
||
$user->join = !empty($data->join) ? $data->join : helper::now();
|
||
$user->jira = 1;
|
||
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_USER)->data($user, 'group')->exec();
|
||
|
||
if(!dao::isError() && !empty($userConfig['group']))
|
||
{
|
||
$group = new stdclass();
|
||
$group->account = $user->account;
|
||
$group->group = $userConfig['group'];
|
||
$group->project = '';
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_USERGROUP)->data($group)->exec();
|
||
}
|
||
}
|
||
|
||
$jiraUserRelation[$data->account] = $user->account;
|
||
$this->createTmpRelation('juser', $data->account, 'zuser', $user->account);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 记录Jira数据和禅道数据的关联关系。
|
||
* Create data tmp relation.
|
||
*
|
||
* @param string $AType
|
||
* @param string|int $AID
|
||
* @param string $BType
|
||
* @param string|int $BID
|
||
* @param string $extra
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function createTmpRelation(string $AType, string|int $AID, string $BType, string|int $BID, string $extra = ''): object
|
||
{
|
||
$relation = new stdclass();
|
||
$relation->AType = $AType;
|
||
$relation->BType = $BType;
|
||
$relation->AID = $AID;
|
||
$relation->BID = $BID;
|
||
$relation->extra = $extra;
|
||
|
||
$this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($relation)->exec();
|
||
|
||
return $relation;
|
||
}
|
||
|
||
/**
|
||
* 导入project数据。
|
||
* Import jira project.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraProject(array $dataList): bool
|
||
{
|
||
$this->app->loadConfig('execution');
|
||
$this->app->loadLang('doc');
|
||
|
||
$projectRoleActor = $this->getJiraProjectRoleActor($dataList);
|
||
$archivedProject = $this->session->jiraMethod == 'api' ? array() : $this->getJiraArchivedProject($dataList);
|
||
$sprintGroup = $this->getJiraSprint(array_keys($dataList));
|
||
$jiraProjectRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jproject')->fetchAll('AID');
|
||
foreach($dataList as $id => $data)
|
||
{
|
||
if(!empty($jiraProjectRelation[$id])) continue;
|
||
if(isset($data->pstatus) && $data->pstatus == 'deleted')
|
||
{
|
||
$this->createTmpRelation('jproject', $id, 'zproject', 0);
|
||
$this->createTmpRelation('jproject', $id, 'zproduct', 0);
|
||
continue;
|
||
}
|
||
|
||
$data->status = in_array($data->id, $archivedProject) || !empty($data->archived) ? 'closed' : 'doing';
|
||
|
||
$project = $this->createProject($data, $projectRoleActor);
|
||
$executions = $this->createExecution($id, $project, $sprintGroup, $projectRoleActor);
|
||
$productID = $this->createProduct($project, $executions);
|
||
$systemID = $this->createSystem($productID);
|
||
|
||
$this->createTmpRelation('jproject', $id, 'zproject', $project->id);
|
||
$this->createTmpRelation('jproject', $id, 'zproduct', $productID);
|
||
$this->createTmpRelation('joldkey', $data->originalkey, 'jnewkey', $data->pkey, $data->id);
|
||
|
||
/* 如果是api方式,则在此刻插入版本信息。 */
|
||
if($this->session->jiraMethod == 'api' && !empty($data->versions))
|
||
{
|
||
foreach($data->versions as $version)
|
||
{
|
||
$zentaoBuild = new stdclass();
|
||
$zentaoBuild->id = $version['id'];
|
||
$zentaoBuild->vname = $version['name'];
|
||
$zentaoBuild->releasedate = isset($version['releaseDate']) ? $version['releaseDate'] : null;
|
||
$zentaoBuild->archived = isset($version['archived']) ? $version['archived'] : 0;
|
||
$zentaoBuild->released = isset($version['released']) ? $version['released'] : 0;
|
||
$zentaoBuild->startdate = isset($version['startDate']) ? $version['startDate'] : null;
|
||
|
||
$build = $this->createBuild((int)$productID, (int)$project->id, $systemID, $zentaoBuild, array(), array());
|
||
|
||
$this->createRelease($build, $zentaoBuild, array(), array());
|
||
$this->createTmpRelation('jversion', $version['id'], 'zversion', $build->id);
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入issue数据。
|
||
* Import jira issue.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraIssue(array $dataList): bool
|
||
{
|
||
$jiraRelation = $this->session->jiraRelation;
|
||
$relations = $jiraRelation ? json_decode($jiraRelation, true) : array();
|
||
|
||
$projectRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jproject')->andWhere('BType')->eq('zproject')->fetchPairs();
|
||
$productRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jproject')->andWhere('BType')->eq('zproduct')->fetchPairs();
|
||
$projectKeys = $this->dao->dbh($this->dbh)->select('extra,AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('joldkey')->andWhere('BType')->eq('jnewkey')->fetchAll('extra');
|
||
$sprintRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jsprint')->andWhere('BType')->eq('zexecution')->fetchPairs();
|
||
$defaultExecution = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jproject')->andWhere('BType')->eq('zexecution')->fetchPairs();
|
||
$issueList = $this->getIssueData();
|
||
$jiraSprintList = $this->getJiraSprintIssue();
|
||
$jiraActions = $this->getJiraWorkflowActions();
|
||
$jiraStatusList = $this->getJiraStatusList();
|
||
$customFields = $this->getJiraData($this->session->jiraMethod, 'customfield');
|
||
$fieldValues = $this->getJiraData($this->session->jiraMethod, 'customfieldvalue');
|
||
$fieldOptions = $this->getJiraData($this->session->jiraMethod, 'customfieldoption');
|
||
$jiraResolutions = $this->getJiraData($this->session->jiraMethod, 'resolution');
|
||
$jiraPriList = $this->getJiraData($this->session->jiraMethod, 'priority');
|
||
|
||
$relations = $this->createWorkflow($relations, $jiraActions, $jiraResolutions, $jiraPriList, $jiraStatusList);
|
||
$relations = $this->createWorkflowField($relations, $customFields, $fieldOptions, $jiraResolutions, $jiraPriList);
|
||
$relations = $this->createWorkflowStatus($relations, $jiraStatusList);
|
||
$relations = $this->createWorkflowAction($relations, $jiraActions);
|
||
$relations = $this->createWorkflowGroup($relations, $projectRelation, $productRelation);
|
||
$relations = $this->createResolution($relations);
|
||
$workflows = $this->dao->dbh($this->dbh)->select('module,`table`')->from(TABLE_WORKFLOW)->where('buildin')->eq('0')->fetchPairs();
|
||
|
||
$comments = $changeGroups = $changeItems = $worklogs = $files = $links = array();
|
||
foreach($dataList as $id => $data)
|
||
{
|
||
if(!empty($issueList[$data->id])) continue;
|
||
|
||
$issueProject = $data->project;
|
||
if(!isset($projectRelation[$issueProject])) continue;
|
||
|
||
$data->execution = !empty($jiraSprintList[$data->id]) ? $jiraSprintList[$data->id] : '';
|
||
|
||
/* 将自定义字段数据赋值给issue对象。 */
|
||
if($this->session->jiraMethod != 'api')
|
||
{
|
||
foreach($fieldValues as $fieldValue)
|
||
{
|
||
if($fieldValue->issue == $data->id)
|
||
{
|
||
if(!empty($fieldValue->datevalue))
|
||
{
|
||
$data->{$fieldValue->customfield} = $this->formatDatetime(date('Y-m-d H:i:s', strtotime($fieldValue->datevalue))); // 日期类型是datevalue
|
||
}
|
||
elseif(!empty($fieldValue->numbervalue))
|
||
{
|
||
$data->{$fieldValue->customfield} = $fieldValue->numbervalue; // 数字类型是numbervalue
|
||
}
|
||
elseif(!empty($fieldValue->textvalue))
|
||
{
|
||
$data->{$fieldValue->customfield} = $fieldValue->textvalue; // 文本类型是textvalue
|
||
}
|
||
elseif(isset($data->{$fieldValue->customfield}))
|
||
{
|
||
$data->{$fieldValue->customfield} .= ',' . $fieldValue->stringvalue; // 多选的情况
|
||
}
|
||
else
|
||
{
|
||
$data->{$fieldValue->customfield} = $fieldValue->stringvalue;
|
||
}
|
||
|
||
$fieldKey = !empty($customFields[$fieldValue->customfield]) ? $customFields[$fieldValue->customfield]->customfieldtypekey : '';
|
||
if($fieldKey == 'com.pyxis.greenhopper.jira:gh-sprint' && !empty($sprintRelation[$fieldValue->stringvalue])) $data->execution = $sprintRelation[$fieldValue->stringvalue];
|
||
}
|
||
}
|
||
}
|
||
|
||
$projectID = $projectRelation[$issueProject];
|
||
$productID = $productRelation[$issueProject];
|
||
$executionID = !empty($data->execution) ? $data->execution : $defaultExecution[$issueProject];
|
||
$zentaoObject = $relations['zentaoObject'][$data->issuetype];
|
||
|
||
if($zentaoObject == 'requirement' || $zentaoObject == 'story' || $zentaoObject == 'epic')
|
||
{
|
||
$this->createStory((int)$productID, (int)$projectID, (int)$executionID, $zentaoObject, $data, $relations, $customFields);
|
||
}
|
||
elseif($zentaoObject == 'task')
|
||
{
|
||
$this->createTask((int)$projectID, (int)$executionID, $data, $relations, $customFields);
|
||
}
|
||
elseif($zentaoObject == 'bug')
|
||
{
|
||
$this->createBug((int)$productID, (int)$projectID, (int)$executionID, $data, $relations, $customFields);
|
||
}
|
||
elseif($zentaoObject == 'testcase')
|
||
{
|
||
$this->createCase((int)$productID, (int)$projectID, (int)$executionID, $data, $relations, $customFields);
|
||
}
|
||
elseif($zentaoObject == 'feedback')
|
||
{
|
||
$this->createFeedback((int)$productID, $data, $relations, $customFields);
|
||
}
|
||
elseif($zentaoObject == 'ticket')
|
||
{
|
||
$this->createTicket((int)$productID, $data, $relations, $customFields);
|
||
}
|
||
else
|
||
{
|
||
$this->createFlowData((int)$productID, (int)$projectID, $zentaoObject, $data, $relations, $customFields, $workflows);
|
||
}
|
||
|
||
$oldKey = zget($projectKeys[$issueProject], 'AID', '');
|
||
$newKey = zget($projectKeys[$issueProject], 'BID', '');
|
||
$issueKey = $oldKey ? $oldKey . '-' . $data->issuenum : $newKey . '-' . $data->issuenum;
|
||
$this->createTmpRelation('jissueid', $data->id, 'jfilepath', '', "{$oldKey}/10000/{$issueKey}/");
|
||
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
if(!empty($data->links)) $links = arrayUnion($links, $data->links);
|
||
if(!empty($data->worklogs)) $worklogs = arrayUnion($worklogs, $data->worklogs);
|
||
if(!empty($data->comments)) $comments = arrayUnion($comments, $data->comments);
|
||
if(!empty($data->changeItems)) $changeItems = arrayUnion($changeItems, $data->changeItems);
|
||
if(!empty($data->changeGroups)) $changeGroups = arrayUnion($changeGroups, $data->changeGroups);
|
||
if(!empty($data->files)) $files = arrayUnion($files, $data->files);
|
||
}
|
||
}
|
||
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
foreach($links as $index => $link) $links[$index] = $this->buildIssuelinkData($link);
|
||
foreach($worklogs as $index => $worklog) $worklogs[$index] = $this->buildWorklogData($worklog);
|
||
foreach($comments as $index => $comment) $comments[$index] = $this->buildActionData($comment);
|
||
foreach($changeItems as $index => $item) $changeItems[$index] = $this->buildChangeItemData($item);
|
||
foreach($changeGroups as $index => $group) $changeGroups[$index] = $this->buildChangeGroupData($group);
|
||
foreach($files as $index => $file) $files[$index] = $this->buildFileData($file);
|
||
|
||
$this->importJiraIssueLink($links);
|
||
$this->importJiraWorkLog($worklogs);
|
||
$this->importJiraAction($comments);
|
||
$this->importJiraChangeItem($changeItems, $changeGroups);
|
||
$this->importJiraFile($files);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入版本数据。
|
||
* Import jira build.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraBuild(array $dataList): bool
|
||
{
|
||
$issueList = $this->getIssueData();
|
||
|
||
$projectRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)
|
||
->where('AType')->eq('jproject')
|
||
->andWhere('BType')->eq('zproject')
|
||
->fetchPairs();
|
||
|
||
$projectProduct = $this->dao->dbh($this->dbh)->select('project,product')->from(TABLE_PROJECTPRODUCT)->where('project')->in(array_values($projectRelation))->fetchPairs();
|
||
$productSystem = $this->dao->dbh($this->dbh)->select('id,product')->from(TABLE_SYSTEM)->fetchAll('product');
|
||
|
||
$versionGroup = array();
|
||
$nodeassociation = $this->getJiraData($this->session->jiraMethod, 'nodeassociation');
|
||
$fixVersion = $this->getJiraData($this->session->jiraMethod, 'fixversion');
|
||
$affectsVersion = $this->getJiraData($this->session->jiraMethod, 'affectsversion');
|
||
foreach($nodeassociation as $node)
|
||
{
|
||
foreach($node as $key => $value)
|
||
{
|
||
$key = strtolower($key);
|
||
$node->$key = $value;
|
||
}
|
||
|
||
if($node->sink_node_entity == 'Version' && $node->source_node_entity == 'Issue' && ($node->association_type == 'IssueFixVersion' || $node->association_type == 'IssueVersion'))
|
||
{
|
||
$data = new stdClass();
|
||
$data->versionid = $node->sink_node_id;
|
||
$data->issueid = $node->source_node_id;
|
||
$data->relation = $node->association_type;
|
||
$versionGroup[$node->sink_node_id][] = $data;
|
||
}
|
||
}
|
||
foreach($fixVersion as $version)
|
||
{
|
||
$data = new stdClass();
|
||
$data->versionid = $version->version;
|
||
$data->issueid = $version->issue;
|
||
$data->relation = 'IssueFixVersion';
|
||
$versionGroup[$version->version][] = $data;
|
||
}
|
||
foreach($affectsVersion as $version)
|
||
{
|
||
$data = new stdClass();
|
||
$data->versionid = $version->version;
|
||
$data->issueid = $version->issue;
|
||
$data->relation = 'IssueVersion';
|
||
$versionGroup[$version->version][] = $data;
|
||
}
|
||
|
||
$versionRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jversion')->fetchAll('AID');
|
||
foreach($dataList as $data)
|
||
{
|
||
if(!empty($versionRelation[$data->id])) continue;
|
||
|
||
$buildProject = $data->project;
|
||
if(empty($projectRelation[$buildProject])) continue;
|
||
$projectID = $projectRelation[$buildProject];
|
||
$productID = $projectProduct[$projectID];
|
||
$system = !empty($productSystem[$productID]) ? $productSystem[$productID]->id : 0;
|
||
$build = $this->createBuild((int)$productID, (int)$projectID, $system, $data, $versionGroup, $issueList);
|
||
|
||
$this->createRelease($build, $data, !empty($versionGroup[$data->id]) ? $versionGroup[$data->id] : array(), $issueList);
|
||
|
||
$this->createTmpRelation('jversion', $data->id, 'zbuild', $build->id);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入issue link数据。
|
||
* Import jira issue link.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraIssueLink(array $dataList): bool
|
||
{
|
||
$issueLinkTypeList = array();
|
||
$issueList = $this->getIssueData();
|
||
$jiraRelation = $this->session->jiraRelation;
|
||
$relations = $jiraRelation ? json_decode($jiraRelation, true) : array();
|
||
|
||
if($this->config->edition != 'open') $relations = $this->createRelation($relations);
|
||
|
||
$storyLink = $taskLink = $duplicateLink = $relatesLink = array();
|
||
$issueLinkTypeList = zget($relations, 'zentaoLinkType', array());
|
||
foreach($dataList as $issueLink)
|
||
{
|
||
$linkType = $issueLink->linktype;
|
||
if($linkType == 'jiraSubTask') $taskLink[$issueLink->source][] = $issueLink->destination;
|
||
|
||
if(empty($issueLinkTypeList[$linkType])) continue;
|
||
if($issueLinkTypeList[$linkType] == 'subStoryLink') $storyLink[$issueLink->source][] = $issueLink->destination;
|
||
if($issueLinkTypeList[$linkType] == 'subTaskLink') $taskLink[$issueLink->source][] = $issueLink->destination;
|
||
if($issueLinkTypeList[$linkType] == 'duplicate') $duplicateLink[$issueLink->source] = $issueLink->destination;
|
||
if($issueLinkTypeList[$linkType] == 'relates') $relatesLink[$issueLink->source] = $issueLink->destination;
|
||
}
|
||
|
||
$this->updateSubStory($storyLink, $issueList);
|
||
$this->updateSubTask($taskLink, $issueList);
|
||
$this->updateDuplicateStoryAndBug($duplicateLink, $issueList);
|
||
$this->updateRelatesObject($relatesLink, $issueList);
|
||
if($this->config->edition != 'open') $this->updateObjectRelation($issueLinkTypeList, $dataList, $issueList);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入工作日志数据。
|
||
* Import jira worklog.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraWorkLog(array $dataList): bool
|
||
{
|
||
$issueList = $this->getIssueData();
|
||
$worklogRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jworklog')->fetchAll('AID');
|
||
foreach($dataList as $data)
|
||
{
|
||
if(!empty($worklogRelation[$data->id])) continue;
|
||
|
||
$issueID = $data->issueid;
|
||
if(!isset($issueList[$issueID])) continue;
|
||
|
||
$objectType = zget($issueList[$issueID], 'BType', '');
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
|
||
if(empty($objectID)) continue;
|
||
|
||
$effort = new stdclass();
|
||
$effort->vision = $this->config->vision;
|
||
$effort->objectID = $objectID;
|
||
$effort->date = !empty($data->created) ? $this->formatDate(substr($data->created, 0, 10)) : null;
|
||
$effort->account = $this->getJiraAccount(isset($data->author) ? $data->author : '');
|
||
$effort->consumed = round($data->timeworked / 3600, 2);
|
||
$effort->work = $data->worklogbody;
|
||
$effort->objectType = substr($objectType, 1);
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_EFFORT)->data($effort)->exec();
|
||
$effortID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
$this->createTmpRelation('jworklog', $data->id, 'zeffort', $effortID);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入issue评论数据。
|
||
* Import jira action.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraAction(array $dataList): bool
|
||
{
|
||
$issueList = $this->getIssueData();
|
||
|
||
$actionRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jaction')->fetchAll('AID');
|
||
|
||
foreach($dataList as $data)
|
||
{
|
||
if(!empty($actionRelation[$data->id])) continue;
|
||
|
||
$issueID = $data->issueid;
|
||
$comment = $data->actionbody;
|
||
if(empty($comment)) continue;
|
||
|
||
if(!isset($issueList[$issueID])) continue;
|
||
|
||
$objectType = zget($issueList[$issueID], 'BType', '');
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
|
||
if(empty($objectID)) continue;
|
||
$comment = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $comment);
|
||
|
||
$action = new stdclass();
|
||
$action->objectType = substr($objectType, 1);
|
||
$action->objectID = $objectID;
|
||
$action->actor = $this->getJiraAccount(isset($data->author) ? $data->author : '');
|
||
$action->action = 'commented';
|
||
$action->date = isset($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : '';
|
||
$action->comment = $comment;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$actionID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
$this->createTmpRelation('jaction', $data->id, 'zaction', $actionID);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入issue变更记录。
|
||
* Import jira issue change item.
|
||
*
|
||
* @param array $dataList
|
||
* @param array $changeGroups
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraChangeItem(array $dataList, array $changeGroups = array()): bool
|
||
{
|
||
$issueList = $this->getIssueData();
|
||
|
||
$changeGroup = $changeGroups ?: $this->getJiraData($this->session->jiraMethod, 'changegroup');
|
||
$changeRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jchangeitem')->fetchAll('AID');
|
||
foreach($dataList as $data)
|
||
{
|
||
if(!empty($changeRelation[$data->id])) continue;
|
||
$group = $changeGroup[$data->groupid];
|
||
|
||
$issueID = $group->issueid;
|
||
if(!isset($issueList[$issueID])) continue;
|
||
|
||
$objectType = zget($issueList[$issueID], 'BType', '');
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
|
||
if(empty($objectID)) continue;
|
||
|
||
$action = new stdclass();
|
||
$action->objectType = substr($objectType, 1);
|
||
$action->objectID = $objectID;
|
||
$action->actor = $this->getJiraAccount(isset($group->author) ? $group->author : '');
|
||
$action->action = 'commented';
|
||
$action->date = isset($group->created) ? $this->formatDatetime(substr($group->created, 0, 19)) : '';
|
||
$action->comment = sprintf($this->lang->convert->jira->changeItems, $data->field, $data->oldstring, $data->newstring);
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$actionID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
$this->createTmpRelation('jchangeitem', $data->id, 'zaction', $actionID);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 导入file数据。
|
||
* Import jira file.
|
||
*
|
||
* @param array $dataList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function importJiraFile(array $dataList): bool
|
||
{
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
$this->app->loadClass('jira', true);
|
||
$jiraApi = json_decode($this->session->jiraApi, true);
|
||
$jiraApi = new jira($jiraApi['domain'], $jiraApi['admin'], $jiraApi['token']);
|
||
}
|
||
|
||
$this->loadModel('file');
|
||
$issueList = $this->getIssueData();
|
||
|
||
$filePaths = $this->dao->dbh($this->dbh)->select('AID,extra')->from(JIRA_TMPRELATION)
|
||
->where('AType')->eq('jissueid')
|
||
->andWhere('BType')->eq('jfilepath')
|
||
->fetchPairs();
|
||
|
||
$fileRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jfile')->fetchAll('AID');
|
||
foreach($dataList as $fileAttachment)
|
||
{
|
||
if(!empty($fileRelation[$fileAttachment->id])) continue;
|
||
|
||
$issueID = $fileAttachment->issueid;
|
||
if(!isset($issueList[$issueID])) continue;
|
||
|
||
$objectType = zget($issueList[$issueID], 'BType', '');
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
|
||
if(empty($objectID)) continue;
|
||
|
||
$fileID = $fileAttachment->id;
|
||
$fileName = $fileAttachment->filename;
|
||
$parts = explode('.', $fileName);
|
||
$extension = array_pop($parts);
|
||
$pathname = $this->file->setPathName((int)$fileID, $extension);
|
||
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
$fileContent = $jiraApi->downloadFile($fileAttachment->content);
|
||
file_put_contents($this->file->savePath . $pathname, $fileContent);
|
||
if(!is_file($this->file->savePath . $pathname) || filesize($this->file->savePath . $pathname) == 0) continue;
|
||
}
|
||
else
|
||
{
|
||
$jiraFile = $this->app->getTmpRoot() . 'attachments/' . $filePaths[$issueID] . $fileID;
|
||
if(!is_file($jiraFile)) continue;
|
||
}
|
||
|
||
$file = new stdclass();
|
||
$file->pathname = $pathname;
|
||
$file->title = $fileName;
|
||
$file->extension = substr($extension, 0, 30); // 防止超长报错
|
||
$file->size = $fileAttachment->filesize;
|
||
$file->objectType = substr($objectType, 1);
|
||
$file->objectID = $objectID;
|
||
$file->extra = in_array($file->objectType, array('epic', 'requirement', 'story')) ? '1' : '';
|
||
$file->addedBy = $this->getJiraAccount(isset($fileAttachment->author) ? $fileAttachment->author : '');
|
||
$file->addedDate = !empty($fileAttachment->created) ? $this->formatDatetime(substr($fileAttachment->created, 0, 19)) : null;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_FILE)->data($file)->exec();
|
||
|
||
$fileID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
if($this->session->jiraMethod != 'api') copy($jiraFile, $this->file->savePath . $file->pathname);
|
||
|
||
if(in_array($file->objectType, array('epic', 'requirement', 'story')))
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORYSPEC)->set("files=IF(files IS NOT NULL, CONCAT(files,',{$fileID},'), '{$fileID}')")->where('story')->eq($file->objectID)->andWhere('version')->eq($file->extra)->exec();
|
||
|
||
}
|
||
|
||
$this->createTmpRelation('jfile', $fileAttachment->id, 'zfile', $fileID);
|
||
}
|
||
|
||
$this->processJiraIssueContent($issueList);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建团队成员。
|
||
* Create team member.
|
||
*
|
||
* @param int $objectID
|
||
* @param string $createdBy
|
||
* @param string $type
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createTeamMember(int $objectID, string $createdBy, string $type): bool
|
||
{
|
||
if(!$createdBy) return false;
|
||
|
||
$member = new stdclass();
|
||
$member->root = $objectID;
|
||
$member->account = $createdBy;
|
||
$member->role = '';
|
||
$member->join = helper::now();
|
||
$member->type = $type;
|
||
$member->days = 0;
|
||
$member->hours = $this->config->execution->defaultWorkhours;
|
||
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_TEAM)->data($member)->exec();
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 为项目或者迭代创建默认的文档库。
|
||
* Create doc lib.
|
||
*
|
||
* @param int $productID
|
||
* @param int $projectID
|
||
* @param int $executionID
|
||
* @param string $name
|
||
* @param string $type
|
||
* @access protected
|
||
* @return boll
|
||
*/
|
||
protected function createDocLib(int $productID, int $projectID, int $executionID, string $name, string $type): bool
|
||
{
|
||
$docLib = new stdclass();
|
||
$docLib->product = $productID;
|
||
$docLib->project = $projectID;
|
||
$docLib->execution = $executionID;
|
||
$docLib->name = $name;
|
||
$docLib->type = $type;
|
||
$docLib->main = '1';
|
||
$docLib->acl = 'default';
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_DOCLIB)->data($docLib)->exec();
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建项目。
|
||
* Create project.
|
||
*
|
||
* @param object $data
|
||
* @param array $projectRoleActor
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function createProject(object $data, array $projectRoleActor): object
|
||
{
|
||
/* Create project. */
|
||
$project = new stdclass();
|
||
$project->name = substr($data->pname, 0, 90);
|
||
$project->code = $data->pkey;
|
||
$project->desc = isset($data->description) ? $data->description : '';
|
||
$project->status = $data->status;
|
||
$project->type = 'project';
|
||
$project->model = 'scrum';
|
||
$project->grade = 1;
|
||
$project->acl = 'open';
|
||
$project->auth = 'extend';
|
||
$project->begin = !empty($data->created) ? $this->formatDate(substr($data->created, 0, 10)) : helper::now();
|
||
$project->end = date('Y-m-d', time() + 30 * 24 * 3600);
|
||
$project->days = helper::diffDate($project->end, $project->begin) + 1;
|
||
$project->PM = $this->getJiraAccount(isset($data->lead) ? $data->lead : '');
|
||
$project->openedBy = $this->getJiraAccount(isset($data->lead) ? $data->lead : '');
|
||
$project->openedDate = helper::now();
|
||
$project->openedVersion = $this->config->version;
|
||
$project->storyType = 'story,epic,requirement';
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_PROJECT)->data($project)->exec();
|
||
|
||
$projectID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->loadModel('action')->create('project', $projectID, 'opened');
|
||
|
||
$this->dao->dbh($this->dbh)->update(TABLE_PROJECT)->set('`order`')->eq($projectID * 5)->set('path')->eq(",$projectID,")->where('id')->eq($projectID)->exec();
|
||
|
||
$this->createTeamMember($projectID, $project->openedBy, 'project');
|
||
/* 如果Jira项目有团队成员。 */
|
||
if(!empty($projectRoleActor[$data->id]))
|
||
{
|
||
foreach($projectRoleActor[$data->id] as $userID)
|
||
{
|
||
$account = $this->getJiraAccount($userID);
|
||
if(!$account) continue;
|
||
if($account == $project->openedBy) continue;
|
||
|
||
$this->createTeamMember($projectID, $account, 'project');
|
||
}
|
||
}
|
||
|
||
$this->createDocLib(0, $projectID, 0, $this->lang->doclib->main['project'], 'project');
|
||
|
||
$project->id = $projectID;
|
||
return $project;
|
||
}
|
||
|
||
/**
|
||
* 为项目创建默认的同名执行。
|
||
* Create default execution.
|
||
*
|
||
* @param int $jiraProjectID
|
||
* @param object $project
|
||
* @param array $projectRoleActor
|
||
* @access protected
|
||
* @return int
|
||
*/
|
||
protected function createDefaultExecution(int $jiraProjectID, object $project, array $projectRoleActor): int
|
||
{
|
||
/* Create default execution. */
|
||
$execution = new stdclass();
|
||
$execution->name = $project->name;
|
||
$execution->code = $project->code;
|
||
$execution->desc = $project->desc;
|
||
$execution->status = 'wait';
|
||
$execution->project = $project->id;
|
||
$execution->parent = $project->id;
|
||
$execution->grade = 1;
|
||
$execution->type = 'sprint';
|
||
$execution->acl = 'open';
|
||
$execution->begin = $project->begin;
|
||
$execution->end = $project->end;
|
||
$execution->days = helper::diffDate($execution->end, $execution->begin) + 1;
|
||
$execution->PM = $project->PM;
|
||
$execution->openedBy = $project->openedBy;
|
||
$execution->openedDate = helper::now();
|
||
$execution->openedVersion = $this->config->version;
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_PROJECT)->data($execution)->exec();
|
||
$executionID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->loadModel('action')->create('execution', $executionID, 'opened');
|
||
|
||
$this->dao->dbh($this->dbh)->update(TABLE_PROJECT)->set('`order`')->eq($executionID * 5)->set('path')->eq(",{$project->id},$executionID,")->where('id')->eq($executionID)->exec();
|
||
|
||
$this->createTeamMember($executionID, $execution->openedBy, 'execution');
|
||
|
||
/* 如果Jira项目有团队成员。 */
|
||
if(!empty($projectRoleActor[$jiraProjectID]))
|
||
{
|
||
foreach($projectRoleActor[$jiraProjectID] as $userID)
|
||
{
|
||
$account = $this->getJiraAccount($userID);
|
||
if(!$account) continue;
|
||
if($account == $execution->openedBy) continue;
|
||
|
||
$this->createTeamMember($executionID, $account, 'execution');
|
||
}
|
||
}
|
||
|
||
$this->createDocLib(0, $project->id, $executionID, $this->lang->doclib->main['execution'], 'execution');
|
||
$this->createTmpRelation('jproject', $jiraProjectID, 'zexecution', $executionID);
|
||
|
||
return $executionID;
|
||
}
|
||
|
||
/**
|
||
* 创建执行。
|
||
* Create execution.
|
||
*
|
||
* @param int $jiraProjectID
|
||
* @param object $project
|
||
* @param array $sprintGroup
|
||
* @param array $projectRoleActor
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createExecution(int $jiraProjectID, object $project, array $sprintGroup, array $projectRoleActor): array
|
||
{
|
||
$executions = array();
|
||
$executions[] = $this->createDefaultExecution($jiraProjectID, $project, $projectRoleActor);
|
||
|
||
if(!empty($sprintGroup[$jiraProjectID]))
|
||
{
|
||
$zentaoStatus = array('future' => 'wait', 'active' => 'doing', 'closed' => 'closed');
|
||
foreach($sprintGroup[$jiraProjectID] as $sprint)
|
||
{
|
||
/* Create execution. */
|
||
$execution = new stdclass();
|
||
$execution->name = $sprint->name;
|
||
$execution->code = 'jirasprint' . $sprint->id;
|
||
$execution->status = isset($sprint->state) ? zget($zentaoStatus, $sprint->state, 'wait') : 'wait';
|
||
$execution->project = $project->id;
|
||
$execution->parent = $project->id;
|
||
$execution->grade = 1;
|
||
$execution->desc = !empty($sprint->goal) ? $sprint->goal : '';
|
||
$execution->type = 'sprint';
|
||
$execution->acl = 'open';
|
||
$execution->begin = !empty($sprint->startDate) ? $this->formatDate(substr($sprint->startDate, 0, 10)) : helper::now();
|
||
$execution->end = !empty($sprint->endDate) ? $this->formatDate(substr($sprint->endDate, 0, 10)) : date('Y-m-d', time() + 24 * 3600);
|
||
$execution->days = helper::diffDate($execution->end, $execution->begin) + 1;
|
||
$execution->PM = $project->PM;
|
||
$execution->openedBy = $project->openedBy;
|
||
$execution->openedDate = helper::now();
|
||
$execution->openedVersion = $this->config->version;
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_PROJECT)->data($execution)->exec();
|
||
$executionID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->loadModel('action')->create('execution', $executionID, 'opened');
|
||
|
||
$this->dao->dbh($this->dbh)->update(TABLE_PROJECT)->set('`order`')->eq($executionID * 5)->set('path')->eq(",{$project->id},$executionID,")->where('id')->eq($executionID)->exec();
|
||
|
||
$this->createTeamMember($executionID, $execution->openedBy, 'execution');
|
||
|
||
if(!empty($projectRoleActor[$jiraProjectID]))
|
||
{
|
||
foreach($projectRoleActor[$jiraProjectID] as $userID)
|
||
{
|
||
$account = $this->getJiraAccount($userID);
|
||
if(!$account) continue;
|
||
if($account == $execution->openedBy) continue;
|
||
|
||
$this->createTeamMember($executionID, $account, 'execution');
|
||
}
|
||
}
|
||
|
||
$this->createDocLib(0, $project->id, $executionID, $this->lang->doclib->main['execution'], 'execution');
|
||
$this->createTmpRelation('jsprint', $sprint->id, 'zexecution', $executionID);
|
||
|
||
$executions[] = $executionID;
|
||
}
|
||
}
|
||
|
||
return $executions;
|
||
}
|
||
|
||
/**
|
||
* 创建产品。
|
||
* Create product.
|
||
*
|
||
* @param object $project
|
||
* @param array $executions
|
||
* @access protected
|
||
* @return int
|
||
*/
|
||
protected function createProduct(object $project, array $executions): int
|
||
{
|
||
/* Create product. */
|
||
$product = new stdclass();
|
||
$product->name = $project->name;
|
||
$product->code = $project->code;
|
||
$product->type = 'normal';
|
||
$product->status = 'normal';
|
||
$product->acl = 'open';
|
||
$product->createdBy = $project->openedBy;
|
||
$product->createdDate = helper::now();
|
||
$product->createdVersion = $this->config->version;
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_PRODUCT)->data($product)->exec();
|
||
$productID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->loadModel('action')->create('product', $productID, 'opened');
|
||
|
||
$this->dao->dbh($this->dbh)->update(TABLE_PRODUCT)->set('`order`')->eq($productID * 5)->where('id')->eq($productID)->exec();
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_PROJECTPRODUCT)->set('project')->eq($project->id)->set('product')->eq($productID)->set('branch')->eq('0')->exec();
|
||
|
||
$this->createDocLib($productID, 0, 0, $this->lang->doclib->main['product'], 'product');
|
||
|
||
/* 关联产品与迭代。 */
|
||
foreach($executions as $executionID) $this->dao->dbh($this->dbh)->replace(TABLE_PROJECTPRODUCT)->set('project')->eq($executionID)->set('product')->eq($productID)->set('branch')->eq('0')->exec();
|
||
|
||
return $productID;
|
||
}
|
||
|
||
/**
|
||
* 创建应用。
|
||
* Create system.
|
||
*
|
||
* @param int $productID
|
||
* @access protected
|
||
* @return int
|
||
*/
|
||
protected function createSystem(int $productID): int
|
||
{
|
||
$product = $this->fetchByID($productID, 'product');
|
||
|
||
/* 创建产品同名的应用。 */
|
||
$system = new stdclass();
|
||
$system->name = substr($product->name, 0, 80);
|
||
$system->product = $productID;
|
||
$system->status = 'active';
|
||
$system->desc = '';
|
||
$system->integrated = 0;
|
||
$system->createdBy = $this->app->user->account;
|
||
$system->createdDate = helper::now();
|
||
|
||
$this->dao->insert(TABLE_SYSTEM)->data($system)->autoCheck()->exec();
|
||
|
||
$systemID = $this->dao->lastInsertID();
|
||
$this->loadModel('action')->create('system', $systemID, 'created');
|
||
|
||
return $systemID;
|
||
}
|
||
|
||
|
||
/**
|
||
* 处理工作流内置字段数据。
|
||
* Process buildin field Data.
|
||
*
|
||
* @param string $module
|
||
* @param object $data
|
||
* @param object $object
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @param bool $buildinFlow
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function processBuildinFieldData(string $module, object $data, object $object, array $relations, array $customFields, bool $buildinFlow = false)
|
||
{
|
||
$jiraFields = !empty($relations["zentaoField{$data->issuetype}"]) ? $relations["zentaoField{$data->issuetype}"] : array();
|
||
foreach($jiraFields as $jiraField => $zentaoField)
|
||
{
|
||
if(!empty($data->{$jiraField}))
|
||
{
|
||
$fields = $this->session->jiraMethod == 'api' ? zget($customFields, $data->issuetype, array()) : $customFields;
|
||
$controlCode = !empty($fields[$jiraField]->customfieldtypekey) ? $fields[$jiraField]->customfieldtypekey : 'com.atlassian.jira.plugin.system.customfieldtypes:textfield';
|
||
|
||
if(strpos($controlCode, 'multiuserpicker') !== false || strpos($controlCode, 'people') !== false)
|
||
{
|
||
$users = explode(',', $data->{$jiraField});
|
||
|
||
$object->{$zentaoField} = '';
|
||
foreach($users as $user) $object->{$zentaoField} .= $this->getJiraAccount($user) . ',';
|
||
}
|
||
elseif(strpos($controlCode, 'userpicker') !== false)
|
||
{
|
||
$object->{$zentaoField} = $this->getJiraAccount($data->{$jiraField});
|
||
}
|
||
else
|
||
{
|
||
$object->{$zentaoField} = $data->{$jiraField};
|
||
}
|
||
}
|
||
}
|
||
foreach($this->lang->convert->jira->buildinFields as $fieldCode => $buildinField)
|
||
{
|
||
$field = str_replace(range(0, 9), range('a', 'z'), $module . $fieldCode);
|
||
if(isset($buildinField['buildin']) && $buildinField['buildin'] === $buildinFlow) continue;
|
||
if($this->config->edition == 'open') continue;
|
||
if(!empty($data->{$buildinField['jiraField']}))
|
||
{
|
||
$object->{$field} = $data->{$buildinField['jiraField']};
|
||
if($fieldCode == 'reporter')
|
||
{
|
||
$object->{$field} = $this->getJiraAccount($object->{$field});
|
||
}
|
||
if($fieldCode == 'timeoriginalestimate' || $fieldCode == 'timespent')
|
||
{
|
||
$object->{$field} = round($object->{$field} / 3600, 2);
|
||
}
|
||
}
|
||
}
|
||
|
||
return $object;
|
||
}
|
||
|
||
/**
|
||
* 创建需求。
|
||
* Create story.
|
||
*
|
||
* @param int $productID
|
||
* @param int $projectID
|
||
* @param int $executionID
|
||
* @param string $type
|
||
* @param object $data
|
||
* @param array $reasonList
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createStory(int $productID, int $projectID, int $executionID, string $type, object $data, array $relations, array $customFields): bool
|
||
{
|
||
$this->app->loadLang('story');
|
||
$this->loadModel('action');
|
||
|
||
$story = new stdclass();
|
||
$story->title = $data->summary;
|
||
$story->type = $type;
|
||
$story->product = $productID;
|
||
$story->pri = $data->priority ? $data->priority : 3;
|
||
$story->version = 1;
|
||
$story->grade = 1;
|
||
$story->stage = $this->convertStage($data->issuestatus, $data->issuetype, $relations);
|
||
$story->status = $this->convertStatus('story', $data->issuestatus, $data->issuetype, $relations);
|
||
$story->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$story->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
$story->assignedTo = $this->getJiraAccount(isset($data->assignee) ? $data->assignee : '');
|
||
|
||
if($story->assignedTo) $story->assignedDate = helper::now();
|
||
|
||
if($data->resolution)
|
||
{
|
||
$story->closedReason = zget($relations["zentaoReason{$data->issuetype}"], $data->resolution, '');
|
||
if($story->closedReason && !isset($this->lang->story->reasonList[$story->closedReason])) $story->closedReason = 'done';
|
||
}
|
||
|
||
$story = $this->processBuildinFieldData('story', $data, $story, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_STORY)->data($story)->exec();
|
||
|
||
if(!dao::isError())
|
||
{
|
||
$storyID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORY)->set('root')->eq($storyID)->set('path')->eq(",{$storyID},")->where('id')->eq($storyID)->exec();
|
||
|
||
$storyDesc = new stdclass();
|
||
$storyDesc->story = $storyID;
|
||
$storyDesc->version = 1;
|
||
$storyDesc->title = $story->title;
|
||
$storyDesc->spec = isset($data->description) ? $data->description : '';
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_STORYSPEC)->data($storyDesc)->exec();
|
||
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_PROJECTSTORY)->set('project')->eq($projectID)
|
||
->set('product')->eq($productID)
|
||
->set('story')->eq($storyID)
|
||
->set('version')->eq('1')
|
||
->exec();
|
||
|
||
if($type == 'story')
|
||
{
|
||
$this->dao->dbh($this->dbh)->replace(TABLE_PROJECTSTORY)->set('project')->eq($executionID)
|
||
->set('product')->eq($productID)
|
||
->set('story')->eq($storyID)
|
||
->set('version')->eq('1')
|
||
->exec();
|
||
}
|
||
|
||
/* 通过Api 导入时,Issue关联的版本在Issue中体现。 */
|
||
if(!empty($data->fixVersions))
|
||
{
|
||
foreach($data->fixVersions as $version)
|
||
{
|
||
$versionID = $version['id'];
|
||
$zentaoVersionID = $this->dao->dbh($this->dbh)->select('BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jversion')->andWhere('BType')->eq('zversion')->andWhere('AID')->eq($versionID)->fetch('BID');
|
||
if($zentaoVersionID) $this->dao->dbh($this->dbh)->update(TABLE_BUILD)->set('stories')->eq("CONCAT(stories,',',{$storyID})")->where('id')->eq($zentaoVersionID)->exec();
|
||
}
|
||
}
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->product = ",$productID,";
|
||
$action->objectType = 'story';
|
||
$action->objectID = $storyID;
|
||
$action->actor = $story->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $story->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
$this->createTmpRelation("j$type", $data->id, "z$type", $storyID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', $type);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建任务。
|
||
* Create task.
|
||
*
|
||
* @param int $projectID
|
||
* @param int $executionID
|
||
* @param object $data
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createTask(int $projectID, int $executionID, object $data, array $relations, array $customFields): bool
|
||
{
|
||
$this->app->loadLang('task');
|
||
$this->loadModel('action');
|
||
|
||
$task = new stdclass();
|
||
$task->project = $projectID;
|
||
$task->execution = $executionID;
|
||
$task->name = $data->summary;
|
||
$task->type = 'devel';
|
||
$task->estimate = !empty($data->timeoriginalestimate) ? round($data->timeoriginalestimate / 3600, 2) : 0;
|
||
$task->left = !empty($data->timeestimate) ? round($data->timeestimate / 3600, 2) : 0;
|
||
$task->consumed = !empty($data->timespent) ? round($data->timespent / 3600, 2) : 0;
|
||
$task->pri = $data->priority ? $data->priority : 3;
|
||
$task->status = $this->convertStatus('task', $data->issuestatus, $data->issuetype, $relations);
|
||
$task->desc = isset($data->description) ? $data->description : '';
|
||
$task->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$task->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
$task->assignedTo = $this->getJiraAccount(isset($data->assignee) ? $data->assignee : '');
|
||
$task->deadline = !empty($data->duedate) ? $this->formatDate(substr($data->duedate, 0, 10)) : null;
|
||
|
||
if($task->assignedTo) $task->assignedDate = helper::now();
|
||
|
||
if($data->resolution)
|
||
{
|
||
$reasonList = $relations["zentaoReason{$data->issuetype}"];
|
||
$task->closedReason = zget($reasonList, $data->resolution, '');
|
||
if($task->closedReason && !isset($this->lang->task->reasonList[$task->closedReason])) $task->closedReason = 'cancel';
|
||
}
|
||
|
||
$task = $this->processBuildinFieldData('task', $data, $task, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_TASK)->data($task)->exec();
|
||
$taskID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$this->dao->dbh($this->dbh)->update(TABLE_TASK)->set('path')->eq(",{$taskID},")->where('id')->eq($taskID)->exec();
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->objectType = 'task';
|
||
$action->objectID = $taskID;
|
||
$action->product = ",0,";
|
||
$action->project = $projectID;
|
||
$action->execution = $executionID;
|
||
$action->actor = $task->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $task->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
$this->createTmpRelation('jtask', $data->id, 'ztask', $taskID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', 'task');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建BUG。
|
||
* Create bug.
|
||
*
|
||
* @param int $productID
|
||
* @param int $projectID
|
||
* @param int $executionID
|
||
* @param object $data
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createBug(int $productID, int $projectID, int $executionID, object $data, array $relations, array $customFields): bool
|
||
{
|
||
$this->app->loadLang('bug');
|
||
$this->loadModel('action');
|
||
|
||
$bug = new stdclass();
|
||
$bug->product = $productID;
|
||
$bug->project = $projectID;
|
||
$bug->execution = $executionID;
|
||
$bug->title = $data->summary;
|
||
$bug->pri = $data->priority ? $data->priority : 3;
|
||
$bug->severity = 3;
|
||
$bug->status = $this->convertStatus('bug', $data->issuestatus, $data->issuetype, $relations);
|
||
$bug->steps = isset($data->description) ? $data->description : '';
|
||
$bug->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$bug->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
$bug->openedBuild = 'trunk';
|
||
$bug->assignedTo = $bug->status == 'closed' ? 'closed' : $this->getJiraAccount(isset($data->assignee) ? $data->assignee : '');
|
||
$bug->deadline = !empty($data->duedate) ? $this->formatDate(substr($data->duedate, 0, 10)) : null;
|
||
|
||
if($bug->assignedTo) $bug->assignedDate = helper::now();
|
||
|
||
if($data->resolution)
|
||
{
|
||
$resolutionList = $relations["zentaoResolution{$data->issuetype}"];
|
||
$bug->resolution = zget($resolutionList, $data->resolution, '');
|
||
if($bug->resolution && !isset($this->lang->bug->resolutionList[$bug->resolution])) $bug->resolution = 'fixed';
|
||
}
|
||
|
||
$bug = $this->processBuildinFieldData('bug', $data, $bug, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_BUG)->data($bug)->exec();
|
||
$bugID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->objectType = 'bug';
|
||
$action->objectID = $bugID;
|
||
$action->product = ",$productID,";
|
||
$action->project = $projectID ? $projectID : 0;
|
||
$action->execution = $executionID ? $executionID : 0;
|
||
$action->actor = $bug->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $bug->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
/* 通过Api 导入时,Issue关联的版本在Issue中体现。 */
|
||
if(!empty($data->fixVersions))
|
||
{
|
||
foreach($data->fixVersions as $version)
|
||
{
|
||
$versionID = $version['id'];
|
||
$zentaoVersionID = $this->dao->dbh($this->dbh)->select('BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jversion')->andWhere('BType')->eq('zversion')->andWhere('AID')->eq($versionID)->fetch('BID');
|
||
if($zentaoVersionID) $this->dao->dbh($this->dbh)->update(TABLE_BUILD)->set('bugs')->eq("CONCAT(bugs,',',{$bugID})")->where('id')->eq($zentaoVersionID)->exec();
|
||
}
|
||
}
|
||
|
||
$this->createTmpRelation('jbug', $data->id, 'zbug', $bugID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', 'bug');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建测试用例。
|
||
* Create case.
|
||
*
|
||
* @param int $productID
|
||
* @param int $projectID
|
||
* @param int $executionID
|
||
* @param object $data
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createCase(int $productID, int $projectID, int $executionID, object $data, array $relations, array $customFields): bool
|
||
{
|
||
$this->loadModel('action');
|
||
|
||
$case = new stdclass();
|
||
$case->product = $productID;
|
||
$case->project = $projectID;
|
||
$case->execution = $executionID;
|
||
$case->type = 'feature';
|
||
$case->version = '1';
|
||
$case->title = $data->summary;
|
||
$case->pri = $data->priority ? $data->priority : 3;
|
||
$case->status = $this->convertStatus('testcase', $data->issuestatus, $data->issuetype, $relations);
|
||
$case->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$case->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
|
||
$case = $this->processBuildinFieldData('testcase', $data, $case, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_CASE)->data($case)->exec();
|
||
$caseID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->objectType = 'testcase';
|
||
$action->objectID = $caseID;
|
||
$action->product = ",$productID,";
|
||
$action->execution = $executionID ? $executionID : 0;
|
||
$action->actor = $case->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $case->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
$spec = new stdclass();
|
||
$spec->case = $caseID;
|
||
$spec->version = '1';
|
||
$spec->title = $case->title;
|
||
$spec->precondition = zget($case, 'precondition', '');
|
||
$spec->files = '';
|
||
$this->dao->insert(TABLE_CASESPEC)->data($spec)->exec();
|
||
|
||
$this->loadModel('testcase')->syncCase2Project($case, $caseID);
|
||
|
||
$this->createTmpRelation('jcase', $data->id, 'ztestcase', $caseID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', 'case');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建反馈。
|
||
* Create feedback.
|
||
*
|
||
* @param int $productID
|
||
* @param object $data
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createFeedback(int $productID, object $data, array $relations, array $customFields): bool
|
||
{
|
||
$this->loadModel('action');
|
||
|
||
$feedback = new stdclass();
|
||
$feedback->product = $productID;
|
||
$feedback->title = $data->summary;
|
||
$feedback->public = '1';
|
||
$feedback->pri = $data->priority ? $data->priority : 3;
|
||
$feedback->status = $this->convertStatus('feedback', $data->issuestatus, $data->issuetype, $relations);
|
||
$feedback->desc = isset($data->description) ? $data->description : '';
|
||
$feedback->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$feedback->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
$feedback->assignedTo = $feedback->status == 'closed' ? 'closed' : $this->getJiraAccount(isset($data->assignee) ? $data->assignee : '');
|
||
|
||
if($feedback->assignedTo) $feedback->assignedDate = helper::now();
|
||
|
||
if($data->resolution)
|
||
{
|
||
$reasonList = $relations["zentaoReason{$data->issuetype}"];
|
||
$feedback->closedReason = zget($reasonList, $data->resolution, '');
|
||
if($feedback->closedReason && !isset($this->lang->feedback->closedReasonList[$feedback->closedReason])) $feedback->closedReason = 'refuse';
|
||
}
|
||
|
||
$feedback = $this->processBuildinFieldData('feedback', $data, $feedback, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_FEEDBACK)->data($feedback)->exec();
|
||
$feedbackID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->objectType = 'feedback';
|
||
$action->objectID = $feedbackID;
|
||
$action->product = ",$productID,";
|
||
$action->actor = $feedback->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $feedback->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
$this->loadModel('feedback')->updateSubStatus($feedbackID, $feedback->status);
|
||
|
||
$this->createTmpRelation('jfeedback', $data->id, 'zfeedback', $feedbackID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', 'feedback');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建工单。
|
||
* Create ticket.
|
||
*
|
||
* @param int $productID
|
||
* @param object $data
|
||
* @param array $relations
|
||
* @param array $customFields
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createTicket(int $productID, object $data, array $relations, $customFields): bool
|
||
{
|
||
$this->loadModel('action');
|
||
|
||
$ticket = new stdclass();
|
||
$ticket->product = $productID;
|
||
$ticket->title = $data->summary;
|
||
$ticket->type = 'code';
|
||
$ticket->pri = $data->priority ? $data->priority : 3;
|
||
$ticket->status = $this->convertStatus('ticket', $data->issuestatus, $data->issuetype, $relations, $relations);
|
||
$ticket->desc = isset($data->description) ? $data->description : '';
|
||
$ticket->openedBy = $this->getJiraAccount(isset($data->creator) ? $data->creator : '');
|
||
$ticket->openedDate = !empty($data->created) ? $this->formatDatetime(substr($data->created, 0, 19)) : null;
|
||
$ticket->assignedTo = $ticket->status == 'closed' ? 'closed' : $this->getJiraAccount(isset($data->assignee) ? $data->assignee : '');
|
||
$ticket->openedBuild = 'trunk';
|
||
|
||
if($ticket->assignedTo) $ticket->assignedDate = helper::now();
|
||
|
||
if($data->resolution)
|
||
{
|
||
$reasonList = $relations["zentaoReason{$data->issuetype}"];
|
||
$ticket->closedReason = zget($reasonList, $data->resolution, '');
|
||
if($ticket->closedReason && !isset($this->lang->ticket->closedReasonList[$ticket->closedReason])) $ticket->closedReason = 'refuse';
|
||
}
|
||
|
||
$ticket = $this->processBuildinFieldData('ticket', $data, $ticket, $relations, $customFields);
|
||
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_TICKET)->data($ticket)->exec();
|
||
$ticketID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
/* Create opened action from openedDate. */
|
||
$action = new stdclass();
|
||
$action->objectType = 'ticket';
|
||
$action->objectID = $ticketID;
|
||
$action->product = ",$productID,";
|
||
$action->actor = $ticket->openedBy;
|
||
$action->action = 'opened';
|
||
$action->date = $ticket->openedDate;
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_ACTION)->data($action)->exec();
|
||
$this->action->saveIndex($action->objectType, $action->objectID, $action->action);
|
||
|
||
$this->createTmpRelation('jticket', $data->id, 'zticket', $ticketID, 'issue');
|
||
$this->createTmpRelation('jissueid', $data->id, 'zissuetype', '', 'ticket');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建版本。
|
||
* Create build.
|
||
*
|
||
* @param int $productID
|
||
* @param int $projectID
|
||
* @param int $systemID
|
||
* @param object $data
|
||
* @param array $versionGroup
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return object
|
||
*/
|
||
protected function createBuild(int $productID, int $projectID, int $systemID, object $data, array $versionGroup, array $issueList): object
|
||
{
|
||
/* Create build. */
|
||
$build = new stdclass();
|
||
$build->product = $productID;
|
||
$build->project = $projectID;
|
||
$build->system = $systemID;
|
||
$build->name = $data->vname;
|
||
$build->date = !empty($data->releasedate) ? $this->formatDate(substr($data->releasedate, 0, 10)) : null;
|
||
$build->builder = $this->app->user->account;
|
||
$build->createdBy = $this->app->user->account;
|
||
$build->createdDate = helper::now();
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_BUILD)->data($build)->exec();
|
||
|
||
$buildID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
$versionid = $data->id;
|
||
|
||
/* Process build data. */
|
||
if(isset($versionGroup[$versionid]))
|
||
{
|
||
foreach($versionGroup[$versionid] as $issue)
|
||
{
|
||
$issueID = $issue->issueid;
|
||
if(empty($issueList[$issueID])) continue;
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
$issueType = zget($issueList[$issueID], 'BType', '');
|
||
if(!$issueType || ($issueType != 'zstory' && $issueType != 'zbug')) continue;
|
||
|
||
if($issue->relation == 'IssueFixVersion')
|
||
{
|
||
if($issueType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUILD)->set("`stories` = IF(`stories` IS NOT NULL, CONCAT(`stories`, ',$objectID'), '{$objectID}')")->where('id')->eq($buildID)->exec();
|
||
}
|
||
if($issueType == 'zbug')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUILD)->set("`bugs` = IF(`bugs` IS NOT NULL, CONCAT(`bugs`, ',$objectID'), {$objectID})")->where('id')->eq($buildID)->exec();
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set('resolvedBuild')->eq($buildID)->where('id')->eq($objectID)->exec();
|
||
}
|
||
}
|
||
if($issue->relation == 'IssueVersion' && $issueType == 'zbug')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set("`openedBuild` = IF(`openedBuild` = 'trunk', $buildID, CONCAT(`openedBuild`, ',$buildID'))")->where('id')->eq($objectID)->exec();
|
||
}
|
||
}
|
||
}
|
||
|
||
$build->id = $buildID;
|
||
return $build;
|
||
}
|
||
|
||
/**
|
||
* 创建发布。
|
||
* Create release.
|
||
*
|
||
* @param object $build
|
||
* @param object $data
|
||
* @param array $releaseIssue
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createRelease(object $build, object $data, array $releaseIssue, array $issueList): bool
|
||
{
|
||
$status = 'normal';
|
||
if(empty($data->released)) $status = 'wait';
|
||
if(!empty($data->archived)) $status = 'terminate';
|
||
|
||
/* Create release. */
|
||
$release = new stdclass();
|
||
$release->product = $build->product;
|
||
$release->project = $build->project;
|
||
$release->system = $build->system;
|
||
$release->build = $build->id;
|
||
$release->name = $build->name;
|
||
$release->date = helper::isZeroDate($data->startdate) ? null : $this->formatDate(substr($data->startdate, 0, 10));
|
||
$release->desc = isset($data->description) ? $data->description : '';
|
||
$release->status = $status;
|
||
$release->releasedDate = !empty($data->released) && !empty($data->releasedate) ? $this->formatDate(substr($data->releasedate, 0, 10)) : null;
|
||
$release->createdBy = $this->app->user->account;
|
||
$release->createdDate = helper::now();
|
||
$this->dao->dbh($this->dbh)->insert(TABLE_RELEASE)->data($release)->exec();
|
||
|
||
$releaseID = $this->dao->dbh($this->dbh)->lastInsertID();
|
||
|
||
/* Process release data. */
|
||
foreach($releaseIssue as $issue)
|
||
{
|
||
$issueID = $issue->issueid;
|
||
if(empty($issueList[$issueID])) continue;
|
||
|
||
$objectID = zget($issueList[$issueID], 'BID', '');
|
||
$issueType = zget($issueList[$issueID], 'BType', '');
|
||
if(!$issueType || ($issueType != 'zstory' && $issueType != 'zbug')) continue;
|
||
|
||
if($issue->relation == 'IssueFixVersion')
|
||
{
|
||
if($issueType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_RELEASE)->set("stories = IF(stories IS NOT NULL, CONCAT(stories, ',$objectID'), '{$objectID}')")->where('id')->eq($releaseID)->exec();
|
||
}
|
||
else
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_RELEASE)->set("bugs = IF(bugs IS NOT NULL, CONCAT(bugs, ',$objectID'), '{$objectID}')")->where('id')->eq($releaseID)->exec();
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Jira内置字段新增成工作流字段。
|
||
* Create buildin field.
|
||
*
|
||
* @param string $module
|
||
* @param array $resolutions
|
||
* @param array $priList
|
||
* @param false $buildin
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createBuildinField(string $module, array $resolutions, array $priList, $buildin = false): bool
|
||
{
|
||
$this->loadModel('workflowfield');
|
||
foreach($this->lang->convert->jira->buildinFields as $fieldCode => $buildinField)
|
||
{
|
||
if(isset($buildinField['buildin']) && $buildinField['buildin'] !== $buildin) continue;
|
||
|
||
$options = array('code' => array(), 'name' => array());
|
||
if($fieldCode == 'pri')
|
||
{
|
||
foreach($priList as $pri)
|
||
{
|
||
$options['code'][] = $pri->id;
|
||
$options['name'][] = $pri->pname;
|
||
}
|
||
}
|
||
if($fieldCode == 'resolution')
|
||
{
|
||
foreach($resolutions as $resolution)
|
||
{
|
||
$options['code'][] = $resolution->id;
|
||
$options['name'][] = $resolution->pname;
|
||
}
|
||
}
|
||
|
||
$field = new stdclass();
|
||
$field->name = $buildinField['name'];
|
||
$field->field = str_replace(range(0, 9), range('a', 'z'), $module . $fieldCode);
|
||
$field->control = $buildinField['control'];
|
||
$field->type = $buildinField['type'];
|
||
$field->length = $buildinField['length'];
|
||
$field->integerDigits = in_array($field->type, $this->config->workflowfield->numberTypes) ? '10' : '';
|
||
$field->decimalDigits = in_array($field->type, $this->config->workflowfield->numberTypes) ? '2' : '';;
|
||
$field->expression = '';
|
||
$field->optionType = $buildinField['optionType'];
|
||
$field->sql = '';
|
||
$field->options = $options;
|
||
$field->placeholder = '';
|
||
$field->module = $module;
|
||
$field->group = '0';
|
||
$field->createdBy = $this->app->user->account;
|
||
$field->createdDate = helper::now();
|
||
|
||
if($field->type != 'text') $field->default = '';
|
||
|
||
$result = $this->workflowfield->create($module, $field, null, true);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 为工作流生成默认布局。
|
||
* Create default layout.
|
||
*
|
||
* @param array $fields
|
||
* @param object $flow
|
||
* @param int $group
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createDefaultLayout(array $fields, object $flow, int $group = 0): bool
|
||
{
|
||
foreach(array('browse', 'create', 'edit', 'view') as $action)
|
||
{
|
||
if($flow->module == 'feedback' && $action == 'view') $action = 'adminview';
|
||
foreach($fields as $field)
|
||
{
|
||
if($field->field == 'deleted') continue;
|
||
if(($action == 'create' || $action == 'edit') && in_array($field->field, array('id', 'parent', 'createdBy', 'createdDate', 'editedBy', 'editedDate', 'assignedBy', 'assignedDate', 'deleted'))) continue;
|
||
|
||
$layout = new stdclass();
|
||
$layout->module = $flow->module;
|
||
$layout->action = $action;
|
||
$layout->field = $field->field;
|
||
$layout->vision = $this->config->vision;
|
||
$layout->group = $group;
|
||
if(in_array($action, array('view', 'edit', 'adminview'))) $layout->position = 'info';
|
||
$this->dao->insert(TABLE_WORKFLOWLAYOUT)->data($layout)->autoCheck()->exec();
|
||
}
|
||
if($action == 'browse' && !empty($fields))
|
||
{
|
||
$layout->field = 'actions';
|
||
$hasExists = $this->dao->select('*')->from(TABLE_WORKFLOWLAYOUT)->where('module')->eq($layout->module)->andWhere('action')->eq($layout->action)->andWhere('group')->eq($layout->group)->andWhere('field')->eq('actions')->fetch();
|
||
if(!$hasExists) $this->dao->insert(TABLE_WORKFLOWLAYOUT)->data($layout)->autoCheck()->exec();
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Jira对象新增成工作流。
|
||
* Create workflow.
|
||
*
|
||
* @param array $relations
|
||
* @param array $jiraActions
|
||
* @param array $jiraResolutions
|
||
* @param array $jiraPriList
|
||
* @param array $jiraStatusList
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createWorkflow(array $relations, array $jiraActions, array $jiraResolutions, array $jiraPriList, array $jiraStatusList = array()): array
|
||
{
|
||
if($this->config->edition == 'open') return $relations;
|
||
$this->loadModel('workflow');
|
||
$this->loadModel('workflowfield');
|
||
|
||
$issueTypeList = $this->getJiraData($this->session->jiraMethod, 'issuetype');
|
||
$customFields = $this->getJiraCustomField();
|
||
$flowRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jissuetype')->andWhere('BType')->eq('zworkflow')->fetchAll('AID');
|
||
foreach($relations['zentaoObject'] as $jiraCode => $zentaoCode)
|
||
{
|
||
if($zentaoCode != 'add_custom') continue;
|
||
|
||
/* Jira事务类型新增成禅道工作流。 */
|
||
if(empty($flowRelation[$jiraCode]))
|
||
{
|
||
$flow = new stdclass();
|
||
$flow->name = substr(zget($issueTypeList[$jiraCode], 'pname', ''), 0, 30);
|
||
$flow->icon = 'flow';
|
||
$flow->module = 'jira' . $jiraCode;
|
||
$flow->approval = 'disabled';
|
||
$flow->type = 'flow';
|
||
$flow->table = 'zt_flow_' . $flow->module;
|
||
$flow->createdBy = $this->app->user->account;
|
||
$flow->createdDate = helper::now();
|
||
$flow->vision = $this->config->vision;
|
||
$flow->role = 'custom';
|
||
$flow->status = 'wait';
|
||
$flow->belong = 'project';
|
||
$this->dao->insert(TABLE_WORKFLOW)->data($flow)->autoCheck()->exec();
|
||
|
||
$flowID = $this->dao->lastInsertId();
|
||
$this->loadModel('action')->create('workflow', $flowID, 'Created');
|
||
|
||
$flow = $this->workflow->getByID($flowID);
|
||
$this->workflow->createFields($flow);
|
||
$this->workflow->createActions($flow);
|
||
$this->workflow->createLabels($flow);
|
||
|
||
$this->createBuildinField($flow->module, $jiraResolutions, $jiraPriList);
|
||
|
||
$fields = $this->workflowfield->getList($flow->module);
|
||
$this->createDefaultLayout($fields, $flow);
|
||
|
||
/* 将工作流发布到指定位置。 */
|
||
$_POST = array();
|
||
$_POST['navigator'] = 'secondary';
|
||
$_POST['app'] = 'scrum';
|
||
$_POST['positionModule'] = 'settings';
|
||
$_POST['position'] = 'before';
|
||
$_POST['syncRelease'] = 'all';
|
||
$_POST['module'] = $flow->module;
|
||
|
||
if($this->config->systemMode == 'light') $_POST['app'] = 'project';
|
||
if(isset($this->lang->scrum->menu->other['dropMenu']) && $this->config->systemMode != 'light')
|
||
{
|
||
$_POST['positionModule'] = 'other';
|
||
$_POST['dropMenu'] = 'issue';
|
||
}
|
||
$this->workflow->release($flowID);
|
||
|
||
$this->createTmpRelation('jissuetype', $jiraCode, 'zworkflow', $flow->module);
|
||
$relations['zentaoObject'][$jiraCode] = $flow->module;
|
||
}
|
||
else
|
||
{
|
||
$relations['zentaoObject'][$jiraCode] = $flowRelation[$jiraCode]->BID;
|
||
}
|
||
|
||
$customField = zget($customFields, $jiraCode, array());
|
||
foreach($customField as $id => $field)
|
||
{
|
||
$relations["jiraField{$jiraCode}"][] = $id;
|
||
$relations["zentaoField{$jiraCode}"][$id] = 'add_field';
|
||
}
|
||
|
||
$statusList = zget($jiraStatusList, $jiraCode, array());
|
||
foreach($statusList as $id => $status)
|
||
{
|
||
$relations["jiraStatus{$jiraCode}"][] = $id;
|
||
$relations["zentaoStatus{$jiraCode}"][$id] = 'add_flow_status';
|
||
}
|
||
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
$jiraAction = !empty($jiraActions[$jiraCode]['actions']) ? $jiraActions[$jiraCode]['actions'] : array();
|
||
}
|
||
else
|
||
{
|
||
$jiraAction = $jiraActions['actions'];
|
||
}
|
||
|
||
foreach($jiraAction as $id => $action)
|
||
{
|
||
if(!empty($action['name']) && $action['name'] == 'Create') continue;
|
||
$relations["jiraAction{$jiraCode}"][] = $id;
|
||
$relations["zentaoAction{$jiraCode}"][$id] = 'add_action';
|
||
}
|
||
}
|
||
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* Jira字段新增成工作流字段。
|
||
* Create workflow field.
|
||
*
|
||
* @param array $relations
|
||
* @param array $fields
|
||
* @param array $fieldOptions
|
||
* @param array $jiraResolutions
|
||
* @param array $jiraPriList
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createWorkflowField(array $relations, array $fields, array $fieldOptions, array $jiraResolutions, array $jiraPriList): array
|
||
{
|
||
if($this->config->edition == 'open') return $relations;
|
||
|
||
$this->loadModel('workflowfield');
|
||
$jiraFieldControl = $this->config->convert->jiraFieldControl;
|
||
$fieldRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jcustomfield')->fetchGroup('extra', 'AID');
|
||
|
||
/* 创建内置字段。 */
|
||
foreach($relations['zentaoObject'] as $jiraCode => $module) $this->createBuildinField($module, $jiraResolutions, $jiraPriList, true);
|
||
|
||
/* 创建自定义字段。 */
|
||
foreach($relations as $stepKey => $fieldList)
|
||
{
|
||
if(strpos($stepKey, 'zentaoField') === false) continue;
|
||
$jiraCode = str_replace('zentaoField', '', $stepKey);
|
||
$module = $relations['zentaoObject'][$jiraCode];
|
||
|
||
foreach($fieldList as $jiraField => $zentaoField)
|
||
{
|
||
if($zentaoField != 'add_field') continue;
|
||
|
||
if(empty($fieldRelation[$module][$jiraField]))
|
||
{
|
||
$jiraFields = $this->session->jiraMethod == 'api' ? zget($fields, $jiraCode, array()) : $fields;
|
||
$controlCode = !empty($jiraFields[$jiraField]->customfieldtypekey) ? $jiraFields[$jiraField]->customfieldtypekey : 'com.atlassian.jira.plugin.system.customfieldtypes:textfield';
|
||
|
||
$options = array('code' => array(), 'name' => array());
|
||
foreach($fieldOptions as $optionID => $fieldOption)
|
||
{
|
||
if($fieldOption->customfield != $jiraField) continue;
|
||
$options['code'][] = $optionID;
|
||
$options['name'][] = $fieldOption->customvalue;
|
||
}
|
||
|
||
if(empty($jiraFieldControl[$controlCode])) $controlCode = !empty($options['code']) ? 'com.atlassian.jira.plugin.system.customfieldtypes:select' : 'com.atlassian.jira.plugin.system.customfieldtypes:textfield';
|
||
|
||
$field = new stdclass();
|
||
$field->name = substr(zget($jiraFields[$jiraField], 'cfname', ''), 0, 60);
|
||
$field->field = 'jirafield' . str_replace(range(0, 9), range('a', 'z'), uniqid());
|
||
$field->control = $jiraFieldControl[$controlCode]['control'];
|
||
$field->type = $jiraFieldControl[$controlCode]['type'];
|
||
$field->length = $jiraFieldControl[$controlCode]['length'];
|
||
$field->integerDigits = in_array($field->type, $this->config->workflowfield->numberTypes) ? '10' : '';
|
||
$field->decimalDigits = in_array($field->type, $this->config->workflowfield->numberTypes) ? '2' : '';;
|
||
$field->expression = '';
|
||
$field->optionType = (strpos($controlCode, 'userpicker') !== false || strpos($controlCode, 'people') !== false) ? 'user' : 'custom';
|
||
$field->sql = '';
|
||
$field->options = $options;
|
||
$field->default = '';
|
||
$field->placeholder = '';
|
||
$field->module = $module;
|
||
$field->group = '0';
|
||
$field->createdBy = $this->app->user->account;
|
||
$field->createdDate = helper::now();
|
||
|
||
if(strpos(',select,multi-select,radio,checkbox,', ",$field->control,") !== false && $field->optionType == 'custom' && empty($options['code'])) $field->control = 'text';
|
||
$result = $this->workflowfield->create($module, $field, null, true);
|
||
|
||
$relation = $this->createTmpRelation('jcustomfield', $jiraField, 'zworkflowfield', $field->field, $field->module);
|
||
|
||
$fieldRelation[$module][$jiraField] = $relation;
|
||
$relations[$stepKey][$jiraField] = $field->field;
|
||
}
|
||
else
|
||
{
|
||
$relations[$stepKey][$jiraField] = $fieldRelation[$module][$jiraField]->BID;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* Jira状态新增成工作流状态。
|
||
* Create workflow status.
|
||
*
|
||
* @param array $relations
|
||
* @param array $jiraStatusList
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createWorkflowStatus(array $relations, array $jiraStatusList): array
|
||
{
|
||
if($this->config->edition == 'open') return $relations;
|
||
|
||
$this->loadModel('custom');
|
||
$currentLang = $this->app->getClientLang();
|
||
foreach($relations as $stepKey => $statusList)
|
||
{
|
||
if(strpos($stepKey, 'zentaoStatus') === false) continue;
|
||
$jiraCode = str_replace('zentaoStatus', '', $stepKey);
|
||
$module = $relations['zentaoObject'][$jiraCode];
|
||
foreach($statusList as $jiraStatus => $zentaoStatus)
|
||
{
|
||
if($zentaoStatus != 'add_case_status' && $zentaoStatus != 'add_flow_status') continue;
|
||
|
||
$zentaoCode = $jiraStatus;
|
||
if($zentaoStatus == 'add_case_status')
|
||
{
|
||
$items = $this->loadModel('setting')->getItem("module=testcase§ion=statusList");
|
||
if(empty($items))
|
||
{
|
||
$this->loadModel('testcase');
|
||
$items = $this->lang->testcase->statusList;
|
||
foreach($items as $key => $value) $this->custom->setItem("{$currentLang}.testcase.statusList.$key.1", $value);
|
||
}
|
||
$this->custom->setItem("{$currentLang}.testcase.statusList.$zentaoCode.0", zget($jiraStatusList[$jiraCode][$jiraStatus], 'pname', ''));
|
||
}
|
||
elseif($zentaoStatus == 'add_flow_status')
|
||
{
|
||
$fieldOptions = $this->dao->select('options')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($module)->andWhere('field')->eq('status')->fetch('options');
|
||
$fieldOptions = json_decode($fieldOptions);
|
||
if(!$fieldOptions) $fieldOptions = new stdclass();
|
||
$fieldOptions->{$zentaoCode} = zget($jiraStatusList[$jiraCode][$jiraStatus], 'pname', '');
|
||
$this->dao->update(TABLE_WORKFLOWFIELD)->set('options')->eq(json_encode($fieldOptions))->where('module')->eq($module)->andWhere('field')->eq('status')->exec();
|
||
}
|
||
$relations[$stepKey][$jiraStatus] = $zentaoCode;
|
||
}
|
||
}
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* 导入jira状态变更为工作流扩展动作。
|
||
* Process workflow hooks.
|
||
*
|
||
* @param array $jiraAction
|
||
* @param array $jiraStepList
|
||
* @param string $module
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function processWorkflowHooks(array $jiraAction, array $jiraStepList, string $module): array
|
||
{
|
||
if($this->session->jiraMethod != 'api' && empty($jiraAction['results']['unconditional-result']['@attributes']['step'])) return array();
|
||
|
||
$hooks = array();
|
||
$field = new stdclass();
|
||
$field->field = 'status';
|
||
$field->paramType = 'custom';
|
||
|
||
if($this->session->jiraMethod == 'api')
|
||
{
|
||
$field->param = $jiraAction['to'];
|
||
}
|
||
else
|
||
{
|
||
$jiraStep = $jiraAction['results']['unconditional-result']['@attributes']['step'];
|
||
$field->param = is_array($jiraStepList[$jiraStep]) ? $jiraStepList[$jiraStep][0] : $jiraStepList[$jiraStep];
|
||
}
|
||
|
||
$where = new stdclass();
|
||
$where->field = 'id';
|
||
$where->logicalOperator = 'and';
|
||
$where->operator = 'equal';
|
||
$where->paramType = 'record';
|
||
$where->param = 'id';
|
||
|
||
$hook = new stdclass();
|
||
$hook->action = 'update';
|
||
$hook->table = $module;
|
||
$hook->conditionType = 'data';
|
||
$hook->sqlResult = 'empty';
|
||
$hook->message = '';
|
||
$hook->comment = '';
|
||
$hook->fields = array($field);
|
||
$hook->conditions = array();
|
||
$hook->wheres = array($where);
|
||
$hook->sqlVars = array();
|
||
$hook->formVars = array();
|
||
$hook->recordVars = array($where->param);
|
||
$hook->formulaVars = array();
|
||
|
||
list($sql) = $this->loadModel('workflowhook')->check($hook);
|
||
$hook->sql = $sql;
|
||
|
||
$hooks[] = $hook;
|
||
return $hooks;
|
||
}
|
||
|
||
/**
|
||
* Jira动作新增成工作流动作。
|
||
* Create workflow action.
|
||
*
|
||
* @param array $relations
|
||
* @param array $jiraActions
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createWorkflowAction(array $relations, array $jiraActions): array
|
||
{
|
||
if($this->config->edition == 'open') return $relations;
|
||
|
||
$this->loadModel('workflowaction');
|
||
$this->loadModel('workflowhook');
|
||
$currentLang = $this->app->getClientLang();
|
||
$flowActionRelation = $this->dao->dbh($this->dbh)->select('*')->from(JIRA_TMPRELATION)->where('AType')->eq('jflowaction')->fetchGroup('extra', 'AID');
|
||
foreach($relations as $stepKey => $actionList)
|
||
{
|
||
if(strpos($stepKey, 'zentaoAction') === false) continue;
|
||
$jiraCode = str_replace('zentaoAction', '', $stepKey);
|
||
$module = $relations['zentaoObject'][$jiraCode];
|
||
foreach($actionList as $jiraAction => $zentaoAction)
|
||
{
|
||
if($zentaoAction != 'add_action') continue;
|
||
|
||
if(empty($flowActionRelation[$module][$jiraAction]))
|
||
{
|
||
$actionGroup = $this->session->jiraMethod == 'api' ? zget($jiraActions, $jiraCode, array()) : $jiraActions;
|
||
if(!empty($actionGroup['actions'])) $hooks = $this->processWorkflowHooks($actionGroup['actions'][$jiraAction], !empty($actionGroup['steps']) ? $actionGroup['steps'] : array(), $module);
|
||
|
||
$action = new stdclass();
|
||
$action->name = $actionGroup['actions'][$jiraAction]['name'];
|
||
$action->action = $module . 'action' . $jiraAction;
|
||
$action->type = 'single';
|
||
$action->batchMode = 'same';
|
||
$action->open = 'none';
|
||
$action->position = 'browseandview';
|
||
$action->show = 'dropdownlist';
|
||
$action->desc = '';
|
||
$action->module = $module;
|
||
$action->method = 'operate';
|
||
$action->extensionType = 'override';
|
||
$action->conditions = '[]';
|
||
$action->hooks = helper::jsonEncode($hooks);
|
||
$action->status = 'disable';
|
||
$action->createdBy = $this->app->user->account;
|
||
$action->createdDate = helper::now();
|
||
$action->vision = $this->config->vision;
|
||
$action->group = '0';
|
||
$action->order = '999';
|
||
|
||
$this->dao->insert(TABLE_WORKFLOWACTION)->data($action)->exec();
|
||
|
||
$actionID = $this->dao->lastInsertId();
|
||
$action->id = $actionID;
|
||
$this->workflowaction->createFields($action);
|
||
|
||
$relation = $this->createTmpRelation('jflowaction', $jiraAction, 'zworkflowaction', $action->action, $module);
|
||
|
||
$flowActionRelation[$module][$jiraAction] = $relation;
|
||
$relations[$stepKey][$jiraAction] = $action->action;
|
||
|
||
}
|
||
else
|
||
{
|
||
$relations[$stepKey][$jiraAction] = $flowActionRelation[$module][$jiraAction]->BID;
|
||
}
|
||
}
|
||
}
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* 为项目创建工作流分组.
|
||
* Create group.
|
||
*
|
||
* @param string $type
|
||
* @param string $name
|
||
* @param array $objectList
|
||
* @param int $jiraProjectID
|
||
* @param int $zentaoProjectID
|
||
* @param array $productRelations
|
||
* @param array $projectFieldList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function createGroup(string $type, string $name, array $objectList, int $jiraProjectID, int $zentaoProjectID, array $productRelations, array $projectFieldList): bool
|
||
{
|
||
$group = new stdclass();
|
||
$group->name = substr($name, 0, 80) . $this->lang->workflowgroup->template;
|
||
$group->projectModel = $type == 'project' ? 'scrum' : '';
|
||
$group->projectType = $type == 'project' ? 'product' : '';
|
||
$group->type = $type;
|
||
$group->status = 'normal';
|
||
$group->createdBy = $this->app->user->account;
|
||
$group->createdDate = helper::now();
|
||
$group->disabledModules = '';
|
||
$group->exclusive = '1';
|
||
|
||
$this->dao->dbh($this->dbh);
|
||
$groupID = $this->workflowgroup->create($group);
|
||
$this->action->create('workflowgroup', $groupID, 'created');
|
||
|
||
$group->id = $groupID;
|
||
$flows = $this->workflowgroup->getFlows($group);
|
||
$actionList = array('browse', 'create', 'edit', 'view', 'adminview');
|
||
foreach($flows as $flow)
|
||
{
|
||
/* 只有该项目或者产品启用的事务类型才激活模板。 */
|
||
if(in_array($flow->module, $objectList))
|
||
{
|
||
$this->workflowgroup->setExclusive($flow->id, $groupID);
|
||
|
||
$this->dao->dbh($this->dbh)->update(TABLE_WORKFLOWACTION)->set('extensionType')->eq('extend')->where('module')->eq($flow->module)->andWhere('`group`')->eq($groupID)->andWhere('action')->in($actionList)->exec();
|
||
|
||
/* 内置字段添加到布局。 */
|
||
$fields = array();
|
||
foreach($this->lang->convert->jira->buildinFields as $fieldCode => $buildinField)
|
||
{
|
||
if(isset($buildinField['buildin']) && $buildinField['buildin'] == false) continue;
|
||
$field = new stdclass();
|
||
$field->field = str_replace(range(0, 9), range('a', 'z'), $flow->module . $fieldCode);
|
||
$fields[$fieldCode] = $field;
|
||
}
|
||
if($flow->buildin) $this->createDefaultLayout($fields, $flow, $groupID);
|
||
|
||
/* 扩展字段添加到布局。 */
|
||
if(empty($projectFieldList[$jiraProjectID][$flow->module])) continue;
|
||
$this->createDefaultLayout($projectFieldList[$jiraProjectID][$flow->module], $flow, $groupID);
|
||
}
|
||
elseif(!$flow->buildin)
|
||
{
|
||
$disabledModules[] = $flow->module;
|
||
}
|
||
}
|
||
|
||
if(!empty($disabledModules)) $this->dao->update(TABLE_WORKFLOWGROUP)->set('disabledModules')->eq(implode(',', array_unique($disabledModules)))->where('id')->eq($groupID)->exec();
|
||
if($type == 'product') $this->dao->dbh($this->dbh)->update(TABLE_PRODUCT)->set('workflowGroup')->eq($groupID)->where('id')->eq($productRelations[$jiraProjectID])->exec();
|
||
if($type == 'project') $this->dao->dbh($this->dbh)->update(TABLE_PROJECT)->set('workflowGroup')->eq($groupID)->where('id')->eq($zentaoProjectID)->exec();
|
||
$this->createTmpRelation('jproject', $jiraProjectID, 'zworkflowgroup', $groupID);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 创建工作部流程模版。
|
||
* Create workflow group.
|
||
*
|
||
* @param array $relations
|
||
* @param array $projectRelations
|
||
* @param array $productRelations
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createWorkflowGroup(array $relations, array $projectRelations, array $productRelations): array
|
||
{
|
||
if($this->config->edition == 'open') return $relations;
|
||
|
||
$this->loadModel('action');
|
||
$this->loadModel('workflowgroup');
|
||
$groupRelations = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jproject')->andWhere('BType')->eq('zworkflowgroup')->fetchPairs();
|
||
$projectList = $this->getJiraData($this->session->jiraMethod, 'project');
|
||
$projectIssueTypeList = $this->getIssueTypeList($relations);
|
||
$projectFieldList = $this->getJiraFieldGroupByProject($relations);
|
||
foreach($projectRelations as $jiraProjectID => $zentaoProjectID)
|
||
{
|
||
if(!empty($groupRelations[$jiraProjectID])) continue;
|
||
if(empty($projectList[$jiraProjectID])) continue;
|
||
|
||
$project = $projectList[$jiraProjectID];
|
||
$issueTypeList = !empty($projectIssueTypeList[$jiraProjectID]) ? $projectIssueTypeList[$jiraProjectID] : array();
|
||
|
||
$this->createGroup('project', $project->pname, $issueTypeList, (int)$jiraProjectID, (int)$zentaoProjectID, $productRelations, $projectFieldList);
|
||
$this->createGroup('product', $project->pname, $issueTypeList, (int)$jiraProjectID, (int)$zentaoProjectID, $productRelations, $projectFieldList);
|
||
}
|
||
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* Jira解决方案新增成禅道关闭原因或解决方案。
|
||
* Create zentao resolution.
|
||
*
|
||
* @param array $relations
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function createResolution(array $relations): array
|
||
{
|
||
$this->loadModel('custom');
|
||
$currentLang = $this->app->getClientLang();
|
||
$jiraResolutions = $this->getJiraData($this->session->jiraMethod, 'resolution');
|
||
foreach($relations as $stepKey => $resolutionList)
|
||
{
|
||
if(strpos($stepKey, 'zentaoResolution') === false && strpos($stepKey, 'zentaoReason') === false) continue;
|
||
$jiraCode = strpos($stepKey, 'zentaoResolution') !== false ? str_replace('zentaoResolution', '', $stepKey) : str_replace('zentaoReason', '', $stepKey);
|
||
$module = $relations['zentaoObject'][$jiraCode];
|
||
foreach($resolutionList as $jiraResolution => $zentaoResolution)
|
||
{
|
||
if($zentaoResolution != 'add_resolution' && $zentaoResolution != 'add_reason' && $zentaoResolution != 'add_closedReason') continue;
|
||
|
||
$zentaoCode = 'jira' . $jiraResolution;
|
||
if($zentaoResolution == 'add_resolution') $section = 'resolutionList';
|
||
if($zentaoResolution == 'add_reason') $section = 'reasonList';
|
||
if($zentaoResolution == 'add_closedReason') $section = 'closedReasonList';
|
||
|
||
$items = $this->loadModel('setting')->getItem("module={$module}§ion={$section}");
|
||
if(empty($items))
|
||
{
|
||
$this->app->loadLang($module);
|
||
$items = $this->lang->story->reasonList;
|
||
if($module == 'bug') $items = $this->lang->bug->resolutionList;
|
||
if($module == 'task') $items = $this->lang->task->reasonList;
|
||
if($module == 'feedback' || $module == 'ticket') $items = $this->lang->{$module}->closedReasonList;
|
||
foreach($items as $key => $value) $this->custom->setItem("{$currentLang}.{$module}.{$section}.{$key}.1", $value);
|
||
}
|
||
$this->custom->setItem("{$currentLang}.{$module}.{$section}.{$zentaoCode}.0", zget($jiraResolutions[$jiraResolution], 'pname', ''));
|
||
|
||
$relations[$stepKey][$jiraResolution] = $zentaoCode;
|
||
}
|
||
}
|
||
return $relations;
|
||
}
|
||
|
||
/**
|
||
* 更新子需求。
|
||
* Update sub story.
|
||
*
|
||
* @param array $storyLink
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function updateSubStory(array $storyLink, array $issueList): bool
|
||
{
|
||
foreach($storyLink as $source => $dest)
|
||
{
|
||
if(!isset($issueList[$source])) continue;
|
||
|
||
$parentType = zget($issueList[$source], 'BType', '');
|
||
if(!in_array($parentType, array('zepic', 'zrequirement', 'zstory'))) continue;
|
||
|
||
$parentID = zget($issueList[$source], 'BID', '');
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORY)
|
||
->set('parent')->eq('0')
|
||
->set('isParent')->eq('1')
|
||
->set('grade')->eq('1')
|
||
->set('root')->eq($parentID)
|
||
->set('path')->eq(",{$parentID},")
|
||
->where('id')->eq($parentID)
|
||
->exec();
|
||
|
||
foreach($dest as $childID)
|
||
{
|
||
if(!isset($issueList[$childID])) continue;
|
||
|
||
$childType = zget($issueList[$childID], 'BType', '');
|
||
if(!in_array($childType, array('zepic', 'zrequirement', 'zstory'))) continue;
|
||
|
||
$grade = $parentType == $childType ? 2 : 1;
|
||
|
||
$childrenID = zget($issueList[$childID], 'BID', '');
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORY)
|
||
->set('parent')->eq($parentID)
|
||
->set('isParent')->eq('0')
|
||
->set('grade')->eq($grade)
|
||
->set('root')->eq($parentID)
|
||
->set('path')->eq(",{$parentID},{$childrenID},")
|
||
->where('id')->eq($childrenID)
|
||
->exec();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新子任务。
|
||
* Update sub task.
|
||
*
|
||
* @param array $taskLink
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function updateSubTask(array $taskLink, array $issueList): bool
|
||
{
|
||
foreach($taskLink as $source => $dest)
|
||
{
|
||
if(!isset($issueList[$source])) continue;
|
||
if(zget($issueList[$source], 'BType', '') != 'ztask') continue;
|
||
|
||
$parentID = zget($issueList[$source], 'BID', '');
|
||
$this->dao->dbh($this->dbh)->update(TABLE_TASK)
|
||
->set('parent')->eq('0')
|
||
->set('isParent')->eq('1')
|
||
->set('path')->eq(",{$parentID},")
|
||
->where('id')->eq($parentID)
|
||
->exec();
|
||
|
||
$parentExecution = $this->dao->select('execution')->from(TABLE_TASK)->where('id')->eq($parentID)->fetch('execution');
|
||
foreach($dest as $childID)
|
||
{
|
||
if(!isset($issueList[$childID])) continue;
|
||
if(zget($issueList[$childID], 'BType', '') != 'ztask') continue;
|
||
|
||
$childrenID = zget($issueList[$childID], 'BID', '');
|
||
$this->dao->dbh($this->dbh)->update(TABLE_TASK)
|
||
->set('parent')->eq($parentID)
|
||
->set('execution')->eq($parentExecution) // 子任务的执行随父任务
|
||
->set('isParent')->eq('0')
|
||
->set('path')->eq(",{$parentID},{$childrenID},")
|
||
->where('id')->eq($childrenID)
|
||
->exec();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新重复的需求和bug。
|
||
* Update duplicate story and bug.
|
||
*
|
||
* @param array $duplicateLink
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function updateDuplicateStoryAndBug(array $duplicateLink, array $issueList): bool
|
||
{
|
||
foreach($duplicateLink as $source => $dest)
|
||
{
|
||
$sourceObjectType = zget($issueList[$source], 'BType', '');
|
||
$sourceObjectID = zget($issueList[$source], 'BID', '');
|
||
$destObjectType = zget($issueList[$dest], 'BType', '');
|
||
$destObjectID = zget($issueList[$dest], 'BID', '');
|
||
|
||
if(empty($sourceObjectType) || empty($destObjectType)) continue;
|
||
if($sourceObjectType != 'zstory' && $sourceObjectType != 'zbug') continue;
|
||
if($sourceObjectType != $destObjectType) continue;
|
||
|
||
if($sourceObjectType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORY)->set('duplicateStory')->eq($destObjectID)->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
else
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set('duplicateBug')->eq($destObjectID)->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新相关对象数据。
|
||
* Update relates object.
|
||
*
|
||
* @param array $relatesLink
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function updateRelatesObject(array $relatesLink, array $issueList): bool
|
||
{
|
||
foreach($relatesLink as $source => $dest)
|
||
{
|
||
$sourceObjectType = zget($issueList[$source], 'BType', '');
|
||
$sourceObjectID = zget($issueList[$source], 'BID', '');
|
||
$destObjectType = zget($issueList[$dest], 'BType', '');
|
||
$destObjectID = zget($issueList[$dest], 'BID', '');
|
||
|
||
if(empty($sourceObjectType) || empty($destObjectType)) continue;
|
||
|
||
if($sourceObjectType == 'ztask' && $destObjectType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_TASK)->set('story')->eq($destObjectID)->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
elseif($sourceObjectType == 'zstory' && $destObjectType == 'ztask')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_TASK)->set('story')->eq($sourceObjectID)->where('id')->eq($destObjectID)->exec();
|
||
}
|
||
elseif($sourceObjectType == 'zstory' && $destObjectType == 'zbug')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set('story')->eq($sourceObjectID)->set('storyVersion')->eq(1)->where('id')->eq($destObjectID)->exec();
|
||
}
|
||
elseif($sourceObjectType == 'zbug' && $destObjectType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set('story')->eq($destObjectID)->set('storyVersion')->eq(1)->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
elseif($sourceObjectType == 'zstory' && $destObjectType == 'zstory')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_STORY)->set("linkStories=concat(linkStories, ',{$destObjectID}')")->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
elseif($sourceObjectType == 'zbug' && $destObjectType == 'zbug')
|
||
{
|
||
$this->dao->dbh($this->dbh)->update(TABLE_BUG)->set("relatedBug=concat(relatedBug, ',{$destObjectID}')")->where('id')->eq($sourceObjectID)->exec();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 处理jira事务的正文内容。
|
||
* Process jira issue content.
|
||
*
|
||
* @param array $issueList
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function processJiraIssueContent(array $issueList): bool
|
||
{
|
||
$issueTypeList = array();
|
||
foreach($issueList as $relation) $issueTypeList[$relation->BType] = substr($relation->BType, 1);
|
||
|
||
$fileGroup = array();
|
||
$fileList = $this->dao->dbh($this->dbh)->select('*')->from(TABLE_FILE)->where('objectType')->in($issueTypeList)->fetchAll();
|
||
foreach($fileList as $file) $fileGroup[$file->objectType][$file->objectID][$file->title] = $file;
|
||
|
||
foreach($issueList as $relation)
|
||
{
|
||
$objectType = substr($relation->BType, 1);
|
||
$objectID = $relation->BID;
|
||
if(empty($fileGroup[$objectType][$objectID])) continue;
|
||
|
||
$fileList = $fileGroup[$objectType][$objectID];
|
||
if($objectType == 'testcase') continue;
|
||
if($objectType == 'requirement' || $objectType == 'story' || $objectType == 'epic')
|
||
{
|
||
$content = $this->dao->dbh($this->dbh)->select('`spec`')->from(TABLE_STORYSPEC)->where('`story`')->eq($objectID)->fetch('spec');
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_STORYSPEC)->set('`spec`')->eq($content)->where('`story`')->eq($objectID)->exec();
|
||
}
|
||
elseif($objectType == 'bug')
|
||
{
|
||
$content = $this->dao->dbh($this->dbh)->select('`steps`')->from(TABLE_BUG)->where('id')->eq($objectID)->fetch('steps');
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_BUG)->set('`steps`')->eq($content)->where('id')->eq($objectID)->exec();
|
||
}
|
||
elseif($objectType == 'task')
|
||
{
|
||
$content = $this->dao->dbh($this->dbh)->select('`desc`')->from(TABLE_TASK)->where('id')->eq($objectID)->fetch('desc');
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_TASK)->set('`desc`')->eq($content)->where('id')->eq($objectID)->exec();
|
||
}
|
||
elseif($objectType == 'ticket')
|
||
{
|
||
$content = $this->dao->dbh($this->dbh)->select('`desc`')->from(TABLE_TICKET)->where('id')->eq($objectID)->fetch('desc');
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_TICKET)->set('`desc`')->eq($content)->where('id')->eq($objectID)->exec();
|
||
}
|
||
elseif($objectType == 'feedback')
|
||
{
|
||
$content = $this->dao->dbh($this->dbh)->select('`desc`')->from(TABLE_FEEDBACK)->where('id')->eq($objectID)->fetch('desc');
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_FEEDBACK)->set('`desc`')->eq($content)->where('id')->eq($objectID)->exec();
|
||
}
|
||
else
|
||
{
|
||
$field = str_replace(range(0, 9), range('a', 'z'), $objectType . 'desc');
|
||
$content = $this->dao->dbh($this->dbh)->select("`{$field}`")->from("`zt_flow_{$objectType}`")->where('id')->eq($objectID)->fetch($field);
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update("`zt_flow_{$objectType}`")->set("`{$field}`")->eq($content)->where('id')->eq($objectID)->exec();
|
||
}
|
||
|
||
$actions = $this->dao->dbh($this->dbh)->select('id,comment')->from(TABLE_ACTION)->where('objectType')->eq($objectType)->andWhere('objectID')->eq($objectID)->fetchAll();
|
||
foreach($actions as $action)
|
||
{
|
||
$content = $action->comment;
|
||
$content = $this->processJiraContent($content, $fileList);
|
||
if($content) $this->dao->dbh($this->dbh)->update(TABLE_ACTION)->set('`comment`')->eq($content)->where('`id`')->eq($action->id)->exec();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 处理jira编辑器中的内容。
|
||
* Process jira content.
|
||
*
|
||
* @param string $content
|
||
* @param array $fileList
|
||
* @access protected
|
||
* @return string
|
||
*/
|
||
protected function processJiraContent(string $content, array $fileList): string
|
||
{
|
||
if(empty($content)) return '';
|
||
|
||
/* 旧版jira匹配规则。 */
|
||
preg_match_all('/!(.*?)!/', $content, $matches);
|
||
if(!empty($matches[0]))
|
||
{
|
||
foreach($matches[1] as $key => $fileName)
|
||
{
|
||
$fileName = substr($fileName, 0, strpos($fileName, '|'));
|
||
if(empty($fileList[$fileName])) continue;
|
||
|
||
$file = $fileList[$fileName];
|
||
$url = helper::createLink('file', 'read', "t={$file->extension}&fileID={$file->id}");
|
||
$content = str_replace($matches[0][$key], "<img src=\"{{$file->id}.{$file->extension}}\" alt=\"{$url}\"/>", $content);
|
||
}
|
||
}
|
||
|
||
/* 新版jira匹配规则。 */
|
||
preg_match_all('/<img[^>]*alt="([^"]*)"[^>]*>/i', $content, $matches);
|
||
if(!empty($matches[0]))
|
||
{
|
||
foreach($matches[1] as $key => $fileName)
|
||
{
|
||
if(empty($fileList[$fileName])) continue;
|
||
|
||
$file = $fileList[$fileName];
|
||
$url = helper::createLink('file', 'read', "t={$file->extension}&fileID={$file->id}");
|
||
$content = str_replace($matches[0][$key], "<img src=\"{{$file->id}.{$file->extension}}\" alt=\"{$url}\"/>", $content);
|
||
}
|
||
}
|
||
|
||
return $content;
|
||
}
|
||
}
|