helper.class.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. <?php /**
  2. * ZenTaoPHP的helper类。
  3. * The helper 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. * 该类实现了一些常用的方法
  14. * The helper class, contains the tool functions.
  15. *
  16. * @package framework
  17. */
  18. include __DIR__ . '/base/helper.class.php';
  19. class helper extends baseHelper
  20. {
  21. /**
  22. * @param bool $source
  23. */
  24. public static function getViewType($source = false)
  25. {
  26. global $config, $app;
  27. if($config->requestType != 'GET')
  28. {
  29. $pathInfo = $app->getPathInfo();
  30. if(!empty($pathInfo))
  31. {
  32. $dotPos = strrpos((string) $pathInfo, '.');
  33. if($dotPos)
  34. {
  35. $viewType = substr((string) $pathInfo, $dotPos + 1);
  36. }
  37. else
  38. {
  39. $config->default->view = $config->default->view == 'mhtml' ? 'html' : $config->default->view;
  40. }
  41. }
  42. }
  43. elseif($config->requestType == 'GET')
  44. {
  45. if(isset($_GET[$config->viewVar]))
  46. {
  47. $viewType = $_GET[$config->viewVar];
  48. }
  49. else
  50. {
  51. /* Set default view when url has not module name. such as only domain. */
  52. $config->default->view = ($config->default->view == 'mhtml' and isset($_GET[$config->moduleVar])) ? 'html' : $config->default->view;
  53. }
  54. }
  55. if($source and isset($viewType)) return $viewType;
  56. if(isset($viewType) and strpos((string) $config->views, ',' . $viewType . ',') === false) $viewType = $config->default->view;
  57. return $viewType ?? $config->default->view;
  58. }
  59. /**
  60. * Encode json for $.parseJSON
  61. *
  62. * @param array $data
  63. * @param int $options
  64. * @static
  65. * @access public
  66. * @return string
  67. */
  68. public static function jsonEncode4Parse($data, $options = 0)
  69. {
  70. $json = json_encode($data);
  71. if($options) $json = str_replace(array("'", '"'), array('\u0027', '\u0022'), $json);
  72. $escapers = array("\\", "/", "\"", "'", "\n", "\r", "\t", "\x08", "\x0c", "\\\\u");
  73. $replacements = array("\\\\", "\\/", "\\\"", "\'", "\\n", "\\r", "\\t", "\\f", "\\b", "\\u");
  74. return str_replace($escapers, $replacements, $json);
  75. }
  76. /**
  77. * Verify that the system has opened on the feature.
  78. *
  79. * @param string $feature scrum_risk | risk | scrum
  80. * @static
  81. * @access public
  82. * @return bool
  83. */
  84. public static function hasFeature($feature)
  85. {
  86. global $config;
  87. if(in_array($feature, array('waterfall', 'waterfallplus')))
  88. {
  89. return strpos(",$config->disabledFeatures,", ",{$feature},") === false; // 轻量级模式排除瀑布、融合瀑布模型
  90. }
  91. if(strpos($feature, '_') !== false)
  92. {
  93. $code = explode('_', $feature);
  94. $code = $code[0] . ucfirst($code[1]);
  95. return strpos(",$config->disabledFeatures,", ",{$code},") === false;
  96. }
  97. $hasFeature = false;
  98. $canConfigFeature = false;
  99. foreach($config->featureGroup as $group => $modules)
  100. {
  101. foreach($modules as $module)
  102. {
  103. if($feature != $group && $feature != $module) continue;
  104. $canConfigFeature = true;
  105. $hasFeature |= helper::hasFeature("{$group}_{$module}");
  106. }
  107. }
  108. return !$canConfigFeature || ($hasFeature && strpos(",$config->disabledFeatures,", ",{$feature},") === false);
  109. }
  110. /**
  111. * Convert encoding.
  112. *
  113. * @param string $string
  114. * @param string $fromEncoding
  115. * @param string $toEncoding
  116. * @static
  117. * @access public
  118. * @return string
  119. */
  120. public static function convertEncoding($string, $fromEncoding, $toEncoding = 'utf-8')
  121. {
  122. $toEncoding = str_replace('utf8', 'utf-8', $toEncoding);
  123. if(function_exists('mb_convert_encoding'))
  124. {
  125. /* Remove like utf-8//TRANSLIT. */
  126. $position = strpos($toEncoding, '//');
  127. if($position !== false) $toEncoding = substr($toEncoding, 0, $position);
  128. /* Check string encoding. */
  129. $encodings = array_merge(array('GB2312', 'GBK', 'BIG5'), mb_list_encodings());
  130. $encoding = strtolower(mb_detect_encoding($string, $encodings));
  131. if($encoding == $toEncoding) return $string;
  132. return mb_convert_encoding($string, $toEncoding, $encoding);
  133. }
  134. elseif(function_exists('iconv'))
  135. {
  136. if($fromEncoding == $toEncoding) return $string;
  137. $errorlevel = error_reporting();
  138. error_reporting(0);
  139. $convertString = iconv($fromEncoding, $toEncoding, $string);
  140. error_reporting($errorlevel);
  141. /* iconv error then return original. */
  142. if(!$convertString) return $string;
  143. return $convertString;
  144. }
  145. return $string;
  146. }
  147. /**
  148. * Calculate two working days.
  149. *
  150. * @param string $begin
  151. * @param string $end
  152. * @return bool|float
  153. */
  154. public static function workDays($begin, $end)
  155. {
  156. $begin = strtotime($begin);
  157. $end = strtotime($end);
  158. if($end < $begin) return false;
  159. $double = floor(($end - $begin) / (7 * 24 * 3600));
  160. $begin = date('w', $begin);
  161. $end = date('w', $end);
  162. $end = $begin > $end ? $end + 5 : $end;
  163. return $double * 5 + $end - $begin;
  164. }
  165. /**
  166. * Unify string to standard chars.
  167. *
  168. * @param string $string
  169. * @param string $to
  170. * @static
  171. * @access public
  172. * @return string
  173. */
  174. public static function unify($string, $to = ',')
  175. {
  176. $labels = array('_', '、', ' ', '-', '?', '@', '&', '%', '~', '`', '+', '*', '/', '\\', ',', '。');
  177. $string = str_replace($labels, $to, $string);
  178. return preg_replace("/[{$to}]+/", $to, trim($string, $to));
  179. }
  180. /**
  181. * Format version to semver formate.
  182. *
  183. * @param string $version
  184. * @static
  185. * @access public
  186. * @return string
  187. */
  188. public static function formatVersion($version)
  189. {
  190. return preg_replace_callback(
  191. '/([0-9]+)((?:\.[0-9]+)?)((?:\.[0-9]+)?)(?:[\s\-\+]?)((?:[a-z]+)?)((?:\.?[0-9]+)?)/i',
  192. function($matches)
  193. {
  194. $major = $matches[1];
  195. $minor = $matches[2];
  196. $patch = $matches[3];
  197. $preRelease = $matches[4];
  198. $build = $matches[5];
  199. $versionStrs = array($major, $minor ?: ".0", $patch ?: ".0");
  200. if($preRelease ?: $build) array_push($versionStrs, "-");
  201. if($preRelease) array_push($versionStrs, $preRelease);
  202. if($build)
  203. {
  204. if(!$preRelease) array_push($versionStrs, "build");
  205. if(mb_substr($build, 0, 1) !== ".") array_push($versionStrs, ".");
  206. array_push($versionStrs, $build);
  207. }
  208. return implode('', $versionStrs);
  209. },
  210. $version
  211. );
  212. }
  213. /**
  214. * Process traffic.
  215. *
  216. * @param float $traffic
  217. * @param int $precision
  218. * @access public
  219. * @return string
  220. */
  221. public static function formatKB($traffic, $precision = 2)
  222. {
  223. if(!$traffic) return 0;
  224. $base = log((float)$traffic, 1024);
  225. $suffixes = array('B', 'KB', 'MB', 'GB', 'TB');
  226. return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
  227. }
  228. /**
  229. * Trim version to xuanxuan version format.
  230. *
  231. * @param string $version
  232. * @access public
  233. * @return string
  234. */
  235. public function trimVersion($version)
  236. {
  237. return preg_replace_callback(
  238. '/([0-9]+)((?:\.[0-9]+)?)((?:\.[0-9]+)?)(?:[\s\-\+]?)((?:[a-z]+)?)((?:\.?[0-9]+)?)/i',
  239. function($matches)
  240. {
  241. $major = $matches[1];
  242. $minor = $matches[2];
  243. $patch = $matches[3];
  244. $preRelease = $matches[4];
  245. $build = $matches[5];
  246. $versionStrs = array($major, $minor ?: ".0");
  247. if($patch && $patch !== ".0" && $patch !== "0") array_push($versionStrs, $patch);
  248. if($preRelease ?: $build) array_push($versionStrs, " ");
  249. if($preRelease) array_push($versionStrs, $preRelease);
  250. if($build)
  251. {
  252. if(!$preRelease) array_push($versionStrs, "build");
  253. array_push($versionStrs, mb_substr($build, 0, 1) === "." ? substr($build, 1) : $build);
  254. }
  255. return implode('', $versionStrs);
  256. },
  257. $version
  258. );
  259. }
  260. /**
  261. * Request API.
  262. *
  263. * @param string $url
  264. * @static
  265. * @access public
  266. * @return string
  267. */
  268. public static function requestAPI($url)
  269. {
  270. global $config;
  271. $url .= (strpos($url, '?') !== false ? '&' : '?') . $config->sessionVar . '=' . session_id();
  272. if(isset($_SESSION['user'])) $url .= '&account=' . $_SESSION['user']->account;
  273. $response = common::http($url);
  274. $jsonDecode = json_decode((string) $response);
  275. if(empty($jsonDecode)) return $response;
  276. return $jsonDecode;
  277. }
  278. /**
  279. * Get date interval.
  280. *
  281. * @param string $format %Y-%m-%d %H:%i:%s
  282. * @static
  283. * @access public
  284. * @param string|int $begin
  285. * @param string|int $end
  286. * @return object|string
  287. */
  288. public static function getDateInterval($begin, $end = '', $format = '')
  289. {
  290. if(empty($end)) $end = time();
  291. if(is_int($begin)) $begin = date('Y-m-d H:i:s', $begin);
  292. if(is_int($end)) $end = date('Y-m-d H:i:s', $end);
  293. $begin = date_create($begin);
  294. $end = date_create($end);
  295. $interval = date_diff($begin, $end);
  296. if($format)
  297. {
  298. $dateInterval = $interval->format($format);
  299. }
  300. else
  301. {
  302. $dateInterval = new stdClass();
  303. $dateInterval->year = $interval->format('%y');
  304. $dateInterval->month = $interval->format('%m');
  305. $dateInterval->day = $interval->format('%d');
  306. $dateInterval->hour = $interval->format('%H');
  307. $dateInterval->minute = $interval->format('%i');
  308. $dateInterval->secound = $interval->format('%s');
  309. $dateInterval->year = $dateInterval->year == '00' ? 0 : ltrim($dateInterval->year, '0');
  310. $dateInterval->month = $dateInterval->month == '00' ? 0 : ltrim($dateInterval->month, '0');
  311. $dateInterval->day = $dateInterval->day == '00' ? 0 : ltrim($dateInterval->day, '0');
  312. $dateInterval->hour = $dateInterval->hour == '00' ? 0 : ltrim($dateInterval->hour, '0');
  313. $dateInterval->minute = $dateInterval->minute == '00' ? 0 : ltrim($dateInterval->minute, '0');
  314. $dateInterval->secound = $dateInterval->secound == '00' ? 0 : ltrim($dateInterval->secound, '0');
  315. }
  316. return $dateInterval;
  317. }
  318. /**
  319. * 是否是内网。
  320. * Check is intranet.
  321. *
  322. * @return bool
  323. */
  324. public static function isIntranet()
  325. {
  326. return !defined('USE_INTRANET') ? false : USE_INTRANET;
  327. }
  328. /**
  329. * 检查是否启用缓存。
  330. * Check is enable cache.
  331. *
  332. * @return bool
  333. */
  334. public static function isCacheEnabled()
  335. {
  336. if(isset($_GET['_nocache']) || isset($_SERVER['HTTP_X_ZT_REFRESH'])) return false;
  337. global $config;
  338. return $config->cache->enable;
  339. }
  340. /**
  341. * 检查是否启用APCu。
  342. * Check if APCu is enabled.
  343. *
  344. * @access public
  345. * @return bool
  346. */
  347. public static function isAPCuEnabled()
  348. {
  349. return extension_loaded('apcu') && ini_get('apc.enabled') == '1';
  350. }
  351. /**
  352. * 检查条件是否成立。
  353. *
  354. * @param mixed $value1
  355. * @param mixed $value2
  356. * @param string $operator
  357. * @static
  358. * @access public
  359. * @return bool
  360. */
  361. public static function checkCondition($value1, $value2, $operator)
  362. {
  363. $operatorList = array('=' => 'equal', '==' => 'equal', '!=' => 'notequal', '>' => 'gt', '>=' => 'ge', '<' => 'lt', '<=' => 'le');
  364. if(!isset($operatorList[$operator]) && !in_array($operator, $operatorList)) return false;
  365. $operator = isset($operatorList[$operator]) ? zget($operatorList, $operator) : $operator;
  366. $checkFunc = 'check' . $operator;
  367. return validater::$checkFunc($value1, $value2);
  368. }
  369. /**
  370. * 替换 Emoji 为指定字符串。
  371. * Replace Emoji to a specified string.
  372. *
  373. * @param string $subject
  374. * @param string $replace
  375. * @access public
  376. * @return string
  377. */
  378. public static function replaceEmoji($subject, $replace = '[Emoji]')
  379. {
  380. /* 匹配大部分常见 Emoji 范围(包括符号、旗帜、交通工具等)。 */
  381. $pattern = '/[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{1F900}-\x{1F9FF}]/u';
  382. return preg_replace($pattern, $replace, $subject);
  383. }
  384. }
  385. /**
  386. * 检查是否是onlybody模式。
  387. * Check exist onlybody param.
  388. *
  389. * @access public
  390. * @return bool
  391. */
  392. function isonlybody()
  393. {
  394. return helper::inOnlyBodyMode();
  395. }
  396. /**
  397. * 检查页面是否是弹窗中。
  398. * Check page is modal.
  399. *
  400. * @access public
  401. * @return bool
  402. */
  403. function isInModal()
  404. {
  405. return helper::isAjaxRequest('modal');
  406. }
  407. /**
  408. * Format time.
  409. *
  410. * @param string|null $time
  411. * @param string $format
  412. * @access public
  413. * @return string
  414. */
  415. function formatTime($time, $format = '')
  416. {
  417. if($time === null) return '';
  418. $time = str_replace('0000-00-00', '', $time);
  419. $time = str_replace('00:00:00', '', $time);
  420. if(trim($time) == '') return '';
  421. if($format) return date($format, strtotime($time));
  422. return trim($time);
  423. }
  424. /**
  425. * Init page title based on the module name and the method name.
  426. *
  427. * @access public
  428. * @return string
  429. */
  430. function initPageTitle()
  431. {
  432. global $app, $lang;
  433. $module = $app->rawModule;
  434. $method = $app->rawMethod;
  435. if(empty($lang->$module)) $app->loadLang($module);
  436. if(!empty($lang->$module->{$method . 'Action'})) return $lang->$module->{$method . 'Action'};
  437. if(!empty($lang->$module->$method)) return $lang->$module->$method;
  438. return zget($lang, $method);
  439. }
  440. /**
  441. * Init page entity based on configuration of objectNameFields.
  442. *
  443. * @param object $object
  444. * @access public
  445. * @return array
  446. */
  447. function initPageEntity($object)
  448. {
  449. if(empty($object)) return array();
  450. global $app, $config;
  451. $app->loadModuleConfig('action');
  452. $module = $app->getModuleName();
  453. $idField = isset($config->action->objectIdFields[$module]) ? $config->action->objectIdFields[$module] : 'id';
  454. $titleField = isset($config->action->objectNameFields[$module]) ? $config->action->objectNameFields[$module] : 'title';
  455. return array(zget($object, $titleField, ''), zget($object, $idField, 0));
  456. }
  457. /**
  458. * Init table data of zin.
  459. *
  460. * @param array $items
  461. * @param array $fieldList
  462. * @param object $model
  463. * @access public
  464. * @return array
  465. */
  466. function initTableData($items, &$fieldList, $model = null)
  467. {
  468. if(!empty($_GET['orderBy']) && strpos($_GET['orderBy'], '_') !== false) list($orderField, $orderValue) = explode('_', $_GET['orderBy']);
  469. if(!empty($orderField) && !empty($orderValue) && !empty($fieldList[$orderField]))
  470. {
  471. $sortType = false;
  472. $col = $fieldList[$orderField];
  473. if(empty($col['sortType']) && !empty($col['type']) && in_array($col['type'], array('id', 'title'))) $sortType = true;
  474. if(!empty($col['sortType'])) $sortType = $col['sortType'];
  475. if(is_bool($sortType)) $fieldList[$orderField]['sortType'] = $orderValue;
  476. }
  477. $items = setParent($items);
  478. if(empty($fieldList['actions'])) return $items;
  479. foreach($fieldList['actions']['menu'] as $actionMenu)
  480. {
  481. if(is_array($actionMenu))
  482. {
  483. foreach($actionMenu as $actionMenuKey => $actionName)
  484. {
  485. if($actionMenuKey === 'other')
  486. {
  487. foreach($actionName as $otherActionName) initTableActions($fieldList, $otherActionName);
  488. }
  489. else
  490. {
  491. initTableActions($fieldList, $actionName);
  492. }
  493. }
  494. }
  495. else
  496. {
  497. initTableActions($fieldList, $actionMenu);
  498. }
  499. }
  500. global $app, $lang;
  501. if(empty($model))
  502. {
  503. $module = $app->getModuleName();
  504. $model = $app->control->loadModel($module);
  505. }
  506. $maxActionCount = 0;
  507. foreach($items as $item)
  508. {
  509. $item->actions = array();
  510. $actionList = zget($fieldList['actions'], 'list', array());
  511. foreach($fieldList['actions']['menu'] as $actionKey => $actionMenu)
  512. {
  513. if(isset($actionMenu['other']))
  514. {
  515. $currentActionMenu = $actionMenu[0];
  516. initItemActions($item, $currentActionMenu, $actionList, $model);
  517. $otherActionMenus = $actionMenu['other'];
  518. $otherAction = '';
  519. foreach($otherActionMenus as $otherActionMenu)
  520. {
  521. $otherActions = explode('|', $otherActionMenu);
  522. foreach($otherActions as $otherActionName)
  523. {
  524. if(!checkOtherPriv(zget($actionList, $otherActionName, array()), $otherActionName, $item, $model)) continue;
  525. if(in_array($otherActionName, array_column($item->actions, 'name'))) continue;
  526. if(method_exists($model, 'isClickable') && !$model->isClickable($item, $otherActionName)) $otherAction .= '-';
  527. $otherAction .= $otherActionName . ',';
  528. }
  529. }
  530. if($otherAction) $item->actions[] = 'other:' . $otherAction;
  531. }
  532. elseif($actionKey === 'more')
  533. {
  534. $moreAction = '';
  535. foreach($actionMenu as $moreActionName)
  536. {
  537. if(!checkOtherPriv(zget($actionList, $moreActionName, array()), $moreActionName, $item, $model)) continue;
  538. if(method_exists($model, 'isClickable') && !$model->isClickable($item, $moreActionName)) $moreAction .= '-';
  539. $moreAction .= $moreActionName . ',';
  540. }
  541. if($moreAction) $item->actions[] = 'more:' . $moreAction;
  542. }
  543. elseif(is_array($actionMenu)) // Two or more grups.
  544. {
  545. /*
  546. * Menu可能会有多套,如果只有一套可以直接用一维数组。
  547. * There are maybe two or more groups of action menus.
  548. */
  549. $item->actions = array();
  550. $isClickable = false;
  551. foreach($actionMenu as $actionName) $isClickable |= initItemActions($item, $actionName, zget($fieldList['actions'], 'list', array()), $model);
  552. if($isClickable) break; // If the action is clickable, use this group.
  553. }
  554. else // Only one group of action menus.
  555. {
  556. initItemActions($item, $actionMenu, zget($fieldList['actions'], 'list', array()), $model);
  557. }
  558. }
  559. if(count($item->actions) > $maxActionCount) $maxActionCount = count($item->actions);
  560. }
  561. if(isset($fieldList['actions']))
  562. {
  563. $fieldList['actions']['minWidth'] = $maxActionCount * 24 + 24;
  564. if(empty($fieldList['actions']['title'])) $fieldList['actions']['title'] = $lang->actions;
  565. }
  566. if($fieldList['actions']['minWidth'] < 48) $fieldList['actions']['minWidth'] = 48;
  567. return array_values($items);
  568. }
  569. /**
  570. * Check other action priv.
  571. *
  572. * @param array $actionList
  573. * @param string $actionName
  574. * @param object $item
  575. * @param object $model
  576. * @access public
  577. * @return bool
  578. */
  579. function checkOtherPriv($actionConfig, $action, $item, $model)
  580. {
  581. global $app;
  582. $module = $model->getModuleName();
  583. if($module == 'flow') $module = $app->rawModule;
  584. if(!empty($actionConfig['url']['module']) && $module != $actionConfig['url']['module']) $module = $actionConfig['url']['module'];
  585. $method = $action;
  586. if(!empty($actionConfig['url']['method']) && $method != $actionConfig['url']['method']) $method = $actionConfig['url']['method'];
  587. return common::hasPriv($module, $method, $item);
  588. }
  589. /**
  590. * Set the parent property of the data.
  591. *
  592. * @param array $items
  593. * @access public
  594. * @return array
  595. */
  596. function setParent($items)
  597. {
  598. foreach($items as $item)
  599. {
  600. if(isset($item->isParent)) continue;
  601. /* Set parent attribute. */
  602. $item->isParent = false;
  603. if(isset($item->parent) && $item->parent == -1)
  604. {
  605. /* When the parent is -1, the hierarchical structure is displayed incorrectly. */
  606. $item->parent = 0;
  607. $item->isParent = true;
  608. }
  609. if(!empty($item->parent) && isset($items[$item->parent]) && isset($item->type) && $item->type == 'stage') $items[$item->parent]->isParent = true;
  610. }
  611. return $items;
  612. }
  613. /**
  614. * Init column actions of a table.
  615. *
  616. * @param array $fieldList
  617. * @param string $actionMenu
  618. * @access public
  619. * @return void
  620. */
  621. function initTableActions(&$fieldList, $actionMenu)
  622. {
  623. $actions = explode('|', $actionMenu);
  624. foreach($actions as $action)
  625. {
  626. if(!isset($fieldList['actions']['list'][$action])) continue;
  627. $actionConfig = $fieldList['actions']['list'][$action];
  628. if(!empty($actionConfig['icon'])) $actionConfig['text'] = '';
  629. if(!empty($actionConfig['url']['module']) && !empty($actionConfig['url']['method']))
  630. {
  631. $module = $actionConfig['url']['module'];
  632. $method = $actionConfig['url']['method'];
  633. $params = !empty($actionConfig['url']['params']) ? $actionConfig['url']['params'] : array();
  634. $actionConfig['url'] = helper::createLink($module, $method, $params, '', !empty($actionConfig['url']['onlybody']));
  635. }
  636. $fieldList['actions']['actionsMap'][$action] = $actionConfig;
  637. }
  638. }
  639. /**
  640. * Init row actions of a item.
  641. *
  642. * @param object $item
  643. * @param string $actionMenu
  644. * @param array $actionList
  645. * @param object $model
  646. * @access public
  647. * @return bool
  648. */
  649. function initItemActions(&$item, $actionMenu, $actionList, $model)
  650. {
  651. if($actionMenu == 'divider')
  652. {
  653. $item->actions[] = array('name' => 'divider', 'type' => 'divider');
  654. return true;
  655. }
  656. global $app;
  657. $module = $model->getModuleName();
  658. if($module == 'flow') $module = $app->rawModule;
  659. $method = '';
  660. $isClickable = false;
  661. $actions = explode('|', $actionMenu);
  662. foreach($actions as $action)
  663. {
  664. if(!isset($actionList[$action])) continue;
  665. $actionConfig = $actionList[$action];
  666. $notLoadModel = !empty($actionConfig['notLoadModel']) ? $actionConfig['notLoadModel'] : false;
  667. if(!empty($actionConfig['url']['module']) && $module != $actionConfig['url']['module'])
  668. {
  669. $module = $actionConfig['url']['module'];
  670. if(!$notLoadModel)
  671. {
  672. $rawModule = $module == 'projectbuild' ? 'build' : $module;
  673. $model = $app->control->loadModel($rawModule);
  674. }
  675. }
  676. $method = $action;
  677. if(!empty($actionConfig['url']['method']) && $method != $actionConfig['url']['method']) $method = $actionConfig['url']['method'];
  678. if(!method_exists($model, 'isClickable') || $model->isClickable($item, !$notLoadModel ? $method : $action))
  679. {
  680. $isClickable = true;
  681. break;
  682. }
  683. }
  684. if(!$method || !common::hasPriv($module, $method, $item)) return $isClickable;
  685. /* Check flow conditions for this object. */
  686. if($model->config->edition != 'open')
  687. {
  688. static $flowActions = [];
  689. if(empty($flowActions[$module])) $flowActions[$module] = $model->loadModel('workflowaction')->getList($module);
  690. $model->loadModel('flow');
  691. foreach($flowActions[$module] as $flowAction)
  692. {
  693. if($flowAction->action == $method && $flowAction->extensionType != 'none' && $flowAction->status == 'enable' && !empty($flowAction->conditions))
  694. {
  695. $isClickable = $model->flow->checkConditions($flowAction->conditions, $item);
  696. }
  697. }
  698. }
  699. $item->actions[] = array('name' => $action, 'disabled' => !$isClickable);
  700. return $isClickable;
  701. }
  702. /**
  703. * 生成随机数。
  704. * Generate random number.
  705. *
  706. * @access public
  707. * @return int
  708. */
  709. function updateSessionRandom()
  710. {
  711. $random = mt_rand();
  712. $_SESSION['rand'] = $random;
  713. return $random;
  714. }
  715. /**
  716. * 获取可用的界面列表。
  717. * Get available vision list.
  718. *
  719. * @access public
  720. * @return array
  721. */
  722. function getVisions()
  723. {
  724. global $config, $lang;
  725. $visions = array_flip(array_unique(array_filter(explode(',', trim($config->visions, ',')))));
  726. $visionList = $lang->visionList;
  727. return array_intersect_key($visionList, $visions);
  728. }
  729. /**
  730. * Save debug log to the php log file which prefix with 'debug.<today>'.
  731. *
  732. * @param mixed ...$messages
  733. * @return void
  734. */
  735. function debug(...$messages)
  736. {
  737. if(count($messages) > 1)
  738. {
  739. foreach($messages as $message) debug($message);
  740. return;
  741. }
  742. $message = current($messages);
  743. static $times = [];
  744. $time = microtime(true);
  745. $duration = $times ? $time - end($times) : 0;
  746. $times[] = $time;
  747. static $counts = [];
  748. $count = $counts ? end($counts) + 1 : 1;
  749. $counts[] = $count;
  750. $logFile = dirname(__FILE__, 2) . '/tmp/log/debug.' . date('Ymd') . '.log.php';
  751. $uid = $_SERVER['HTTP_X_ZIN_UID'] ?? '';
  752. $count = sprintf('%04d', $count);
  753. $time = date('H:i:s');
  754. $duration = round($duration, 3);
  755. if($duration < 0.001) $duration = 0.001;
  756. if(is_object($message) || is_array($message)) $message = json_encode($message, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
  757. file_put_contents($logFile, $count . ($uid ? " uid=$uid" : '') . " time=$time duration={$duration}s message=$message\n", FILE_APPEND);
  758. }
  759. /**
  760. * 为数组使用字母排序。
  761. * Use alphabetical sorting for arrays.
  762. *
  763. * @param array $data
  764. * @param string $fieldName
  765. * @param string $suffix
  766. * @return void
  767. */
  768. function addPrefixToField(&$data, $fieldName, $suffix = '. ')
  769. {
  770. $key = 0;
  771. foreach($data as &$item)
  772. {
  773. $prefix = '';
  774. $index = $key;
  775. while($index >= 0)
  776. {
  777. $prefix = chr(65 + ($index % 26)) . $prefix;
  778. $index = floor($index / 26) - 1;
  779. }
  780. if(is_array($item))
  781. {
  782. $item[$fieldName] = $prefix . $suffix . $item[$fieldName];
  783. }
  784. else
  785. {
  786. $item->{$fieldName} = $prefix . $suffix . $item->{$fieldName};
  787. }
  788. $key++;
  789. }
  790. }