* @package misc * @version $Id: model.php 4129 2013-01-18 01:58:14Z wwccss $ * @link https://www.zentao.net */ ?> dao->query("show full tables"); while($table = $stmt->fetch(PDO::FETCH_ASSOC)) { $tableName = $table["Tables_in_{$this->config->db->name}"]; $tableType = strtolower($table['Table_type']); if($tableType == 'base table') { $tableStatus = $this->dao->query("$type table $tableName")->fetch(); $tables[$tableName] = strtolower($tableStatus->Msg_text); } } return $tables; } /** * 获取新增年度总结功能的通知。 * Get remind. * * @access public * @return string */ public function getRemind(): string { $remind = ''; if(!empty($this->config->global->showAnnual) && empty($this->config->global->annualShowed)) { $remind = '
' . sprintf($this->lang->misc->annualDesc, helper::createLink('report', 'annualData')) . '
'; $this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.annualShowed", 1); } return $remind; } /** * 获取插件到期通知。 * Get the notification information about plugin expiration. * * @access public * @return string */ public function getPluginRemind(): string { $plugins = $this->loadModel('extension')->getExpiringPlugins(); $remind = ''; $today = helper::today(); $showPluginRemind = (empty($this->config->global->showPluginRemind) || $this->config->global->showPluginRemind != $today) ? true : false; if(!empty($plugins) && $this->app->user->admin && $showPluginRemind) { $pluginButton = html::a(helper::createLink('extension', 'browse'), $this->lang->misc->view, '', "id='pluginButton' class='btn primary wide mr-2' data-app='admin'"); $remind = '' . sprintf($this->lang->misc->expiredTipsForAdmin, count($plugins)) . '
'; $remind .= '' . $pluginButton . '
'; $this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.showPluginRemind", $today); } return $remind; } /** * 获取更新度量库表索引的通知。 * Get the notification information about updating metriclib table index. * * @access public * @return string */ public function getMetriclibRemind(): string { if(!$this->app->user->admin) return ''; if($this->config->edition == 'open') return ''; if($this->config->vision != 'rnd') return ''; $remind = ''; if(empty($this->config->global->metriclibShowed)) { $remind .= '' . $this->lang->misc->metriclibTips . '
'; $remind .= "" . html::a(helper::createLink('admin', 'metriclib'), $this->lang->misc->view, '', "id='metriclibButton' class='btn primary wide' data-app='admin'", false) . '
'; $this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.metriclibShowed", 1); } return $remind; } /** * 检查一键安装包的安全性。 * Check one click package. * * @access public * @return array */ public function checkOneClickPackage(): array { $weakSites = array(); if(strpos('|/zentao/|/biz/|/max/|', "|{$this->config->webRoot}|") !== false) { $databases = array('zentao' => 'zentao', 'zentaobiz' => 'zentaobiz', 'zentaoep' => 'zentaoep', 'zentaomax' => 'zentaomax'); $basePath = dirname($this->app->getBasePath()); foreach($databases as $database) { $zentaoDirName = $database; if(!is_dir($basePath . '/' . $zentaoDirName)) { if($zentaoDirName == 'zentaobiz' && !is_dir($basePath . '/zentaoep')) continue; if($zentaoDirName == 'zentaoep' && !is_dir($basePath . '/zentaobiz')) continue; if($zentaoDirName == 'zentao' || $zentaoDirName == 'zentaomax') continue; if($zentaoDirName == 'zentaobiz') $zentaoDirName = 'zentaoep'; } try { $webRoot = "/{$database}/"; if($database == 'zentao') $webRoot = '/zentao/'; if($database == 'zentaobiz') $webRoot = '/biz/'; if($database == 'zentaoep') $webRoot = '/biz/'; if($database == 'zentaomax') $webRoot = '/max/'; $user = $this->dbh->query("select * from {$database}.`zt_user` where account = 'admin' and password='" . md5('123456') . "'")->fetch(); if($user) { $site = array(); $site['path'] = basename($basePath) . '/' . $zentaoDirName; $site['database'] = $database; $weakSites[$database] = $site; } } catch(Exception $e){} } } return $weakSites; } /** * 获取升级提示的通知。 * Get upgrade remind. * * @access public * @return bool */ public function getUpgradeRemind(): bool { if(!empty($this->config->global->hideUpgradeGuide)) return false; $remind = false; if(empty($this->config->global->showUpgradeGuide)) { $remind = true; $this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.showUpgradeGuide", 1); } return $remind; } /** * 获取最近版本列表. * Get the latest version list * @param string $url * @param string|array $data * @param array $options This is option and value pair, like CURLOPT_HEADER => true. Use curl_setopt function to set options. * @param array $headers Set request headers. * @param string $dataType * @param string $method POST|PATCH|PUT * @param int $timeout * @param bool $httpCode * @param bool $log * @static * @access public * @return string */ public function getLatestVersionList($url, $data = null, $options = array(), $headers = array(), $dataType = 'data', $method = 'POST', $timeout = 30, $httpCode = false, $log = true) { /* Module detection. */ global $lang, $app; if(!extension_loaded('curl')) { if($dataType == 'json') return print($lang->error->noCurlExt); return json_encode(array('result' => 'fail', 'message' => $lang->error->noCurlExt)); } /* Set HTTP request header. */ commonModel::$requestErrors = array(); $requestType = 'GET'; if(!is_array($headers)) $headers = (array)$headers; $headers[] = 'API-RemoteIP: ' . helper::getRemoteIp(); /* Real IP of real user. */ $headers[] = 'API-LocalIP: ' . zget($_SERVER, 'SERVER_ADDR', ''); /* Server IP of self. */ if($dataType == 'json') { $headers[] = 'Content-Type: application/json;charset=utf-8'; if(!empty($data)) $data = json_encode($data); } /* Set curl configuration options. */ $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_setopt($curl, CURLOPT_USERAGENT, 'ZenTao PMS ' . $app->config->version); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_ENCODING,''); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($curl, CURLOPT_HEADER, $httpCode); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 2); curl_setopt($curl, CURLINFO_HEADER_OUT, true); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_REFERER, $_SERVER['HTTP_REFERER']); if(!empty($data)) { if(is_object($data)) $data = (array) $data; if($method == 'POST') curl_setopt($curl, CURLOPT_POST, true); if(in_array($method, array('PATCH', 'PUT'))) curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); $requestType = $method; curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } /* Send request. */ if($options) curl_setopt_array($curl, $options); $response = curl_exec($curl); $errno = curl_errno($curl); $errors = empty($errno) ? 0 : curl_error($curl); $info = curl_getinfo($curl); /* Processing response data. */ if($httpCode) { $httpCode = $info['http_code'] ?? curl_getinfo($curl, CURLINFO_HTTP_CODE); $headerSize = $info['header_size'] ?? curl_getinfo($curl, CURLINFO_HEADER_SIZE); $headerString = substr($response, 0, $headerSize); $body = substr($response, $headerSize); /* Parse header. */ $header = explode("\n", $headerString); $newHeader = array(); foreach($header as $item) { $field = explode(':', $item); if(count($field) < 2) continue; $headerKey = array_shift($field); $newHeader[$headerKey] = implode('', $field); } } curl_close($curl); if($log or $app->config->debug) { $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; $logFile = $app->getLogRoot() . 'saas' . $runMode . '.' . date('Ymd') . '.log.php'; if(!file_exists($logFile)) file_put_contents($logFile, ''); $fh = fopen($logFile, 'a'); if($fh) { fwrite($fh, date('Ymd H:i:s') . ': ' . $app->getURI() . "\n"); fwrite($fh, "{$requestType} url: " . $url . "\n"); if(!empty($data)) fwrite($fh, 'data: ' . print_r($data, true) . "\n"); fwrite($fh, 'results:' . print_r($response, true) . "\n"); if(!empty($errors)) { fwrite($fh, 'errno: ' . $errno . "\n"); fwrite($fh, 'errors: ' . $errors . "\n"); fwrite($fh, 'info: ' . print_r($info, true) . "\n"); } fclose($fh); } } if($errors) commonModel::$requestErrors[] = $errors; return $httpCode ? array($response, $httpCode, 'body' => $body, 'header' => $newHeader, 'errno' => $errno, 'info' => $info, 'response' => $response) : $response; } /** * Get statistics for API. * * @access public * @return array */ public function getStatisticsForAPI(): array { /* 如果用户不同意用户体验,则不返回统计数据。 If the user does not agree with the user experience, do not return statistical data. */ $this->app->loadConfig('admin'); $register = zget($this->config->admin, 'register', array()); $bindCommunity = zget($register, 'bindCommunity', 'false'); $agreeUX = zget($register, 'agreeUX', 'false'); if($bindCommunity == 'false' || $agreeUX == 'false') return array(); $data = array(); if(isset($this->config->misc->statistics)) { $statistics = json_decode($this->config->misc->statistics, true); if(helper::today() == $statistics['date']) return $statistics['data']; // Only statistics once a day } $data['user'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_USER)->where('deleted')->eq(0)->fetch('count'); $data['project'] = $this->dao->select('model, COUNT(1) AS count')->from(TABLE_PROJECT)->where('deleted')->eq(0)->andWhere('type')->eq('project')->groupBy('model')->fetchPairs('model', 'count'); $data['execution'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_PROJECT)->where('deleted')->eq(0)->andWhere('type')->in(array('sprint', 'stage', 'kanban'))->fetch('count'); $data['task'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_TASK)->where('deleted')->eq(0)->fetch('count'); $data['product'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetch('count'); $data['story'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->fetch('count'); $data['bug'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_BUG)->where('deleted')->eq(0)->fetch('count'); $data['case'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_CASE)->where('deleted')->eq(0)->fetch('count'); $data['doc'] = $this->dao->select('COUNT(1) AS count')->from(TABLE_DOC)->where('deleted')->eq(0)->fetch('count'); $data['OS'] = PHP_OS; $data['phpversion'] = PHP_VERSION; $data['dbversion'] = $this->dao->getVersion(); $this->loadModel('setting')->updateItem('system.misc.statistics', json_encode(array('date' => date('Y-m-d'), 'data' => $data))); return $data; } /** * 发送安装过程埋点。 * Sending installation process buried points. * * @param object $data * @access public * @return bool */ public function sendInstallEvent($data) { if(!$data->location || !$data->fingerprint) return false; $apiRoot = $this->config->misc->zentaonet; $apiURL = $apiRoot . "/zentaoevent-install.json"; if(empty($this->config->global->sn)) { $sn = $this->loadModel('setting')->setSN(); } else { $sn = $this->config->global->sn; } $httpData['sn'] = $sn; $httpData['location'] = $data->location; $httpData['fingerprint'] = $data->fingerprint; $httpData['edition'] = $this->config->edition; $httpData['version'] = $this->config->version; $httpData['phpVersion'] = phpversion(); $httpData['os'] = PHP_OS . ' ' . php_uname('m'); $httpData['isDocker'] = file_exists('/.dockerenv'); $httpData['installType'] = ''; $httpData['ip'] = helper::getRemoteIP(); $this->loadModel('common'); $response = common::http($apiURL, $httpData); $response = json_decode($response); if($response && $response->result == 'success') return true; return false; } }