From 02eb03583d009ee28ee8ded600c05119cccd55d1 Mon Sep 17 00:00:00 2001 From: holan20180123 <56391770@qq.com> Date: Sat, 10 Oct 2020 16:56:36 +0800 Subject: [PATCH] * Finish task #8085. --- config/zentaopms.php | 1 + db/update12.4.2.sql | 6 ++ module/common/model.php | 18 ---- module/product/control.php | 1 - module/productplan/control.php | 40 ++------- module/productplan/model.php | 28 +++--- module/project/control.php | 1 - module/story/model.php | 160 +++++++++++++++++---------------- module/upgrade/model.php | 4 + 9 files changed, 112 insertions(+), 147 deletions(-) create mode 100644 db/update12.4.2.sql diff --git a/config/zentaopms.php b/config/zentaopms.php index 31a2d4ee4c..25414f3810 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -169,6 +169,7 @@ define('TABLE_STORY', '`' . $config->db->prefix . 'story`'); define('TABLE_STORYSPEC', '`' . $config->db->prefix . 'storyspec`'); define('TABLE_STORYSTAGE', '`' . $config->db->prefix . 'storystage`'); define('TABLE_PRODUCTPLAN', '`' . $config->db->prefix . 'productplan`'); +define('TABLE_PLANSTORY', '`' . $config->db->prefix . 'planstory`'); define('TABLE_RELEASE', '`' . $config->db->prefix . 'release`'); define('TABLE_PROJECT', '`' . $config->db->prefix . 'project`'); diff --git a/db/update12.4.2.sql b/db/update12.4.2.sql new file mode 100644 index 0000000000..2946802dcf --- /dev/null +++ b/db/update12.4.2.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS `zt_planstory` ( + `plan` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL, + `order` mediumint(9) NOT NULL, + UNIQUE KEY `unique` (`plan`,`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/module/common/model.php b/module/common/model.php index d69472dfd0..6327e1955d 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -1238,24 +1238,6 @@ EOD; $preAndNextObject->pre = ''; $preAndNextObject->next = ''; - if($type == 'story' and !empty($_SESSION['sortedIdList'])) - { - $objects = explode(',', $_SESSION['sortedIdList']); - $key = array_search($objectID, $objects); - - if($key > 0) - { - $preObjectID = $objects[$key - 1]; - $preAndNextObject->pre = $this->loadModel('story')->getByID($preObjectID); - } - if($key < (count($objects) - 1)) - { - $nextObjectID = $objects[$key + 1]; - $preAndNextObject->next = $this->loadModel('story')->getByID($nextObjectID); - } - return $preAndNextObject; - } - /* Get objectIDList. */ $table = $this->config->objectTables[$type]; $queryCondition = $type . 'QueryCondition'; diff --git a/module/product/control.php b/module/product/control.php index 18ca40e34b..f3a2553c0b 100644 --- a/module/product/control.php +++ b/module/product/control.php @@ -112,7 +112,6 @@ class product extends control /* Save session. */ $this->session->set('storyList', $this->app->getURI(true)); $this->session->set('productList', $this->app->getURI(true)); - $this->session->set('sortedIdList', ''); /* Set product, module and query. */ $productID = $this->product->saveState($productID, $this->products); diff --git a/module/productplan/control.php b/module/productplan/control.php index d7c30f1993..ff8ca64d89 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -253,17 +253,6 @@ class productplan extends control $this->app->loadClass('pager', $static = true); if($this->app->getViewType() == 'mhtml') $recPerPage = 10; - $reSort = false; - if($type == 'story' && strpos($orderBy, 'order') !== false) - { - $orderBy = str_replace('order', 'id', $orderBy); - $reSort = true; - } - else - { - $this->session->set('sortedIdList', ''); - } - /* Append id for secend sort. */ $sort = $this->loadModel('common')->appendOrder($orderBy); @@ -273,26 +262,9 @@ class productplan extends control $bugPager = new pager(0, $recPerPage, $type == 'bug' ? $pageID : 1); $storyPager = new pager(0, $recPerPage, $type == 'story' ? $pageID : 1); + /* Get stories of plan. */ $this->loadModel('story'); - if(!$reSort or empty($plan->order)) $planStories = $this->story->getPlanStories($planID, 'all', $type == 'story' ? $sort : 'id_desc', $storyPager); - - if($reSort) - { - if(!empty($plan->order)) - { - $planStories = $this->story->getPlanStories($planID, 'all', $sort); - $planStories = $this->story->sortPlanStory($planStories, $plan->order, $orderBy); - - $storyIDList = implode(',', array_keys($planStories)); - $this->session->set('sortedIdList', $storyIDList); - - $storyPager->recTotal = count($planStories); - - $frontCount = $storyPager->recPerPage * ($storyPager->pageID - 1); - $planStories = array_slice($planStories, $frontCount, $storyPager->recPerPage); - } - $orderBy = str_replace('id', 'order', $orderBy); - } + $planStories = $this->story->getPlanStories($planID, 'all', $type == 'story' ? $sort : 'id_desc', $storyPager); $this->executeHooks($planID); if($plan->parent > 0) $this->view->parentPlan = $this->productplan->getById($plan->parent); @@ -351,11 +323,11 @@ class productplan extends control { if(empty($planID)) return true; - $plan = $this->productplan->getByID($planID, true); - $order = $this->loadModel('story')->sortStoriesOfPlan($planID, $plan->order); + /* Get story id list. */ + $storyIDList = explode(',', trim($this->post->stories, ',')); - $this->dao->update(TABLE_PRODUCTPLAN)->set('`order`')->eq($order)->where('id')->eq((int)$planID)->exec(); - $this->session->set('sortedIdList', $order); + /* Update the story order according to the plan. */ + $this->loadModel('story')->sortStoriesOfPlan($planID, $storyIDList, $this->post->orderBy, $this->post->pageID, $this->post->recPerPage); } /** diff --git a/module/productplan/model.php b/module/productplan/model.php index 130b7d0018..4d3c58cef3 100644 --- a/module/productplan/model.php +++ b/module/productplan/model.php @@ -466,7 +466,6 @@ class productplanModel extends model $stories = $this->story->getByList($this->post->stories); $plan = $this->getByID($planID); - $currentOrder = $plan->order; foreach($this->post->stories as $storyID) { if(!isset($stories[$storyID])) continue; @@ -474,31 +473,27 @@ class productplanModel extends model $story = $stories[$storyID]; if(strpos(",$story->plan,", ",{$planID},") !== false) continue; - /* Fix Bug #1538*/ - $currentOrder = $currentOrder . $storyID . ','; - $oldOrder = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)->where("id")->eq($story->plan)->fetch('order'); - $oldOrder = explode(',', $oldOrder); - unset($oldOrder[array_search($storyID, $oldOrder)]); - $oldOrder = implode(',', $oldOrder); - $this->dao->update(TABLE_PRODUCTPLAN)->set("order")->eq($oldOrder)->where('id')->eq($story->plan)->exec(); - /* Modify the plan linked with the story. */ if($this->session->currentProductType == 'normal' or $story->branch != 0 or empty($story->plan)) { $this->dao->update(TABLE_STORY)->set("plan")->eq($planID)->where('id')->eq((int)$storyID)->exec(); + + /* Update the order of the story in the plan. */ + $this->story->updateStoryOrderOfPlan($storyID, $planID, $story->plan); } else { $plansOfStory = $story->plan . ',' . $planID; $this->dao->update(TABLE_STORY)->set("plan")->eq($plansOfStory)->where('id')->eq((int)$storyID)->andWhere('branch')->eq('0')->exec(); + + /* Update the order of the story in the plan. */ + $this->story->updateStoryOrderOfPlan($storyID, $planID); } $this->action->create('story', $storyID, 'linked2plan', '', $planID); $this->story->setStage($storyID); } - - $this->dao->update(TABLE_PRODUCTPLAN)->set("order")->eq($currentOrder)->where('id')->eq((int)$planID)->exec(); } /** @@ -514,14 +509,11 @@ class productplanModel extends model $plans = array_unique(explode(',', trim(str_replace(",$planID,", ',', ',' . trim($story->plan) . ','). ','))); $this->dao->update(TABLE_STORY)->set('plan')->eq(join(',', $plans))->where('id')->eq((int)$storyID)->exec(); - /* Fix Bug #1538. */ - $oldOrder = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)->where("id")->eq($story->plan)->fetch('order'); - $oldOrder = explode(',', $oldOrder); - unset($oldOrder[array_search($storyID, $oldOrder)]); - $oldOrder = implode(',', $oldOrder); - $this->dao->update(TABLE_PRODUCTPLAN)->set('order')->eq($oldOrder)->where('id')->eq($story->plan)->exec(); + /* Delete the story in the sort of the plan. */ + $this->loadModel('story'); + $this->story->updateStoryOrderOfPlan($storyID, '', $planID); - $this->loadModel('story')->setStage($storyID); + $this->story->setStage($storyID); $this->loadModel('action')->create('story', $storyID, 'unlinkedfromplan', '', $planID); } diff --git a/module/project/control.php b/module/project/control.php index 3c1fdd7d1b..731784e19b 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -689,7 +689,6 @@ class project extends control /* Save session. */ $this->app->session->set('storyList', $this->app->getURI(true)); - $this->session->set('sortedIdList', ''); /* Process the order by field. */ if(!$orderBy) $orderBy = $this->cookie->projectStoryOrder ? $this->cookie->projectStoryOrder : 'pri'; diff --git a/module/story/model.php b/module/story/model.php index 44628da8db..b62b256429 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -211,11 +211,7 @@ class storyModel extends model $this->file->updateObjectID($this->post->uid, $storyID, 'story'); $this->file->saveUpload('story', $storyID, $extra = 1); - if(!empty($story->plan)) - { - $plan[$story->plan][] = $storyID; - $this->updatePlanStoryOrder($plan); - } + if(!empty($story->plan)) $this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan. $data = new stdclass(); $data->story = $storyID; @@ -381,7 +377,8 @@ class storyModel extends model $storyID = $this->dao->lastInsertID(); $this->setStage($storyID); - if($story->plan) $planStories[$story->plan][] = $storyID; + /* Update product plan stories order. */ + if($story->plan) $this->updateStoryOrderOfPlan($storyID, $story->plan); $specData = new stdclass(); $specData->story = $storyID; @@ -433,8 +430,6 @@ class storyModel extends model $mails[$i]->actionID = $actionID; } - /* Update product plan stories order. */ - if(!empty($planStories)) $this->updatePlanStoryOrder($planStories); /* Remove upload image file and session. */ if(!empty($stories->uploadImage) and $this->session->storyImagesFile) @@ -670,8 +665,14 @@ class storyModel extends model if(isset($story->closedReason) and $story->closedReason == 'done') $this->loadModel('score')->create('story', 'close'); - /* Fix bug #3153. */ - if($oldStory->plan != $story->plan and (empty($oldStory->plan) or empty($story->plan))) $this->setStage($storyID); + /* Set new stage and update story sort of plan when story plan has changed. */ + if($oldStory->plan != $story->plan) + { + /* Insert a new story sort in this plan. */ + $this->updateStoryOrderOfPlan($storyID, $story->plan, $oldStory->plan); + + if(empty($oldStory->plan) or empty($story->plan)) $this->setStage($storyID); // Set new stage for this story. + } unset($oldStory->parent); unset($story->parent); @@ -800,25 +801,47 @@ class storyModel extends model } /** - * update plan story order. + * update the story order of plan. * - * @param int $planStories + * @param int $storyID + * @param string $oldPlanIDList + * @param string $planIDList * @access public * @return void */ - public function updatePlanStoryOrder($planStories) + public function updateStoryOrderOfPlan($storyID, $planIDList = '', $oldPlanIDList = '') { - $planIDList = array_keys($planStories); - $plans = $this->dao->select('id, `order`')->from(TABLE_PRODUCTPLAN)->where('id')->in($planIDList)->fetchAll('id'); + $planIDList = $planIDList ? explode(',', $planIDList) : array(); + $oldPlanIDList = $oldPlanIDList ? explode(',', $oldPlanIDList) : array(); - foreach($planStories as $planID => $stories) + /* Get the ids to be inserted and deleted by comparing plan ids. */ + $insertedPlanIDList = array_diff($planIDList, $oldPlanIDList); + $deletedPlanIDList = array_diff($oldPlanIDList, $planIDList); + + /* Delete old story sort of plan. */ + if(!empty($deletedPlanIDList)) { - $data = new stdClass(); - $data->order = implode(',', $stories); - $productPlan = $plans[$planID]; - if(!empty($productPlan->order)) $data->order = $data->order . ',' . $productPlan->order; + foreach($deletedPlanIDList as $planID) + { + $this->dao->delete()->from(TABLE_PLANSTORY)->where('plan')->eq($planID)->andWhere('story')->eq($storyID)->exec(); + } + } - $this->dao->update(TABLE_PRODUCTPLAN)->data($data)->where('id')->eq($planID)->exec(); + if(!empty($insertedPlanIDList)) + { + /* Get last story order of plan list. */ + $lastOrderOfPlans = $this->dao->select('plan, `order`')->from(TABLE_PLANSTORY)->where('plan')->in($insertedPlanIDList)->orderBy('order_asc')->fetchPairs(); + + foreach($insertedPlanIDList as $planID) + { + /* Set story order in new plan. */ + $data = new stdClass(); + $data->plan = $planID; + $data->story = $storyID; + $data->order = zget($lastOrderOfPlans, $planID, 0) + 1; + + $this->dao->replace(TABLE_PLANSTORY)->data($data)->exec(); + } } } @@ -952,8 +975,10 @@ class storyModel extends model if(!dao::isError()) { - $this->executeHooks($storyID); + /* Update story sort of plan when story plan has changed. */ + if($oldStory->plan != $story->plan) $this->updateStoryOrderOfPlan($storyID, $story->plan, $oldStory->plan); + $this->executeHooks($storyID); $this->setStage($storyID); if($story->closedReason == 'done') $this->loadModel('score')->create('story', 'close'); $allChanges[$storyID] = common::createChanges($oldStory, $story); @@ -1292,6 +1317,9 @@ class storyModel extends model /* Remove old plan from the plan field. */ if($oldPlanID) $story->plan = trim(str_replace(",$oldPlanID,", ',', ",$oldStory->plan,"), ','); + /* Update the order of the story in the plan. */ + $this->updateStoryOrderOfPlan($storyID, $planID, $oldStory->plan); + /* Replace plan field if product is normal or not linked to plan or story linked to a branch. */ if($this->session->currentProductType == 'normal') $story->plan = $planID; if(empty($oldPlanID)) $story->plan = $planID; @@ -2184,13 +2212,15 @@ class storyModel extends model */ public function getPlanStories($planID, $status = 'all', $orderBy = 'id_desc', $pager = null) { - $stories = $this->dao->select('*')->from(TABLE_STORY) - ->where("CONCAT(',', plan, ',')")->like("%,$planID,%") - ->beginIF($status and $status != 'all')->andWhere('status')->in($status)->fi() - ->andWhere('deleted')->eq(0) + $stories = $this->dao->select('distinct t1.story, t1.plan, t1.order, t2.*') + ->from(TABLE_PLANSTORY)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') + ->where('t1.plan')->eq($planID) + ->beginIF($status and $status != 'all')->andWhere('t2.status')->in($status)->fi() + ->andWhere('t2.deleted')->eq(0) ->orderBy($orderBy)->page($pager)->fetchAll('id'); - $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story'); + $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false); return $stories; } @@ -2353,34 +2383,6 @@ class storyModel extends model return $allStories; } - /** - * Get all story sort. - * - * @param int $planID - * @param int $planOrder - * @access public - * @return string - */ - public function sortStoriesOfPlan($planID, $planOrder) - { - $orderBy = $this->post->orderBy; - if(strpos($orderBy, 'order') !== false) $orderBy = str_replace('order', 'id', $orderBy); - - $stories = $this->loadModel('story')->getPlanStories($planID, 'all'); - $storyIDList = array_keys($stories); - - if(strpos($this->post->orderBy, 'order') !== false and !empty($planOrder)) $stories = $this->sortPlanStory($stories, $planOrder, $orderBy); - - $frontCount = (int)$this->post->recPerPage * ((int)$this->post->pageID - 1); - $behindCount = (int)$this->post->recPerPage * (int)$this->post->pageID; - $frontIDList = array_slice($storyIDList, 0, $frontCount); - $behindIDList = array_slice($storyIDList, $behindCount, count($storyIDList) - $behindCount); - - $frontIDList = !empty($frontIDList) ? implode(',', $frontIDList) . ',' : ''; - $behindIDList = !empty($behindIDList) ? implode(',', $behindIDList) : ''; - return $frontIDList . $this->post->stories . $behindIDList; - } - /** * Batch get story stage. * @@ -3298,32 +3300,40 @@ class storyModel extends model } /** - * Sort product plan story. + * Update the story order according to the plan. * - * @param int $planStories - * @param string $order + * @param int $planID + * @param array $sortIDList * @param string $orderBy + * @param int $pageID + * @param int $recPerPage * @access public - * @return array + * @return void */ - public function sortPlanStory($planStories, $order = '', $orderBy = 'order_asc') + public function sortStoriesOfPlan($planID, $sortIDList, $orderBy = 'id_desc', $pageID = 1, $recPerPage = 100) { - $stories = array(); - if(!empty($order)) + /* Append id for secend sort. */ + $orderBy = $this->loadModel('common')->appendOrder($orderBy); + + /* Get all stories by plan. */ + $stories = $this->getPlanStories($planID, 'all', $orderBy); + $storyIDList = array_keys($stories); + + /* Calculate how many numbers there are before the sort list and after the sort list. */ + $frontStoryCount = $recPerPage * ($pageID - 1); + $behindStoryCount = $recPerPage * $pageID; + $frontStoryIDList = array_slice($storyIDList, 0, $frontStoryCount); + $behindStoryIDList = array_slice($storyIDList, $behindStoryCount, count($storyIDList) - $behindStoryCount); + + /* Merge to get a new sort list. */ + $newSortIDList = array_merge($frontStoryIDList, $sortIDList, $behindStoryIDList); + + /* Loop update the story order of plan. */ + $order = 1; + foreach($newSortIDList as $storyID) { - if(is_string($order)) $order = explode(',', $order); - if(strpos($orderBy, 'desc') !== false) $order = array_reverse($order, true); - - foreach($order as $id) - { - if(empty($id)) continue; - if(!isset($planStories[$id])) continue; - $stories[$id] = $planStories[$id]; - unset($planStories[$id]); - } - if($planStories) $stories += $planStories; + $this->dao->update(TABLE_PLANSTORY)->set('`order`')->eq($order)->where('story')->eq($storyID)->andWhere('plan')->eq($planID)->exec(); + $order++; } - - return $stories; } } diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 3910ba9cf1..2154e1e2ba 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -610,6 +610,10 @@ class upgradeModel extends model $this->saveLogs('Execute 12_4_1'); $this->execSQL($this->getUpgradeFile('12.4.1')); $this->appendExec('12_4_1'); + case '12_4_2': + $this->saveLogs('Execute 12_4_2'); + $this->execSQL($this->getUpgradeFile('12.4.2')); + $this->appendExec('12_4_2'); } $this->deletePatch();