control.class.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. <?php /**
  2. * ZenTaoPHP的control类。
  3. * The control class file of ZenTaoPHP framework.
  4. *
  5. * The author disclaims copyright to this source code. In place of
  6. * a legal notice, here is a blessing:
  7. *
  8. * May you do good and not evil.
  9. * May you find forgiveness for yourself and forgive others.
  10. * May you share freely, never taking more than you give.
  11. */
  12. /**
  13. * control基类继承与baseControl,所有模块的control类都派生于它。
  14. * The base class of control extends baseControl.
  15. *
  16. * @package framework
  17. */
  18. include __DIR__ . '/base/control.class.php';
  19. class control extends baseControl
  20. {
  21. /**
  22. * Check requiredFields and set exportFields for workflow.
  23. *
  24. * @param string $moduleName
  25. * @param string $methodName
  26. * @param string $appName
  27. * @access public
  28. * @return void
  29. */
  30. public function __construct($moduleName = '', $methodName = '', $appName = '')
  31. {
  32. parent::__construct($moduleName, $methodName, $appName);
  33. $this->app->setOpenApp();
  34. if($this->config->edition == 'open') return false;
  35. /* Code for task #9224. Set requiredFields for workflow. */
  36. if($this->dbh && ($this->app->isServing() || (defined('RUN_MODE') and RUN_MODE == 'api')))
  37. {
  38. $this->extendExportFields();
  39. $this->extendEditorFields();
  40. /* If workflow is created by a normal user, set priv. */
  41. if(isset($this->app->user) and !$this->app->user->admin) $this->setDefaultPrivByWorkflow();
  42. }
  43. }
  44. /**
  45. * Append export fields to the config of this module from workflow.
  46. *
  47. * @access public
  48. * @return void
  49. */
  50. public function extendExportFields()
  51. {
  52. if(isset($this->config->{$this->moduleName}) and strpos((string) $this->methodName, 'export') !== false)
  53. {
  54. if(isset($this->config->{$this->moduleName}->exportFields) or isset($this->config->{$this->moduleName}->list->exportFields))
  55. {
  56. $exportFields = $this->dao->select('field, name')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('canExport')->eq('1')->andWhere('buildin')->eq('0')->fetchAll('field');
  57. if(isset($this->config->{$this->moduleName}->exportFields))
  58. {
  59. foreach($exportFields as $field) $this->config->{$this->moduleName}->exportFields .= ",{$field->field}";
  60. }
  61. if(isset($this->config->{$this->moduleName}->list->exportFields))
  62. {
  63. foreach($exportFields as $field) $this->config->{$this->moduleName}->list->exportFields .= ",{$field->field}";
  64. }
  65. if(isset($this->config->excel->editor[$this->moduleName]))
  66. {
  67. foreach($exportFields as $field)
  68. {
  69. if(isset($field->control) && $field->control == 'richtext') $this->config->excel->editor[$this->moduleName][] = $field->field;
  70. }
  71. }
  72. foreach($exportFields as $flowField => $exportField)
  73. {
  74. if(!isset($this->lang->{$this->moduleName}->$flowField)) $this->lang->{$this->moduleName}->$flowField = $exportField->name;
  75. }
  76. }
  77. }
  78. }
  79. /**
  80. * Append editor fields to the config of this module from workflow.
  81. *
  82. * @access public
  83. * @return void
  84. */
  85. public function extendEditorFields()
  86. {
  87. $moduleName = $this->moduleName;
  88. $methodName = $this->methodName;
  89. $fields = $this->dao->select('field')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('control')->eq('richtext')->andWhere('buildin')->eq('0')->fetchPairs();
  90. if($fields)
  91. {
  92. $editorIdList = array();
  93. foreach($fields as $field) $editorIdList[] = $field;
  94. if(!isset($this->config->{$moduleName})) $this->config->{$moduleName} = new stdclass();
  95. if(!isset($this->config->{$moduleName}->editor)) $this->config->{$moduleName}->editor = new stdclass();
  96. if(!isset($this->config->{$moduleName}->editor->{$methodName})) $this->config->{$moduleName}->editor->{$methodName} = array('id' => '', 'tools' => 'simpleTools');
  97. $this->config->{$moduleName}->editor->{$methodName}['id'] .= ',' . join(',', $editorIdList);
  98. trim($this->config->{$moduleName}->editor->{$methodName}['id'], ',');
  99. }
  100. }
  101. /**
  102. * Det default priv by workflow.
  103. *
  104. * @access public
  105. * @return bool
  106. */
  107. public function setDefaultPrivByWorkflow()
  108. {
  109. $actionList = $this->dao->select('module, `action`')->from(TABLE_WORKFLOWACTION)
  110. ->where('createdBy')->eq($this->app->user->account)
  111. ->andWhere('buildin')->eq('0')
  112. ->fetchGroup('module');
  113. if($actionList)
  114. {
  115. foreach($actionList as $module => $actions)
  116. {
  117. foreach($actions as $action) $this->app->user->rights['rights'][$module][$action->action] = 1;
  118. }
  119. }
  120. $labelList = $this->dao->select('module, code')->from(TABLE_WORKFLOWLABEL)
  121. ->where('createdBy')->eq($this->app->user->account)
  122. ->andWhere('buildin')->eq('0')
  123. ->fetchGroup('module');
  124. if($labelList)
  125. {
  126. foreach($labelList as $module => $labels)
  127. {
  128. foreach($labels as $label)
  129. {
  130. $code = str_replace('browse', '', (string) $label->code);
  131. $this->app->user->rights['rights'][$module][$code] = 1;
  132. }
  133. }
  134. }
  135. return true;
  136. }
  137. /**
  138. * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。
  139. * 调用父类的loadModel方法来避免这个错误。
  140. * Some codes merged from ZDOO called the function loadModel with a non-empty appName which causes an error in zentao.
  141. * Call the parent function with empty appName to avoid this error.
  142. *
  143. * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
  144. * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name.
  145. * @access public
  146. * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object.
  147. */
  148. public function loadModel($moduleName = '', $appName = '')
  149. {
  150. return parent::loadModel($moduleName);
  151. }
  152. /**
  153. * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadZen方法时传递了一个非空的appName,在禅道中会导致错误。
  154. * 调用父类的loadZen方法来避免这个错误。
  155. * Some codes merged from ZDOO called the function loadZen with a non-empty appName which causes an error in zentao.
  156. * Call the parent function with empty appName to avoid this error.
  157. *
  158. * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
  159. * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name.
  160. * @access public
  161. * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object.
  162. */
  163. public function loadZen($moduleName = '', $appName = '')
  164. {
  165. return parent::loadZen($moduleName);
  166. }
  167. /**
  168. * 加载model的class扩展,主要是为了开发加密代码使用。
  169. * 可以将主要的逻辑存放到$moduleName/ext/model/class/$extensionName.class.php中。
  170. * 然后在ext/model/$extension.php的扩展里面使用$this->loadExtension()来调用相应的方法。
  171. * ext/model/class/*.class.php代码可以加密。而ext/model/*.php可以不用加密。
  172. * 因为框架对model的扩展是采取合并文件的方式,ext/model/*.php文件不能加密。
  173. *
  174. * Load extension class of a model thus user can encrypt the code.
  175. * You can put the main extension logic codes in $moduleName/ext/model/class/$extensionName.class.php.
  176. * And call them by the ext/model/$extension.php like this: $this->loadExtension('myextension')->method().
  177. * You can encrypt the code in ext/model/class/*.class.php.
  178. * Because the framework will merge the extension files in ext/model/*.php to the module/model.php.
  179. *
  180. * @param string $extensionName
  181. * @param string $moduleName
  182. * @access public
  183. * @return void
  184. */
  185. public function loadExtension($extensionName, $moduleName = '')
  186. {
  187. if(empty($extensionName)) return false;
  188. if(empty($moduleName)) $moduleName = $this->moduleName;
  189. $moduleName = strtolower((string) $moduleName);
  190. $extensionName = strtolower($extensionName);
  191. $type = 'model';
  192. $className = strtolower(static::class);
  193. if($className == $moduleName . 'zen' || $className == 'ext' . $moduleName . 'zen') $type = 'zen';
  194. /* 设置扩展类的名字。Set the extension class name. */
  195. $extensionClass = $extensionName . ucfirst($moduleName);
  196. if($type != 'model') $extensionClass .= ucfirst($type);
  197. if(isset($this->$extensionClass)) return $this->$extensionClass;
  198. /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */
  199. $moduleExtPath = $this->app->getModuleExtPath($moduleName, $type);
  200. if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php';
  201. if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php';
  202. if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php';
  203. if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['vision'] . 'class/' . $extensionName . '.class.php';
  204. if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['xuan'] . 'class/' . $extensionName . '.class.php';
  205. if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php';
  206. /* 载入父类。Try to import parent model file auto and then import the extension file. */
  207. if(!class_exists($moduleName . ucfirst($type))) helper::import($this->app->getModulePath($this->appName, $moduleName) . $type . '.php');
  208. if(!helper::import($extensionFile)) return false;
  209. if(!class_exists($extensionClass)) return false;
  210. /* 实例化扩展类。Create an instance of the extension class and return it. */
  211. $extensionObject = new $extensionClass();
  212. if($type == 'model') $extensionClass = str_replace(ucfirst($type), '', $extensionClass);
  213. $this->$extensionClass = $extensionObject;
  214. $this->$extensionClass->view = $this->view;
  215. return $extensionObject;
  216. }
  217. /**
  218. * 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。
  219. * Set view files: the main file, extension view file, site extension view file and hook files.
  220. *
  221. * @param string $moduleName module name
  222. * @param string $methodName method name
  223. * @param string $viewDir
  224. * @access public
  225. * @return string the view file
  226. */
  227. public function setViewFile($moduleName, $methodName, $viewDir = 'view')
  228. {
  229. $moduleName = strtolower(trim($moduleName));
  230. $methodName = strtolower(trim($methodName));
  231. $modulePath = $this->app->getModulePath($this->appName, $moduleName);
  232. $viewExtPath = $this->app->getModuleExtPath($moduleName, $viewDir);
  233. $viewType = ($this->viewType == 'mhtml' or $this->viewType == 'xhtml') ? 'html' : $this->viewType;
  234. $mainViewFile = $modulePath . $viewDir . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
  235. /* If the main view file doesn't exist, set the device prefix to empty and reset the main view file. */
  236. if(!file_exists($mainViewFile) and $this->app->clientDevice != 'mobile')
  237. {
  238. $originalPrefix = $this->devicePrefix;
  239. $this->devicePrefix = '';
  240. $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
  241. $this->devicePrefix = $originalPrefix;
  242. }
  243. $viewFile = $mainViewFile;
  244. if(!empty($viewExtPath))
  245. {
  246. $commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  247. $xuanExtViewFile = $viewExtPath['xuan'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  248. $visionExtViewFile = $viewExtPath['vision'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  249. $saasExtViewFile = $viewExtPath['saas'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  250. $customExtViewFile = $viewExtPath['custom'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  251. $siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
  252. /* Get ext files, site > custom > vision > common. */
  253. if(!empty($siteExtViewFile) and file_exists($siteExtViewFile))
  254. {
  255. $viewFile = $siteExtViewFile;
  256. }
  257. else if(file_exists($customExtViewFile))
  258. {
  259. $viewFile = $customExtViewFile;
  260. }
  261. else if(!empty($viewExtPath['vision']) and file_exists($visionExtViewFile))
  262. {
  263. $viewFile = $visionExtViewFile;
  264. }
  265. else if(file_exists($xuanExtViewFile))
  266. {
  267. $viewFile = $xuanExtViewFile;
  268. }
  269. else if(file_exists($saasExtViewFile))
  270. {
  271. $viewFile = $saasExtViewFile;
  272. }
  273. else if(file_exists($commonExtViewFile))
  274. {
  275. $viewFile = $commonExtViewFile;
  276. }
  277. if(!is_file($viewFile)) $viewFile = dirname((string) $viewExtPath['common'], 2) . DS . 'view' . DS . $this->devicePrefix . $methodName . ".{$viewType}.php";
  278. if(!is_file($viewFile)) helper::end(js::error($this->lang->notPage) . js::locate('back'));
  279. /* Get ext hook files. */
  280. $commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  281. if(!empty($viewExtPath['vision']))
  282. {
  283. $visionExtHookFiles = glob($viewExtPath['vision'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  284. $commonExtHookFiles = array_merge((array)$commonExtHookFiles, (array)$visionExtHookFiles);
  285. }
  286. $xuanExtHookFiles = glob($viewExtPath['xuan'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  287. $saasExtHookFiles = glob($viewExtPath['saas'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  288. $customExtHookFiles = glob($viewExtPath['custom'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  289. $siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
  290. $extHookFiles = array_merge((array)$commonExtHookFiles, (array)$xuanExtHookFiles, (array)$saasExtHookFiles, (array)$customExtHookFiles, (array)$siteExtHookFiles);
  291. }
  292. if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles);
  293. return $viewFile;
  294. }
  295. /**
  296. * 默认渲染方法,适用于viewType = html的时候。
  297. * Default parse method when viewType != json, like html.
  298. *
  299. * @param string $moduleName module name
  300. * @param string $methodName method name
  301. * @access public
  302. * @return void
  303. */
  304. public function parseDefault($moduleName, $methodName)
  305. {
  306. /**
  307. * 设置视图文件。(PHP7有一个bug,不能直接$viewFile = $this->setViewFile())。
  308. * Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.)
  309. */
  310. $results = $this->setViewFile($moduleName, $methodName);
  311. $viewFile = $results;
  312. if(is_array($results)) extract($results);
  313. /**
  314. * 获得当前页面的CSS和JS。
  315. * Get css and js codes for current method.
  316. */
  317. $css = $this->getCSS($moduleName, $methodName);
  318. $js = $this->getJS($moduleName, $methodName);
  319. /* If the js or css file doesn't exist, set the device prefix to empty and reset the js or css file. */
  320. if($this->viewType == 'xhtml')
  321. {
  322. $originalPrefix = $this->devicePrefix;
  323. $this->devicePrefix = '';
  324. $css .= $this->getCSS($moduleName, $methodName);
  325. $js .= $this->getJS($moduleName, $methodName);
  326. $this->devicePrefix = $originalPrefix;
  327. }
  328. if($css) $this->view->pageCSS = $css;
  329. if($js) $this->view->pageJS = $js;
  330. /**
  331. * 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。
  332. * Change the dir to the view file to keep the relative paths work.
  333. */
  334. $currentPWD = getcwd();
  335. chdir(dirname($viewFile));
  336. /**
  337. * 使用extract和ob方法渲染$viewFile里面的代码。
  338. * Use extract and ob functions to eval the codes in $viewFile.
  339. */
  340. extract((array)$this->view);
  341. ob_start();
  342. include $viewFile;
  343. if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile;
  344. $this->output .= ob_get_contents();
  345. ob_end_clean();
  346. /**
  347. * 渲染完毕后,再切换回之前的路径。
  348. * At the end, chang the dir to the previous.
  349. */
  350. chdir($currentPWD);
  351. }
  352. /**
  353. * 获取一个方法的输出内容,这样我们可以在一个方法里获取其他模块方法的内容。
  354. * 如果模块名为空,则调用该模块、该方法;如果设置了模块名,调用指定模块指定方法。
  355. *
  356. * Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content.
  357. * If the module name is empty, then use the current module and method. If set, use the user defined module and method.
  358. *
  359. * @param string $moduleName module name.
  360. * @param string $methodName method name.
  361. * @param array $params params.
  362. * @access public
  363. * @return string the parsed html.
  364. * @param string $appName
  365. */
  366. public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '')
  367. {
  368. if($moduleName != $this->moduleName) $this->app->fetchModule = $moduleName;
  369. return parent::fetch($moduleName, $methodName, $params, $appName);
  370. }
  371. /**
  372. * Build operate menu of a method.
  373. *
  374. * @param object $object product|project|productplan|release|build|story|task|bug|testtask|testcase|testsuite
  375. * @param string $type view|browse
  376. * @access public
  377. * @return string
  378. */
  379. public function buildOperateMenu($object, $type = 'view')
  380. {
  381. if($this->config->edition == 'open') return false;
  382. $moduleName = $this->moduleName;
  383. return $this->$moduleName->buildOperateMenu($object, $type);
  384. }
  385. /**
  386. * Execute hooks of a method.
  387. *
  388. * @param int $objectID The id of an object. The object maybe a bug | build | feedback | product | productplan | project | release | story | task | testcase | testsuite | testtask.
  389. * @access public
  390. * @return string
  391. */
  392. public function executeHooks($objectID)
  393. {
  394. if($this->config->edition == 'open') return '';
  395. $moduleName = $this->moduleName;
  396. return $this->$moduleName->executeHooks($objectID);
  397. }
  398. /**
  399. * Set workflow export fields
  400. *
  401. * @access public
  402. * @return array
  403. */
  404. public function getFlowExportFields()
  405. {
  406. if($this->config->edition == 'open') return array();
  407. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return array();
  408. $moduleName = $this->moduleName;
  409. return $this->$moduleName->getFlowExportFields();
  410. }
  411. /**
  412. * Print extend fields.
  413. *
  414. * @param object $object bug | build | feedback | product | productplan | project | release | story | task | testcase | testsuite | testtask
  415. * @param string $type The parent component which fileds displayed in. It should be table or div.
  416. * @param string $extras The extra params.
  417. * columns=2|3|5 Number of the columns merged to display the fields. The default is 1.
  418. * position=left|right The position which the fields displayed in a page.
  419. * inForm=0|1 The fields displayed in a form or not. The default is 1.
  420. * inCell=0|1 The fields displayed in a div with class cell or not. The default is 0.
  421. * @param bool $print
  422. * @param string $moduleName
  423. * @param string $methodName
  424. * @access public
  425. * @return void
  426. */
  427. public function printExtendFields($object, $type, $extras = '', $print = true, $moduleName = '', $methodName = '')
  428. {
  429. if($this->config->edition == 'open') return false;
  430. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return false;
  431. $moduleName = $moduleName ?: $this->app->getModuleName();
  432. $methodName = $methodName ?: $this->app->getMethodName();
  433. $this->loadModel('flow');
  434. if($type == 'html')
  435. {
  436. parse_str($extras, $result);
  437. $html = $this->flow->buildExtendHtmlValue($object, zget($result, 'position', 'info'));
  438. $html .= $this->appendExtendCssAndJS($moduleName, $methodName, $object);
  439. }
  440. else
  441. {
  442. ob_start();
  443. $this->flow->printFields($moduleName, $methodName, $object, $type, $extras);
  444. $html = ob_get_contents();
  445. ob_end_clean();
  446. }
  447. if(!$print) return $html;
  448. echo $html;
  449. }
  450. /**
  451. * 追加工作流新增的字段,给formGridPanel组件使用。
  452. * append workflow fields to form.
  453. *
  454. * @param zin\fieldList $fields
  455. * @param string $moduleName
  456. * @param string $methodName
  457. * @param object $object
  458. * @access public
  459. * @return zin\fieldList
  460. */
  461. public function appendExtendFields($fields, $moduleName = '', $methodName = '', $object = null)
  462. {
  463. if($this->config->edition == 'open') return $fields;
  464. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return $fields;
  465. $moduleName = $moduleName ? $moduleName : $this->app->rawModule;
  466. $methodName = $methodName ? $methodName : $this->app->rawMethod;
  467. $groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
  468. $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
  469. if(!$flow) return $fields;
  470. $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
  471. if(!$action || $action->extensionType != 'extend') return $fields;
  472. $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
  473. $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group);
  474. /* 复制项目时显示被复制项目的工作流字段值。*/
  475. if($moduleName == 'project' && $methodName == 'create')
  476. {
  477. foreach($fieldList as $field)
  478. {
  479. $objectValue = zget($object, $field->field, '');
  480. if($objectValue) $field->default = $objectValue;
  481. }
  482. }
  483. return $this->loadModel('flow')->buildFormFields($fields, $fieldList, array(), $object);
  484. }
  485. /**
  486. * 追加工作流配置的js和css到页面上。
  487. * append workflow js and css to form.
  488. *
  489. * @param string $moduleName
  490. * @param string $methodName
  491. * @param object $object
  492. * @access public
  493. * @return string
  494. */
  495. public function appendExtendCssAndJS($moduleName = '', $methodName = '', $object = null)
  496. {
  497. if($this->config->edition == 'open') return '';
  498. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return '';
  499. $moduleName = $moduleName ? $moduleName : $this->app->rawModule;
  500. $methodName = $methodName ? $methodName : $this->app->rawMethod;
  501. $groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
  502. $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
  503. if(!$flow) return '';
  504. $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
  505. if(!$action || $action->extensionType == 'none') return '';
  506. $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object);
  507. $fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $flow->group);
  508. $html = '';
  509. if(!empty($flow->css)) $html .= "<style>$flow->css</style>";
  510. if(!empty($action->css)) $html .= "<style>$action->css</style>";
  511. $html .= $this->loadModel('flow')->getFormulaScript($moduleName, $action, $fieldList);
  512. if(!empty($action->linkages)) $html .= $this->flow->getLinkageScript($action, $fieldList, $uiID);
  513. if(!empty($flow->js)) $html .= "<script>$flow->js</script>";
  514. if(!empty($action->js)) $html .= "<script>$action->js</script>";
  515. return $html;
  516. }
  517. /**
  518. * 追加工作流新增的字段,给非formGridPanel组件使用。
  519. * append workflow field to form.
  520. *
  521. * @param string $position info|basic
  522. * @param object $object
  523. * @param string $moduleName
  524. * @param string $methodName
  525. * @access public
  526. * @return array
  527. */
  528. public function appendExtendForm($position = 'info', $object = null, $moduleName = '', $methodName = '')
  529. {
  530. if($this->config->edition == 'open') return array();
  531. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return array();
  532. $moduleName = $moduleName ? $moduleName : $this->app->rawModule;
  533. $methodName = $methodName ? $methodName : $this->app->rawMethod;
  534. $this->loadModel('flow');
  535. $this->loadModel('workflowfield');
  536. $groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
  537. $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
  538. if(!$flow) return array();
  539. $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
  540. if(!$action || $action->extensionType != 'extend') return array();
  541. $uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0;
  542. $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $flow->group);
  543. $layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $flow->group);
  544. $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
  545. if($layouts)
  546. {
  547. $allFields = $this->dao->select('field, `default`')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($moduleName)->fetchPairs();
  548. foreach($fieldList as $fieldName => $field)
  549. {
  550. if(isset($allFields[$fieldName])) $field->default = $allFields[$fieldName];
  551. }
  552. foreach($fieldList as $key => $field)
  553. {
  554. if($field->buildin || !$field->show || !isset($layouts[$field->field]) || (!empty($field->position) && $position != $field->position)) unset($fieldList[$key]);
  555. if((is_numeric($field->default) || $field->default) && empty($field->defaultValue)) $field->defaultValue = $field->default;
  556. if($object && empty($object->{$field->field}) && $field->defaultValue !== '') $object->{$field->field} = $field->defaultValue;
  557. if(in_array($field->type, $this->config->workflowfield->numberTypes)) $field = $this->workflowfield->processNumberField($field);
  558. $field->required = !$field->readonly && $notEmptyRule && strpos(",$field->rules,", ",{$notEmptyRule->id},") !== false;
  559. $field->control = $this->flow->buildFormControl($field);
  560. $field->items = $field->options ? array_filter($field->options) : null;
  561. $field->value = isset($object->{$field->field}) ? zget($object, $field->field, '') : '';
  562. $field->width = $field->width != 'auto' ? $field->width : 'full';
  563. if(!$field->value && $field->defaultValue) $field->value = $field->defaultValue;
  564. }
  565. return $fieldList;
  566. }
  567. return array();
  568. }
  569. /**
  570. * Process status of an object according to its subStatus.
  571. *
  572. * @param string $module product | release | story | project | task | bug | testcase | testtask | feedback
  573. * @param object $record a record of above modules.
  574. * @access public
  575. * @return string
  576. */
  577. public function processStatus($module, $record)
  578. {
  579. $moduleName = $this->moduleName;
  580. return $this->$moduleName->processStatus($module, $record);
  581. }
  582. /**
  583. * Print view file.
  584. *
  585. * @param string $viewFile
  586. * @access public
  587. * @return bool|string
  588. */
  589. public function printViewFile($viewFile)
  590. {
  591. if(!file_exists($viewFile)) return false;
  592. if(substr($viewFile, -4) != '.php') return false;
  593. if(strpos($viewFile, '..') !== false) return false;
  594. if(strpos($viewFile, '/view/') === false) return false;
  595. if(strpos($viewFile, $this->app->getModuleRoot()) !== 0 && strpos($viewFile, $this->app->getExtensionRoot()) !== 0) return false;
  596. $currentPWD = getcwd();
  597. chdir(dirname($viewFile));
  598. extract((array)$this->view);
  599. ob_start();
  600. include $viewFile;
  601. $output = ob_get_contents();
  602. ob_end_clean();
  603. chdir($currentPWD);
  604. return $output;
  605. }
  606. /**
  607. * 将工作流扩展的必填字段加到requiredFields里,类似批量操作的会在checkFlow之前手动校验。
  608. * append workflow extension fields to requiredFields.
  609. *
  610. * @param int $objectID
  611. * @access public
  612. * @return void
  613. */
  614. public function extendRequireFields($objectID = 0)
  615. {
  616. if($this->config->edition == 'open') return;
  617. $groupID = $this->loadModel('workflowgroup')->getGroupIDByDataID($this->moduleName, $objectID);
  618. $uiID = $this->loadModel('workflowlayout')->getUIByDataID($this->moduleName, $this->methodName, $objectID);
  619. $fields = $this->loadModel('workflowaction')->getPageFields($this->moduleName, $this->methodName, true, null, $uiID, $groupID);
  620. $layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName, $uiID, $groupID);
  621. $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
  622. foreach($fields as $field)
  623. {
  624. if($field->buildin || !$field->show || !isset($layouts[$field->field])) continue;
  625. if($notEmptyRule && strpos(",$field->rules,", ",$notEmptyRule->id,") !== false)
  626. {
  627. if(!isset($this->config->{$this->moduleName})) $this->config->{$this->moduleName} = new stdclass();
  628. if(!isset($this->config->{$this->moduleName}->{$this->methodName})) $this->config->{$this->moduleName}->{$this->methodName} = new stdclass();
  629. if(!isset($this->config->{$this->moduleName}->{$this->methodName}->requiredFields)) $this->config->{$this->moduleName}->{$this->methodName}->requiredFields = '';
  630. $this->config->{$this->moduleName}->{$this->methodName}->requiredFields .= ",{$field->field}";
  631. }
  632. }
  633. }
  634. /**
  635. * Call the functions declared in the zen files.
  636. *
  637. * @param string $method
  638. * @param array $arguments
  639. * @access public
  640. * @return mixed
  641. */
  642. public function __call($method, $arguments)
  643. {
  644. $moduleName = $this->app->getModuleName();
  645. $zenClass = $moduleName . 'Zen';
  646. if(isset($this->{$zenClass}) && is_callable(array($this->{$zenClass}, $method))) return call_user_func_array(array($this->{$zenClass}, $method), $arguments);
  647. $this->app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, true);
  648. }
  649. /**
  650. * Call the static functions declared in the zen files.
  651. *
  652. * @param string $method
  653. * @param array $arguments
  654. * @access public
  655. * @return mixed
  656. */
  657. public static function __callStatic($method, $arguments)
  658. {
  659. global $app;
  660. $moduleName = $app->getModuleName();
  661. $zenClass = 'ext' . $moduleName . 'Zen';
  662. if(is_callable("{$zenClass}::{$method}")) return call_user_func_array("{$zenClass}::{$method}", $arguments);
  663. $zenClass = $moduleName . 'Zen';
  664. if(is_callable("{$zenClass}::{$method}")) return call_user_func_array("{$zenClass}::{$method}", $arguments);
  665. $app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, true);
  666. }
  667. }