view.html.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. /**
  3. * The view view file of flow module of ZenTaoPMS.
  4. * @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  5. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  6. * @author Yidong Wang <yidong@chandao.com>
  7. * @package flow
  8. * @link https://www.zentao.net
  9. */
  10. namespace zin;
  11. include 'header.html.php';
  12. $activeTab = $flow->module;
  13. if($currentType)
  14. {
  15. if($currentMode == 'bysearch') $activeTab = 'common';
  16. if(isset($linkedDatas[$currentType])) $activeTab = $currentType;
  17. }
  18. $canLink = $this->flow->isClickable($flow->module, 'link', $data);
  19. $sessionName = $flow->module . 'List';
  20. $browseLink = $this->session->$sessionName ? $this->session->$sessionName : $this->createLink($flow->module, 'browse');
  21. $loadLink = $this->createLink($flow->module, 'link', "dataID=$data->id&linkType=LINKTYPE&mode=MODE");
  22. jsVar('moduleName', $flow->module);
  23. jsVar('linkType', $currentType);
  24. jsVar('viewMode', $currentMode);
  25. jsVar('loadLink', $loadLink);
  26. detailHeader
  27. (
  28. to::prefix(backBtn
  29. (
  30. setClass('primary'),
  31. set::icon('back'),
  32. setData('app', $app->tab),
  33. $lang->goback
  34. ), entityLabel(set(array('entityID' => $data->id, 'text' => zget($dataPairs, $data->id), 'textClass' => array('font-bold')))))
  35. );
  36. $obj = $this;
  37. $showFields = function($fields, $childFields, $relations, $data, $position = 'info') use ($childDatas, $obj)
  38. {
  39. global $lang;
  40. $html = '';
  41. $children = array();
  42. foreach($fields as $field)
  43. {
  44. if(empty($field->show)) continue;
  45. if(isset($childFields[$field->field]))
  46. {
  47. $children[$field->field] = $field->name;
  48. continue;
  49. }
  50. if($field->position != $position) continue;
  51. if($field->control == 'file')
  52. {
  53. $filesName = "{$field->field}files";
  54. if(!empty($data->{$filesName})) $html .= '<p><strong>' . $field->name . $lang->hyphen . '</strong>' . $this->flow->getFieldValue($field, $data) . '</p>';
  55. }
  56. else
  57. {
  58. $attr = '';
  59. $relation = zget($relations, $field->field, '');
  60. if($relation && strpos(",$relation->actions,", ',many2one,') === false && strpos(",$relation->actions,", ',many2many,') === false)
  61. {
  62. $prevDataID = isset($data->{$field->field}) ? $data->{$field->field} : 0;
  63. if($prevDataID) $attr = "class='prevField' data-prev='{$relation->prev}' data-next='{$relation->next}' data-action='view' data-dataid='$prevDataID'";
  64. }
  65. if($field->control == 'textarea') $attr .= " class='article'";
  66. $html .= "<p $attr><strong>{$field->name}</strong>";
  67. if($field->control == 'textarea') $html .= '<br/>';
  68. $fieldValue = '';
  69. if(!empty($data->{$field->field}))
  70. {
  71. if(is_array($data->{$field->field}))
  72. {
  73. foreach($data->{$field->field} as $value) $fieldValue .= $obj->flow->processFieldValue($field, $relation, $value) . ' ';
  74. }
  75. else
  76. {
  77. $fieldValue = $obj->flow->processFieldValue($field, $relation, $data->{$field->field});
  78. }
  79. }
  80. else
  81. {
  82. if(isset($data->{$field->field}) && $data->{$field->field} === '0') $fieldValue = '0';
  83. }
  84. if($fieldValue !== '') $html .= $field->control == 'textarea' ? $fieldValue : ' ' . $lang->hyphen . ' ' . $fieldValue;
  85. $html .= '</p>';
  86. }
  87. }
  88. foreach($children as $child => $childName)
  89. {
  90. if(empty($childDatas[$child])) continue;
  91. $html .= "<div class='panel panel-block mt-2'>
  92. <div class='panel-heading'><strong>{$childName}</strong></div>
  93. <table class='table table-hover condensed table-fixed'>
  94. <thead><tr>";
  95. foreach($childFields[$child] as $childField)
  96. {
  97. if(!$childField->show) continue;
  98. $childWidth = ($childField->width && $childField->width != 'auto' ? $childField->width . 'px' : 'auto');
  99. $html .= "<th class='text-left' style='width:{$childWidth}'>{$childField->name}</th>";
  100. }
  101. $html .= "</tr></thead><tbody>";
  102. foreach($childDatas[$child] as $childData)
  103. {
  104. $html .= "<tr>";
  105. foreach($childFields[$child] as $childField)
  106. {
  107. if(!$childField->show) continue;
  108. if(strpos(',date,datetime,', ",$childField->control,") !== false)
  109. {
  110. $childValue = formatTime($childData->{$childField->field});
  111. }
  112. else
  113. {
  114. if(is_array($childData->{$childField->field}))
  115. {
  116. $childValues = array();
  117. foreach($childData->{$childField->field} as $value)
  118. {
  119. if(!empty($value)) $childValues[] = zget($childField->options, $value);
  120. }
  121. $childValue = implode(',', $childValues);
  122. }
  123. else
  124. {
  125. $childValue = zget($childField->options, $childData->{$childField->field});
  126. }
  127. }
  128. $html .= "<td title='{$childValue}'>{$childValue}</td>";
  129. }
  130. $html .= "</tr>";
  131. }
  132. $html .= "</tbody></table></div>";
  133. }
  134. return $html;
  135. };
  136. $showSide = function() use ($processBlocks, $childFields, $relations, $obj, $data, $flow)
  137. {
  138. $buildBlock = function($blockFields) use ($obj, $childFields, $relations, $data)
  139. {
  140. $html = "<table class='table borderless condensed mb-2'>";
  141. foreach($blockFields as $field)
  142. {
  143. if(!$field->show) continue;
  144. if(isset($childFields[$field->field])) continue;
  145. /* Display data of the prev flow. */
  146. $attr = '';
  147. $link = '';
  148. $relation = zget($relations, $field->field, '');
  149. if($relation && strpos(",$relation->actions,", ',many2one,') === false)
  150. {
  151. $prevDataID = isset($data->{$field->field}) ? $data->{$field->field} : 0;
  152. $attr = "class='prevTR' data-prev='{$relation->prev}' data-next='{$relation->next}' data-action='view' data-dataid='$prevDataID'";
  153. if(hasPriv($relation->prev, 'view')) $link = createLink($relation->prev, 'view', "dataID=$prevDataID");
  154. }
  155. $html .= "<tr {$attr}><th class='w-24 text-right'>{$field->name}</th><td>";
  156. if($field->control == 'file')
  157. {
  158. $filesName = "{$field->field}files";
  159. $html .= $obj->fetch('file', 'printFiles', array('files' => $data->{$filesName}, 'fieldset' => 'false'));
  160. }
  161. elseif(is_array($data->{$field->field}))
  162. {
  163. foreach($data->{$field->field} as $value) $html .= zget($field->options, $value) . ' ';
  164. }
  165. else
  166. {
  167. if(strpos(',date,datetime,', ",$field->control,") !== false)
  168. {
  169. $html .= formatTime($data->{$field->field});
  170. }
  171. else
  172. {
  173. $fieldValue = zget($field->options, $data->{$field->field});
  174. $html .= $link ? html::a($link, $fieldValue) : $fieldValue;
  175. }
  176. }
  177. $html .= "</td></tr>";
  178. }
  179. $html .= "</table>";
  180. return $html;
  181. };
  182. $html = '';
  183. foreach($processBlocks as $blockKey => $block)
  184. {
  185. $html .= "<div class='panel canvas mb-3'>";
  186. if(empty($block->tabs))
  187. {
  188. if($block->name) $html .= "<div class='panel-heading'><strong>{$block->name}</strong></div>";
  189. }
  190. else
  191. {
  192. $html .= "<div class='panel-heading'>";
  193. $html .= "<ul class='tabs-nav nav nav-tabs gap-x-2'>";
  194. $index = 1;
  195. foreach($block->tabs as $tabKey => $tab)
  196. {
  197. $class = $index == 1 ? "font-medium active font-bold text-md" : '';
  198. $html .= "<li class='nav-item'><a class='{$class}' data-toggle='tab' href='#{$blockKey}_{$tabKey}Tab'>{$tab->name}</a></li>";
  199. $index ++;
  200. }
  201. $html .= "</ul></div>";
  202. }
  203. if(!empty($block->tabs))
  204. {
  205. $html .= "<div class='tab-content'>";
  206. $index = 1;
  207. foreach($block->tabs as $tabKey => $tab)
  208. {
  209. $class = $index == 1 ? 'active' : '';
  210. $html .= "<div id='{$blockKey}_{$tabKey}Tab' class='{$class} tab-pane'>";
  211. $html .= $buildBlock($tab->fields);
  212. $html .= "</div>";
  213. $index ++;
  214. }
  215. $html .= "</div>";
  216. }
  217. elseif(!empty($block->fields))
  218. {
  219. $html .= $buildBlock($block->fields);
  220. }
  221. $html .= "</div>";
  222. }
  223. global $lang;
  224. $relatedObjectHTML = wg(relatedObjectList(set::objectID($data->id), set::objectType("workflow_{$flow->module}"), set::browseType('byObject')))->renderInner();
  225. $html .= "<div class='panel canvas mb-3'><div class='panel-heading'><strong>{$lang->custom->relateObject}</strong></div><div class='px-2 pb-2'>$relatedObjectHTML</div></div>";
  226. return $html;
  227. };
  228. $canUnlink = $this->flow->isClickable($flow->module, 'unlink', $data);
  229. $buildLinked = function($linkType, $dataList) use($canUnlink, $obj, $flow, $data, $currentType, $uiID, $activeTab)
  230. {
  231. global $config, $lang;
  232. $html = '';
  233. if(in_array($linkType, $config->flow->linkPairs))
  234. {
  235. list($linkedApp, $linkedModule) = $obj->flow->extractAppAndModule($linkType);
  236. $html .= $obj->fetch($linkedModule, 'linked', "module=$flow->module&dataID=$data->id&linkType=$currentType", $linkedApp);
  237. return $html;
  238. }
  239. $linkedFields = $obj->workflowaction->getFields($linkType, 'browse', true, $dataList, $uiID);
  240. $active = $activeTab == $linkType ? 'active' : '';
  241. $batchActions = '';
  242. $canBatchAction = $config->flow->showBatchActionsInLinkedPage;
  243. if($canBatchAction)
  244. {
  245. $batchActions = $obj->flow->buildBatchActions($linkType);
  246. $canBatchAction = !empty($batchActions);
  247. }
  248. if($currentMode != 'browse') return $html;
  249. $html .= "<form id='unlink{$linkType}Form' method='post' class='main-table' data-ride='table'>";
  250. $html .= "<table class='table table-form linkedTable'><thead>";
  251. $index = 1;
  252. foreach($linkedFields as $field)
  253. {
  254. if(!$field->show) continue;
  255. if(!$canUnlink && $field->field == 'actions') continue;
  256. $width = ($field->width && $field->width != 'auto' ? $field->width . 'px' : 'auto');
  257. $fieldName = $field->name;
  258. if($index == 1 && ($canUnlink or $canBatchAction)) $fieldName = "<div class='checkbox-primary check-all' title='{$this->lang->selectAll}'><label>{$field->name}</label></div>";
  259. $html .= "<th class='text-{$field->position}' style='width:{$width}'>{$fieldName}</th>";
  260. $index++;
  261. }
  262. $html .= "</thead><tbody>";
  263. foreach($dataList as $linkedData)
  264. {
  265. $html .= "<tr>";
  266. $index = 1;
  267. foreach($linkedFields as $field)
  268. {
  269. if(!$field->show or $field->field == 'actions') continue;
  270. $output = '';
  271. if(is_array($linkedData->{$field->field}))
  272. {
  273. foreach($linkedData->{$field->field} as $value) $output .= zget($field->options, $value) . ' ';
  274. }
  275. else
  276. {
  277. if($field->field == 'id')
  278. {
  279. $output = $linkedData->id;
  280. if(hasPriv($linkType, 'view'))
  281. {
  282. $action = $obj->workflowaction->getByModuleAndAction($linkType, 'view');
  283. $attr = $action->open == 'modal' ? "data-toggle='modal'" : '';
  284. $output = baseHTML::a(helper::createLink($linkType, 'view', "dataID={$linkedData->id}"), $linkedData->id, $attr);
  285. }
  286. }
  287. else
  288. {
  289. $output = zget($field->options, $linkedData->{$field->field});
  290. }
  291. }
  292. $html .= "<td class='text-{$field->position}' title='" . strip_tags(str_replace("</p>", "\n", str_replace(array("\n", "\r"), "", $output))) . "'>";
  293. $fieldValue = $output;
  294. if($index == 1 && ($canUnlink or $canBatchAction))
  295. {
  296. $fieldValue = "<div class='checkbox-primary'><input type='checkbox' name='dataIDList[]' value='{$linkedData->id}' id='dataIDList{$linkedData->id}'><label for='dataIDList{$linkedData->id}'>" . ($field->field == 'id' ? $output : '') . "</label></div>";
  297. if($field->field == 'id')$fieldValue .= $output;
  298. }
  299. $index++;
  300. }
  301. if($canUnlink)
  302. {
  303. $html .= "<td class='nowrap'>";
  304. $confirm = sprintf($lang->flow->unlinkConfirm, zget($linkPairs, $linkType));
  305. $html .= baseHTML::a(createLink($flow->module, 'unlink', "dataID=$data->id&linkType=$linkType&linkedID=$linkedData->id"), $lang->flow->unlink, "class='unlink' data-confirm='$confirm' onclick='unlinkData(this)'");
  306. $html .= "</td>";
  307. }
  308. $html .= "</tr>";
  309. }
  310. $html .= "</tbody></table><div class='table-footer'>";
  311. if($canUnlink or $canBatchAction)
  312. {
  313. $html .= "<div class='checkbox-primary check-all'><label>{$lang->selectAll}</label></div>";
  314. $html .= "<div class='table-actions btn-toolbar'>";
  315. if($canUnlink)
  316. {
  317. $actionLink = $this->createLink($flow->module, 'unlink', "dataID=$data->id&linkType=$linkType");
  318. $html .= baseHTML::a('javascript:;', $lang->flow->unlink, "class='btn' onclick=\"setFormAction('$actionLink', '', this)\"");
  319. }
  320. if($canBatchAction) $html .= $batchActions;
  321. $html .= "</div>";
  322. }
  323. $html .= "<div class='table-statistic'></div></div></form>";
  324. return $html;
  325. };
  326. $buildOperateMenu = function() use($flow, $data, $obj, $browseLink, $groupID)
  327. {
  328. if(zget($data, 'deleted', '0') == '1' || !$flow) return '';
  329. global $lang, $config, $app;
  330. $obj->loadModel('workflowaction');
  331. $menu = "<div class='center sticky mt-4 bottom-4'><div class='bg-black text-fore-in-dark backdrop-blur bg-opacity-60 rounded p-1.5'><div class='toolbar'>";
  332. $menu .= "<a class='btn ghost open-url' data-back='{$flow->module}-browse,my-index'><i class='icon icon-back'></i><span class='text'>{$lang->goback}</span></a>";
  333. $menu .= "<div class='divider toolbar-divider'></div>";
  334. $relations = $obj->dao->select('next, actions')->from(TABLE_WORKFLOWRELATION)->where('prev')->eq($flow->module)->fetchPairs();
  335. $actions = $obj->workflowaction->getList($flow->module, 'status_desc,order_asc', $groupID);
  336. foreach($actions as $action) $menu .= $obj->flow->buildActionMenu($flow->module, $action, $data, 'view', $relations);
  337. if(!empty($config->openedApproval) && $flow->approval == 'enabled' && hasPriv('approval', 'progress') && !empty($data->approval))
  338. {
  339. $menu .= "<div class='divider toolbar-divider'></div>";
  340. $menu .= html::a(helper::createLink('approval', 'progress', "approvalID={$data->approval}"), $lang->flow->approvalProgress, '', "class='btn ghost' data-toggle='modal'");
  341. }
  342. $menu .= '</div></div></div>';
  343. $menu = str_replace(array('<a href', 'btn-link', 'deleter'), array("<a data-app='{$app->tab}' href", 'ghost', 'ajax-submit deleter'), $menu);
  344. $menu = preg_replace("/(class=[\"|\'][^\"\']+ajax-submit deleter[\"|\'])/", "data-confirm='{$lang->confirmDelete}' $1", $menu);
  345. return $menu;
  346. };
  347. $tabPanes = array();
  348. $tabPanes[] = tabPane
  349. (
  350. set::key($flow->module),
  351. set::title($flow->name . $lang->flow->detail),
  352. set::active($activeTab == $flow->module),
  353. setClass('relative'),
  354. $canLink ? div
  355. (
  356. setClass('tab-actions'),
  357. btn(setClass('primary'), setData('toggle', 'modal'), set::url('#linkTypeBox'), set::text($lang->workflowaction->default->actions['link']))
  358. ) : null,
  359. row
  360. (
  361. cell
  362. (
  363. setClass('mainbar w-3/4 mr-3'),
  364. html("<div class='panel canvas mb-3'><div class='panel-body'>" . $showFields($fields, $childFields, $relations, $data) . '</div></div>'),
  365. history(set::objectType($flow->module), set::objectID($data->id)),
  366. html($buildOperateMenu())
  367. ),
  368. cell
  369. (
  370. setClass('sidebar w-1/4'),
  371. html($showSide())
  372. )
  373. )
  374. );
  375. foreach($linkedDatas as $linkType => $datas)
  376. {
  377. $tabPanes[] = tabPane
  378. (
  379. set::key($linkType),
  380. set::title(zget($linkPairs, $linkType, '')),
  381. set::active($activeTab == $linkType),
  382. setClass('relative'),
  383. $canLink ? div
  384. (
  385. setClass('tab-actions'),
  386. btn(setClass('primary'), setData('toggle', 'modal'), set::url('#linkTypeBox'), set::text($lang->workflowaction->default->actions['link']))
  387. ) : null,
  388. html($buildLinked($linkType, $datas))
  389. );
  390. }
  391. if($canLink)
  392. {
  393. $tabPanes[] = tabPane
  394. (
  395. set::key('common'),
  396. set::active($activeTab == 'common'),
  397. div
  398. (
  399. setClass('tab-actions'),
  400. btn(setClass('primary'), setData('toggle', 'modal'), set::url('#linkTypeBox'), set::text($lang->workflowaction->default->actions['link']))
  401. )
  402. );
  403. modal
  404. (
  405. setID('linkTypeBox'),
  406. set::closeBtn(true),
  407. set::title($lang->workflowaction->default->actions['link']),
  408. on::change('[name=linkType]', 'linkData'),
  409. formGroup
  410. (
  411. set::label($lang->flow->selectLinkType),
  412. picker(set::name('linkType'), set::items(array('' => '') + $linkPairs))
  413. )
  414. );
  415. }
  416. $buttons = array();
  417. $linkCreator = createLink($app->rawModule, $app->rawMethod, 'dataID={id}');
  418. if(!empty($preAndNext->pre))
  419. {
  420. $buttons[] = btn
  421. (
  422. setClass('detail-prev-btn absolute top-0 left-0 inverse rounded-full w-12 h-12 center bg-opacity-40 backdrop-blur ring-0'),
  423. set::icon('angle-left icon-2x text-canvas'),
  424. setData('app', $app->tab),
  425. set('hint', "#{$preAndNext->pre->id}"),
  426. set('url', str_replace('{id}', "{$preAndNext->pre->id}", $linkCreator))
  427. );
  428. }
  429. if(!empty($preAndNext->next))
  430. {
  431. $buttons[] = btn
  432. (
  433. setClass('detail-next-btn absolute top-0 right-0 inverse rounded-full w-12 h-12 center bg-opacity-40 backdrop-blur ring-0'),
  434. set::icon('angle-right icon-2x text-canvas'),
  435. setData('app', $app->tab),
  436. set('hint', "#{$preAndNext->next->id}"),
  437. set('url', str_replace('{id}', "{$preAndNext->next->id}", $linkCreator))
  438. );
  439. }
  440. div
  441. (
  442. on::init()->call('loadAllPrevData'),
  443. tabs($tabPanes),
  444. !empty($buttons) ? div
  445. (
  446. setClass('detail-prev-next fixed top-0 left-0 bottom-0 right-0 z-10 pointer-events-none'),
  447. div
  448. (
  449. setClass('container relative pointer-events-auto'),
  450. setStyle(array('top' => '50%', 'margin' => '-24px auto auto')),
  451. $buttons
  452. )
  453. ) : null
  454. );