model.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. <?php
  2. /**
  3. * The model file of personnel of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package personnel
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class personnelModel extends model
  13. {
  14. /**
  15. * 获取项目集可访问人员。
  16. * Access to program set invest staff.
  17. *
  18. * @param int $programID
  19. * @param int $deptID
  20. * @param string $browseType
  21. * @param int $queryID
  22. * @access public
  23. * @return array
  24. */
  25. public function getAccessiblePersonnel($programID = 0, $deptID = 0, $browseType = 'all', $queryID = 0)
  26. {
  27. $accessibleQuery = '';
  28. if($browseType == 'bysearch')
  29. {
  30. $query = $queryID ? $this->loadModel('search')->getQuery($queryID) : '';
  31. if($query)
  32. {
  33. $this->session->set('accessibleQuery', $query->sql);
  34. $this->session->set('accessibleForm', $query->form);
  35. }
  36. if($this->session->accessibleQuery == false) $this->session->set('accessibleQuery', ' 1=1');
  37. $accessibleQuery = $this->session->accessibleQuery;
  38. }
  39. /* Determine who can be accessed based on access control. */
  40. $program = $this->loadModel('program')->getByID($programID);
  41. if(!$program) return array();
  42. /* Get accessible account of program. */
  43. $accessibleQuery = preg_replace('/`(\w+)`/', 't2.`$1`', $accessibleQuery);
  44. $personnelList = array();
  45. $personnelList = $this->dao->select('t2.id,t2.dept,t2.account,t2.role,t2.realname,t2.gender')->from(TABLE_USERVIEW)->alias('t1')
  46. ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
  47. ->where('t2.deleted')->eq(0)
  48. ->beginIF($program->acl != 'open')->andWhere("CONCAT(',', t1.programs, ',')")->like("%,$programID,%")->fi()
  49. ->beginIF($deptID > 0)->andWhere('t2.dept')->eq($deptID)->fi()
  50. ->beginIF($browseType == 'bysearch')->andWhere($accessibleQuery)->fi()
  51. ->fetchAll('id');
  52. /* If the program's acl is open and the user cannot access this program, remove it. */
  53. if($program->acl == 'open')
  54. {
  55. foreach($personnelList as $personnel)
  56. {
  57. if(!$this->canViewProgram($programID, $personnel->account)) unset($personnelList[$personnel->id]);
  58. }
  59. }
  60. return $personnelList;
  61. }
  62. /**
  63. * 检查你是否有权限查看项目集。
  64. * Check if you have permission to view the program.
  65. *
  66. * @param int $programID
  67. * @param string $account
  68. * @access public
  69. * @return bool
  70. */
  71. public function canViewProgram($programID, $account)
  72. {
  73. if($this->app->user->admin) return true;
  74. /* Get group acl and group info. */
  75. static $groupAcl = array();
  76. static $groupInfo = array();
  77. if(empty($groupAcl))
  78. {
  79. $groupAcl = $this->dao->select('id,acl')->from(TABLE_GROUP)->fetchPairs();
  80. foreach($groupAcl as $groupID => $group) $groupInfo[$groupID] = json_decode($groupAcl[$groupID]);
  81. }
  82. /* Get user groups. */
  83. static $userGroups = array();
  84. if(empty($userGroups)) $userGroups = $this->dao->select('*')->from(TABLE_USERGROUP)->fetchGroup('account', 'group');
  85. /* Check permission. */
  86. $programRight = false;
  87. if(isset($userGroups[$account]))
  88. {
  89. foreach($userGroups[$account] as $groupID => $userGroup)
  90. {
  91. $group = isset($groupInfo[$groupID]) ? $groupInfo[$groupID] : '';
  92. if(!isset($group->programs) || in_array($programID, $group->programs))
  93. {
  94. $programRight = true;
  95. break;
  96. }
  97. }
  98. }
  99. return $programRight;
  100. }
  101. /**
  102. * 获取参与项目集的人员列表。
  103. * Get invest person list.
  104. *
  105. * @param int $programID
  106. * @access public
  107. * @return array
  108. */
  109. public function getInvest($programID = 0)
  110. {
  111. /* Get all projects under the current program. */
  112. $projects = $this->dao->select('id,model,type,parent,path,name')->from(TABLE_PROJECT)
  113. ->where('type')->eq('project')
  114. ->andWhere('path')->like("%,{$programID},%")
  115. ->andWhere('deleted')->eq('0')
  116. ->orderBy('id_desc')
  117. ->fetchAll('id');
  118. if(empty($projects)) return array();
  119. /* Get account pairs. */
  120. $accountPairs = $this->getInvolvedProjects($projects);
  121. if(empty($accountPairs)) return array();
  122. /* Get execution, task, bug, story, issue and risk pairs, and get user list. */
  123. $executionPairs = $this->getInvolvedExecutions($projects);
  124. $taskInvest = $this->getProjectTaskInvest($projects, $accountPairs);
  125. $bugAndStoryInvest = $this->getBugAndStoryInvest($accountPairs, $programID);
  126. if($this->config->edition == 'max' || $this->config->edition == 'ipd')
  127. {
  128. $issueInvest = $this->getIssueInvest($accountPairs, $projects);
  129. $riskInvest = $this->getRiskInvest($accountPairs, $projects);
  130. }
  131. $users = $this->loadModel('user')->getListByAccounts(array_keys($accountPairs), 'account');
  132. /* Build personnel list. */
  133. $personnelList = array();
  134. foreach($accountPairs as $account => $projects)
  135. {
  136. $user = zget($users, $account, '');
  137. if(empty($user)) continue;
  138. if(!isset($personnelList[$user->role])) $personnelList[$user->role] = array();
  139. $personnelList[$user->role][$account]['realname'] = $user ? $user->realname : $account;
  140. $personnelList[$user->role][$account]['account'] = $account;
  141. $personnelList[$user->role][$account]['role'] = $user ? zget($this->lang->user->roleList, $user->role) : '';
  142. $personnelList[$user->role][$account]['projects'] = $projects;
  143. $personnelList[$user->role][$account]['executions'] = zget($executionPairs, $account, 0);
  144. $personnelList[$user->role][$account] += $taskInvest[$account];
  145. $personnelList[$user->role][$account] += $bugAndStoryInvest[$account];
  146. if($this->config->edition == 'max' || $this->config->edition == 'ipd')
  147. {
  148. $personnelList[$user->role][$account] += $issueInvest[$account];
  149. $personnelList[$user->role][$account] += $riskInvest[$account];
  150. }
  151. }
  152. krsort($personnelList);
  153. return $personnelList;
  154. }
  155. /**
  156. * 获取用户参与的项目风险。
  157. * Get user project risk invest.
  158. *
  159. * @param array $accounts
  160. * @param array $projects
  161. * @access public
  162. * @return array
  163. */
  164. public function getRiskInvest($accounts, $projects)
  165. {
  166. /* Get the risks in projects. */
  167. $risks = $this->dao->select('id,createdBy,resolvedBy,status,assignedTo')->from(TABLE_RISK)
  168. ->where('project')->in(array_keys($projects))
  169. ->andWhere('deleted')->eq(0)
  170. ->fetchAll('id');
  171. /* Initialization personnel risks. */
  172. $invest = array();
  173. foreach($accounts as $account => $project)
  174. {
  175. $invest[$account]['createdRisk'] = 0;
  176. $invest[$account]['resolvedRisk'] = 0;
  177. $invest[$account]['pendingRisk'] = 0;
  178. }
  179. /* Build invest risks. */
  180. foreach($risks as $risk)
  181. {
  182. if($risk->createdBy && isset($invest[$risk->createdBy])) $invest[$risk->createdBy]['createdRisk'] += 1;
  183. if($risk->resolvedBy && isset($invest[$risk->resolvedBy])) $invest[$risk->resolvedBy]['resolvedRisk'] += 1;
  184. if($risk->assignedTo && isset($invest[$risk->assignedTo]) && $risk->status == 'active') $invest[$risk->assignedTo]['pendingRisk'] += 1;
  185. }
  186. return $invest;
  187. }
  188. /**
  189. * 获取用户参与的项目问题。
  190. * Get user project issue invest.
  191. *
  192. * @param array $accounts
  193. * @param array $projects
  194. * @access public
  195. * @return array
  196. */
  197. public function getIssueInvest($accounts, $projects)
  198. {
  199. /* Get issues in the projects. */
  200. $issues = $this->dao->select('id,createdBy,resolvedBy,status,assignedTo')->from(TABLE_ISSUE)
  201. ->where('project')->in(array_keys($projects))
  202. ->andWhere('deleted')->eq(0)
  203. ->fetchAll('id');
  204. /* Initialization personnel issues. */
  205. $invest = array();
  206. foreach($accounts as $account => $project)
  207. {
  208. $invest[$account]['createdIssue'] = 0;
  209. $invest[$account]['resolvedIssue'] = 0;
  210. $invest[$account]['pendingIssue'] = 0;
  211. }
  212. /* Build invest issues. */
  213. foreach($issues as $issue)
  214. {
  215. if($issue->createdBy && isset($invest[$issue->createdBy])) $invest[$issue->createdBy]['createdIssue'] += 1;
  216. if($issue->resolvedBy && isset($invest[$issue->resolvedBy])) $invest[$issue->resolvedBy]['resolvedIssue'] += 1;
  217. if($issue->assignedTo && isset($invest[$issue->assignedTo]) && in_array($issue->status, array('unconfirmed', 'confirmed', 'active'))) $invest[$issue->assignedTo]['pendingIssue'] += 1;
  218. }
  219. return $invest;
  220. }
  221. /**
  222. * 获取用户参与的 bug 和需求。
  223. * Get user bug and story invest.
  224. *
  225. * @param array $accounts
  226. * @param int $programID
  227. * @access public
  228. * @return array
  229. */
  230. public function getBugAndStoryInvest($accounts, $programID)
  231. {
  232. /* Get invest products. */
  233. $productPairs = $this->loadModel('product')->getPairs('', $programID, '', 'all');
  234. $productKeys = array_keys($productPairs);
  235. /* Get invest bugs, requirements and stories. */
  236. $bugs = $this->dao->select('id,status,openedBy,assignedTo,resolvedBy')->from(TABLE_BUG)
  237. ->where('product')->in($productKeys)
  238. ->andWhere('deleted')->eq(0)
  239. ->fetchAll('id');
  240. $requirement = $this->dao->select('openedBy, count(id) as number')->from(TABLE_STORY)
  241. ->where('product')->in($productKeys)
  242. ->andWhere('openedBy')->in(array_keys($accounts))
  243. ->andWhere('type')->eq('requirement')
  244. ->andWhere('deleted')->eq(0)
  245. ->groupBy('openedBy')
  246. ->fetchPairs('openedBy');
  247. $story = $this->dao->select('openedBy, count(id) as number')->from(TABLE_STORY)
  248. ->where('product')->in($productKeys)
  249. ->andWhere('openedBy')->in(array_keys($accounts))
  250. ->andWhere('type')->eq('story')
  251. ->andWhere('deleted')->eq(0)
  252. ->groupBy('openedBy')
  253. ->fetchPairs('openedBy');
  254. /* Initialize bugs and requirements related to personnel. */
  255. $invest = array();
  256. foreach($accounts as $account => $project) $invest[$account]['createdBug'] = $invest[$account]['resolvedBug'] = $invest[$account]['pendingBug'] = $invest[$account]['UR'] = $invest[$account]['SR'] = 0;
  257. /* Calculate the number of user invest bugs, requirements and stories. */
  258. foreach($requirement as $account => $number) $invest[$account]['UR'] = $number;
  259. foreach($story as $account => $number) $invest[$account]['SR'] = $number;
  260. foreach($bugs as $bug)
  261. {
  262. if($bug->openedBy && isset($invest[$bug->openedBy])) $invest[$bug->openedBy]['createdBug'] += 1;
  263. if($bug->resolvedBy && isset($invest[$bug->resolvedBy])) $invest[$bug->resolvedBy]['resolvedBug'] += 1;
  264. if($bug->assignedTo && $bug->status == 'active' && isset($invest[$bug->assignedTo])) $invest[$bug->assignedTo]['pendingBug'] += 1;
  265. }
  266. return $invest;
  267. }
  268. /**
  269. * 获取项目成员帐户和参与项目的数量。
  270. * Get the project member accounts and the number of participating projects.
  271. *
  272. * @param array $projects
  273. * @access public
  274. * @return array
  275. */
  276. public function getInvolvedProjects($projects)
  277. {
  278. return $this->dao->select('account, count(root) as projects')->from(TABLE_TEAM)
  279. ->where('root')->in(array_keys($projects))
  280. ->andWhere('type')->eq('project')
  281. ->groupBy('account')
  282. ->fetchPairs('account');
  283. }
  284. /**
  285. * 获取项目下的迭代或阶段。
  286. * Gets the iteration or phase under the project.
  287. *
  288. * @param array $projects
  289. * @access public
  290. * @return array
  291. */
  292. public function getInvolvedExecutions($projects)
  293. {
  294. $executions = $this->dao->select('id,id')->from(TABLE_PROJECT)
  295. ->where('type')->in('stage,sprint')
  296. ->andWhere('project')->in(array_keys($projects))
  297. ->andWhere('deleted')->eq('0')
  298. ->fetchPairs();
  299. return $this->dao->select('account, count(root) as executions')->from(TABLE_TEAM)
  300. ->where('root')->in(array_keys($executions))
  301. ->andWhere('type')->in('execution')
  302. ->groupBy('account')
  303. ->fetchPairs('account');
  304. }
  305. /**
  306. * 获取投入项目的任务。
  307. * Get project task invest.
  308. *
  309. * @param array $projects
  310. * @param array $accounts
  311. * @access public
  312. * @return array
  313. */
  314. public function getProjectTaskInvest($projects, $accounts)
  315. {
  316. /* Get the tasks in the projects. */
  317. $tasks = $this->dao->select('id,status,openedBy,finishedBy,assignedTo,project,`left`,mode')->from(TABLE_TASK)
  318. ->where('project')->in(array_keys($projects))
  319. ->andWhere('deleted')->eq('0')
  320. ->fetchAll('id');
  321. $taskTeams = $this->dao->select('account,task,`left`')->from(TABLE_TASKTEAM)->where('task')->in(array_keys($tasks))->andWhere('status')->eq('wait')->fetchGroup('task', 'account');
  322. /* Initialize personnel related tasks. */
  323. $invest = array();
  324. foreach($accounts as $account => $project)
  325. {
  326. $invest[$account]['createdTask'] = $invest[$account]['finishedTask'] = $invest[$account]['pendingTask'] = $invest[$account]['consumedTask'] = $invest[$account]['leftTask'] = 0;
  327. }
  328. /* Number of tasks per person. */
  329. $userTasks = array();
  330. foreach($tasks as $task)
  331. {
  332. if($task->openedBy && isset($invest[$task->openedBy]))
  333. {
  334. $invest[$task->openedBy]['createdTask'] += 1;
  335. $userTasks[$task->openedBy][$task->id] = $task->id;
  336. }
  337. if($task->finishedBy && isset($invest[$task->finishedBy]))
  338. {
  339. $invest[$task->finishedBy]['finishedTask'] += 1;
  340. $userTasks[$task->finishedBy][$task->id] = $task->id;
  341. }
  342. if($task->assignedTo && empty($task->mode))
  343. {
  344. if($task->status == 'wait')
  345. {
  346. $invest[$task->assignedTo]['pendingTask'] += 1;
  347. if(!isset($invest[$task->assignedTo]['leftTask'])) $invest[$task->assignedTo]['leftTask'] = 0;
  348. $invest[$task->assignedTo]['leftTask'] += $task->left;
  349. }
  350. $userTasks[$task->assignedTo][$task->id] = $task->id;
  351. }
  352. if(!empty($taskTeams[$task->id]) && !empty($task->mode))
  353. {
  354. foreach(array_keys($taskTeams[$task->id]) as $team)
  355. {
  356. if(!isset($invest[$team])) continue;
  357. if(!isset($invest[$team]['leftTask'])) $invest[$team]['leftTask'] = 0;
  358. $invest[$team]['leftTask'] += $task->left;
  359. $userTasks[$team][$task->id] = $task->id;
  360. }
  361. }
  362. }
  363. /* The number of hours per person. */
  364. $userHours = $this->getUserHours($userTasks);
  365. foreach($userHours as $account => $hours)
  366. {
  367. if(!isset($invest[$account]['leftTask'])) $invest[$account]['leftTask'] = 0;
  368. $invest[$account]['leftTask'] += $hours->left;
  369. $invest[$account]['consumedTask'] = $hours->consumed;
  370. }
  371. return $invest;
  372. }
  373. /**
  374. * 获取用户工时。
  375. * Get user hours.
  376. *
  377. * @param array $userTasks
  378. * @access public
  379. * @return array
  380. */
  381. public function getUserHours($userTasks)
  382. {
  383. $accounts = array();
  384. $taskIdList = array();
  385. foreach($userTasks as $account => $taskID)
  386. {
  387. $accounts[] = $account;
  388. $taskIdList = array_merge($taskIdList, $taskID);
  389. }
  390. $userHours = array();
  391. $effortList = $this->dao->select('id,account,objectID,`left`,consumed')->from(TABLE_EFFORT)
  392. ->where('account')->in($accounts)
  393. ->andWhere('deleted')->eq(0)
  394. ->andWhere('objectID')->in($taskIdList)
  395. ->andWhere('objectType')->eq('task')
  396. ->orderBy('id_asc')
  397. ->fetchGroup('account', 'id');
  398. foreach($effortList as $account => $efforts)
  399. {
  400. $latestLeft = array();
  401. $userHours[$account] = new stdclass();
  402. $userHours[$account]->left = 0;
  403. $userHours[$account]->consumed = 0;
  404. foreach($efforts as $effort)
  405. {
  406. $latestLeft[$effort->objectID] = $effort->left;
  407. $userHours[$account]->consumed += $effort->consumed;
  408. }
  409. $userHours[$account]->left = array_sum($latestLeft);
  410. }
  411. return $userHours;
  412. }
  413. /**
  414. * 获取要复制的对象。
  415. * Get objects to copy.
  416. *
  417. * @param int $objectID
  418. * @param string $objectType
  419. * @param bool $addCount
  420. * @access public
  421. * @return array
  422. */
  423. public function getCopiedObjects($objectID, $objectType, $addCount = false)
  424. {
  425. /* Get objects. */
  426. $objects = array();
  427. if($objectType == 'sprint')
  428. {
  429. $execution = $this->loadModel('execution')->getByID($objectID);
  430. $parentID = $execution->project;
  431. $project = $this->execution->getByID($parentID);
  432. $objects = array($project->id => $project->name);
  433. $objects += $this->dao->select('id,name')->from(TABLE_EXECUTION)
  434. ->where('project')->eq($parentID)
  435. ->andWhere('id')->in($this->app->user->view->sprints)
  436. ->andWhere('deleted')->eq(0)
  437. ->orderBy('openedDate_desc')
  438. ->limit('10')
  439. ->fetchPairs();
  440. }
  441. elseif($objectType == 'project')
  442. {
  443. $path = $this->dao->select('path')->from(TABLE_PROJECT)->where('id')->eq($objectID)->fetch('path');
  444. $path = explode(',', trim($path, ','));
  445. $parentID = $path[0] == $objectID ? 0 : $path[0];
  446. $objects = $this->loadModel('project')->getPairsByProgram((int)$parentID);
  447. }
  448. elseif($objectType == 'product')
  449. {
  450. $parentID = $this->dao->select('program')->from(TABLE_PRODUCT)->where('id')->eq($objectID)->fetch('program');
  451. $objects = $this->loadModel('product')->getPairs('', $parentID);
  452. }
  453. elseif($objectType == 'program')
  454. {
  455. $objects = $this->loadModel('program')->getPairs();
  456. }
  457. unset($objects[$objectID]);
  458. if(!$addCount) return $objects;
  459. /* Append object count. */
  460. if($objectType == 'sprint') $objectType = 'execution';
  461. $countPairs = $this->dao->select('root, COUNT(1) AS count')->from(TABLE_TEAM)
  462. ->where('type')->eq($objectType)
  463. ->andWhere('root')->in(array_keys($objects))
  464. ->beginIF($objectType == 'execution')->orWhere('(type')->eq('project')->andWhere('root')->eq($parentID)->markRight(1)->fi()
  465. ->groupBy('root')
  466. ->fetchPairs('root');
  467. foreach($objects as $objectID => $objectName)
  468. {
  469. $memberCount = zget($countPairs, $objectID, 0);
  470. $countTip = $memberCount > 1 ? str_replace('member', 'members', $this->lang->personnel->countTip) : $this->lang->personnel->countTip;
  471. $objects[$objectID] = $objectName . sprintf($countTip, $memberCount);
  472. }
  473. return $objects;
  474. }
  475. /**
  476. * 获取项目集的白名单。
  477. * Get whitelist.
  478. *
  479. * @param int $objectID
  480. * @param string $objectType program|project|product|sprint
  481. * @param string $orderBy
  482. * @param object $pager
  483. * @access public
  484. * @return array
  485. */
  486. public function getWhitelist($objectID = 0, $objectType = '', $orderBy = 'id_desc', $pager = null)
  487. {
  488. return $this->dao->select('t1.id,t1.account,t2.realname,t2.dept,t2.role,t2.phone,t2.qq,t2.weixin,t2.email')->from(TABLE_ACL)->alias('t1')
  489. ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account')
  490. ->where('t1.objectID')->eq($objectID)
  491. ->andWhere('t1.type')->eq('whitelist')
  492. ->andWhere('t1.objectType')->eq($objectType)
  493. ->andWhere('t2.realname')->ne('')
  494. ->orderBy($orderBy)
  495. ->page($pager)
  496. ->fetchAll();
  497. }
  498. /**
  499. * 测试获取对象白名单人员账号。
  500. * Get whitelisted accounts.
  501. *
  502. * @param int $objectID
  503. * @param string $objectType
  504. * @access public
  505. * @return array
  506. */
  507. public function getWhitelistAccount($objectID = 0, $objectType = '')
  508. {
  509. return $this->dao->select('account,account')->from(TABLE_ACL)->where('objectID')->eq($objectID)->andWhere('objectType')->eq($objectType)->fetchPairs();
  510. }
  511. /**
  512. * 更新白名单人员。
  513. * Update users from access control lists.
  514. *
  515. * @param array $users
  516. * @param string $objectType program|project|product|sprint
  517. * @param int $objectID
  518. * @param string $type whitelist|blacklist
  519. * @param string $source upgrade|add|sync
  520. * @param string $updateType increase|replace
  521. * @access public
  522. * @return bool
  523. */
  524. public function updateWhitelist($users = array(), $objectType = '', $objectID = 0, $type = 'whitelist', $source = 'add', $updateType = 'replace')
  525. {
  526. $oldWhitelist = $this->dao->select('account,objectType,objectID,type,source')->from(TABLE_ACL)->where('objectID')->eq($objectID)->andWhere('objectType')->eq($objectType)->fetchAll('account');
  527. if($updateType == 'replace') $this->dao->delete()->from(TABLE_ACL)->where('objectID')->eq($objectID)->andWhere('objectType')->eq($objectType)->exec();
  528. $users = array_unique(array_filter($users));
  529. $accounts = array();
  530. foreach($users as $account)
  531. {
  532. $accounts[$account] = $account;
  533. if(isset($oldWhitelist[$account]))
  534. {
  535. if($updateType == 'replace') $this->dao->insert(TABLE_ACL)->data($oldWhitelist[$account])->exec();
  536. continue;
  537. }
  538. $acl = new stdClass();
  539. $acl->account = $account;
  540. $acl->objectType = $objectType;
  541. $acl->objectID = $objectID;
  542. $acl->type = $type;
  543. $acl->source = $source;
  544. $this->dao->insert(TABLE_ACL)->data($acl)->autoCheck()->exec();
  545. if(!dao::isError()) $this->loadModel('user')->updateUserView(array($acl->objectID), $acl->objectType, array($acl->account));
  546. }
  547. /* Update whitelist field of the object. */
  548. $objectTable = $objectType == 'product' ? TABLE_PRODUCT : TABLE_PROJECT;
  549. if($updateType == 'increase')
  550. {
  551. $oldWhitelist = $this->dao->select('whitelist')->from($objectTable)->where('id')->eq($objectID)->fetch('whitelist');
  552. $groups = $this->dao->select('id')->from(TABLE_GROUP)->where('id')->in($oldWhitelist)->fetchPairs('id', 'id');
  553. $oldWhitelist = $groups ? $this->dao->select('account,account')->from(TABLE_USERGROUP)->where('`group`')->in($groups)->fetchPairs() : $this->dao->select('account,account')->from(TABLE_USER)->where('account')->in($oldWhitelist)->fetchPairs();
  554. if($oldWhitelist) $accounts = array_unique(array_merge($accounts, $oldWhitelist));
  555. }
  556. $whitelist = !empty($accounts) ? ',' . implode(',', $accounts) : '';
  557. $whitelist = trim($whitelist, ',');
  558. $this->dao->update($objectTable)->set('whitelist')->eq($whitelist)->where('id')->eq($objectID)->exec();
  559. /* Get the accounts that have been deleted from the whitelist. */
  560. $deletedAccounts = array_diff(array_keys($oldWhitelist), $accounts);
  561. /* Synchronization of people from the product whitelist to the program set. */
  562. $this->updateParentWhitelist($objectType, $objectID, $accounts, $source, $updateType, $deletedAccounts, $objectTable);
  563. /* Update user view. */
  564. $this->loadModel('user')->updateUserView(array($objectID), $objectType, $deletedAccounts);
  565. return !dao::isError();
  566. }
  567. /**
  568. * 更新对象的父级白名单。
  569. * Update parent whitelist of the object.
  570. *
  571. * @param string $objectType
  572. * @param int $objectID
  573. * @param array $accounts
  574. * @param string $source
  575. * @param string $updateType
  576. * @param array $deletedAccounts
  577. * @param string $objectTable
  578. * @access private
  579. * @return bool
  580. */
  581. private function updateParentWhitelist($objectType, $objectID, $accounts, $source, $updateType, $deletedAccounts, $objectTable)
  582. {
  583. if($objectType != 'product' && $objectType != 'sprint') return false;
  584. $field = $objectType == 'product' ? 'program' : 'project';
  585. $object = $this->dao->select('id,' . $field)->from($objectTable)->where('id')->eq($objectID)->fetch();
  586. if(empty($object)) return false;
  587. /* Update parent whitelist. */
  588. $parentWhitelist = $this->getWhitelistAccount($object->{$field}, $field);
  589. $newWhitelist = array_merge($parentWhitelist, $accounts);
  590. $source = $source == 'upgrade' ? 'upgrade' : 'sync';
  591. $this->updateWhitelist($newWhitelist, $field, $object->{$field}, 'whitelist', $source, $updateType);
  592. /* Removal of persons from centralized parent whitelisting. */
  593. if($updateType == 'replace')
  594. {
  595. foreach($deletedAccounts as $account) $this->deleteProgramWhitelist($objectID, $account);
  596. }
  597. return !dao::isError();
  598. }
  599. /**
  600. * 从产品的白名单内删除用户。
  601. * Delete product whitelist.
  602. *
  603. * @param int $productID
  604. * @param string $account
  605. * @access public
  606. * @return bool
  607. */
  608. public function deleteProductWhitelist($productID, $account = '')
  609. {
  610. $product = $this->dao->select('id,whitelist')->from(TABLE_PRODUCT)->where('id')->eq($productID)->fetch();
  611. if(empty($product)) return false;
  612. /* Delete the account's acl in the product. */
  613. $result = $this->dao->delete()->from(TABLE_ACL)
  614. ->where('objectID')->eq($productID)
  615. ->andWhere('account')->eq($account)
  616. ->andWhere('objectType')->eq('product')
  617. ->andWhere('source')->eq('sync')
  618. ->exec();
  619. /* Update user view when delete success. */
  620. if($result)
  621. {
  622. $newWhitelist = str_replace(',' . $account, '', $product->whitelist);
  623. $this->dao->update(TABLE_PRODUCT)->set('whitelist')->eq($newWhitelist)->where('id')->eq($productID)->exec();
  624. $viewProducts = $this->dao->select('products')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch('products');
  625. $newViewProducts = trim(str_replace(",{$productID},", ',', ",{$viewProducts},"), ',');
  626. $this->dao->update(TABLE_USERVIEW)->set('products')->eq($newViewProducts)->where('account')->eq($account)->exec();
  627. }
  628. return !dao::isError();
  629. }
  630. /**
  631. * 从项目集的白名单内删除用户。
  632. * Delete project whitelist.
  633. *
  634. * @param int $programID
  635. * @param string $account
  636. * @access public
  637. * @return void
  638. */
  639. public function deleteProgramWhitelist($programID = 0, $account = '')
  640. {
  641. $program = $this->loadModel('program')->getByID($programID);
  642. if(empty($program)) return false;
  643. /* Get the product's whitelist in the program. */
  644. $products = $this->dao->select('id')->from(TABLE_PRODUCT)->where('program')->eq($programID)->andWhere('deleted')->eq('0')->fetchPairs('id');
  645. $whitelist = $this->dao->select('*')->from(TABLE_ACL)->where('objectID')->in($products)->andWhere('account')->eq($account)->andWhere('objectType')->eq('product')->fetch();
  646. /* Determine if the user exists in other products in the program set. */
  647. if(empty($whitelist))
  648. {
  649. $result = $this->dao->delete()->from(TABLE_ACL)
  650. ->where('objectID')->eq($programID)
  651. ->andWhere('account')->eq($account)
  652. ->andWhere('objectType')->eq('program')
  653. ->andWhere('source')->eq('sync')
  654. ->exec();
  655. /* Update user view when delete success. */
  656. if($result)
  657. {
  658. $newWhitelist = str_replace(',' . $account, '', $program->whitelist);
  659. $this->dao->update(TABLE_PROGRAM)->set('whitelist')->eq($newWhitelist)->where('id')->eq($programID)->exec();
  660. $viewPrograms = $this->dao->select('programs')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch('programs');
  661. $newViewPrograms = trim(str_replace(",{$programID},", '', ",{$viewPrograms},"), ',');
  662. $this->dao->update(TABLE_USERVIEW)->set('programs')->eq($newViewPrograms)->where('account')->eq($account)->exec();
  663. }
  664. }
  665. $this->loadModel('user')->updateUserView(array($programID), 'program', array($account));
  666. return !dao::isError();
  667. }
  668. /**
  669. * 从项目的白名单内删除用户。
  670. * Delete project whitelist.
  671. *
  672. * @param int $objectID
  673. * @param string $account
  674. * @access public
  675. * @return bool
  676. */
  677. public function deleteProjectWhitelist($objectID = 0, $account = '')
  678. {
  679. $project = $this->dao->select('id,project,whitelist')->from(TABLE_PROJECT)->where('id')->eq($objectID)->fetch();
  680. if(empty($project)) return false;
  681. /* Get project's whitelist. */
  682. $projectID = $project->project ? $project->project : $objectID;
  683. $sprints = $this->dao->select('id')->from(TABLE_PROJECT)->where('project')->eq($projectID)->andWhere('deleted')->eq('0')->fetchPairs('id');
  684. $whitelist = $this->dao->select('*')->from(TABLE_ACL)->where('objectID')->in($sprints)->andWhere('account')->eq($account)->andWhere('objectType')->eq('sprint')->fetch();
  685. /* Determine if the user exists in other sprints in the project set. */
  686. if(empty($whitelist))
  687. {
  688. $result = $this->dao->delete()->from(TABLE_ACL)
  689. ->where('objectID')->eq($projectID)
  690. ->andWhere('account')->eq($account)
  691. ->andWhere('objectType')->eq('project')
  692. ->andWhere('source')->eq('sync')
  693. ->exec();
  694. /* Update user view when delete success. */
  695. if($result)
  696. {
  697. $newWhitelist = str_replace(',' . $account, '', $project->whitelist);
  698. $this->dao->update(TABLE_PROJECT)->set('whitelist')->eq($newWhitelist)->where('id')->eq($projectID)->exec();
  699. $viewProjects = $this->dao->select('projects')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch('projects');
  700. $newViewProjects = trim(str_replace(",{$projectID},", ',', ",{$viewProjects},"), ',');
  701. $this->dao->update(TABLE_USERVIEW)->set('projects')->eq($newViewProjects)->where('account')->eq($account)->exec();
  702. }
  703. }
  704. $this->loadModel('user')->updateUserView(array($projectID), 'project', array($account));
  705. return !dao::isError();
  706. }
  707. /**
  708. * 删除执行的白名单。
  709. * Delete execution whitelist.
  710. *
  711. * @param int $executionID
  712. * @param string $account
  713. * @access public
  714. * @return bool
  715. */
  716. public function deleteExecutionWhitelist($executionID, $account = '')
  717. {
  718. $execution = $this->dao->select('id,whitelist')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch();
  719. if(empty($execution)) return false;
  720. /* Delete account's acl in the execution. */
  721. $result = $this->dao->delete()->from(TABLE_ACL)
  722. ->where('objectID')->eq($executionID)
  723. ->andWhere('account')->eq($account)
  724. ->andWhere('objectType')->eq('sprint')
  725. ->andWhere('source')->eq('sync')
  726. ->exec();
  727. /* Update user view when delete success. */
  728. if($result)
  729. {
  730. $newWhitelist = str_replace(',' . $account, '', $execution->whitelist);
  731. $this->dao->update(TABLE_EXECUTION)->set('whitelist')->eq($newWhitelist)->where('id')->eq($executionID)->exec();
  732. $viewExecutions = $this->dao->select('sprints')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch('sprints');
  733. $newViewExecutions = trim(str_replace(",{$executionID},", '', ",{$viewExecutions},"), ',');
  734. $this->dao->update(TABLE_USERVIEW)->set('sprints')->eq($newViewExecutions)->where('account')->eq($account)->exec();
  735. }
  736. return !dao::isError();
  737. }
  738. /**
  739. * 根据部门创建访问链接。
  740. * Create access links by department.
  741. *
  742. * @param object $dept
  743. * @param int $programID
  744. * @access public
  745. * @return string
  746. */
  747. public function createMemberLink($dept = null, $programID = 0)
  748. {
  749. return helper::createLink('personnel', 'accessible', "program={$programID}&deptID={$dept->id}");
  750. }
  751. /**
  752. * 构建搜索表单。
  753. * Build search form.
  754. *
  755. * @param int $queryID
  756. * @param string $actionURL
  757. * @access public
  758. * @return void
  759. */
  760. public function buildSearchForm($queryID = 0, $actionURL = '')
  761. {
  762. $this->config->personnel->accessible->search['actionURL'] = $actionURL;
  763. $this->config->personnel->accessible->search['queryID'] = $queryID;
  764. $this->loadModel('search')->setSearchParams($this->config->personnel->accessible->search);
  765. }
  766. /**
  767. * 判断操作是否可以点击。
  768. * Judge an action is clickable or not.
  769. *
  770. * @param object $whitelist
  771. * @param string $action
  772. * @access public
  773. * @return bool
  774. */
  775. public function isClickable($whitelist, $action)
  776. {
  777. return true;
  778. }
  779. /**
  780. * 从白名单中移除人员。
  781. * Removing users from the white list.
  782. *
  783. * @param object $acl
  784. * @access public
  785. * @return bool
  786. */
  787. public function unbindWhitelist($acl)
  788. {
  789. /* Update whitelist and delete acl. */
  790. $objectTable = $acl->objectType == 'product' ? TABLE_PRODUCT : TABLE_PROJECT;
  791. $whitelist = $this->dao->select('whitelist')->from($objectTable)->where('id')->eq($acl->objectID)->fetch('whitelist');
  792. $newWhitelist = str_replace(",{$acl->account},", '', ",{$whitelist},");
  793. $newWhitelist = trim($newWhitelist, ',');
  794. $this->dao->update($objectTable)->set('whitelist')->eq($newWhitelist)->where('id')->eq($acl->objectID)->exec();
  795. $this->dao->delete()->from(TABLE_ACL)->where('id')->eq($acl->id)->exec();
  796. if($acl->objectType == 'project')
  797. {
  798. $project = $this->loadModel('project')->getByID($acl->objectID);
  799. if(!$project->hasProduct)
  800. {
  801. $shadowProduct = $this->loadModel('product')->getShadowProductByProject($acl->objectID);
  802. if($shadowProduct)
  803. {
  804. $this->dao->update(TABLE_PRODUCT)->set('whitelist')->eq($newWhitelist)->where('id')->eq($shadowProduct->id)->exec();
  805. $this->dao->delete()->from(TABLE_ACL)->where('objectType')->eq('product')->andWhere('objectID')->eq($shadowProduct->id)->andWhere('account')->eq($acl->account)->exec();
  806. if($shadowProduct->program) $this->deleteProgramWhitelist($shadowProduct->program, $acl->account);
  807. $this->loadModel('user')->updateUserView(array($shadowProduct->id), 'product', array($acl->account));
  808. }
  809. }
  810. }
  811. return !dao::isError();
  812. }
  813. }