diff --git a/module/convert/control.php b/module/convert/control.php index 0db409d0fa..af203828e8 100644 --- a/module/convert/control.php +++ b/module/convert/control.php @@ -263,50 +263,60 @@ class convert extends control /** * Import jira notice. * - * @param string $type db|file + * @param string $mehotd db|file * @access public * @return void */ - public function importNotice($type = 'db') + public function importNotice($method = 'db') { - if($_POST) + if($this->server->request_method == 'POST') { - $dbName = $this->post->dbName; - if(!$dbName) + if($method == 'db') { - $response['result'] = 'fail'; - $response['message'] = $this->lang->convert->jira->dbNameEmpty; - return print($this->send($response)); + $dbName = $this->post->dbName; + if(!$dbName) + { + $response['result'] = 'fail'; + $response['message'] = $this->lang->convert->jira->dbNameEmpty; + return print($this->send($response)); + } + + if(!$this->convert->dbExists($dbName)) + { + $response['result'] = 'fail'; + $response['message'] = $this->lang->convert->jira->invalidDB; + return print($this->send($response)); + } + + $this->session->set('jiraDB', $dbName); + $link = $this->createLink('convert', 'mapJira2Zentao', "method=db&dbName={$this->post->dbName}"); + } + else + { + $this->convert->splitFile(); + $link = $this->createLink('convert', 'mapJira2Zentao', "method=file"); } - if(!$this->convert->dbExists($dbName)) - { - $response['result'] = 'fail'; - $response['message'] = $this->lang->convert->jira->invalidDB; - return print($this->send($response)); - } - - $this->session->set('jiraDB', $dbName); $response['result'] = 'success'; $response['message'] = $this->lang->saveSuccess; - $response['locate'] = $this->createLink('convert', 'mapJira2Zentao', "type=db&dbName={$this->post->dbName}"); + $response['locate'] = $link; return print($this->send($response)); } - $this->view->title = $this->lang->convert->jira->method; - $this->view->type = $type; + $this->view->title = $this->lang->convert->jira->method; + $this->view->method = $method; $this->display(); } /** * Map jira objects to zentao. * - * @param string $type db|file + * @param string $method db|file * @param string $dbName * @access public * @return void */ - public function mapJira2Zentao($type = 'db', $dbName = '') + public function mapJira2Zentao($method = 'db', $dbName = '') { $this->app->loadLang('story'); $this->app->loadLang('bug'); @@ -318,36 +328,44 @@ class convert extends control $response['result'] = 'success'; $response['message'] = $this->lang->saveSuccess; - $response['locate'] = $this->createLink('convert', 'initJiraUser', "type=$type"); + $response['locate'] = $this->createLink('convert', 'initJiraUser', "method=$method"); return print($this->send($response)); } - if($type == 'db') + if($method == 'db') { $dbh = $this->convert->connectDB($dbName); - $issueTypePairs = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_ISSUETYPE)->fetchPairs(); - $linkTypePairs = $this->dao->dbh($dbh)->select('ID,LINKNAME')->from(JIRA_ISSUELINKTYPE)->fetchPairs(); - $resolutions = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_RESOLUTION)->fetchPairs(); - $statusList = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_ISSUESTATUS)->fetchPairs(); + $issueTypeList = $this->dao->dbh($dbh)->select('*')->from(JIRA_ISSUETYPE)->fetchAll('ID'); + $linkTypeList = $this->dao->dbh($dbh)->select('*, LINKNAME as linkname')->from(JIRA_ISSUELINKTYPE)->fetchAll('ID'); + $resolutionList = $this->dao->dbh($dbh)->select('*')->from(JIRA_RESOLUTION)->fetchAll('ID'); + $statusList = $this->dao->dbh($dbh)->select('*')->from(JIRA_ISSUESTATUS)->fetchAll('ID'); + } + else + { + $issueTypeList = $this->convert->getJiraDataFromFile('issuetype'); + $linkTypeList = $this->convert->getJiraDataFromFile('issuelinktype'); + $resolutionList = $this->convert->getJiraDataFromFile('resolution'); + $statusList = $this->convert->getJiraDataFromFile('status'); } $this->view->title = $this->lang->convert->jira->mapJira2Zentao; - $this->view->issueTypePairs = $issueTypePairs; - $this->view->linkTypePairs = $linkTypePairs; - $this->view->resolutions = $resolutions; + $this->view->issueTypeList = $issueTypeList; + $this->view->linkTypeList = $linkTypeList; + $this->view->resolutionList = $resolutionList; $this->view->statusList = $statusList; + $this->view->method = $method; $this->display(); } /** * Init jira user. * - * @param string $type db|file + * @param string $method db|file * @access public * @return void */ - public function initJiraUser($type = 'db') + public function initJiraUser($method = 'db') { $this->app->loadLang('user'); @@ -373,7 +391,7 @@ class convert extends control $response['result'] = 'success'; $response['message'] = $this->lang->saveSuccess; - $response['locate'] = $this->createLink('convert', 'importJira', "type=$type"); + $response['locate'] = $this->createLink('convert', 'importJira', "method=$method"); return print($this->send($response)); } @@ -395,32 +413,25 @@ class convert extends control { set_time_limit(0); - if($method = 'db') - { - if(helper::isAjaxRequest()) + if(helper::isAjaxRequest()) + { + $result = $method == 'db' ? $this->convert->importJiraFromDB($type, $lastID, $createTable) : $this->convert->importJiraFromFile($type, $lastID, $createTable); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + if(isset($result['finished']) and $result['finished']) { - $result = $this->convert->importJiraFromDB($type, $lastID, $createTable); - if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - if(isset($result['finished']) and $result['finished']) - { - return print $this->send(array('result' => 'finished', 'message' => $this->lang->convert->jira->importSuccessfully)); - } - else - { - $type = zget($this->lang->convert->jira->objectList, $result['type'], $result['type']); + return print $this->send(array('result' => 'finished', 'message' => $this->lang->convert->jira->importSuccessfully)); + } + else + { + $type = zget($this->lang->convert->jira->objectList, $result['type'], $result['type']); - $response['result'] = 'unfinished'; - $response['message'] = sprintf($this->lang->convert->jira->importResult, $type, $type, $result['count']); - $response['type'] = $type; - $response['count'] = $result['count']; - $response['next'] = inlink('importJira', "method=$method&type={$result['type']}&lastID={$result['lastID']}"); - return print $this->send($response); - } - } - } - else - { - //$this->convert->importJiraFromFile(); + $response['result'] = 'unfinished'; + $response['message'] = sprintf($this->lang->convert->jira->importResult, $type, $type, $result['count']); + $response['type'] = $type; + $response['count'] = $result['count']; + $response['next'] = inlink('importJira', "method=$method&type={$result['type']}&lastID={$result['lastID']}"); + return print $this->send($response); + } } $this->view->type = $type; diff --git a/module/convert/lang/zh-cn.php b/module/convert/lang/zh-cn.php index d3146b2994..f487f0c2ad 100644 --- a/module/convert/lang/zh-cn.php +++ b/module/convert/lang/zh-cn.php @@ -149,6 +149,7 @@ $lang->convert->jira->passwordDifferent = '两次密码不一致!'; $lang->convert->jira->passwordEmpty = '密码不能为空!'; $lang->convert->jira->importSuccessfully = 'Jira导入完成!'; $lang->convert->jira->importResult = "导入 %s 数据, 已处理 %s 条记录;"; +$lang->convert->jira->importing = '数据导入中,请不要切换其它页面'; $lang->convert->jira->zentaoObjectList['task'] = '任务'; $lang->convert->jira->zentaoObjectList['requirement'] = '用户需求'; @@ -164,6 +165,7 @@ $lang->convert->jira->importSteps['db'][1] = '备份禅道数据库,备份Ji $lang->convert->jira->importSteps['db'][2] = '导入数据时使用禅道会给服务器造成性能压力,请尽量保证导入数据时无人使用禅道。'; $lang->convert->jira->importSteps['db'][3] = '将Jira数据库导入到禅道使用的Mysql中,名字和禅道数据库区别开来。'; $lang->convert->jira->importSteps['db'][4] = "将Jira附件目录 attachments 放到 %s 下,确保禅道服务器磁盘空间足够。"; + $lang->convert->jira->importSteps['db'][5] = "上述步骤完成后,请输入Jira数据库名字进行下一步。"; $lang->convert->jira->importSteps['file'][1] = '备份禅道数据库,备份Jira数据库。'; $lang->convert->jira->importSteps['file'][2] = '导入数据时使用禅道会给服务器造成性能压力,请尽量保证导入数据时无人使用禅道。'; diff --git a/module/convert/model.php b/module/convert/model.php index e36c70652d..e6d62be413 100644 --- a/module/convert/model.php +++ b/module/convert/model.php @@ -95,13 +95,15 @@ class convertModel extends model } /** - * Get jira data. + * Get jira data from db. * - * @param int $module + * @param string $module + * @param int $lastID + * @param int $limit * @access public * @return void */ - public function getJiraData($module = '', $lastID = 0, $limit = 0) + public function getJiraDataFromDB($module = '', $lastID = 0, $limit = 0) { $dataList = array(); if($module == 'user') @@ -165,6 +167,104 @@ class convertModel extends model return $dataList; } + public function getJiraDataFromFile($module, $lastID = 0, $limit = 0) + { + $xmlContent = file_get_contents($this->app->getTmpRoot() . 'jirafile/' . $module . '.xml'); + $xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent); + $parsedXML = simplexml_load_string($xmlContent, SimpleXMLElement::class, LIBXML_NOCDATA); + + $dataList = array(); + $parsedXML = $this->object2Array($parsedXML); + foreach($parsedXML as $key => $xmlArray) + { + if(strtolower($key) != strtolower($module)) continue; + foreach($xmlArray as $key => $attributes) + { + if(isset($attributes['description'])) + { + $desc = $attributes['description']; + unset($attributes['description']); + } + else + { + $desc = ''; + } + + foreach($attributes as $value) + { + if(!empty($desc)) $value['description'] = $desc; + $dataList[$value['id']] = $value; + } + } + } + + if($limit) + { + $dataList = array_slice($dataList, $lastID, $limit, true); + if(empty($dataList)) return array(); + } + + if($module == 'user') + { + foreach($dataList as $key => $data) + { + $user = new stdclass(); + $user->account = $data['lowerUserName']; + $user->realname = $data['lowerDisplayName']; + $user->email = $data['emailAddress']; + $user->join = $data['createdDate']; + $dataList[$key] = $user; + } + } + elseif($module == 'project') + { + foreach($dataList as $key => $data) + { + $project = new stdclass(); + $project->ID = $data['id']; + $project->pname = $data['name']; + $project->pkey = $data['key']; + $project->oldKey = $data['originalkey']; + $project->DESCRIPTION = $data['description']; + $project->LEAD = $data['lead']; + + $dataList[$key] = $project; + } + } + elseif($module == 'issue') + { + } + elseif($module == 'build') + { + } + elseif($module == 'issuelink') + { + } + elseif($module == 'action') + { + } + elseif($module == 'file') + { + } + + return $dataList; + } + + public function object2Array($parsedXML) + { + if(is_object($parsedXML)) + { + $parsedXML = (array)$parsedXML; + } + + if(is_array($parsedXML)) + { + foreach($parsedXML as $key => $value) $parsedXML[$key] = $this->object2Array($value); + } + + return $parsedXML; + } + /** * Import jira from db. * @@ -190,7 +290,7 @@ class convertModel extends model while(true) { - $dataList = $this->getJiraData($module, $lastID, $limit); + $dataList = $this->getJiraDataFromDB($module, $lastID, $limit); if(empty($dataList)) { @@ -210,17 +310,56 @@ class convertModel extends model } } - /* Set project min start date. */ - $executionProject = $this->dao->dbh($this->dbh)->select('id,project')->from(TABLE_PROJECT)->where('type')->eq('sprint')->andWhere('project')->ne(0)->fetchPairs(); - $minOpenedDateList = $this->dao->dbh($this->dbh)->select('id,execution,min(openedDate) as minOpenedDate')->from(TABLE_TASK)->where('execution')->in(array_keys($executionProject))->groupBy('execution')->fetchPairs('execution', 'minOpenedDate'); + $this->afterExec(); + return array('finished' => true); + } - foreach($minOpenedDateList as $executionID => $minOpenedDate) + /** + * Import jira from file. + * + * @param string $type + * @param int $lastID + * @param bool $createTable + * @access public + * @return void + */ + public function importJiraFromFile($type = '', $lastID = 0, $createTable = false) + { + if($createTable) $this->createTmpTable4Jira(); + + $limit = 1000; + $nextObject = false; + if(empty($type)) $type = key($this->lang->convert->jira->objectList); + + foreach($this->lang->convert->jira->objectList as $module => $moduleName) { - $projectID = $executionProject[$executionID]; - $this->dao->update(TABLE_PROJECT)->set('begin')->eq($minOpenedDate)->where('id')->eq($projectID)->orWhere('id')->eq($executionID)->exec(); + if($module != $type and !$nextObject) continue; + if($module == $type) $nextObject = true; + + while(true) + { + $dataList = $this->getJiraDataFromFile($module, $lastID, $limit); + + if(empty($dataList)) + { + $lastID = 0; + break; + } + + if($module == 'user') $this->importJiraUser($dataList); + if($module == 'project') $this->importJiraProject($dataList); + if($module == 'issue') $this->importJiraIssue($dataList); + if($module == 'build') $this->importJiraBuild($dataList); + if($module == 'issuelink') $this->importJiraIssueLink($dataList); + if($module == 'action') $this->importJiraAction($dataList); + if($module == 'file') $this->importJiraFile($dataList); + + $offset = $lastID + $limit; + return array('type' => $module, 'count' => count($dataList), 'lastID' => $offset); + } } - $this->dbh->exec("DROP TABLE" . JIRA_TMPRELATION); + $this->afterExec(); return array('finished' => true); } @@ -250,7 +389,7 @@ class convertModel extends model $user->type = 'inside'; $user->join = $data->join; - $this->dao->dbh($this->dbh)->insert(TABLE_USER)->data($user, 'group')->exec(); + $this->dao->dbh($this->dbh)->replace(TABLE_USER)->data($user, 'group')->exec(); if(!dao::isError()) { @@ -398,15 +537,20 @@ class convertModel extends model $projectRelation['BType'] = 'zproject'; $projectRelation['AID'] = $id; $projectRelation['BID'] = $projectID; - $this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($projectRelation)->exec(); $executionRelation['AType'] = 'jproject'; $executionRelation['BType'] = 'zexecution'; $executionRelation['AID'] = $id; $executionRelation['BID'] = $executionID; - $this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($executionRelation)->exec(); + + $keyRelation['AType'] = 'joldkey'; + $keyRelation['BType'] = 'jnewkey'; + $keyRelation['AID'] = $data->ORIGINALKEY; + $keyRelation['BID'] = $data->pkey; + $keyRelation['extra'] = $data->ID; + $this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($keyRelation)->exec(); } } @@ -430,6 +574,11 @@ class convertModel extends model ->andWhere('BType')->eq('zproject') ->fetchPairs(); + $projectKeys = $this->dao->dbh($this->dbh)->select('extra as ID, AID as oldKey, BID as newKey')->from(JIRA_TMPRELATION) + ->where('AType')->eq('joldkey') + ->andWhere('BType')->eq('jnewkey') + ->fetchAll('ID'); + $projectProduct = $this->dao->dbh($this->dbh)->select('project,product')->from(TABLE_PROJECTPRODUCT) ->where('project')->in(array_values($projectRelation)) ->fetchPairs(); @@ -452,8 +601,6 @@ class convertModel extends model if(!empty($relations['zentaoResolution'][$id])) $resolutionList[$jiraCode] = $relations['zentaoResolution'][$id]; } - $projectKeys = $this->dao->dbh($this->sourceDBH)->select('ID, ORIGINALKEY as oldKey, pkey as newKey')->from(JIRA_PROJECT)->fetchAll('ID'); - foreach($dataList as $id => $data) { $issueType = isset($issueTypeList[$data->issuetype]) ? $issueTypeList[$data->issuetype] : 'task'; @@ -1033,6 +1180,62 @@ class convertModel extends model return $this->dao->dbh($this->sourceDBH)->select('lower_user_name')->from(JIRA_USER)->where('user_key')->eq($userKey)->fetch('lower_user_name'); } + public function splitFile() + { + $filePath = $this->app->getTmpRoot() . 'jirafile/'; + $fileName = 'entities.xml'; + $file = $filePath . $fileName; + $handle = fopen($file, "r"); + + $usingData = array(); + $headerList = array(' '', ' '', ' '', ' '', ' '', ' '', ' '', ' '', ' '', ' '', ' '', ' '', ' ''); + + while(!feof($handle)) + { + $itemStr = fgets($handle); + foreach($headerList as $object) + { + $itemName = $object; + $itemName .= ' '; + + if(strpos($itemStr, $itemName) === false) continue; + + if(strpos($itemStr, '/>') === false) + { + $end = $footerList[$object]; + while(true) + { + $followItemStr = fgets($handle); + $itemStr .= $followItemStr; + if(strpos($itemStr, $end) !== false) break; + } + } + + $object = str_replace('<', '', $object); + $object = strtolower($object); + $data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $itemStr); + if(!file_exists($filePath . $object . '.xml')) $data = "\n \n" . $data; + file_put_contents($filePath . $object . '.xml', $data, FILE_APPEND); + } + } + + foreach($headerList as $object) + { + $object = str_replace('<', '', $object); + $object = strtolower($object); + file_put_contents($filePath . $object . '.xml', '', FILE_APPEND); + } + + fclose($handle); + } + + /** + * Create tmp table for import jira. + * + * @access public + * @return void + */ public function createTmpTable4Jira() { $sql = <<dao->dbh($this->dbh)->select('id,project')->from(TABLE_PROJECT)->where('type')->eq('sprint')->andWhere('project')->ne(0)->fetchPairs(); + $minOpenedDateList = $this->dao->dbh($this->dbh)->select('id,execution,min(openedDate) as minOpenedDate')->from(TABLE_TASK)->where('execution')->in(array_keys($executionProject))->groupBy('execution')->fetchPairs('execution', 'minOpenedDate'); + + foreach($minOpenedDateList as $executionID => $minOpenedDate) + { + $projectID = $executionProject[$executionID]; + $this->dao->update(TABLE_PROJECT)->set('begin')->eq($minOpenedDate)->where('id')->eq($projectID)->orWhere('id')->eq($executionID)->exec(); + } + + $this->dbh->exec("DROP TABLE" . JIRA_TMPRELATION); + } } diff --git a/module/convert/view/importjira.html.php b/module/convert/view/importjira.html.php index e764849fd9..a4b779376f 100644 --- a/module/convert/view/importjira.html.php +++ b/module/convert/view/importjira.html.php @@ -17,18 +17,20 @@
-
    +
      + +
    -
    convert->jira->start, '', "class='btn btn-primary' id='execButton'");?>
    +
    convert->jira->start, '', "class='btn btn-primary' id='execButton'");?>
    -