| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- <?php
- /**
- * The model file of design module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
- * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Shujie Tian <tianshujie@easycorp.ltd>
- * @package design
- * @version $Id: model.php 5107 2020-09-02 09:46:12Z tianshujie@easycorp.ltd $
- * @link https://www.zentao.net
- */
- class designModel extends model
- {
- /**
- * 创建一个设计。
- * Create a design.
- *
- * @param object $design
- * @access public
- * @return bool|int
- */
- public function create($design)
- {
- $design = $this->loadModel('file')->processImgURL($design, 'desc', (string)$this->post->uid);
- $this->dao->insert(TABLE_DESIGN)->data($design, 'docVersions,docs')
- ->autoCheck()
- ->batchCheck($this->config->design->create->requiredFields, 'notempty')
- ->exec();
- if(dao::isError()) return false;
- $designID = $this->dao->lastInsertID();
- $this->file->updateObjectID($this->post->uid, $designID, 'design');
- $files = $this->file->saveUpload('design', $designID);
- $spec = new stdclass();
- $spec->design = $designID;
- $spec->version = 1;
- $spec->name = $design->name;
- $spec->desc = $design->desc;
- $spec->files = empty($files) ? '' : implode(',', array_keys($files));
- $spec->docs = isset($design->docs) ? $design->docs : '';
- $this->dao->insert(TABLE_DESIGNSPEC)->data($spec)->exec();
- return $designID;
- }
- /**
- * 批量创建设计。
- * Batch create designs.
- *
- * @param int $projectID
- * @param int $productID
- * @param array $designs
- * @access public
- * @return bool
- */
- public function batchCreate($projectID = 0, $productID = 0, $designs = array())
- {
- $this->loadModel('action');
- $stories = is_array($_POST['story']) ? $this->loadModel('story')->getByList($this->post->story) : array();
- foreach($designs as $rowID => $design)
- {
- $design->product = $productID;
- $design->project = $projectID;
- $design->createdBy = $this->app->user->account;
- if(!empty($stories[$design->story])) $design->storyVersion = $stories[$design->story]->version;
- $this->dao->insert(TABLE_DESIGN)->data($design)->autoCheck()->batchCheck($this->config->design->batchcreate->requiredFields, 'notempty')->exec();
- if(dao::isError())
- {
- foreach(dao::getError() as $field => $error) dao::$errors["{$field}[{$rowID}]"] = $error;
- return false;
- }
- $designID = $this->dao->lastInsertID();
- if($this->config->edition != 'open' && !empty($design->story))
- {
- if(!isset($stories[$design->story])) continue;
- $relation = new stdClass();
- $relation->AID = $design->story;
- $relation->AType = $stories[$design->story]->type;
- $relation->relation = 'generated';
- $relation->BID = $designID;
- $relation->BType = 'design';
- $relation->product = 0;
- $this->dao->replace(TABLE_RELATION)->data($relation)->exec();
- }
- $this->action->create('design', $designID, 'created');
- }
- return true;
- }
- /**
- * 编辑一个设计。
- * Update a design.
- *
- * @param int $designID
- * @param object $design
- * @access public
- * @return bool|array
- */
- public function update($designID = 0, $design = null)
- {
- $oldDesign = $this->getByID($designID);
- if(!$oldDesign) return false;
- $design = $this->loadModel('file')->processImgURL($design, 'desc', (string)$this->post->uid);
- $this->dao->update(TABLE_DESIGN)->data($design, 'deleteFiles,renameFiles,files,docs,oldDocs,docVersions')->autoCheck()->batchCheck($this->config->design->edit->requiredFields, 'notempty')->where('id')->eq($designID)->exec();
- if(dao::isError()) return false;
- $this->file->processFileDiffsForObject('design', $oldDesign, $design);
- $addedFiles = empty($design->addedFiles) ? '' : implode(',', array_keys($design->addedFiles)) . ',';
- $designFiles = $oldDesign->files = implode(',', array_keys($oldDesign->files));
- foreach($design->deleteFiles as $fileID) $designFiles = str_replace(",$fileID,", ',', ",$designFiles,");
- $files = $addedFiles . trim($designFiles, ',');
- $designChanged = ($oldDesign->name != $design->name || $oldDesign->desc != $design->desc || !empty($files) || $oldDesign->docs != $design->docs);
- if($designChanged)
- {
- $version = $oldDesign->version + 1;
- $spec = new stdclass();
- $spec->design = $designID;
- $spec->version = $version;
- $spec->name = $design->name;
- $spec->desc = $design->desc;
- $spec->files = empty($files) ? '' : $files;
- $spec->docs = isset($design->docs) ? $design->docs : '';
- $this->dao->insert(TABLE_DESIGNSPEC)->data($spec)->exec();
- $this->dao->update(TABLE_DESIGN)->set('version')->eq($version)->where('id')->eq($designID)->exec();
- }
- return common::createChanges($oldDesign, $design);
- }
- /**
- * 更新设计的指派人。
- * Update assign of design.
- *
- * @param int $designID
- * @param object $design
- * @access public
- * @return array|bool
- */
- public function assign($designID = 0, $design = null)
- {
- $oldDesign = $this->getByID($designID);
- if(!$oldDesign) return false;
- $this->dao->update(TABLE_DESIGN)->data($design)->autoCheck()->where('id')->eq($designID)->exec();
- if(dao::isError()) return false;
- return common::createChanges($oldDesign, $design);
- }
- /**
- * 设计关联代码提交。
- * Design link commits.
- *
- * @param int $designID
- * @param int $repoID
- * @param array $revisions
- * @access public
- * @return bool
- */
- public function linkCommit($designID = 0, $repoID = 0, $revisions = array())
- {
- $repo = $this->loadModel('repo')->getByID($repoID);
- if(!isset($repo->SCM)) return true;
- /* If the repo type is Gitlab, first store the commit log in the repohistory table and get the commit ID. */
- if(in_array($repo->SCM, $this->config->repo->notSyncSCM))
- {
- $logs = array();
- foreach($this->session->designRevisions as $commit)
- {
- if(in_array($commit->revision, $revisions))
- {
- $log = new stdclass();
- $log->committer = $commit->committer;
- $log->revision = $commit->revision;
- $log->comment = isset($commit->comment) ? $commit->comment : '';
- $log->time = date('Y-m-d H:i:s', strtotime($commit->time));
- $logs[] = $log;
- }
- }
- $this->repo->saveCommit($repoID, array('commits' => $logs), 0);
- }
- $revisions = $this->dao->select('id')->from(TABLE_REPOHISTORY)->where('revision')->in($revisions)->andWhere('repo')->eq($repoID)->fetchPairs('id');
- $this->designTao->updateLinkedCommits($designID, $repoID, $revisions);
- $oldCommit = $this->dao->findByID($designID)->from(TABLE_DESIGN)->fetch('commit');
- $revisions = implode(',', $revisions);
- $commit = $oldCommit ? $oldCommit . ',' . $revisions : $revisions;
- $design = new stdclass();
- $design->commit = $commit;
- $design->commitedBy = $this->app->user->account;
- $this->dao->update(TABLE_DESIGN)->data($design)->autoCheck()->where('id')->eq($designID)->exec();
- return !dao::isError();
- }
- /**
- * 设计解除代码提交关联。
- * Design unlink a commit.
- *
- * @param int $designID
- * @param int $commitID
- * @access public
- * @return bool
- */
- public function unlinkCommit($designID = 0, $commitID = 0)
- {
- /* Delete linked commit in the relation table. */
- $this->dao->delete()->from(TABLE_RELATION)
- ->where('AType')->eq('design')
- ->andwhere('AID')->eq($designID)
- ->andwhere('BType')->eq('commit')
- ->andwhere('relation')->eq('completedin')
- ->beginIF(!empty($commitID))->andWhere('BID')->eq($commitID)->fi()
- ->exec();
- $this->dao->delete()->from(TABLE_RELATION)
- ->where('AType')->eq('commit')
- ->andwhere('BID')->eq($designID)
- ->andwhere('BType')->eq('design')
- ->andwhere('relation')->eq('completedfrom')
- ->beginIF(!empty($commitID))->andWhere('AID')->eq($commitID)->fi()
- ->exec();
- /* Update linked commit in the design table. */
- $commit = $this->dao->select('BID')->from(TABLE_RELATION)->where('AType')->eq('design')->andWhere('AID')->eq($designID)->andWhere('BType')->eq('commit')->andwhere('relation')->eq('completedin')->fetchAll('BID');
- $commit = implode(",", array_keys($commit));
- $this->dao->update(TABLE_DESIGN)->set('commit')->eq($commit)->where('id')->eq($designID)->exec();
- return !dao::isError();
- }
- /**
- * 通过ID获取设计信息。
- * Get design information by ID.
- *
- * @param int $designID
- * @access public
- * @return object|bool
- * @param int $version
- */
- public function getByID($designID = 0, $version = 0)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getDesign();
- $design = $this->dao->select('*')->from(TABLE_DESIGN)->where('id')->eq($designID)->fetch();
- if(!$design) return false;
- if($version == 0) $version = $design->version;
- $this->app->loadLang('product');
- $this->loadModel('file');
- $spec = $this->dao->select('name,`desc`,files,docs,docVersions')->from(TABLE_DESIGNSPEC)->where('design')->eq($designID)->andWhere('version')->eq($version)->fetch();
- $design->name = !empty($spec->name) ? $spec->name : $design->name;
- $design->desc = !empty($spec->desc) ? $spec->desc : '';
- $design->files = !empty($spec->files) ? $this->file->getByIdList($spec->files) : array();
- $design->docs = !empty($spec->docs) ? $spec->docs : '';
- $design->docVersions = !empty($spec->docVersions) ? json_decode($spec->docVersions, true) : array();
- $design->productName = $design->product ? $this->dao->findByID($design->product)->from(TABLE_PRODUCT)->fetch('name') : $this->lang->product->all;
- $design->project = (int)$design->project;
- $design->product = (int)$design->product;
- $revisions = $this->dao->select('id,revision')->from(TABLE_REPOHISTORY)->where('id')->in($design->commit)->fetchPairs('id', 'revision');
- $design->commit = '';
- $relations = $this->loadModel('common')->getRelations('design', $designID, 'commit');
- foreach($relations as $relation)
- {
- $revision = zget($revisions, $relation->BID, '');
- $design->commit .= html::a(helper::createLink('design', 'revision', "revisionID={$relation->BID}&projectID={$design->project}"), "# {$revision}", '', "title='{$revision}' class='flex clip'");
- }
- if($design->story > 0)
- {
- $storyInfo = $this->loadModel('story')->fetchByID((int)$design->story);
- $design->storyInfo = $storyInfo;
- $design->needConfirm = $storyInfo->version != $design->storyVersion;
- }
- return $this->loadModel('file')->replaceImgURL($design, 'desc');
- }
- /**
- * 获取设计 id=>value 的键值对数组。
- * Get design id=>value pairs.
- *
- * @param int $productID
- * @param string $type all|HLDS|DDS|DBDS|ADS
- * @access public
- * @return object
- */
- public function getPairs($productID = 0, $type = 'all')
- {
- $designs = $this->dao->select('id, name')->from(TABLE_DESIGN)
- ->where('product')->eq($productID)
- ->andWhere('deleted')->eq(0)
- ->beginIF($type != 'all')->andWhere('type')->eq($type)->fi()
- ->fetchPairs();
- foreach($designs as $id => $name) $designs[$id] = $id . ':' . $name;
- return $designs;
- }
- /**
- * 获取设计变更后受影响的任务。
- * Get affected tasks after design changed.
- *
- * @param int $design
- * @access public
- * @return object
- */
- public function getAffectedScope($design = null)
- {
- if(!isset($design->id)) return $design;
- /* Get affected tasks. */
- $design->tasks = $this->dao->select('*')->from(TABLE_TASK)
- ->where('deleted')->eq(0)
- ->andWhere('status')->ne('closed')
- ->andWhere('design')->eq($design->id)
- ->orderBy('id desc')->fetchAll();
- return $design;
- }
- /**
- * 获取设计列表数据。
- * Get design list.
- *
- * @param int|array $productID
- * @param int|array $projectID
- * @param string $type all|bySearch|HLDS|DDS|DBDS|ADS
- * @param int $param
- * @param string $orderBy
- * @param int $pager
- * @access public
- * @return object[]
- */
- public function getList($projectID = 0, $productID = 0, $type = 'all', $param = 0, $orderBy = 'id_desc', $pager = null)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getDesigns();
- if($type == 'bySearch')
- {
- $designs = $this->getBySearch($projectID, $productID, $param, $orderBy, $pager);
- }
- else
- {
- $designs = $this->dao->select('*')->from(TABLE_DESIGN)
- ->where('deleted')->eq(0)
- ->beginIF($projectID)->andWhere('project')->in($projectID)->fi()
- ->beginIF($type != 'all')->andWhere('type')->in($type)->fi()
- ->beginIF($productID)->andWhere('product')->in(is_numeric($productID) ? "0,$productID" : array_merge($productID, array(0)))->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- $stories = $this->loadModel('story')->getByList(array_column($designs, 'story'));
- foreach($designs as $designID => $design)
- {
- if(isset($stories[$design->story]))
- {
- $storyInfo = $stories[$design->story];
- $designs[$designID]->needConfirm = $storyInfo->version != $design->storyVersion;
- }
- }
- }
- return $designs;
- }
- /**
- * 获取设计关联的代码提交。
- * Get commit.
- *
- * @param int $designID
- * @param object $pager
- * @access public
- * @return object|bool
- */
- public function getCommit($designID = 0, $pager = null)
- {
- $design = $this->dao->select('*')->from(TABLE_DESIGN)->where('id')->eq($designID)->fetch();
- if(!$design) return false;
- $design->project = (int)$design->project;
- $design->product = (int)$design->product;
- $design->commit = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('id')->in($design->commit)->page($pager)->fetchAll('id', false);
- $this->loadModel('repo');
- foreach($design->commit as $commit)
- {
- $commit->originalComment = $commit->comment;
- $commit->comment = $this->repo->replaceCommentLink($commit->comment);
- }
- return $design;
- }
- /**
- * 获取搜索后的设计列表数据。
- * Get designs by search.
- *
- * @param int $projectID
- * @param int $productID
- * @param int $queryID
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return object[]
- */
- public function getBySearch($projectID = 0, $productID = 0, $queryID = 0, $orderBy = 'id_desc', $pager = null)
- {
- if($queryID)
- {
- $query = $this->loadModel('search')->getQuery($queryID);
- if($query)
- {
- $this->session->set('designQuery', $query->sql);
- $this->session->set('designForm', $query->form);
- }
- }
- else
- {
- if($this->session->designQuery === false) $this->session->set('designQuery', ' 1 = 1');
- }
- return $this->dao->select('*')->from(TABLE_DESIGN)
- ->where($this->session->designQuery)
- ->andWhere('deleted')->eq('0')
- ->andWhere('project')->eq($projectID)
- ->beginIF($productID)->andWhere('product')->in(is_numeric($productID) ? "0,$productID" : array_merge($productID, array(0)))->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- }
- /**
- * 通过ID获取提交记录。
- * Get commit by ID.
- *
- * @param int $revisionID
- * @access public
- * @return object|bool
- */
- public function getCommitByID($revisionID = 0)
- {
- return $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('id')->eq($revisionID)->fetch();
- }
- /**
- * 获取设计关联的提交数据。
- * Get the commit data for the associated designs
- * @param int $repoID
- * @param array $revisions
- * @access public
- * @return bool
- */
- public function getLinkedCommits($repoID, $revisions)
- {
- return $this->dao->select('t1.revision,t3.id AS id,t3.name AS title')
- ->from(TABLE_REPOHISTORY)->alias('t1')
- ->leftJoin(TABLE_RELATION)->alias('t2')->on("t2.relation='completedin' AND t2.BType='commit' AND t2.BID=t1.id")
- ->leftJoin(TABLE_DESIGN)->alias('t3')->on("t2.AType='design' AND t2.AID=t3.id")
- ->where('t1.revision')->in($revisions)
- ->andWhere('t1.repo')->eq($repoID)
- ->andWhere('t3.id')->notNULL()
- ->orderBy('id')
- ->fetchGroup('revision', 'id');
- }
- /**
- * 确认设计的需求变更。
- * Confirm story change of design.
- *
- * @param int $designID
- * @access public
- * @return bool
- */
- public function confirmStoryChange($designID)
- {
- $design = $this->fetchByID($designID);
- if($design)
- {
- $story = $this->loadModel('story')->fetchByID((int)$design->story);
- if($story) $this->dao->update(TABLE_DESIGN)->set('storyVersion')->eq($story->version)->where('id')->eq($designID)->exec();
- }
- return dao::isError();
- }
- /**
- * 获取项目下冻结的设计类型。
- * Get design type of frozen.
- *
- * @param int $projectID
- * @access public
- * @return array
- */
- public function getFrozenDesignType($projectID)
- {
- return $this->dao->select('t1.deliverable')->from(TABLE_PROJECTDELIVERABLE)->alias('t1')
- ->leftJoin(TABLE_DELIVERABLE)->alias('t2')->on('t1.deliverable = t2.id')
- ->where('t1.project')->eq($projectID)
- ->andWhere('t1.frozen')->ne('')
- ->andWhere('t2.category')->in('HLDS,DDS,DBDS,ADS')
- ->fetchPairs();
- }
- /**
- * 判断当前动作是否可以点击。
- * Adjust the action is clickable.
- *
- * @param object $object
- * @param string $action
- * @access public
- * @return bool
- */
- public static function isClickable($object, $action)
- {
- $action = strtolower($action);
- if($action == 'confirmstorychange') return !empty($object->needConfirm);
- if(in_array($action, array('edit', 'delete')) && !empty($object->frozen)) return false;
- return true;
- }
- }
|