view.html.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <?php
  2. /**
  3. * The view file of doc module of ZenTaoPMS.
  4. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(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 Sun Guangming<sunguangming@easycorp.ltd>
  7. * @package doc
  8. * @link https://www.zentao.net
  9. */
  10. namespace zin;
  11. include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php');
  12. jsVar('docID', $docID);
  13. if(!isInModal()) include 'lefttree.html.php';
  14. $versionList = array();
  15. for($itemVersion = $doc->version; $itemVersion > 0; $itemVersion--)
  16. {
  17. $versionList[] = array('text' => "#$itemVersion", 'url' => createLink('doc', 'view', "docID={$docID}&version={$itemVersion}"), 'key' => $itemVersion, 'active' => $itemVersion == $version);
  18. }
  19. $canCollect = hasPriv('doc', 'collect') && !$doc->deleted;
  20. if($canCollect)
  21. {
  22. $star = strpos($doc->collector, ',' . $app->user->account . ',') !== false ? 'star' : 'star-empty';
  23. $starBtn = "<a title='{$lang->doc->collect}' class='btn btn-link square doc-collect-btn' href='javascript:collectDoc(\"$doc->id\")'><img src='" . "static/svg/{$star}.svg" . '\'></a>';
  24. }
  25. /* 导入资产库的按钮. */
  26. $importLibItems = array();
  27. if($config->vision == 'rnd' and ($config->edition == 'max' or $config->edition == 'ipd') and $app->tab == 'project')
  28. {
  29. $canImportToPracticeLib = (common::hasPriv('doc', 'importToPracticeLib') and helper::hasFeature('practicelib'));
  30. $canImportToComponentLib = (common::hasPriv('doc', 'importToComponentLib') and helper::hasFeature('componentlib'));
  31. if($canImportToPracticeLib) $importLibItems[] = array('text' => $lang->doc->importToPracticeLib, 'url' => '#importToPracticeLib', 'data-toggle' => 'modal', 'data-size' => 'sm');
  32. if($canImportToComponentLib) $importLibItems[] = array('text' => $lang->doc->importToComponentLib, 'url' => '#importToComponentLib', 'data-toggle' => 'modal', 'data-size' => 'sm');
  33. $importLibBtn = $importLibItems ? dropdown
  34. (
  35. btn(setClass('ghost btn square btn-default'), icon('diamond')),
  36. set::items($importLibItems)
  37. ) : null;
  38. }
  39. $createInfo = $doc->status == 'draft' ? zget($users, $doc->addedBy) . " {$lang->hyphen} " . substr($doc->addedDate, 0, 10) . (common::checkNotCN() ? ' ' : '') . $lang->doc->createAB : zget($users, $doc->releasedBy) . " {$lang->hyphen} " . substr($doc->releasedDate, 0, 10) . (common::checkNotCN() ? ' ' : '') . $lang->doc->release;
  40. $keywordsLabel = array();
  41. if($doc->keywords)
  42. {
  43. foreach($doc->keywords as $keywords)
  44. {
  45. if(!$keywords) continue;
  46. $keywordsLabel[] = span(setClass('label secondary-outline'), $keywords);
  47. }
  48. }
  49. $docMoreActions = array();
  50. $isCreator = isset($doc->addedBy) && $doc->addedBy == $this->app->user->account;
  51. $canMoveDoc = $isCreator && hasPriv('doc', 'movedoc');
  52. if($canMoveDoc && ($lib->type === 'custom' || $lib->type === 'mine'))
  53. {
  54. $docMoreActions[] = array
  55. (
  56. 'icon' => 'folder-move',
  57. 'hint' => $lang->doc->moveDocAction,
  58. 'text' => $lang->doc->moveDocAction,
  59. 'url' => createLink('doc', 'moveDoc', "docID=$doc->id&libID=&space=&locate=true"),
  60. 'data-toggle' => 'modal',
  61. 'data-size' => 'sm'
  62. );
  63. }
  64. if(hasPriv('doc', 'delete') && !$doc->deleted)
  65. {
  66. $docMoreActions[] = array
  67. (
  68. 'url' => createLink('doc', 'delete', "docID=$doc->id"),
  69. 'icon' => 'trash',
  70. 'text' => $lang->delete,
  71. 'data-confirm' => array('message' => $lang->doc->confirmDelete, 'icon' => 'icon-exclamation-sign', 'iconClass' => 'warning-pale rounded-full icon-2x')
  72. );
  73. }
  74. if($canExport)
  75. {
  76. $docMoreActions[] = array
  77. (
  78. 'class' => 'ghost export',
  79. 'url' => createLink('doc', $exportMethod, "libID={$libID}&moduleID={$moduleID}&docID={$doc->id}"),
  80. 'icon' => 'export',
  81. 'text' => $lang->export,
  82. 'target' => '_self',
  83. );
  84. }
  85. if(!empty($docMoreActions)) $docMoreActions[] = array('type' => 'divider');
  86. $docMoreActions[] = array
  87. (
  88. 'id' => 'hisTrigger',
  89. 'icon' => 'clock',
  90. 'text' => $lang->history,
  91. 'onClick' => jsRaw('() => $("#docPanel").toggleClass("show-history")')
  92. );
  93. /* Build editor group. */
  94. $editorGroup = '';
  95. if(!empty($editors))
  96. {
  97. $space = common::checkNotCN() ? ' ' : '';
  98. $items = array();
  99. foreach($editors as $editor)
  100. {
  101. $info = zget($users, $editor->account) . ' ' . substr($editor->date, 0, 10) . $space . $lang->doc->update;
  102. $items[] = array('text' => $info);
  103. }
  104. $docMoreActions[] = array
  105. (
  106. 'icon' => 'info',
  107. 'text' => $lang->doc->updateInfo,
  108. 'items' => $items
  109. );
  110. }
  111. $docHeader = div
  112. (
  113. setID('docHeader'),
  114. setClass('w-full row items-center gap-4 sticky top-0 z-10 bg-canvas py-3 pl-4 pr-2'),
  115. div
  116. (
  117. setClass('flex-auto row items-center min-w-0 gap-2'),
  118. div
  119. (
  120. setClass('title clip inline-flex text-xl font-bold'),
  121. set::title($doc->title),
  122. $doc->title
  123. ),
  124. $doc->status != 'draft' ? dropdown
  125. (
  126. setID('versionDropdown'),
  127. set::items($versionList),
  128. btn
  129. (
  130. set::type('gray-pale'),
  131. setClass('rounded-full size-xs gap-1'),
  132. '#' . ($version ? $version : $doc->version)
  133. )
  134. ) : null,
  135. $doc->deleted ? span(setClass('label danger size-sm'), $lang->doc->deleted) : null
  136. ),
  137. div
  138. (
  139. setClass('flex-none flex items-center'),
  140. $editorGroup ? setClass('hasEditor') : null,
  141. div
  142. (
  143. setClass('toolbar'),
  144. setID('docToolbar'),
  145. btn
  146. (
  147. setClass('btn ghost square'),
  148. icon('fullscreen'),
  149. set::url('javascript:$("#docPanel").fullscreen()')
  150. ),
  151. $canCollect ? html($starBtn) : null,
  152. ($config->vision == 'rnd' and ($config->edition == 'max' or $config->edition == 'ipd') and $app->tab == 'project') ? $importLibBtn : null,
  153. common::hasPriv('doc', 'edit') && !$doc->deleted ? btn
  154. (
  155. set::type('ghost'),
  156. set::url(createLink('doc', 'edit', "docID=$doc->id")),
  157. $doc->type != 'text' ? setData('toggle', 'modal') : null,
  158. setData('app', $app->tab),
  159. setClass('doc-edit-btn'),
  160. set::icon('edit')
  161. ) : null,
  162. dropdown
  163. (
  164. btn
  165. (
  166. setID('docMoreActionsBtn'),
  167. set::type('ghost'),
  168. set::caret(false),
  169. set::square(),
  170. span(setClass('more-vert'))
  171. ),
  172. set::placement('bottom-end'),
  173. set::items($docMoreActions)
  174. )
  175. )
  176. )
  177. );
  178. $contentDom = div
  179. (
  180. setID('docContent'),
  181. setClass('flex-auto min-w-0 px-4'),
  182. div
  183. (
  184. setClass('row items-center py-1 gap-3'),
  185. div
  186. (
  187. setClass('text-gray inline-flex items-center gap-2'),
  188. icon('contacts'),
  189. $createInfo
  190. ),
  191. div
  192. (
  193. setClass('text-gray inline-flex items-center gap-2'),
  194. icon('star-empty'),
  195. $doc->collects ? $doc->collects : 0
  196. ),
  197. div
  198. (
  199. setClass('text-gray inline-flex items-center gap-2'),
  200. icon('eye'),
  201. $doc->views
  202. )
  203. ),
  204. $keywordsLabel ? div
  205. (
  206. setClass('row items-center gap-2 mt-1 pl-px'),
  207. $keywordsLabel
  208. ) : null,
  209. div
  210. (
  211. setID('docEditor'),
  212. setClass('detail-content article'),
  213. $doc->contentType === 'doc' ? pageEditor
  214. (
  215. set::size('auto'),
  216. set::readonly(true),
  217. set::value($doc->content)
  218. ) : editor
  219. (
  220. set::resizable(false),
  221. set::markdown($doc->contentType == 'markdown'),
  222. set::readonly(true),
  223. set::hideUI(true),
  224. set::size('auto'),
  225. html($doc->content)
  226. )
  227. ),
  228. div
  229. (
  230. setID('docFile'),
  231. $doc->files ? setClass('surface px-4 pb-2 rounded mb-4') : null,
  232. $doc->files ? fileList
  233. (
  234. setClass('bg-transparent'),
  235. set::padding(false),
  236. set::objectType('doc'),
  237. set::objectID($doc->id),
  238. set::files($doc->files)
  239. ) : null
  240. )
  241. );
  242. $treeDom = isset($outlineTree) ? div
  243. (
  244. setID('docOutline'),
  245. setClass('sticky overflow-y-auto scrollbar-hover'),
  246. setStyle('max-height', 'calc(100vh - 130px)'),
  247. setStyle('top', '58px'),
  248. on::click('.tree-item>.listitem')->call('scrollIntoDoc', jsRaw('$this')),
  249. tree
  250. (
  251. set::className('pl-4'),
  252. set::items($outlineTree),
  253. set::defaultNestedShow(true)
  254. )
  255. ) : null;
  256. $toggleTreeBtn = isset($outlineTree) ? btn(setID('outlineToggle'), setClass('canvas ring-0 absolute right-2 top-0'), icon('menu-arrow-left'), on::click()->do('$("#docPanel").toggleClass("show-outline")')) : null;
  257. $historyDom = div
  258. (
  259. setId('docHistory'),
  260. setClass('relative'),
  261. btn
  262. (
  263. setID('closeBtn'),
  264. setClass('canvas ring-0 absolute right-2 top-0'),
  265. set::type('ghost'),
  266. set::icon('close'),
  267. on::click()->do('$("#docPanel").removeClass("show-history")')
  268. ),
  269. history(set::objectID($doc->id), set::objectType('doc'))
  270. );
  271. panel
  272. (
  273. setID('docPanel'),
  274. setClass('ring scrollbar-hover overflow-y-auto'),
  275. set::bodyClass('w-full p-0'),
  276. set::bodyProps(array('id' => 'docBody')),
  277. $docHeader,
  278. div
  279. (
  280. setClass('flex-auto w-full row relative'),
  281. $contentDom,
  282. div
  283. (
  284. setID('docSidebar'),
  285. setClass('flex-none border-l'),
  286. setStyle('width', 'var(--doc-sidebar-width)'),
  287. $treeDom,
  288. $historyDom
  289. ),
  290. $toggleTreeBtn
  291. )
  292. );
  293. if($importLibItems)
  294. {
  295. modal
  296. (
  297. setID('importToPracticeLib'),
  298. formPanel
  299. (
  300. set::title($lang->doc->importToPracticeLib),
  301. set::actions(array('submit')),
  302. set::submitBtnText($lang->import),
  303. set::url(createLink('doc', 'importToPracticeLib', "doc={$doc->id}")),
  304. set::formClass('mt-6'),
  305. formGroup
  306. (
  307. set::label($lang->doc->practiceLib),
  308. setID('practiceLib'),
  309. set::name('lib'),
  310. set::items($practiceLibs),
  311. set::required(true)
  312. ),
  313. !common::hasPriv('assetlib', 'approvePractice') && !common::hasPriv('assetlib', 'batchApprovePractice') ? formGroup
  314. (
  315. set::label($lang->doc->approver),
  316. picker
  317. (
  318. setID('practiceApprover'),
  319. set::name('assignedTo'),
  320. set::items($practiceApprovers),
  321. set::required(true)
  322. )
  323. ) : null
  324. )
  325. );
  326. modal
  327. (
  328. setID('importToComponentLib'),
  329. formPanel
  330. (
  331. set::title($lang->doc->importToComponentLib),
  332. set::actions(array('submit')),
  333. set::submitBtnText($lang->import),
  334. set::url(createLink('doc', 'importToComponentLib', "doc={$doc->id}")),
  335. set::formClass('mt-6'),
  336. formGroup
  337. (
  338. set::label($lang->doc->componentLib),
  339. setID('componentLib'),
  340. set::name('lib'),
  341. set::items($componentLibs),
  342. set::required(true)
  343. ),
  344. !common::hasPriv('assetlib', 'approveComponent') && !common::hasPriv('assetlib', 'batchApproveComponent') ? formGroup
  345. (
  346. set::label($lang->doc->approver),
  347. picker
  348. (
  349. setID('componentApprover'),
  350. set::name('assignedTo'),
  351. set::items($componentApprovers),
  352. set::required(true)
  353. )
  354. ) : null
  355. )
  356. );
  357. }