control.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. /**
  3. * The control file of block module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yidong Wang <yidong@cnezsoft.com>
  8. * @package block
  9. * @link https://www.zentao.net
  10. */
  11. class block extends control
  12. {
  13. /**
  14. * construct.
  15. *
  16. * @access public
  17. * @return void
  18. */
  19. public function __construct($moduleName = '', $methodName = '')
  20. {
  21. parent::__construct($moduleName, $methodName);
  22. /* 如果为外部调用,判断密钥,如果密钥不通过,返回空字符串。 */
  23. if($this->methodName != 'admin' && $this->methodName != 'dashboard' && $this->isExternalCall() && !$this->loadModel('sso')->checkKey()) helper::end('');
  24. }
  25. /**
  26. * 展示当前仪表盘下的区块列表。
  27. * Display blocks for dashboard.
  28. *
  29. * @param string $dashboard
  30. * @param int $projectID
  31. * @access public
  32. * @return void
  33. */
  34. public function dashboard($dashboard, $projectID = 0)
  35. {
  36. /* 获取传入应用对应的区块列表 以及 获取当前应用下区块启用状态。 */
  37. $blocks = $this->block->getMyDashboard($dashboard);
  38. $isInitiated = $this->block->getBlockInitStatus($dashboard);
  39. /* 判断用户是否为首次登录 ,判断条件 当前用户没有该 app 下的区块数据 且 没有设置过该 app 下的区块启用状态 且不是演示模式。 */
  40. if(empty($blocks) && !$isInitiated && !commonModel::isTutorialMode())
  41. {
  42. $this->blockZen->initBlock($dashboard); // 初始化该 app 下区块数据。
  43. $blocks = $this->block->getMyDashboard($dashboard); // 获取初始化后的区块列表。
  44. }
  45. /* 处理页面需要的数据格式。 */
  46. $blocks = $this->blockZen->processBlockForRender($blocks, $projectID);
  47. /* 如果页面渲染方式为 json 的话,直接返回 json 格式数据 ,主要是为了给应用提供数据。 */
  48. if($this->app->getViewType() == 'json') return print(json_encode($blocks));
  49. /* 为项目仪表盘页面,设置1.5级导航的项目ID. */
  50. if($this->app->rawModule == 'project' && $this->app->rawMethod == 'index') $this->view->projectID = $this->view->objectID = $this->session->project;
  51. if(($this->app->rawModule == 'qa' && $this->app->rawMethod == 'index') || ($this->app->rawModule == 'product' && $this->app->rawMethod == 'dashboard')) $this->view->productID = $this->view->objectID = $this->session->product;
  52. $this->view->title = zget($this->lang->block->dashboard, $dashboard, $this->lang->block->dashboard['default']);
  53. $this->view->blocks = $blocks;
  54. $this->view->projectID = $projectID;
  55. $this->view->dashboard = $dashboard;
  56. $this->display();
  57. }
  58. /**
  59. * 输出一个区块信息。
  60. * Print a block.
  61. *
  62. * @param int $blockID
  63. * @param string $params
  64. * @access public
  65. * @return void
  66. */
  67. public function printBlock($blockID, $params = '')
  68. {
  69. session_write_close();
  70. $block = $this->block->getByID($blockID);
  71. /* 如果是外部调用,判断密码并组织外部需要的返回信息。 */
  72. if($this->blockZen->isExternalCall())
  73. {
  74. if(!$this->block->checkAPI($this->get->hash)) return;
  75. $this->blockZen->organizaExternalData($block); // 组织外部需要返回的信息。
  76. }
  77. if(empty($block)) return '';
  78. if($params)
  79. {
  80. $params = helper::safe64Decode($params);
  81. parse_str($params, $params);
  82. }
  83. /* 根据 block 的 code 值,选择性调用 zen 中 print + $code + Block 方法获取区块数据。 */
  84. if(isset($block->params->num) && !isset($block->params->count)) $block->params->count = $block->params->num;
  85. $code = $block->code;
  86. if($code == 'statistic' || $code == 'list' || $code == 'overview' || $code == 'team') $code = $block->module . ucfirst($code);
  87. $function = 'print' . ucfirst($code) . 'Block';
  88. if(method_exists('blockZen', $function)) $this->blockZen->$function($block, (array)$params);
  89. /* 补全 moreLink 信息。 */
  90. $projectID = isset($params['projectID']) ? $params['projectID'] : 0;
  91. $this->blockZen->createMoreLink($block, (int)$projectID);
  92. /* 组织渲染页面需要的数据。*/
  93. $this->view->moreLink = $block->moreLink;
  94. $this->view->block = $block;
  95. $this->view->longBlock = $block->width >= 2;
  96. $this->view->isExternalCall = $this->blockZen->isExternalCall();
  97. $this->view->params = $params;
  98. $this->view->metricTime = $this->dao->select('lastTime')->from(TABLE_CRON)->where('command')->eq('moduleName=metric&methodName=updateDashboardMetricLib')->fetch('lastTime');
  99. /* 根据 viewType 值 ,判断是否需要返回 json 数据。 */
  100. $viewType = (isset($block->params->viewType) && $block->params->viewType == 'json') ? 'json' : 'html';
  101. if($viewType == 'json') return $this->blockZen->printBlock4Json();
  102. $this->display('block', strtolower($code) . 'block');
  103. }
  104. /**
  105. * 创建区块。
  106. * Create a block under a dashboard.
  107. *
  108. * @param string $dashboard
  109. * @param string $module
  110. * @param string $code
  111. * @access public
  112. * @return void
  113. */
  114. public function create($dashboard, $module = '', $code = '')
  115. {
  116. /* 处理表单提交事件。 */
  117. if($_POST)
  118. {
  119. /* 获取表单提交内容。 */
  120. $formData = form::data($this->config->block->form->create)->get();
  121. $formData->dashboard = $dashboard;
  122. $formData->account = $this->app->user->account;
  123. $formData->vision = $this->config->vision;
  124. /* 如果是HTML区块,则对html区块数据做处理后将内容存放到params字段中。 */
  125. if($formData->module == 'html')
  126. {
  127. $formData = $this->loadModel('file')->processImgURL($formData, 'html', $this->post->uid);
  128. $formData->params['html'] = $formData->html;
  129. }
  130. /* 将params数组转成json格式,方便在数据库中存储。 */
  131. $formData->params = json_encode($formData->params);
  132. unset($formData->html);
  133. /* 根据module和code生成区块的宽度和高度。 */
  134. $defaultSize = $this->config->block->defaultSize; // 默认为区块的统一默认尺寸。
  135. if(!empty($this->config->block->size[$formData->module][$formData->code])) $defaultSize = $this->config->block->size[$formData->module][$formData->code];
  136. if(!empty($this->config->block->size[$formData->module][$formData->code][$formData->width])) $formData->height = $this->config->block->size[$formData->module][$formData->code][$formData->width];
  137. if(empty($formData->width)) $formData->width = reset(array_keys($defaultSize));
  138. if(empty($formData->height)) $formData->height = reset($defaultSize);
  139. /* 设置区块距离左侧的宽度和距离顶部的高度。 */
  140. $formData->left = $formData->width == 1 ? 2 : 0;
  141. $formData->top = $this->block->computeBlockTop($formData);
  142. /* 执行区块的数据插入并且返回数据给view层。 */
  143. $blockID = $this->block->create($formData);
  144. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  145. $this->loadModel('score')->create('block', 'set');
  146. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => "loadComponent('#dashboard'); setTimeout(() => $('#dashboard .dashboard-block[data-id=\"$blockID\"]').scrollIntoView({behavior: 'smooth'}), 500);", 'closeModal' => true));
  147. }
  148. $modules = $this->blockZen->getAvailableModules($dashboard); // 不同的仪表盘获取不同的可选择的模块列表。
  149. unset($modules['']);
  150. if(empty($modules)) $module = $dashboard; // 如果该仪表盘下没有模块列表,则模块同仪表盘。
  151. if(empty($module) && !empty($modules)) $module = current(array_keys($modules)); // 如果当前没有选择模块,则选中第一个。
  152. $codes = $this->blockZen->getAvailableCodes($module); // 根据仪表盘和模块获取可用的区块列表。
  153. $code = $codes && empty($code) ? key($codes) : $code;
  154. $params = $this->blockZen->getAvailableParams($module, $code); // 根据所属模块和区块code获取参数配置项列表。
  155. $this->view->title = $this->lang->block->createBlock;
  156. $this->view->dashboard = $dashboard;
  157. $this->view->module = $module;
  158. $this->view->code = $code;
  159. $this->view->modules = $modules;
  160. $this->view->codes = $codes;
  161. $this->view->params = $params;
  162. $this->view->blockTitle = $this->blockZen->getBlockTitle($modules, $module, $codes, $code, $params);
  163. $this->display();
  164. }
  165. /**
  166. * 编辑区块。
  167. * Edit a block.
  168. *
  169. * @param int $blockID
  170. * @param string $module
  171. * @param string $code
  172. * @access public
  173. * @return void
  174. */
  175. public function edit($blockID, $module = '', $code = '')
  176. {
  177. /* 处理表单提交事件。 */
  178. if($_POST)
  179. {
  180. /* 获取表单提交内容。 */
  181. $formData = form::data($this->config->block->form->edit)->get();
  182. $formData->id = $blockID;
  183. /* 如果是HTML区块,则对html区块数据做处理后将内容存放到params字段中。 */
  184. if($formData->module == 'html')
  185. {
  186. $formData = $this->loadModel('file')->processImgURL($formData, 'html', $this->post->uid);
  187. $formData->params['html'] = $formData->html;
  188. }
  189. /* 将params数组转成json格式,方便在数据库中存储。 */
  190. $formData->params = json_encode($formData->params);
  191. unset($formData->html);
  192. /* 根据表单中选择的宽度匹配配置项中的高度。 */
  193. if(!empty($this->config->block->size[$formData->module][$formData->code][$formData->width])) $formData->height = $this->config->block->size[$formData->module][$formData->code][$formData->width];
  194. /* 根据区块ID获取更新前区块信息,判断区块宽度是否发生变化。 */
  195. $block = $this->block->getByID($blockID);
  196. $isWidthChanged = $block->width != $formData->width;
  197. /* 执行区块的数据变更并且返回数据给view层。 */
  198. $this->block->update($formData);
  199. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  200. $this->loadModel('score')->create('block', 'set');
  201. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => $isWidthChanged ? "loadComponent('#dashboard')" : "$('#dashboard').dashboard('load', '$blockID')", 'closeModal' => true));
  202. }
  203. $block = $this->block->getByID($blockID); // 根据区块ID获取区块信息。
  204. $module = $module ? $module : $block->module; // 获取当前选中的模块,默认取区块的所属模块。
  205. $code = $code ? $code : $block->code; // 获取当前选中的区块code,默认取区块的所属code。
  206. $modules = $this->blockZen->getAvailableModules($block->dashboard); // 根据所属仪表盘获取可选择的模块列表。
  207. $codes = $this->blockZen->getAvailableCodes($module); // 根据所属仪表盘和选中模块获取可选择的区块列表。
  208. $params = $this->blockZen->getAvailableParams($module, $code); // 根据当前选中模块和选中区块code获取参数配置项列表。
  209. $this->view->title = $this->lang->block->editBlock;
  210. $this->view->block = $block;
  211. $this->view->dashboard = $block->dashboard;
  212. $this->view->module = $module;
  213. $this->view->modules = $this->blockZen->getAvailableModules($block->dashboard);
  214. $this->view->codes = $codes;
  215. $this->view->code = in_array($code, array_keys($codes)) ? $code : ''; // 当变更了所属模块(module)后, 判断当前code在是否还包含在codes中,没有的话置空code。
  216. $this->view->params = $params;
  217. $this->view->blockTitle = $module == $block->module && $code == $block->code ? $block->title : $this->blockZen->getBlockTitle($modules, $module, $codes, $code, $params);
  218. $this->display();
  219. }
  220. /**
  221. * 根据区块ID删除一个区块。
  222. * Delete a block by id.
  223. *
  224. * @param int $blockID
  225. * @access public
  226. * @return void
  227. */
  228. public function delete($blockID)
  229. {
  230. /* 执行区块的数据删除并且返回数据给view层。 */
  231. $this->block->deleteBlock($blockID);
  232. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  233. $this->loadModel('score')->create('block', 'set'); // 设置区块后的积分奖励操作。
  234. return $this->send(array('result' => 'success', 'load' => true));
  235. }
  236. /**
  237. * 永久关闭区块。
  238. * Close block forever.
  239. *
  240. * @param int $blockID
  241. * @access public
  242. * @return void
  243. */
  244. public function close($blockID)
  245. {
  246. /* 所有配置过该区块的人都要删掉此区块,所以要按照module和code来删除, 不能用ID来删。 */
  247. $block = $this->block->getByID($blockID);
  248. $this->block->deleteBlock(0, $block->module, $block->code);
  249. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  250. /* 将关闭的区块保存到配置信息以便恢复时使用。 */
  251. $closedBlock = isset($this->config->block->closed) ? $this->config->block->closed : '';
  252. $this->loadModel('setting')->setItem('system.block.closed', $closedBlock . ",{$block->module}|{$block->code}");
  253. return $this->send(array('result' => 'success'));
  254. }
  255. /**
  256. * 将当前用户该仪表盘下的区块恢复默认布局。
  257. * Reset dashboard layout.
  258. *
  259. * @param string $dashboard
  260. * @access public
  261. * @return void
  262. */
  263. public function reset($dashboard)
  264. {
  265. /* 执行恢复布局并且返回数据给view层。 */
  266. $this->block->reset($dashboard);
  267. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  268. return $this->send(array('result' => 'success', 'load' => true));
  269. }
  270. /**
  271. * 修改当前用户仪表盘区块的布局。
  272. * Change dashboard layout.
  273. *
  274. * @access public
  275. * @return void
  276. */
  277. public function layout()
  278. {
  279. if(!$_POST) return $this->send(array('result' => 'fail', 'message' => $this->lang->error->noData));
  280. $blocksLayout = $this->post->block;
  281. $this->block->updateLayout($blocksLayout);
  282. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  283. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
  284. }
  285. }