From 19dc3cdae5cc31f91786c32ec0d3c430d82153c5 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Thu, 19 Oct 2017 11:17:25 +0800 Subject: [PATCH 01/40] =?UTF-8?q?Finish=20task#3255=20=E7=A6=85=E9=81=93?= =?UTF-8?q?=E4=B8=AD=E6=89=80=E6=9C=89=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E9=83=BD=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=AE=9A=E4=B9=89=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=AD=97=E6=AE=B5=E7=9A=84=E5=8A=9F=E8=83=BD,cost:12?= =?UTF-8?q?=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/bug/config.php | 4 +- module/bug/view/browse.html.php | 33 ++++- module/bug/view/browsedata.html.php | 114 ----------------- module/bug/view/datatabledata.html.php | 29 ----- module/datatable/control.php | 11 +- module/datatable/model.php | 8 +- module/datatable/view/ajaxcustom.html.php | 6 +- module/product/view/browse.html.php | 32 ++++- module/product/view/browsedata.html.php | 103 ---------------- module/product/view/datatabledata.html.php | 29 ----- module/project/view/datatabledata.html.php | 39 ------ module/project/view/task.html.php | 27 +++- module/project/view/taskdata.html.php | 130 -------------------- module/story/config.php | 48 ++++---- module/task/config.php | 2 +- module/testcase/config.php | 68 +++++----- module/testcase/view/browse.html.php | 33 ++++- module/testcase/view/browsedata.html.php | 99 --------------- module/testcase/view/datatabledata.html.php | 29 ----- module/testtask/config.php | 2 +- module/testtask/view/cases.html.php | 36 +++++- module/testtask/view/casesdata.html.php | 79 ------------ module/testtask/view/datatabledata.html.php | 36 ------ 23 files changed, 228 insertions(+), 769 deletions(-) delete mode 100644 module/bug/view/browsedata.html.php delete mode 100644 module/bug/view/datatabledata.html.php delete mode 100644 module/product/view/browsedata.html.php delete mode 100644 module/product/view/datatabledata.html.php delete mode 100644 module/project/view/datatabledata.html.php delete mode 100644 module/project/view/taskdata.html.php delete mode 100644 module/testcase/view/browsedata.html.php delete mode 100644 module/testcase/view/datatabledata.html.php delete mode 100644 module/testtask/view/casesdata.html.php delete mode 100644 module/testtask/view/datatabledata.html.php diff --git a/module/bug/config.php b/module/bug/config.php index ed27b6af24..c9596cd8ac 100644 --- a/module/bug/config.php +++ b/module/bug/config.php @@ -165,7 +165,7 @@ $config->bug->search['params']['lastEditedDate']= array('operator' => '=', $config->bug->search['params']['deadline'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date'); $config->bug->datatable = new stdclass(); -$config->bug->datatable->defaultField = array('id', 'severity', 'pri', 'title', 'status', 'openedBy', 'openedDate', 'assignedTo', 'resolvedBy', 'resolution', 'resolvedDate', 'actions'); +$config->bug->datatable->defaultField = array('id', 'severity', 'pri', 'title', 'status', 'openedBy', 'openedDate', 'assignedTo', 'resolvedBy', 'resolution', 'actions'); $config->bug->datatable->fieldList['id']['title'] = 'idAB'; $config->bug->datatable->fieldList['id']['fixed'] = 'left'; @@ -174,7 +174,7 @@ $config->bug->datatable->fieldList['id']['required'] = 'yes'; $config->bug->datatable->fieldList['severity']['title'] = 'severityAB'; $config->bug->datatable->fieldList['severity']['fixed'] = 'left'; -$config->bug->datatable->fieldList['severity']['width'] = '50'; +$config->bug->datatable->fieldList['severity']['width'] = '40'; $config->bug->datatable->fieldList['severity']['required'] = 'no'; $config->bug->datatable->fieldList['pri']['title'] = 'P'; diff --git a/module/bug/view/browse.html.php b/module/bug/view/browse.html.php index d2443e5a5e..a761bba051 100644 --- a/module/bug/view/browse.html.php +++ b/module/bug/view/browse.html.php @@ -168,16 +168,37 @@ js::set('branch', $branch); moduleName . ucfirst($this->methodName); $useDatatable = (isset($this->config->datatable->$datatableId->mode) and $this->config->datatable->$datatableId->mode == 'datatable'); - $file2Include = $useDatatable ? dirname(__FILE__) . '/datatabledata.html.php' : dirname(__FILE__) . '/browsedata.html.php'; $vars = "productID=$productID&branch=$branch&browseType=$browseType¶m=$param&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; - include $file2Include; + if($useDatatable) include '../../common/view/datatable.html.php'; + + $setting = $this->datatable->getSetting('bug'); + $widths = $this->datatable->setFixedFieldWidth($setting); + $columns = 0; ?> + ' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='bugIDList[]'> + + + $value) + { + if($value->show) + { + $this->datatable->printHead($value, $orderBy, $vars); + $columns ++; + } + } + ?> + + + + + + $value) $this->bug->printCell($value, $bug, $users, $builds, $branches, $modulePairs, $projects, $plans, $stories, $tasks);?> + + + - cookie->windowWidth >= $this->config->wideSize ? 13 : 11; - if($browseType == 'needconfirm') $columns = 8; - ?> - - + +
diff --git a/module/bug/view/browsedata.html.php b/module/bug/view/browsedata.html.php deleted file mode 100644 index 46c59e165b..0000000000 --- a/module/bug/view/browsedata.html.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @package bug - * @version $Id$ - * @link http://www.zentao.net - */ -?> - - - - - - - - - - - - - - - - - cookie->windowWidth >= $this->config->wideSize):?> - - - - - - - - cookie->windowWidth >= $this->config->wideSize):?> - - - - - - - - - - - id");?> - - - - - - confirmed;?> - - - - - - - - - - cookie->windowWidth >= $this->config->wideSize):?> - - - - - - - - cookie->windowWidth >= $this->config->wideSize):?> - - - - - - - - - diff --git a/module/bug/view/datatabledata.html.php b/module/bug/view/datatabledata.html.php deleted file mode 100644 index 47be55c06c..0000000000 --- a/module/bug/view/datatabledata.html.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @package bug - * @version $Id$ - * @link http://www.zentao.net - */ -?> -datatable->getSetting('bug'); -$widths = $this->datatable->setFixedFieldWidth($setting); -extract($widths); -?> -
idAB);?> bug->severityAB);?> priAB);?> bug->title);?> bug->statusAB);?> bug->deadline);?>bug->story);?>actions;?>openedByAB);?>bug->openedDateAB);?>assignedToAB);?>bug->resolvedByAB);?>bug->resolutionAB);?>bug->resolvedDateAB);?>actions;?>
- - id));?> - bug->severityList, $bug->severity, $bug->severity);?>'>bug->severityList, $bug->severity, $bug->severity);?>bug->priList, $bug->pri, $bug->pri);?>'>bug->priList, $bug->pri, $bug->pri);?> - [{$lang->bug->confirmedList[$bug->confirmed]}] "; - if($bug->branch)echo "{$branches[$bug->branch]} "; - if($modulePairs and $bug->module)echo "{$modulePairs[$bug->module]} "; - echo html::a($bugLink, $bug->title, null, "style='color: $bug->color'"); - ?> - - needconfirm) - { - echo "({$lang->story->changed} "; - echo html::a($this->createLink('bug', 'confirmStoryChange', "bugID=$bug->id"), $lang->confirm, 'hiddenwin'); - echo ")"; - } - else - { - echo $lang->bug->statusList[$bug->status]; - } - ?> - deadline, 0, 4) > 0) echo substr($bug->deadline, 5, 6)?>createLink('story', 'view', "stoyID=$bug->story"), $bug->storyTitle, '_blank');?>bug->confirmStoryChange = $lang->confirm; common::printIcon('bug', 'confirmStoryChange', "bugID=$bug->id", '', 'list', '', 'hiddenwin')?>openedBy, $bug->openedBy);?>openedDate, 5, 11)?>assignedTo == $this->app->user->account) echo 'class="red"';?>>assignedTo, $bug->assignedTo);?>resolvedBy, $bug->resolvedBy)?>bug->resolutionList, $bug->resolution);?>resolvedDate, 5, 11)?> - id"; - common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'search', '', 'iframe', true); - common::printIcon('bug', 'assignTo', $params, $bug, 'list', '', '', 'iframe', true); - common::printIcon('bug', 'resolve', $params, $bug, 'list', '', '', 'iframe', true); - common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true); - common::printIcon('bug', 'edit', $params, $bug, 'list'); - common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=bugID=$bug->id", $bug, 'list', 'copy'); - ?> -
- - $value) $this->datatable->printHead($value, $orderBy, $vars);?> - - - - - $value) $this->bug->printCell($value, $bug, $users, $builds, $branches, $modulePairs, $projects, $plans, $stories, $tasks);?> - - - diff --git a/module/datatable/control.php b/module/datatable/control.php index 02391e4667..bfcbd6fea0 100644 --- a/module/datatable/control.php +++ b/module/datatable/control.php @@ -62,11 +62,12 @@ class datatable extends control $this->loadModel('testcase'); $this->app->loadConfig('testtask'); $this->config->testcase->datatable->defaultField = $this->config->testtask->datatable->defaultField; - $this->config->testcase->datatable->fieldList['assignedTo']['title'] = 'assignedTo'; - $this->config->testcase->datatable->fieldList['assignedTo']['fixed'] = 'no'; - $this->config->testcase->datatable->fieldList['assignedTo']['width'] = '80'; - $this->config->testcase->datatable->fieldList['assignedTo']['required'] = 'no'; - $this->config->testcase->datatable->fieldList['actions']['width'] = '100'; + $this->config->testcase->datatable->fieldList['actions']['width'] = '100'; + } + if($module == 'testcase') + { + $this->loadModel('testcase'); + unset($this->config->testcase->datatable->fieldList['assignedTo']); } $this->view->module = $module; diff --git a/module/datatable/model.php b/module/datatable/model.php index 28d2e68fbf..342778e247 100644 --- a/module/datatable/model.php +++ b/module/datatable/model.php @@ -72,6 +72,7 @@ class datatableModel extends model $set->fixed = $fieldList[$id]['fixed']; $set->title = $fieldList[$id]['title']; $set->sort = isset($fieldList[$id]['sort']) ? $fieldList[$id]['sort'] : 'yes'; + $set->name = isset($fieldList[$id]['name']) ? $fieldList[$id]['name'] : ''; $setting[$key] = $set; } } @@ -123,7 +124,12 @@ class datatableModel extends model $id = $col->id; if($col->show) { - echo "config->global->flow != 'onlyTask'):?> From c9805574df39ca0cd7a12254c0afa737d45ba908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Mon, 23 Oct 2017 17:15:45 +0800 Subject: [PATCH 23/40] * task children style --- module/project/css/task.css | 5 ++++- module/project/view/task.html.php | 15 ++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/module/project/css/task.css b/module/project/css/task.css index 050b253d51..92018e1102 100644 --- a/module/project/css/task.css +++ b/module/project/css/task.css @@ -24,4 +24,7 @@ .pl-5px{padding-left:5px;} tbody > tr > td > .btn-icon {margin-right: 2px} -.table-children td{background-color:#fff0f5!important;} \ No newline at end of file +.table-children{border-left:2px solid #ccc;border-right:2px solid #ccc;} +.table-children.table-child-top{border-top:2px solid #ccc;} +.table-children.table-child-bottom{border-bottom:2px solid #ccc;} +.table-children td{background-color:#f0f8ff!important;} diff --git a/module/project/view/task.html.php b/module/project/view/task.html.php index 311b74f614..db7f63aea1 100644 --- a/module/project/view/task.html.php +++ b/module/project/view/task.html.php @@ -70,13 +70,18 @@ js::set('browseType', $browseType); $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table');?> - children)):?> - children as $child):?> - + children)) + { + $childrenNum = count($task->children); + foreach($task->children as $key=>$child) + { + ?> + parent-id;?>' data-id='id?>'> $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table');?> - - + + From 1508828fc15866ee8903d6c76952654afc488b38 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Mon, 23 Oct 2017 17:21:04 +0800 Subject: [PATCH 24/40] * fix bug select all. --- module/bug/view/browse.html.php | 2 +- module/product/view/browse.html.php | 2 +- module/project/view/task.html.php | 2 +- module/testcase/view/browse.html.php | 2 +- module/testtask/view/cases.html.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/module/bug/view/browse.html.php b/module/bug/view/browse.html.php index 4d3af96683..85ea8cc01b 100644 --- a/module/bug/view/browse.html.php +++ b/module/bug/view/browse.html.php @@ -175,7 +175,7 @@ js::set('branch', $branch); $widths = $this->datatable->setFixedFieldWidth($setting); $columns = 0; ?> -
"; + $fixed = $col->fixed == 'no' ? 'true': 'false'; + $width = is_numeric($col->width) ? "{$col->width}px" : $col->width; + $sorter = (isset($col->sort) and $col->sort == 'no') ? '' : '{sorter:false}'; + $title = isset($col->name) ? "title='$col->name'" : ''; + + echo ""; if($id == 'actions') { echo $this->lang->actions; diff --git a/module/datatable/view/ajaxcustom.html.php b/module/datatable/view/ajaxcustom.html.php index ea1c2686d3..37f34fcdcd 100644 --- a/module/datatable/view/ajaxcustom.html.php +++ b/module/datatable/view/ajaxcustom.html.php @@ -62,7 +62,8 @@
' data-key='' data-fixed='' data-width=''>   ({$lang->datatable->required})"?>
- datatable->width?> '> + + datatable->width?> '>
@@ -179,8 +180,9 @@ $(function() var setting = $('.col').map(function(index) { var $col = $(this); - var sets = {id: $col.data('key'), order: index + 1, show: !$col.hasClass('disabled'), width: $col.find('input').val(), fixed: $col.data('fixed')}; + var sets = {id: $col.data('key'), order: index + 1, show: !$col.hasClass('disabled'), width: $col.find('input#width').val(), fixed: $col.data('fixed')}; if(sets.width !== 'auto') sets.width += 'px'; + if($col.find('#name').size() > 0) sets.name = $col.find('#name').val(); return sets; }).get(); diff --git a/module/product/view/browse.html.php b/module/product/view/browse.html.php index 098c419bbf..8bb6c7f7b5 100644 --- a/module/product/view/browse.html.php +++ b/module/product/view/browse.html.php @@ -104,13 +104,39 @@ moduleName . ucfirst($this->methodName); $useDatatable = (isset($this->config->datatable->$datatableId->mode) and $this->config->datatable->$datatableId->mode == 'datatable'); - $file2Include = $useDatatable ? dirname(__FILE__) . '/datatabledata.html.php' : dirname(__FILE__) . '/browsedata.html.php'; $vars = "productID=$productID&branch=$branch&browseType=$browseType¶m=$param&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; - include $file2Include; + + if($useDatatable) include '../../common/view/datatable.html.php'; + if(!$useDatatable) include '../../common/view/tablesorter.html.php'; + $setting = $this->datatable->getSetting('product'); + $widths = $this->datatable->setFixedFieldWidth($setting); + $columns = 0; ?> + ' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='storyIDList[]'> + + + $value) + { + if($value->show) + { + $this->datatable->printHead($value, $orderBy, $vars); + $columns ++; + } + } + ?> + + + + + + $value) $this->story->printCell($value, $story, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases);?> + + + - - + From 371b945d32baadd23a1d46844e1b6cd942d6336a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 15:49:07 +0800 Subject: [PATCH 11/40] framework --- framework/base/control.class.php | 887 ++++++ framework/base/helper.class.php | 747 +++++ framework/base/model.class.php | 302 ++ framework/base/router.class.php | 2517 +++++++++++++++++ framework/control.class.php | 23 + framework/helper.class.php | 143 + framework/model.class.php | 38 + framework/router.class.php | 159 ++ .../tests/helper/.101.setmember.onelevel.et | 13 + .../tests/helper/.110.setmember.multilevel.et | 6 + .../tests/helper/.201.getwebroot.inbrowse.et | 2 + .../tests/helper/.202.getwebroot.inshell.et | 8 + .../tests/helper/101.setmember.onelevel.php | 27 + .../tests/helper/110.setmember.multilevel.php | 22 + .../tests/helper/201.getwebroot.inbrowse.php | 17 + .../tests/helper/202.getwebroot.inshell.php | 43 + 16 files changed, 4954 insertions(+) create mode 100644 framework/base/control.class.php create mode 100644 framework/base/helper.class.php create mode 100644 framework/base/model.class.php create mode 100644 framework/base/router.class.php create mode 100644 framework/control.class.php create mode 100644 framework/helper.class.php create mode 100644 framework/model.class.php create mode 100755 framework/router.class.php create mode 100644 framework/tests/helper/.101.setmember.onelevel.et create mode 100644 framework/tests/helper/.110.setmember.multilevel.et create mode 100644 framework/tests/helper/.201.getwebroot.inbrowse.et create mode 100644 framework/tests/helper/.202.getwebroot.inshell.et create mode 100755 framework/tests/helper/101.setmember.onelevel.php create mode 100755 framework/tests/helper/110.setmember.multilevel.php create mode 100755 framework/tests/helper/201.getwebroot.inbrowse.php create mode 100755 framework/tests/helper/202.getwebroot.inshell.php diff --git a/framework/base/control.class.php b/framework/base/control.class.php new file mode 100644 index 0000000000..23dde557c2 --- /dev/null +++ b/framework/base/control.class.php @@ -0,0 +1,887 @@ +post->key来引用$_POST变量。 + * The $post object, useer can access a post var by $this->post->key. + * + * @var ojbect + * @access public + */ + public $post; + + /** + * $get对象,用户可以通过$this->get->key来引用$_GET变量。 + * The $get object, useer can access a get var by $this->get->key. + * + * @var ojbect + * @access public + */ + public $get; + + /** + * $session对象,用户可以通过$this->session->key来引用$_SESSION变量。 + * The $session object, useer can access a session var by $this->session->key. + * + * @var ojbect + * @access public + */ + public $session; + + /** + * $server对象,用户可以通过$this->server->key来引用$_SERVER变量。 + * The $server object, useer can access a server var by $this->server->key. + * + * @var ojbect + * @access public + */ + public $server; + + /** + * $cookie对象,用户可以通过$this->cookie->key来引用$_COOKIE变量。 + * The $cookie object, useer can access a cookie var by $this->cookie->key. + * + * @var ojbect + * @access public + */ + public $cookie; + + /** + * 当前模块的名称。 + * The name of current module. + * + * @var string + * @access public + */ + public $moduleName; + + /** + * $view用于存放从control传到view视图的数据。 + * The vars assigned to the view page. + * + * @var object + * @access public + */ + public $view; + + /** + * 视图的类型,比如html, json。 + * The type of the view, such html, json. + * + * @var string + * @access public + */ + public $viewType; + + /** + * 输出到浏览器的内容。 + * The content to display. + * + * @var string + * @access public + */ + public $output; + + /** + * 客户端设备。 + * The client device. + * + * @var string + * @access public + */ + public $clientDevice; + + /** + * 不同设备下视图文件的前缀。 + * The prefix of view file for mobile or PC. + * + * @var string + * @access public + */ + public $devicePrefix; + + /** + * 构造方法。 + * + * 1. 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用; + * 2. 设置当前模块,读取该模块的model类; + * 3. 初始化$view视图类。 + * + * The construct function. + * + * 1. global the global vars, refer them by the class member such as $this->app. + * 2. set the pathes of current module, and load it's model class. + * 3. auto assign the $lang and $config to the view. + * + * @param string $moduleName + * @param string $methodName + * @param string $appName + * @access public + * @return void + */ + public function __construct($moduleName = '', $methodName = '', $appName = '') + { + /* + * 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用。 + * Global the globals, and refer them as a class member. + */ + global $app, $config, $lang, $dbh, $common; + $this->app = $app; + $this->config = $config; + $this->lang = $lang; + $this->dbh = $dbh; + $this->viewType = $this->app->getViewType(); + $this->appName = $appName ? $appName : $this->app->getAppName(); + + /** + * 设置当前模块,读取该模块的model类。 + * Load the model file auto. + */ + $this->setModuleName($moduleName); + $this->setMethodName($methodName); + $this->loadModel($this->moduleName, $appName); + + /** + * 如果客户端是手机的话,视图文件增加m.前缀。 + * If the clent is mobile, add m. as prefix for view file. + */ + $this->setClientDevice(); + $this->setDevicePrefix(); + + /** + * 初始化$view视图类。 + * Init the view vars. + */ + $this->view = new stdclass(); + $this->view->app = $app; + $this->view->lang = $lang; + $this->view->config = $config; + $this->view->common = $common; + $this->view->title = ''; + + /** + * 设置超级变量,从$app引用过来。 + * Set super vars. + */ + $this->setSuperVars(); + } + + //-------------------- Model相关方法(Model related methods) --------------------// + + /* + * 设置模块名。 + * Set the module name. + * + * @param string $moduleName 模块名,如果为空,则从$app中获取. The module name, if empty, get it from $app. + * @access public + * @return void + */ + public function setModuleName($moduleName = '') + { + $this->moduleName = $moduleName ? strtolower($moduleName) : $this->app->getModuleName(); + } + + /** + * 设置方法名。 + * Set the method name. + * + * @param string $methodName 方法名,如果为空,则从$app中获取。The method name, if empty, get it from $app. + * @access public + * @return void + */ + public function setMethodName($methodName = '') + { + $this->methodName = $methodName ? strtolower($methodName) : $this->app->getMethodName(); + } + + /** + * 加载指定模块的model文件。 + * Load the model file of one module. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. + */ + public function loadModel($moduleName = '', $appName = '') + { + if(empty($moduleName)) $moduleName = $this->moduleName; + if(empty($appName)) $appName = $this->appName; + + global $loadedModels; + if(isset($loadedModels[$appName][$moduleName])) + { + $this->$moduleName = $loadedModels[$appName][$moduleName]; + $this->dao = $this->$moduleName->dao; + return $this->$moduleName; + } + + $modelFile = $this->app->setModelFile($moduleName, $appName); + + /** + * 如果没有model文件,尝试加载config配置信息。 + * If no model file, try load config. + */ + if(!helper::import($modelFile)) + { + $this->app->loadModuleConfig($moduleName, $appName); + $this->app->loadLang($moduleName, $appName); + $this->dao = new dao(); + return false; + } + + /** + * 如果没有扩展文件,model类名是$moduleName + 'model',如果有扩展,还需要增加ext前缀。 + * If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix. + */ + $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; + if(!class_exists($modelClass)) + { + $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; + if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); + } + + /** + * 初始化model对象,在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量,方便引用。 + * Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object. + */ + $loadedModels[$appName][$moduleName] = new $modelClass($appName); + $this->$moduleName = $loadedModels[$appName][$moduleName]; + $this->dao = $this->$moduleName->dao; + return $this->$moduleName; + } + + /** + * 设置超级全局变量,方便直接引用。 + * Set the super vars. + * + * @access public + * @return void + */ + public function setSuperVars() + { + $this->post = $this->app->post; + $this->get = $this->app->get; + $this->server = $this->app->server; + $this->session = $this->app->session; + $this->cookie = $this->app->cookie; + } + + /** + * 设置客户端的设备类型。 + * Set client device. + * + * @access public + * @return void + */ + public function setClientDevice() + { + $this->clientDevice = $this->app->clientDevice; + } + + /** + * 如果客户端是手机的话,视图文件增加m.前缀。 + * If the clent is mobile, add m. as prefix for view file. + * + * @access public + * @return void + */ + public function setDevicePrefix() + { + $this->devicePrefix = zget($this->config->devicePrefix, $this->viewType, ''); + } + + //-------------------- 视图相关方法(View related methods) --------------------// + + /** + * 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。 + * Set view files: the main file, extension view file, site extension view file and hook files. + * + * @param string $moduleName module name + * @param string $methodName method name + * @access public + * @return string the view file + */ + public function setViewFile($moduleName, $methodName) + { + $moduleName = strtolower(trim($moduleName)); + $methodName = strtolower(trim($methodName)); + + $modulePath = $this->app->getModulePath($this->appName, $moduleName); + $viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view'); + + $viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType; + $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php'; + $viewFile = $mainViewFile; + + if(!empty($viewExtPath)) + { + $commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; + $siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; + + $viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile; + $viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile; + if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true); + + $commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); + $siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); + $extHookFiles = array_merge((array) $commonExtHookFiles, (array) $siteExtHookFiles); + } + + if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles); + return $viewFile; + } + + /** + * 获取某一个视图文件的扩展。 + * Get the extension file of an view. + * + * @param string $viewFile + * @access public + * @return string|bool If extension view file exists, return the path. Else return fasle. + */ + public function getExtViewFile($viewFile) + { + /** + * 首先找sitecode下的扩展文件,如果没有,再找ext下的扩展文件。 + * Find extViewFile in ext/_$siteCode/view first, then try ext/view/. + */ + if($this->app->siteCode) + { + $extPath = dirname(dirname(realpath($viewFile))) . "/ext/_{$this->app->siteCode}/view"; + $extViewFile = $extPath . basename($viewFile); + + if(file_exists($extViewFile)) + { + helper::cd($extPath); + return $extViewFile; + } + } + + $extPath = dirname(dirname(realpath($viewFile))) . '/ext/view/'; + $extViewFile = $extPath . basename($viewFile); + if(file_exists($extViewFile)) + { + helper::cd($extPath); + return $extViewFile; + } + return false; + } + + /** + * 获取适用于当前方法的css:该模块公用的css + 当前方法的css + 扩展的css。 + * Get css codes applied to current method: module common css + method css + extension css. + * + * @param string $moduleName + * @param string $methodName + * @access public + * @return string + */ + public function getCSS($moduleName, $methodName) + { + $moduleName = strtolower(trim($moduleName)); + $methodName = strtolower(trim($methodName)); + + $modulePath = $this->app->getModulePath($this->appName, $moduleName); + $cssExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'css') ; + + $css = ''; + $mainCssFile = $modulePath . 'css' . DS . $this->devicePrefix . 'common.css'; + $methodCssFile = $modulePath . 'css' . DS . $this->devicePrefix . $methodName . '.css'; + if(file_exists($mainCssFile)) $css .= file_get_contents($mainCssFile); + if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile); + + if(!empty($cssExtPath)) + { + $cssMethodExt = $cssExtPath['common'] . $methodName . DS; + $cssCommonExt = $cssExtPath['common'] . 'common' . DS; + + $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); + if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); + + $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); + if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); + + if(!empty($cssExtPath['site'])) + { + $cssMethodExt = $cssExtPath['site'] . $methodName . DS; + $cssCommonExt = $cssExtPath['site'] . 'common' . DS; + $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); + if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); + + $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); + if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); + } + } + + return $css; + } + + /** + * 获取适用于当前方法的js:该模块公用的js + 当前方法的js + 扩展的js。 + * Get js codes applied to current method: module common js + method js + extension js. + * + * @param string $moduleName + * @param string $methodName + * @access public + * @return string + */ + public function getJS($moduleName, $methodName) + { + $moduleName = strtolower(trim($moduleName)); + $methodName = strtolower(trim($methodName)); + + $modulePath = $this->app->getModulePath($this->appName, $moduleName); + $jsExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'js'); + + $js = ''; + $mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . 'common.js'; + $methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . '.js'; + if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile); + if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile); + + if(!empty($jsExtPath)) + { + $jsMethodExt = $jsExtPath['common'] . $methodName . DS; + $jsCommonExt = $jsExtPath['common'] . 'common' . DS; + + $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); + if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); + + $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); + if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); + + if(!empty($jsExtPath['site'])) + { + $jsMethodExt = $jsExtPath['site'] . $methodName . DS; + $jsCommonExt = $jsExtPath['site'] . 'common' . DS; + + $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); + if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); + + $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); + if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); + } + } + + return $js; + } + + /** + * 向$view传递一个变量。 + * Assign one var to the view vars. + * + * @param string $name the name. + * @param mixed $value the value. + * @access public + * @return void + */ + public function assign($name, $value) + { + $this->view->$name = $value; + } + + /** + * 清空$output。 + * Clear the output. + * + * @access public + * @return void + */ + public function clear() + { + $this->output = ''; + } + + /** + * 渲染视图文件。 + * Parse view file. + * + * @param string $moduleName module name, if empty, use current module. + * @param string $methodName method name, if empty, use current method. + * @access public + * @return string the parsed result. + */ + public function parse($moduleName = '', $methodName = '') + { + if(empty($moduleName)) $moduleName = $this->moduleName; + if(empty($methodName)) $methodName = $this->methodName; + + if($this->viewType == 'json') $this->parseJSON($moduleName, $methodName); + if($this->viewType != 'json') $this->parseDefault($moduleName, $methodName); + + return $this->output; + } + + /** + * 渲染json格式。 + * Parse json format. + * + * @param string $moduleName module name + * @param string $methodName method name + * @access public + * @return void + */ + public function parseJSON($moduleName, $methodName) + { + unset($this->view->app); + unset($this->view->config); + unset($this->view->lang); + unset($this->view->header); + unset($this->view->position); + unset($this->view->moduleTree); + unset($this->view->common); + unset($this->view->pager->app); + unset($this->view->pager->lang); + + $output['status'] = is_object($this->view) ? 'success' : 'fail'; + $output['data'] = json_encode($this->view); + $output['md5'] = md5(json_encode($this->view)); + + $this->output = json_encode($output); + } + + /** + * 默认渲染方法,适用于viewType = html的时候。 + * Default parse method when viewType != json, like html. + * + * @param string $moduleName module name + * @param string $methodName method name + * @access public + * @return void + */ + public function parseDefault($moduleName, $methodName) + { + /** + * 设置视图文件。(PHP7有一个bug,不能直接$viewFile = $this->setViewFile())。 + * Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.) + */ + $results = $this->setViewFile($moduleName, $methodName); + $viewFile = $results; + if(is_array($results)) extract($results); + + /** + * 获得当前页面的CSS和JS。 + * Get css and js codes for current method. + */ + $css = $this->getCSS($moduleName, $methodName); + $js = $this->getJS($moduleName, $methodName); + if($css) $this->view->pageCSS = $css; + if($js) $this->view->pageJS = $js; + + /** + * 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。 + * Change the dir to the view file to keep the relative pathes work. + */ + $currentPWD = getcwd(); + chdir(dirname($viewFile)); + + /** + * 使用extract安定ob方法渲染$viewFile里面的代码。 + * Use extract and ob functions to eval the codes in $viewFile. + */ + extract((array)$this->view); + ob_start(); + include $viewFile; + if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile; + $this->output .= ob_get_contents(); + ob_end_clean(); + + /** + * 渲染完毕后,再切换回之前的路径。 + * At the end, chang the dir to the previous. + */ + chdir($currentPWD); + } + + /** + * 获取一个方法的输出内容,这样我们可以在一个方法里获取其他模块方法的内容。 + * 如果模块名为空,则调用该模块、该方法;如果设置了模块名,调用指定模块指定方法。 + * + * Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content. + * If the module name is empty, then use the current module and method. If set, use the user defined module and method. + * + * @param string $moduleName module name. + * @param string $methodName method name. + * @param array $params params. + * @access public + * @return string the parsed html. + */ + public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '') + { + /** + * 如果模块名为空,则调用该模块、该方法。 + * If the module name is empty, then use the current module and method. + */ + if($moduleName == '') $moduleName = $this->moduleName; + if($methodName == '') $methodName = $this->methodName; + if($appName == '') $appName = $this->appName; + if($moduleName == $this->moduleName and $methodName == $this->methodName) + { + $this->parse($moduleName, $methodName); + return $this->output; + } + + $currentModuleName = $this->moduleName; + $currentMethodName = $this->methodName; + $currentAppName = $this->appName; + $currentParams = $this->app->getParams(); + + /** + * 设置调用指定模块的指定方法。 + * chang the dir to the previous. + */ + $this->app->setModuleName($moduleName); + $this->app->setMethodName($methodName); + + if(!is_array($params)) parse_str($params, $params); + $this->app->params = $params; + + $currentPWD = getcwd(); + + /** + * 设置引用的文件和路径。 + * Set the pathes and files to included. + */ + $modulePath = $this->app->getModulePath($appName, $moduleName); + $moduleControlFile = $modulePath . 'control.php'; + $actionExtPath = $this->app->getModuleExtPath($appName, $moduleName, 'control'); + $file2Included = $moduleControlFile; + + if(!empty($actionExtPath)) + { + /** + * 设置公共扩展。 + * set common extension. + */ + $commonActionExtFile = $actionExtPath['common'] . strtolower($methodName) . '.php'; + $file2Included = file_exists($commonActionExtFile) ? $commonActionExtFile : $moduleControlFile; + + if(!empty($actionExtPath['site'])) + { + /** + * 设置站点扩展。 + * every site has it's extension. + */ + $siteActionExtFile = $actionExtPath['site'] . strtolower($methodName) . '.php'; + $file2Included = file_exists($siteActionExtFile) ? $siteActionExtFile : $file2Included; + } + } + + /** + * 加载控制器文件。 + * Load the control file. + */ + if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true); + chdir(dirname($file2Included)); + if($moduleName != $this->moduleName) helper::import($file2Included); + + /** + * 设置调用的类名。 + * Set the name of the class to be called. + */ + $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; + if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true); + + /** + * 解析参数,创建模块control对象。 + * Parse the params, create the $module control object. + */ + $module = new $className($moduleName, $methodName, $appName); + + /** + * 调用对应方法,使用ob方法获取输出内容。 + * Call the method and use ob function to get the output. + */ + ob_start(); + call_user_func_array(array($module, $methodName), $params); + $output = ob_get_contents(); + ob_end_clean(); + + unset($module); + + /** + * 切换回之前的模块和方法。 + * Chang the module、method to the previous. + */ + $this->app->setModuleName($currentModuleName); + $this->app->setMethodName($currentMethodName); + $this->app->params = $currentParams; + + chdir($currentPWD); + + /** + * 返回内容。 + * Return the content. + */ + return $output; + } + + /** + * 向浏览器输出内容。 + * Print the content of the view. + * + * @param string $moduleName module name + * @param string $methodName method name + * @access public + * @return void + */ + public function display($moduleName = '', $methodName = '') + { + if(empty($this->output)) $this->parse($moduleName, $methodName); + echo $this->output; + } + + /** + * 直接输出data数据,通常用于ajax请求中。 + * Send data directly, for ajax requests. + * + * @param misc $data + * @param string $type + * @access public + * @return void + */ + public function send($data, $type = 'json') + { + if($type != 'json') die(); + + $data = (array) $data; + if(helper::isAjaxRequest() or $this->viewType == 'json') print(json_encode($data)) and die(helper::removeUTF8Bom(ob_get_clean())); + + /** + * 响应非ajax的请求。 + * Response request not ajax. + */ + if(isset($data['result']) and $data['result'] == 'success') + { + if(!empty($data['message'])) echo js::alert($data['message']); + $locate = isset($data['locate']) ? $data['locate'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''); + if(!empty($locate)) die(js::locate($locate)); + die(isset($data['message']) ? $data['message'] : 'success'); + } + + if(isset($data['result']) and $data['result'] == 'fail') + { + if(!empty($data['message'])) + { + $message = json_decode(json_encode((array)$data['message'])); + foreach((array)$message as $item => $errors) $message->$item = implode(',', $errors); + echo js::alert(strip_tags(implode(" ", (array) $message))); + die(js::locate('back')); + } + die('fail'); + } + } + + /** + * 创建一个模块方法的链接。 + * Create a link to one method of one module. + * + * @param string $moduleName module name + * @param string $methodName method name + * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 + * @param string $viewType the view type + * @access public + * @return string the link string. + */ + public function createLink($moduleName, $methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) + { + if(empty($moduleName)) $moduleName = $this->moduleName; + return helper::createLink($moduleName, $methodName, $vars, $viewType, $onlybody); + } + + /** + * 创建当前模块的一个方法链接。 + * Create a link to the inner method of current module. + * + * @param string $methodName method name + * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 + * @param string $viewType the view type + * @access public + * @return string the link string. + */ + public function inlink($methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) + { + return helper::createLink($this->moduleName, $methodName, $vars, $viewType, $onlybody); + } + + /** + * 重定向到另一个页面。 + * Location to another page. + * + * @param string $url the target url. + * @access public + * @return void + */ + public function locate($url) + { + header("location: $url"); + exit; + } +} diff --git a/framework/base/helper.class.php b/framework/base/helper.class.php new file mode 100644 index 0000000000..9066ccc455 --- /dev/null +++ b/framework/base/helper.class.php @@ -0,0 +1,747 @@ + + * db->user = 'wwccss'; + * helper::setMember('lang', 'db.user', 'chunsheng.wang'); + * ?> + * + * @param string $objName the var name of the object. + * @param string $key the key of the member, can be parent.child. + * @param mixed $value the value to be set. + * @static + * @access public + * @return bool + */ + static public function setMember($objName, $key, $value) + { + global $$objName; + if(!is_object($$objName) or empty($key)) return false; + $key = str_replace('.', '->', $key); + $value = serialize($value); + $code = ("\$${objName}->{$key}=unserialize(<< + * 'value1', 'var2' => 'value2'); + * ?> + * + * @param string $moduleName module name, can pass appName like app.module. + * @param string $methodName method name + * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) or key1=value1&key2=value2 + * @param string $viewType the view type + * @param bool $onlyBody pass onlyBody=yes to the link thus the app can control the header and footer hide or show.. + * @static + * @access public + * @return string the link string. + */ + static public function createLink($moduleName, $methodName = 'index', $vars = '', $viewType = '', $onlyBody = false) + { + /* 设置$appName和$moduleName。Set appName and moduleName. */ + global $app, $config; + if(strpos($moduleName, '.') !== false) + { + list($appName, $moduleName) = explode('.', $moduleName); + } + else + { + $appName = $app->getAppName(); + } + if(!empty($appName)) $appName .= '/'; + + /* 处理$viewType和$vars。Set $viewType and $vars. */ + if(empty($viewType)) $viewType = $app->getViewType(); + if(!is_array($vars)) parse_str($vars, $vars); + + /* 生成url链接的开始部分。Set the begin parts of the link. */ + if($config->requestType == 'PATH_INFO') $link = $config->webRoot . $appName; + if($config->requestType != 'PATH_INFO') $link = $config->webRoot . $appName . basename($_SERVER['SCRIPT_NAME']); + if($config->requestType == 'PATH_INFO2') $link .= '/'; + + /** + * #1: RequestType为GET。When the requestType is GET. + * Input: moduleName=article&methodName=index&var1=value1. Output: ?m=article&f=index&var1=value1. + * + */ + if($config->requestType == 'GET') + { + $link .= "?{$config->moduleVar}=$moduleName&{$config->methodVar}=$methodName"; + if($viewType != 'html') $link .= "&{$config->viewVar}=" . $viewType; + foreach($vars as $key => $value) $link .= "&$key=$value"; + return self::processOnlyBodyParam($link, $onlyBody); + } + + /** + * #2: 方法名不是默认值或者是默认值,但有传参。MethodName equals the default method or vars not empty. + * Input: moduleName=article&methodName=view. Output: article-view.html + * Input: moduleName=article&methodName=view. Output: article-index-abc.html + * + */ + if($methodName != $config->default->method or !empty($vars)) + { + $link .= "$moduleName{$config->requestFix}$methodName"; + foreach($vars as $value) $link .= "{$config->requestFix}$value"; + $link .= '.' . $viewType; + + return self::processOnlyBodyParam($link, $onlyBody); + } + + /** + * #3: 方法名为默认值且没有传参且模块名为默认值。MethodName is the default and moduleName is default and vars empty. + * Input: moduleName=index&methodName=index. Output: index.html + * + */ + if($moduleName == $config->default->module) + { + $link .= $config->default->method . '.' . $viewType; + return self::processOnlyBodyParam($link, $onlyBody); + } + + /** + * #4: 方法名为默认值且没有传参且模块名不为默认值,viewType和app指定的相等。MethodName is default but moduleName not and viewType equal app's viewType.. + * Input: moduleName=article&methodName=index&viewType=html. Output: /article/ + * + */ + if($viewType == $app->getViewType()) + { + $link .= $moduleName . '/'; + return self::processOnlyBodyParam($link, $onlyBody); + } + + /** + * #5: 方法名为默认值且没有传参且模块名不为默认值,viewType有另外指定。MethodName is default but moduleName not and viewType no equls app's viewType. + * Input: moduleName=article&methodName=index&viewType=json. Output: /article.json + * + */ + $link .= $moduleName . '.' . $viewType; + return self::processOnlyBodyParam($link, $onlyBody); + } + + /** + * 处理onlyBody 参数。 + * Process the onlyBody param in url. + * + * 如果传参的时候设定了$onlyBody为真,或者当前页面请求中包含了onlybody=yes,在生成链接的时候继续追加。 + * If $onlyBody set to true or onlybody=yes in the url, append onlyBody param to the link. + * + * @param string $link + * @param bool $onlyBody + * @static + * @access public + * @return string + */ + public static function processOnlyBodyParam($link, $onlyBody = false) + { + global $config; + if(!$onlyBody and !self::inOnlyBodyMode()) return $link; + $onlybodyString = $config->requestType != 'GET' ? "?onlybody=yes" : "&onlybody=yes"; + return $link . $onlybodyString; + } + + /** + * 检查是否是onlybody模式。 + * Check in only body mode or not. + * + * @access public + * @return void + */ + public static function inOnlyBodyMode() + { + return (isset($_GET['onlybody']) and $_GET['onlybody'] == 'yes'); + } + + /** + * 使用helper::import()来引入文件,不要直接使用include或者require. + * Using helper::import() to import a file, instead of include or require. + * + * @param string $file the file to be imported. + * @static + * @access public + * @return bool + */ + static public function import($file) + { + if(!is_file($file)) return false; + + static $includedFiles = array(); + if(!isset($includedFiles[$file])) + { + include $file; + $includedFiles[$file] = true; + return true; + } + + return true; + } + + /** + * 将数组或者列表转化成 IN( 'a', 'b') 的形式。 + * Convert a list to IN('a', 'b') string. + * + * @param string|array $idList 列表,可以是数组或者用逗号隔开的列表。The id lists, can be a array or a string joined with comma. + * @static + * @access public + * @return string the string like IN('a', 'b'). + */ + static public function dbIN($idList) + { + if(is_array($idList)) + { + if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) + { + foreach ($idList as $key=>$value) $idList[$key] = addslashes($value); + } + return "IN ('" . join("','", $idList) . "')"; + } + + if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) $idList = addslashes($idList); + return "IN ('" . str_replace(',', "','", str_replace(' ', '', $idList)) . "')"; + } + + /** + * 安全的Base64编码,框架对'/'字符比较敏感,转换为'.'。 + * Create safe base64 encoded string for the framework. + * + * @param string $string the string to encode. + * @static + * @access public + * @return string encoded string. + */ + static public function safe64Encode($string) + { + return strtr(base64_encode($string), '/', '.'); + } + + /** + * 解码base64,先将之前的'.' 转换回'/' + * Decode the string encoded by safe64Encode. + * + * @param string $string the string to decode + * @static + * @access public + * @return string decoded string. + */ + static public function safe64Decode($string) + { + return base64_decode(strtr($string, '.', '/')); + } + + /** + * JSON编码,自动处理转义的问题。 + * JSON encode, process the slashes. + * + * @param mixed $data the object to encode + * @static + * @access public + * @return string decoded string. + */ + static public function jsonEncode($data) + { + return (function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc()) ? addslashes(json_encode($data)) : json_encode($data); + } + + /** + * 判断是否是utf8编码 + * Judge a string is utf-8 or not. + * + * @author hmdker@gmail.com + * @param string $string + * @see http://php.net/manual/en/function.mb-detect-encoding.php + * @static + * @access public + * @return bool + */ + static public function isUTF8($string) + { + $c = 0; + $b = 0; + $bits = 0; + $len = strlen($string); + for($i=0; $i<$len; $i++) + { + $c = ord($string[$i]); + if($c > 128) + { + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1) + { + $i++; + $b=ord($string[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * 去掉UTF-8 Bom头。 + * Remove UTF-8 Bom. + * + * @param string $string + * @access public + * @return string + */ + public static function removeUTF8Bom($string) + { + if(substr($string, 0, 3) == pack('CCC', 239, 187, 191)) return substr($string, 3); + return $string; + } + + /** + * 增强substr方法:支持多字节语言,比如中文。 + * Enhanced substr version: support multibyte languages like Chinese. + * + * @param string $string + * @param int $length + * @param string $append + * @return string + */ + public static function substr($string, $length, $append = '') + { + $rawString = $string; + + if(function_exists('mb_substr')) $string = mb_substr($string, 0, $length, 'utf-8'); + + preg_match_all("/./su", $string, $data); + $string = join("", array_slice($data[0], 0, $length)); + + return ($string != $rawString) ? $string . $append : $string; + } + + /** + * Get browser name and version. + * + * @access public + * @return array + */ + public static function getBrowser() + { + $browser = array('name'=>'unknown', 'version'=>'unknown'); + + if(empty($_SERVER['HTTP_USER_AGENT'])) return $browser; + + $agent = $_SERVER["HTTP_USER_AGENT"]; + + /* Chrome should checked before safari.*/ + if(strpos($agent, 'Firefox') !== false) $browser['name'] = "firefox"; + if(strpos($agent, 'Opera') !== false) $browser['name'] = 'opera'; + if(strpos($agent, 'Safari') !== false) $browser['name'] = 'safari'; + if(strpos($agent, 'Chrome') !== false) $browser['name'] = "chrome"; + + // Check the name of browser + if(strpos($agent, 'MSIE') !== false || strpos($agent, 'rv:11.0')) $browser['name'] = 'ie'; + if(strpos($agent, 'Edge') !== false) $browser['name'] = 'edge'; + + // Check the version of browser + if(preg_match('/MSIE\s(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + if(preg_match('/FireFox\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + if(preg_match('/Opera[\s|\/](\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + if(preg_match('/Chrome\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + + if((strpos($agent, 'Chrome') == false) && preg_match('/Safari\/(\d+)\..*$/i', $agent, $regs)) $browser['version'] = $regs[1]; + if(preg_match('/rv:(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + if(preg_match('/Edge\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; + + return $browser; + } + + /** + * Get client os from agent info. + * + * @static + * @access public + * @return string + */ + public static function getOS() + { + if(empty($_SERVER['HTTP_USER_AGENT'])) return 'unknow'; + + $osList = array(); + $osList['/windows nt 10/i'] = 'Windows 10'; + $osList['/windows nt 6.3/i'] = 'Windows 8.1'; + $osList['/windows nt 6.2/i'] = 'Windows 8'; + $osList['/windows nt 6.1/i'] = 'Windows 7'; + $osList['/windows nt 6.0/i'] = 'Windows Vista'; + $osList['/windows nt 5.2/i'] = 'Windows Server 2003/XP x64'; + $osList['/windows nt 5.1/i'] = 'Windows XP'; + $osList['/windows xp/i'] = 'Windows XP'; + $osList['/windows nt 5.0/i'] = 'Windows 2000'; + $osList['/windows me/i'] = 'Windows ME'; + $osList['/win98/i'] = 'Windows 98'; + $osList['/win95/i'] = 'Windows 95'; + $osList['/win16/i'] = 'Windows 3.11'; + $osList['/macintosh|mac os x/i'] = 'Mac OS X'; + $osList['/mac_powerpc/i'] = 'Mac OS 9'; + $osList['/linux/i'] = 'Linux'; + $osList['/ubuntu/i'] = 'Ubuntu'; + $osList['/iphone/i'] = 'iPhone'; + $osList['/ipod/i'] = 'iPod'; + $osList['/ipad/i'] = 'iPad'; + $osList['/android/i'] = 'Android'; + $osList['/blackberry/i'] = 'BlackBerry'; + $osList['/webos/i'] = 'Mobile'; + + foreach ($osList as $regex => $value) + { + if(preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) return $value; + } + + return 'unknown'; + } + + /** + * 计算两个日期相差的天数,取整。 + * Compute the diff days of two date. + * + * @param string $date1 the first date. + * @param string $date2 the sencond date. + * @access public + * @return int the diff of the two days. + */ + static public function diffDate($date1, $date2) + { + return round((strtotime($date1) - strtotime($date2)) / 86400, 0); + } + + /** + * 获取当前时间,使用common语言文件定义的DT_DATETIME1常量。 + * Get now time use the DT_DATETIME1 constant defined in the lang file. + * + * @access public + * @return datetime now + */ + static public function now() + { + return date(DT_DATETIME1); + } + + /** + * 获取当前日期,使用common语言文件定义的DT_DATE1常量。 + * Get today according to the DT_DATE1 constant defined in the lang file. + * + * @access public + * @return date today + */ + static public function today() + { + return date(DT_DATE1); + } + + /** + * 获取当前日期,使用common语言文件定义的DT_TIME1常量。 + * Get now time use the DT_TIME1 constant defined in the lang file. + * + * @access public + * @return date today + */ + static public function time() + { + return date(DT_TIME1); + } + + /** + * 判断日期是不是零。 + * Judge a date is zero or not. + * + * @access public + * @return bool + */ + static public function isZeroDate($date) + { + return substr($date, 0, 4) == '0000'; + } + + /** + * 列出目录中符合该正则表达式的文件。 + * Get files match the pattern under a directory. + * + * @access public + * @return array the files match the pattern + */ + static public function ls($dir, $pattern = '') + { + if(empty($dir)) return array(); + + $files = array(); + $dir = realpath($dir); + if(is_dir($dir)) $files = glob($dir . DIRECTORY_SEPARATOR . '*' . $pattern); + return empty($files) ? array() : $files; + } + + /** + * 切换目录。第一次调用的时候记录当前的路径,再次调用的时候切换回之前的路径。 + * Change directory: first call, save the $cwd, secend call, change to $cwd. + * + * @param string $path + * @static + * @access public + * @return void + */ + static function cd($path = '') + { + static $cwd = ''; + if($path) $cwd = getcwd(); + !empty($path) ? chdir($path) : chdir($cwd); + } + + /** + * 通过域名获取站点代号。 + * Get siteCode for a domain. + * + * www.xirang.com => xirang + * xirang.com => xirang + * xirang.com.cn => xirang + * xirang.cn => xirang + * xirang => xirang + * + * @param string $domain + * @return string $siteCode + **/ + public static function parseSiteCode($domain) + { + global $config; + + /* 去除域名中的端口部分。Remove the port part of the domain. */ + if(strpos($domain, ':') !== false) $domain = substr($domain, 0, strpos($domain, ':')); + $domain = strtolower($domain); + + /* $config里面有定义或者是localhost,直接返回。 Return directly if defined in $config or is localhost. */ + if(isset($config->siteCodeList[$domain])) return $config->siteCodeList[$domain]; + if($domain == 'localhost') return $domain; + + /* 将域名中的-改为_。Replace '-' with '_' in the domain. */ + $domain = str_replace('-', '_', $domain); + $items = explode('.', $domain); + + /* 类似a.com的形式。 Domain like a.com. */ + $postfix = str_replace($items[0] . '.', '', $domain); + if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; + + /* 类似www.a.com的形式。 Domain like www.a.com. */ + $postfix = str_replace($items[0] . '.' . $items[1] . '.', '', $domain); + if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[1]; + + /* 类似xxx.sub.a.com的形式。 Domain like xxx.sub.a.com. */ + $postfix = str_replace($items[0] . '.' . $items[1] . '.' . $items[2] . '.', '', $domain); + if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; + + return ''; + } + + /** + * 检查是否是AJAX请求。 + * Check is ajax request. + * + * @static + * @access public + * @return bool + */ + public static function isAjaxRequest() + { + if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; + if(isset($_GET['HTTP_X_REQUESTED_WITH']) && $_GET['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; + return false; + } + + /** + * 301跳转。 + * Header 301 Moved Permanently. + * + * @param string $locate + * @access public + * @return void + */ + public static function header301($locate) + { + header('HTTP/1.1 301 Moved Permanently'); + die(header('Location:' . $locate)); + } + + /** + * 获取远程IP。 + * Get remote ip. + * + * @access public + * @return string + */ + public static function getRemoteIp() + { + $ip = ''; + if(!empty($_SERVER["REMOTE_ADDR"])) $ip = $_SERVER["REMOTE_ADDR"]; + if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; + if(!empty($_SERVER['HTTP_CLIENT_IP'])) $ip = $_SERVER['HTTP_CLIENT_IP']; + + return $ip; + } +} + +//------------------------------- 常用函数。Some tool functions.-------------------------------// + +/** + * helper::createLink()的别名,方便创建本模块方法的链接。 + * The short alias of helper::createLink() method to create link to control method of current module. + * + * @param string $methodName the method name + * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) + * @param string $viewType + * @return string the link string. + */ +function inLink($methodName = 'index', $vars = '', $viewType = '') +{ + global $app; + return helper::createLink($app->getModuleName(), $methodName, $vars, $viewType); +} + +/** + * 通过一个静态游标,可以遍历数组。 + * Static cycle a array. + * + * @param array $items the array to be cycled. + * @return mixed + */ +function cycle($items) +{ + static $i = 0; + if(!is_array($items)) $items = explode(',', $items); + if(!isset($items[$i])) $i = 0; + return $items[$i++]; +} + +/** + * 获取当前时间的Unix时间戳,精确到微妙。 + * Get current microtime. + * + * @access public + * @return float current time. + */ +function getTime() +{ + list($usec, $sec) = explode(" ", microtime()); + return ((float)$usec + (float)$sec); +} + +/** + * 打印变量的信息 + * dump a var. + * + * @param mixed $var + * @access public + * @return void + */ +function a($var) +{ + echo ""; + print_r($var); + echo ""; +} + +/** + * 判断是否内外IP。 + * Judge the server ip is local or not. + * + * @access public + * @return void + */ +function isLocalIP() +{ + global $config; + if(isset($config->islocalIP)) return $config->isLocalIP; + $serverIP = $_SERVER['SERVER_ADDR']; + if($serverIP == '127.0.0.1') return true; + if(strpos($serverIP, '10.70') !== false) return false; + return !filter_var($serverIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); +} + +/** + * 获取webRoot。 + * Get web root. + * + * @access public + * @return string + */ +function getWebRoot($full = false) +{ + $path = $_SERVER['SCRIPT_NAME']; + + if(PHP_SAPI == 'cli') + { + if(isset($_SERVER['argv'][1])) + { + $url = parse_url($_SERVER['argv'][1]); + $path = empty($url['path']) ? '/' : rtrim($url['path'], '/'); + } + $path = empty($path) ? '/' : preg_replace('/\/www$/', '/www/', $path); + } + + if($full) + { + $http = (isset($_SERVER['HTTPS']) and strtolower($_SERVER['HTTPS']) != 'off') ? 'https://' : 'http://'; + return $http . $_SERVER['HTTP_HOST'] . substr($path, 0, (strrpos($path, '/') + 1)); + } + + $path = substr($path, 0, (strrpos($path, '/') + 1)); + $path = str_replace('\\', '/', $path); + return $path; +} + +/** + * 当数组/对象变量$var存在$key项时,返回存在的对应值或设定值,否则返回$key或不存在的设定值。 + * When the $var has the $key, return it, esle result one default value. + * + * @param array|object $var + * @param string|int $key + * @param mixed $valueWhenNone value when the key not exits. + * @param mixed $valueWhenExists value when the key exits. + * @access public + * @return string + */ +function zget($var, $key, $valueWhenNone = false, $valueWhenExists = false) +{ + if(!is_array($var) and !is_object($var)) return false; + + $type = is_array($var) ? 'array' : 'object'; + $checkExists = $type == 'array' ? isset($var[$key]) : isset($var->$key); + + if($checkExists) + { + if($valueWhenExists !== false) return $valueWhenExists; + return $type == 'array' ? $var[$key] : $var->$key; + } + + if($valueWhenNone !== false) return $valueWhenNone; + return $key; +} diff --git a/framework/base/model.class.php b/framework/base/model.class.php new file mode 100644 index 0000000000..0b84a41ee2 --- /dev/null +++ b/framework/base/model.class.php @@ -0,0 +1,302 @@ +app. + * 2. set the pathes, config, lang of current module + * + * @param string $appName + * @access public + * @return void + */ + public function __construct($appName = '') + { + global $app, $config, $lang, $dbh; + $this->app = $app; + $this->config = $config; + $this->lang = $lang; + $this->dbh = $dbh; + $this->appName = empty($appName) ? $this->app->getAppName() : $appName; + + $moduleName = $this->getModuleName(); + if($this->config->framework->multiLanguage) $this->app->loadLang($moduleName, $this->appName); + if($moduleName != 'common') $this->app->loadModuleConfig($moduleName, $this->appName); + + $this->loadDAO(); + $this->setSuperVars(); + } + + /** + * 获取该model的模块名,而不是用户请求的模块名。 + * + * 这个方法通过去掉该model类名的'ext'和'model'字符串,来获取当前模块名。 + * 不要使用$app->getModuleName(),因为其返回的是用户请求的模块名。 + * 另一个model可以通过loadModel()加载进来,与请求的模块名不一致。 + * + * Get the module name of this model. Not the module user visiting. + * + * This method replace the 'ext' and 'model' string from the model class name, thus get the module name. + * Not using $app->getModuleName() because it return the module user is visiting. But one module can be + * loaded by loadModel() so we must get the module name of this model. + * + * @access public + * @return string the module name. + */ + public function getModuleName() + { + $parentClass = get_parent_class($this); + $selfClass = get_class($this); + $className = $parentClass == 'model' ? $selfClass : $parentClass; + if($className == 'extensionModel') return 'extension'; + return strtolower(str_ireplace(array('ext', 'Model'), '', $className)); + } + + /** + * 设置全局超级变量。 + * Set the super vars. + * + * @access public + * @return void + */ + public function setSuperVars() + { + $this->post = $this->app->post; + $this->get = $this->app->get; + $this->server = $this->app->server; + $this->cookie = $this->app->cookie; + $this->session = $this->app->session; + } + + /** + * 加载一个模块的model。加载完成后,使用$this->$moduleName来访问这个model对象。 + * 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。 + * + * Load the model of one module. After loaded, can use $this->$moduleName to visit the model object. + * + * @param string $moduleName + * @access public + * @return object|bool the model object or false if model file not exists. + */ + public function loadModel($moduleName, $appName = '') + { + if(empty($moduleName)) return false; + if(empty($appName)) $appName = $this->appName; + + global $loadedModels; + if(isset($loadedModels[$appName][$moduleName])) + { + $this->$moduleName = $loadedModels[$appName][$moduleName]; + return $this->$moduleName; + } + + $modelFile = $this->app->setModelFile($moduleName, $appName); + + if(!helper::import($modelFile)) return false; + $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; + if(!class_exists($modelClass)) + { + $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; + if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); + } + + $loadedModels[$appName][$moduleName] = new $modelClass($appName); + $this->$moduleName = $loadedModels[$appName][$moduleName]; + return $this->$moduleName; + } + + /** + * 加载model的class扩展,主要是为了开发加密代码使用。 + * 可以将主要的逻辑存放到$moduleName/ext/model/class/$extensionName.class.php中。 + * 然后在ext/model/$extension.php的扩展里面使用$this->loadExtension()来调用相应的方法。 + * ext/model/class/*.class.php代码可以加密。而ext/model/*.php可以不用加密。 + * 因为框架对model的扩展是采取合并文件的方式,ext/model/*.php文件不能加密。 + * + * Load extension class of a model thus user can encrypt the code. + * You can put the main extension logic codes in $moduleName/ext/model/class/$extensionName.class.php. + * And call them by the ext/model/$extension.php like this: $this->loadExtension('myextension')->method(). + * You can encrypt the code in ext/model/class/*.class.php. + * Because the framework will merge the extension files in ext/model/*.php to the module/model.php. + * + * @param string $extensionName + * @param string $moduleName + * @access public + * @return void + */ + public function loadExtension($extensionName, $moduleName = '') + { + if(empty($extensionName)) return false; + + /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */ + $extensionName = strtolower($extensionName); + $moduleName = $moduleName ? $moduleName : $this->getModuleName(); + $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model'); + if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php'; + + /* 载入父类。Try to import parent model file auto and then import the extension file. */ + if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($this->appName, $moduleName) . 'model.php'); + if(!helper::import($extensionFile)) return false; + + /* 设置扩展类的名字。Set the extension class name. */ + $extensionClass = $extensionName . ucfirst($moduleName); + if(!class_exists($extensionClass)) return false; + + /* 实例化扩展类。Create an instance of the extension class and return it. */ + $extensionObject = new $extensionClass; + $extensionClass = str_replace('Model', '', $extensionClass); + $this->$extensionClass = $extensionObject; + return $extensionObject; + } + + /** + * 加载DAO。 + * Load DAO. + * + * @access public + * @return void + */ + public function loadDAO() + { + $this->dao = $this->app->loadClass('dao'); + } + + /** + * 删除记录。 + * Delete one record. + * + * @param string $table the table name + * @param string $id the id value of the record to be deleted + * @access public + * @return void + */ + public function delete($table, $id) + { + $this->dao->delete()->from($table)->where('id')->eq($id)->exec(); + } +} diff --git a/framework/base/router.class.php b/framework/base/router.class.php new file mode 100644 index 0000000000..d700da5fc4 --- /dev/null +++ b/framework/base/router.class.php @@ -0,0 +1,2517 @@ +basePath/framework) + * + * @var string + * @access public + */ + public $frameRoot; + + /** + * 类库的根目录。{$this->basePath/lib} + * The root directory of the library($this->basePath/lib). + * + * @var string + * @access public + */ + public $coreLibRoot; + + /** + * 应用名称 + * The appName. + * + * @var string + * @access public + */ + public $appName = ''; + + /** + * 应用程序的根目录。 + * The root directory of the app. + * + * @var string + * @access public + */ + public $appRoot; + + /** + * 临时文件的根目录。 + * The root directory of temp. + * + * @var string + * @access public + */ + public $tmpRoot; + + /** + * 缓存的根目录。 + * The root directory of cache. + * + * @var string + * @access public + */ + public $cacheRoot; + + /** + * WWW目录。 + * The root directory of www. + * + * @var string + * @access public + */ + public $wwwRoot; + + /** + * 附件存放目录。 + * The root directory of data. + * + * @var string + * @access public + */ + public $dataRoot; + + /** + * 日志文件的根目录。 + * The root directory of log. + * + * @var string + * @access public + */ + public $logRoot; + + /** + * 配置文件的根目录。 + * The root directory of config. + * + * @var string + * @access public + */ + public $configRoot; + + /** + * 模块的根目录。 + * The root directory of module. + * + * @var string + * @access public + */ + public $moduleRoot; + + /** + * 主题的根目录。 + * The root directory of theme. + * + * @var string + * @access public + */ + public $themeRoot; + + /** + * 用户使用的语言。 + * The lang of the client user. + * + * @var string + * @access public + */ + public $clientLang; + + /** + * 用户使用的主题。 + * The theme of the client user. + * + * @var string + * @access public + */ + public $clientTheme; + + /** + * 客户端设备类型。 + * The device type of client. + * + * @var string + * @access public + */ + public $clientDevice; + + /** + * 当前模块的control对象。 + * The control object of current module. + * + * @var object + * @access public + */ + public $control; + + /** + * 模块名。 + * The module name + * + * @var string + * @access public + */ + public $moduleName; + + /** + * 当前访问模块的control文件。 + * The control file of the module current visiting. + * + * @var string + * @access public + */ + public $controlFile; + + /** + * 当前访问的方法名。 + * The name of the method current visiting. + * + * @var string + * @access public + */ + public $methodName; + + /** + * 当前方法的扩展文件。 + * The action extension file of current method. + * + * @var string + * @access public + */ + public $extActionFile; + + /** + * 访问的URI。 + * The URI. + * + * @var string + * @access public + */ + public $URI; + + /** + * url地址传递的参数。 + * The params passed in through url. + * + * @var array + * @access public + */ + public $params; + + /** + * 视图类型。 + * The view type. + * + * @var string + * @access public + */ + public $viewType; + + /** + * 全局$config对象。 + * The global $config object. + * + * @var object + * @access public + */ + public $config; + + /** + * 全局$lang对象。 + * The global $lang object. + * + * @var object + * @access public + */ + public $lang; + + /** + * 全局$dbh对象,数据库连接句柄。 + * The global $dbh object, the database connection handler. + * + * @var object + * @access public + */ + public $dbh; + + /** + * 从数据库的句柄。 + * The slave database handler. + * + * @var object + * @access public + */ + public $slaveDBH; + + /** + * $post对象,用于访问$_POST变量。 + * The $post object, used to access the $_POST var. + * + * @var ojbect + * @access public + */ + public $post; + + /** + * $get对象,用于访问$_GET变量。 + * The $get object, used to access the $_GET var. + * + * @var ojbect + * @access public + */ + public $get; + + /** + * $session对象,用于访问$_SESSION变量。 + * The $session object, used to access the $_SESSION var. + * + * @var ojbect + * @access public + */ + public $session; + + /** + * $server对象,用于访问$_SERVER变量。 + * The $server object, used to access the $_SERVER var. + * + * @var ojbect + * @access public + */ + public $server; + + /** + * $cookie对象,用于访问$_COOKIE变量。 + * The $cookie object, used to access the $_COOKIE var. + * + * @var ojbect + * @access public + */ + public $cookie; + + /** + * 网站代号。 + * The code of current site. + * + * @var string + * @access public + */ + public $siteCode; + + /** + * 构造方法, 设置路径,类,超级变量等。注意: + * 1.应该使用createApp()方法实例化router类; + * 2.如果$appRoot为空,框架会根据$appName计算应用路径。 + * + * The construct function. + * Prepare all the paths, classes, super objects and so on. + * Notice: + * 1. You should use the createApp() method to get an instance of the router. + * 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName + * + * @param string $appName the name of the app + * @param string $appRoot the root path of the app + * @access public + * @return void + */ + public function __construct($appName = 'demo', $appRoot = '') + { + $this->setPathFix(); + $this->setBasePath(); + $this->setFrameRoot(); + $this->setCoreLibRoot(); + $this->setAppRoot($appName, $appRoot); + $this->setTmpRoot(); + $this->setCacheRoot(); + $this->setLogRoot(); + $this->setConfigRoot(); + $this->setModuleRoot(); + $this->setWwwRoot(); + $this->setThemeRoot(); + $this->setDataRoot(); + $this->loadMainConfig(); + + $this->loadClass('front', $static = true); + $this->loadClass('filter', $static = true); + $this->loadClass('dao', $static = true); + $this->loadClass('mobile', $static = true); + + $this->setSuperVars(); + $this->setDebug(); + $this->setErrorHandler(); + $this->setTimezone(); + $this->startSession(); + + if($this->config->framework->multiSite) $this->setSiteCode() && $this->loadExtraConfig(); + if($this->config->framework->autoConnectDB) $this->connectDB(); + if($this->config->framework->multiLanguage) $this->setClientLang(); + + $needDetectDevice = zget($this->config->framework->detectDevice, $this->clientLang, false); + $this->clientDevice = $needDetectDevice ? $this->setClientDevice() : 'desktop'; + + if($this->config->framework->multiLanguage) $this->loadLang('common'); + if($this->config->framework->multiTheme) $this->setClientTheme(); + } + + /** + * 创建一个应用。 + * Create an application. + * + * @param string $appName 应用名称。 The name of the app. + * @param string $appRoot 应用根路径。The root path of the app. + * @param string $className 应用类名,如果对router类做了扩展,需要指定类名。When extends router class, you should pass in the child router class name. + * @static + * @access public + * @return object the app object + */ + public static function createApp($appName = 'demo', $appRoot = '', $className = '') + { + if(empty($className)) $className = __CLASS__; + return new $className($appName, $appRoot); + } + + //-------------------- 路径相关方法(Path related methods)--------------------// + + /** + * 设置应用名称。 + * Set app name. + * + * @param string $appName + * @access public + * @return void + */ + public function setAppName($appName) + { + $this->appName = $appName; + } + + /** + * 设置目录分隔符。 + * Set the path directory separator. + * + * @access public + * @return void + */ + public function setPathFix() + { + define('DS', DIRECTORY_SEPARATOR); + } + + /** + * 设置基础目录。 + * Set the base path. + * + * @access public + * @return void + */ + public function setBasePath() + { + $this->basePath = realpath(dirname(dirname(dirname(__FILE__)))) . DS; + } + + /** + * 设置框架根目录。 + * Set the frame root. + * + * @access public + * @return void + */ + public function setFrameRoot() + { + $this->frameRoot = $this->basePath . 'framework' . DS; + } + + /** + * 设置类库的根目录。 + * Set the app lib root. + * + * @access public + * @return void + */ + public function setCoreLibRoot() + { + $this->coreLibRoot = $this->basePath . 'lib' . DS; + } + + /** + * 设置应用的根目录。 + * Set the app root. + * + * @param string $appName + * @param string $appRoot + * @access public + * @return void + */ + public function setAppRoot($appName = 'demo', $appRoot = '') + { + if(empty($appRoot)) $this->appRoot = $this->basePath . 'app' . DS . $appName . DS; + if(!empty($appRoot)) $this->appRoot = realpath($appRoot) . DS; + if(!is_dir($this->appRoot)) $this->triggerError("The app you call not found in {$this->appRoot}", __FILE__, __LINE__, $exit = true); + } + + /** + * 设置临时文件的根目录。 + * Set the tmp root. + * + * @access public + * @return void + */ + public function setTmpRoot() + { + $this->tmpRoot = $this->basePath . 'tmp' . DS; + } + + /** + * 设置缓存的根目录。 + * Set the cache root. + * + * @access public + * @return void + */ + public function setCacheRoot() + { + $this->cacheRoot = $this->tmpRoot . 'cache' . DS; + } + + /** + * 设置log的根目录。 + * Set the log root. + * + * @access public + * @return void + */ + public function setLogRoot() + { + $this->logRoot = $this->tmpRoot . 'log' . DS; + } + + /** + * 设置config配置文件的根目录。 + * Set the config root. + * + * @access public + * @return void + */ + public function setConfigRoot() + { + $this->configRoot = $this->basePath . 'config' . DS; + } + + /** + * 设置模块的根目录。 + * Set the module root. + * + * @access public + * @return void + */ + public function setModuleRoot() + { + $this->moduleRoot = $this->basePath . 'module' . DS; + } + + /** + * 设置www的根目录。 + * Set the www root. + * + * @access public + * @return void + */ + public function setWwwRoot() + { + $this->wwwRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), DS) . DS; + } + + /** + * 设置主题根目录。 + * Set the theme root. + * + * @access public + * @return void + */ + public function setThemeRoot() + { + $this->themeRoot = $this->wwwRoot . 'theme' . DS; + } + + /** + * 设置data根目录。 + * Set the data root. + * + * @access public + * @return void + */ + public function setDataRoot() + { + $this->dataRoot = $this->wwwRoot . 'data' . DS; + } + + /** + * 设置超级变量。 + * Set the super vars. + * + * @access public + * @return void + */ + public function setSuperVars() + { + $this->post = new super('post'); + $this->get = new super('get'); + $this->server = new super('server'); + $this->cookie = new super('cookie'); + $this->session = new super('session'); + + unset($GLOBALS); + unset($_REQUEST); + + $_FILES = validater::filterFiles(); + $_POST = validater::filterSuper($_POST); + $_GET = validater::filterSuper($_GET); + $_COOKIE = validater::filterSuper($_COOKIE); + } + + /** + * 设置Debug模式。 + * set Debug. + * + * @access public + * @return void + */ + public function setDebug() + { + if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT); + } + + /** + * 设置错误处理句柄。 + * Set the error handler. + * + * @access public + * @return void + */ + public function setErrorHandler() + { + set_error_handler(array($this, 'saveError')); + register_shutdown_function(array($this, 'shutdown')); + } + + /** + * 获取应用名称 + * Get app name + * + * @access public + * @return string + */ + public function getAppName() + { + return $this->appName; + } + + /** + * 获取$basePath,即基础路径。 + * Get the $basePath var. + * + * @access public + * @return string + */ + public function getBasePath() + { + return $this->basePath; + } + + /** + * 获取$frameRoot,即框架根目录。 + * Get the $frameRoot var. + * + * @access public + * @return string + */ + public function getFrameRoot() + { + return $this->frameRoot; + } + + /** + * 获取$appRoot变量,即应用的根目录。 + * Get the $appRoot var. + * + * @access public + * @return string + */ + public function getAppRoot() + { + return $this->appRoot; + } + + /** + * 获取$wwwRoot变量。 + * Get the $wwwRoot var + * + * @access public + * @return string + */ + public function getWwwRoot() + { + return $this->wwwRoot; + } + + /** + * 获取$coreLibRoot变量,即应用类库的根目录。 + * Get the $coreLibRoot var. + * + * @access public + * @return string + */ + public function getCoreLibRoot() + { + return $this->coreLibRoot; + } + + /** + * 获取$tmpRoot变量,即临时文件的根目录。 + * Get the $tmpRoot var. + * + * @access public + * @return string + */ + public function getTmpRoot() + { + return $this->tmpRoot; + } + + /** + * 获取$cacheRoot变量,即缓存文件的根目录。 + * Get the $cacheRoot var. + * + * @access public + * @return string + */ + public function getCacheRoot() + { + return $this->cacheRoot; + } + + /** + * 获取$logRoot变量,即日志文件的根目录。 + * Get the $logRoot var. + * + * @access public + * @return string + */ + public function getLogRoot() + { + return $this->logRoot; + } + + /** + * 获取$configRoot变量,即配置文件的根目录。 + * Get the $configRoot var. + * + * @access public + * @return string + */ + public function getConfigRoot() + { + return $this->configRoot; + } + + /** + * 获取$moduleRoot变量,即应用模块的根目录。 + * Get the $moduleRoot var. + * + * @param string $appName + * @access public + * @return string + */ + public function getModuleRoot($appName = '') + { + if($appName == '') return $this->moduleRoot; + return dirname($this->moduleRoot) . DS . $appName . DS; + } + + /** + * 获取$webRoot,即应用的路径。 + * Get the $webRoot var. + * + * @access public + * @return string + */ + public function getWebRoot() + { + return $this->config->webRoot; + } + + /** + * 获取$themeRoot变量,即主题的根目录。 + * Get the $themeRoot var. + * + * @access public + * @return string + */ + public function getThemeRoot() + { + return $this->themeRoot; + } + + /** + * 获取$dataRoot目录 + * Get the $dataRoot var + * + * @access public + * @return string + */ + public function getDataRoot() + { + return $this->dataRoot; + } + + //------ 客户端环境有关的函数(Client environment related functions) ------// + + /** + * 根据配置设置当前时区。 + * Set the time zone according to the config. + * + * @access public + * @return void + */ + public function setTimezone() + { + if(isset($this->config->timezone)) date_default_timezone_set($this->config->timezone); + } + + /** + * 开启 session + * Start the session. + * + * @access public + * @return void + */ + public function startSession() + { + if(!defined('SESSION_STARTED')) + { + $sessionName = $this->config->sessionVar; + session_name($sessionName); + if(isset($_GET[$this->config->sessionVar])) session_id($_GET[$this->config->sessionVar]); + session_start(); + define('SESSION_STARTED', true); + } + } + + /** + * 根据用户浏览器的语言设置和服务器配置,选择显示的语言。 + * 优先级:$lang参数 > session > cookie > 浏览器 > 配置文件。 + * + * Set the language. + * Using the order of method $lang param, session, cookie, browser and the default lang. + * + * @param string $lang zh-cn|zh-tw|zh-hk|en + * @access public + * @return void + */ + public function setClientLang($lang = '') + { + if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $this->clientLang = $this->parseHttpAcceptLang(); + if(isset($_COOKIE['lang'])) $this->clientLang = $_COOKIE['lang']; + if(isset($_SESSION['lang'])) $this->clientLang = $_SESSION['lang']; + if(!empty($lang)) $this->clientLang = $lang; + + if(!empty($this->clientLang)) + { + $this->clientLang = strtolower($this->clientLang); + if(!isset($this->config->langs[$this->clientLang])) $this->clientLang = $this->config->default->lang; + } + else + { + $this->clientLang = $this->config->default->lang; + } + + setcookie('lang', $this->clientLang, $this->config->cookieLife, $this->config->webRoot); + if(!isset($_COOKIE['lang'])) $_COOKIE['lang'] = $this->clientLang; + + return true; + } + + /** + * 从HTTP_ACCEPT_LANGUAGE中提出去支持的语言。 + * Parse the lang str from HTTP_ACCEPT_LANGUAGE header. + * + * @access public + * @return string + */ + public function parseHttpAcceptLang() + { + if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return ''; + + $raw = $_SERVER['HTTP_ACCEPT_LANGUAGE']; + $pos = strpos($raw, ','); + $lang = $pos === false ? $raw : substr($raw, 0, $pos); + + /* Fix clientLang for ie >= 10. https://www.drupal.org/node/365615. */ + if(stripos($lang, 'hans')) $lang = 'zh-cn'; + if(stripos($lang, 'hant')) $lang = 'zh-tw'; + return $lang; + } + + /** + * 设置客户端使用的主题,判断逻辑与客户端的语言相同。 + * 主题的css和图片文件应该存放在www/theme/$themeName路径。 + * + * Set the theme the client user using. The logic is same as the clientLang. + * The css and images files of an theme should saved at www/theme/$themeName + * + * @param string $theme + * @access public + * @return void + */ + public function setClientTheme($theme = '') + { + if(isset($this->config->client->theme)) $this->clientTheme = $this->config->client->theme; + if(isset($_COOKIE['theme'])) $this->clientTheme = $_COOKIE['theme']; + if(!empty($theme)) $this->clientTheme = $theme; + + if(!empty($this->clientTheme)) + { + $this->clientTheme = strtolower($this->clientTheme); + if(!isset($this->lang->themes[$this->clientTheme])) $this->clientTheme = $this->config->default->theme; + } + else + { + $this->clientTheme = $this->config->default->theme; + } + + setcookie('theme', $this->clientTheme, $this->config->cookieLife, $this->config->webRoot); + if(!isset($_COOKIE['theme'])) $_COOKIE['theme'] = $this->clientTheme; + + return true; + } + + /** + * 设置客户端的设备类型。 + * Set client device. + * + * @access public + * @return void + */ + public function setClientDevice() + { + $this->clientDevice = 'desktop'; + + if($this->cookie->device == 'mobile') $this->clientDevice = 'mobile'; + if($this->cookie->device == 'desktop') $this->clientDevice = 'desktop'; + + if(strpos('mobile,desktop', $this->cookie->device) === false) + { + $mobile = new mobile(); + $this->clientDevice = ($mobile->isMobile() and !$mobile->isTablet()) ? 'mobile' : 'desktop'; + } + + setcookie('device', $this->clientDevice, $this->config->cookieLife, $this->config->webRoot); + if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice; + + return $this->clientDevice; + } + + /** + * 设置站点代号,可以针对不同的站点来加载不同的扩展。 + * Set the code of current site, thus can load diffrent extension of diffrent site. + * + * @access public + * @return void + */ + public function setSiteCode() + { + return $this->siteCode = helper::parseSiteCode($this->server->http_host); + } + + /** + * 获取$clientLang变量,即客户端的语言。 + * Get the $clientLang var. + * + * @access public + * @return string + */ + public function getClientLang() + { + return $this->clientLang; + } + + /** + * 获取$clientTheme变量。 + * Get the $clientTheme var. + * + * @access public + * @return string + */ + public function getClientTheme() + { + return $this->config->webRoot . 'theme/' . $this->clientTheme . '/'; + } + + /** + * 获得客户端的终端设备。 + * Get the client device. + * + * @access public + * @return void + */ + public function getClientDevice() + { + return $this->clientDevice; + } + + //-------------------- 请求相关的方法(Request related methods) --------------------// + + /** + * 解析本次请求的入口方法,根据请求的类型(PATH_INFO GET),调用相应的方法。 + * The entrance of parseing request. According to the requestType, call related methods. + * + * @access public + * @return void + */ + public function parseRequest() + { + if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2') + { + $this->parsePathInfo(); + $this->setRouteByPathInfo(); + } + elseif($this->config->requestType == 'GET') + { + $this->parseGET(); + $this->setRouteByGET(); + } + else + { + $this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true); + } + } + + /** + * PATH_INFO方式解析,获取$URI和$viewType。 + * Parse PATH_INFO, get the $URI and $viewType. + * + * @access public + * @return void + */ + public function parsePathInfo() + { + $pathInfo = $this->getPathInfo(); + if(!empty($pathInfo)) + { + $dotPos = strrpos($pathInfo, '.'); + if($dotPos) + { + $this->URI = substr($pathInfo, 0, $dotPos); + $this->viewType = substr($pathInfo, $dotPos + 1); + if(strpos($this->config->views, ',' . $this->viewType . ',') === false) + { + $this->viewType = $this->config->default->view; + } + } + else + { + $this->URI = $pathInfo; + $this->viewType = $this->config->default->view; + } + } + else + { + $this->viewType = $this->config->default->view; + } + } + + /** + * 从$_SERVER或者$_ENV全局变量根据pathinfo变量名获取$PATH_INFO值。 + * PATH_INFO的变量名几乎都是'PATH_INFO',但也有可能是ORIG_PATH_INFO。 + * + * Get $PATH_INFO from $_SERVER or $_ENV by the pathinfo var name. + * Mostly, the var name of PATH_INFO is PATH_INFO, but may be ORIG_PATH_INFO. + * + * @access public + * @return string the PATH_INFO + */ + public function getPathInfo() + { + if(isset($_SERVER['PATH_INFO'])) + { + $value = $_SERVER['PATH_INFO']; + } + elseif(isset($_SERVER['ORIG_PATH_INFO'])) + { + $value = $_SERVER['ORIG_PATH_INFO']; + } + elseif(isset($this->URI)) + { + $value = $this->URI; + $subpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', dirname($_SERVER['SCRIPT_FILENAME'])); + if($subpath != '/') $subpath = '/' . $subpath; + if($subpath != '' and $subpath != '/' and strpos($value, $subpath) === 0) $value = substr($value, strlen($subpath)); + } + else + { + $value = @getenv('PATH_INFO'); + if(empty($value)) $value = @getenv('ORIG_PATH_INFO'); + } + + if(strpos($value, $_SERVER['SCRIPT_NAME']) !== false) $value = str_replace($_SERVER['SCRIPT_NAME'], '', $value); + if(strpos($value, '?') === false) return trim($value, '/'); + + $value = parse_url($value); + $pathInfo = trim(zget($value, 'path', ''), '/'); + if(trim($pathInfo, '/') == trim($this->config->webRoot, '/')) $pathInfo = ''; + + return $pathInfo; + } + + /** + * GET请求方式解析,获取$URI和$viewType。 + * Parse GET, get $URI and $viewType. + * + * @access public + * @return void + */ + public function parseGET() + { + if(isset($_GET[$this->config->viewVar])) + { + $this->viewType = $_GET[$this->config->viewVar]; + if(strpos($this->config->views, ',' . $this->viewType . ',') === false) $this->viewType = $this->config->default->view; + } + else + { + $this->viewType = $this->config->default->view; + } + $this->URI = $_SERVER['REQUEST_URI']; + } + + /** + * 获取$URL。 + * Get the $URL. + * + * @param bool $full true, the URI contains the webRoot, else only hte URI. + * @access public + * @return string + */ + public function getURI($full = false) + { + if($full and $this->config->requestType == 'PATH_INFO') + { + if($this->URI) return $this->config->webRoot . $this->URI . '.' . $this->viewType; + return $this->config->webRoot; + } + return $this->URI; + } + + /** + * 获取$vewType变量。 + * Get the $viewType var. + * + * @access public + * @return string + */ + public function getViewType() + { + return $this->viewType; + } + + //-------------------- 模块及扩展设置(Module and extension) --------------------// + + /** + * 加载common模块。 + * + * common模块比较特别,它会执行几乎每次请求都需要执行的操作,例如: + * 打开session,检查权限等等。 + * 加载完$lang, $config, $dbh后,需要在入口文件(www/index.php)中手动调用该方法。 + * + * Load the common module + * + * The common module is a special module, which can be used to do some common things. For examle: + * start session, check priviledge and so on. + * This method should called manually in the router file(www/index.php) after the $lang, $config, $dbh loaded. + * + * @access public + * @return object|bool the common model object or false if not exits. + */ + public function loadCommon() + { + $this->setModuleName('common'); + $commonModelFile = $this->setModelFile('common'); + if(!file_exists($commonModelFile)) return false; + + helper::import($commonModelFile); + + if($this->config->framework->extensionLevel == 0 and class_exists('commonModel')) return new commonModel(); + if($this->config->framework->extensionLevel > 0 and class_exists('extCommonModel')) return new extCommonModel(); + + if(class_exists('commonModel')) return new commonModel(); + return false; + } + + /** + * 设置要被调用的模块名。 + * Set the name of the module to be called. + * + * @param string $moduleName the module name + * @access public + * @return void + */ + public function setModuleName($moduleName = '') + { + if($this->checkModuleName($moduleName)) $this->moduleName = strtolower($moduleName); + } + + /** + * 设置要被调用的控制器文件。 + * Set the control file of the module to be called. + * + * @param bool $exitIfNone 没有找到该控制器文件的情况:如果该参数为true,则终止程序;如果为false,则打印错误日志 + * If control file not foundde, how to do. True, die the whole app. false, log error. + * @access public + * @return bool + */ + public function setControlFile($exitIfNone = true) + { + $this->controlFile = $this->moduleRoot . $this->moduleName . DS . 'control.php'; + if(file_exists($this->controlFile)) return true; + $this->triggerError("the control file $this->controlFile not found.", __FILE__, __LINE__, $exitIfNone); + } + + /** + * 设置要被调用的方法名。 + * Set the name of the method calling. + * + * @param string $methodName + * @access public + * @return void + */ + public function setMethodName($methodName = '') + { + if($this->checkMethodName($methodName)) $this->methodName = strtolower($methodName); + } + + /** + * 获取一个模块的路径。 + * Get the path of one module. + * + * @param string $appName the app name + * @param string $moduleName the module name + * @access public + * @return string the module path + */ + public function getModulePath($appName = '', $moduleName = '') + { + if($moduleName == '') $moduleName = $this->moduleName; + + if($this->checkModuleName($moduleName)) + { + $modulePath = $this->getModuleRoot($appName) . strtolower($moduleName) . DS; + return $modulePath; + } + } + + /** + * 获取一个模块的扩展路径。 Get extension path of one module. + * + * If the extensionLevel == 0, return empty array. + * If the extensionLevel == 1, return the common extension directory. + * If the extensionLevel == 2, return the common and site extension directories. + * + * @param string $appName the app name + * @param string $moduleName the module name + * @param string $ext the extension type, can be control|model|view|lang|config + * @access public + * @return string the extension path. + */ + public function getModuleExtPath($appName, $moduleName, $ext) + { + /* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */ + if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array(); + + /* When extensionLevel == 1. */ + $modulePath = $this->getModulePath($appName, $moduleName); + $paths = array(); + $paths['common'] = $modulePath . 'ext' . DS . $ext . DS; + if($this->config->framework->extensionLevel == 1) return $paths; + + /* When extensionLevel == 2. */ + $paths['site'] = empty($this->siteCode) ? '' : $modulePath . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS; + return $paths; + } + + /** + * 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii. + * + * @param string $var + * @access public + * @return bool + */ + public function checkModuleName($var) + { + global $filter; + $rule = $filter->default->moduleName; + if(validater::checkByRule($var, $rule)) return true; + $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); + } + + /** + * 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii. + * + * @param string $var + * @access public + * @return bool + */ + public function checkMethodName($var) + { + global $filter; + $rule = $filter->default->methodName; + if($this->config->framework->filterParam == 2 and isset($filter->{$this->moduleName}->methodName)) $rule = $filter->{$this->moduleName}->methodName; + + if(validater::checkByRule($var, $rule)) return true; + $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); + } + + /** + * 设置Action的扩展文件。 Set the action extension file. + * + * @access public + * @return bool + */ + public function setActionExtFile() + { + $moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control'); + + /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext pathes return false.*/ + if(empty($moduleExtPaths)) return false; + + /* 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。If extensionLevel == 2 and site extensionFile exists, return it. */ + if($this->config->framework->extensionLevel == 2 and !empty( $moduleExtPaths['site'])) + { + $this->extActionFile = $moduleExtPaths['site'] . $this->methodName . '.php'; + if(file_exists($this->extActionFile)) return true; + } + + /* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */ + $this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php'; + return file_exists($this->extActionFile); + } + + /** + * 设置一个模块的model文件,如果存在model扩展,一起合并。 + * Set the model file of one module. If there's an extension file, merge it with the main model file. + * + * @param string $moduleName the module name + * @param string $appName the app name + * @static + * @access public + * @return string the model file + */ + public function setModelFile($moduleName, $appName = '') + { + if($appName == '') $appName = $this->getAppName(); + + /* 设置主model文件。 Set the main model file. */ + $mainModelFile = $this->getModulePath($appName, $moduleName) . 'model.php'; + if($this->config->framework->extensionLevel == 0) return $mainModelFile; + + /* 计算扩展的文件和hook文件。Compute the extension files and hook files. */ + $hookFiles = array(); + $extFiles = array(); + $siteExtended = false; + + $modelExtPaths = $this->getModuleExtPath($appName, $moduleName, 'model'); + foreach($modelExtPaths as $extType => $modelExtPath) + { + if(empty($modelExtPath)) continue; + + $tmpHookFiles = helper::ls($modelExtPath . 'hook/', '.php'); + $tmpExtFiles = helper::ls($modelExtPath, '.php'); + $hookFiles = array_merge($hookFiles, $tmpHookFiles); + $extFiles = array_merge($extFiles, $tmpExtFiles); + + if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles))) $siteExtended = true; + } + + /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */ + if(empty($extFiles) and empty($hookFiles)) return $mainModelFile; + + /* 计算合并之后的modelFile路径。Compute the merged model file path. */ + $extModelPrefix = ($siteExtended and !empty($this->siteCode)) ? $this->siteCode{0} . DS . $this->siteCode : ''; + $mergedModelDir = $this->getTmpRoot() . 'model' . DS . ($extModelPrefix ? $extModelPrefix . DS : ''); + $mergedModelFile = $mergedModelDir . $moduleName . '.php'; + if(!is_dir($mergedModelDir)) mkdir($mergedModelDir, 0755, true); + + /* 判断生成的缓存文件是否需要更新。 Judge whether the merged model file needed update or not. */ + if(!$this->needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile)) return $mergedModelFile; + + /* 合并扩展和hook文件。Merge the extension and hook files. */ + $modelLines = $this->mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir); + $this->mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile); + + return $mergedModelFile; + } + + /** + * 检查合并之后的model文件是否需要更新。Check whether the merged model file need update or not. + * + * @param string $mainModelFile + * @param string $mergedModelFile + * @param string $modelExtPaths + * @param array $extFiles + * @param array $hookFiles + * @access public + * @return bool + */ + public function needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile) + { + $lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0; + + foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true; + foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) return true; + + $modelExtPath = $modelExtPaths['common']; + $modelHookPath = $modelExtPaths['common'] . 'hook/'; + if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; + if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; + + if(!empty($modelExtPaths['site'])) + { + $modelExtPath = $modelExtPaths['site']; + $modelHookPath = $modelExtPaths['site'] . 'hook/'; + if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; + if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; + } + + if(filemtime($mainModelFile) > $lastTime) return true; + + return false; + } + + /** + * 将model的扩展文件合并在一起。Merge model ext files. + * + * @param string $moduleName + * @param string $mainModelFile + * @param array $extFiles + * @param string $mergedModelDir + * @access public + * @return void + */ + public function mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir) + { + /* 设置类名。Set the class names. */ + $modelClass = "{$moduleName}Model"; + $tmpModelClass = "tmpExt$modelClass"; + + /* 开始拼装代码。Prepare the codes. */ + $modelLines = "getBasePath());\n"; + $modelLines .= "helper::import('" . str_replace($this->getBasePath(), '', $mainModelFile) . "');\n"; + $modelLines .= "helper::cd();\n"; + $modelLines .= "class $tmpModelClass extends $modelClass \n{\n"; + + /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into model lines. */ + foreach($extFiles as $extFile) $modelLines .= self::removePHPTAG($extFile); + + /* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */ + $replaceMark = '//**//'; + $modelLines .= "\n$replaceMark\n}"; + + /* 生成一个临时的model扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged model file and import it for merge hook codes using. */ + $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; + if(@file_put_contents($tmpModelFile, $modelLines)) + { + if(!class_exists($tmpModelClass)) include $tmpModelFile; + return $modelLines; + } + + $this->triggerError("ERROR: $tmpModelFile not writable.", __FILE__, __LINE__, true); + } + + /** + * 合并model的hook脚本。Merge hook files for a model. + * + * @access public + * @return void + */ + public function mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile) + { + /* 定义相关变量。Init vars. */ + $modelClass = $moduleName . 'Model'; + $extModelClass = 'ext' . $modelClass; + $tmpModelClass = 'tmpExt' . $modelClass; + $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; + $replaceMark = '//**//'; + + /* 读取hook文件。Get hook codes need to merge. */ + $hookCodes = array(); + foreach($hookFiles as $hookFile) + { + /* 通过文件名获得其对应的方法名。Get methods according it's filename. */ + $fileName = baseName($hookFile); + list($method) = explode('.', $fileName); + $hookCodes[$method][] = self::removePHPTAG($hookFile); + } + + /* 合并Hook文件。Cycle the hook methods and merge hook codes. */ + $hookedMethods = array_keys($hookCodes); + $mainModelCodes = file($mainModelFile); + $mergedModelCodes = file($tmpModelFile); + foreach($hookedMethods as $method) + { + /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */ + if(!method_exists($tmpModelClass, $method)) continue; + $methodRelfection = new reflectionMethod($tmpModelClass, $method); + $definedFile = $methodRelfection->getFileName(); + $startLine = $methodRelfection->getStartLine() . ' '; + $endLine = $methodRelfection->getEndLine() . ' '; + + /* 将Hook脚本和老的代码合并在一起,并替换原来的定义。Merge hook codes with old codes and replace back. */ + $oldCodes = $definedFile == $tmpModelFile ? $mergedModelCodes : $mainModelCodes; + $oldCodes = join("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1)); + $openBrace = strpos($oldCodes, '{'); + $newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . join("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1); + + if($definedFile == $tmpModelFile) $modelLines = str_replace($oldCodes, $newCodes, $modelLines); + if($definedFile != $tmpModelFile) $modelLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $modelLines); + } + + /* 保存最终的Model文件。Save the last merged model file. */ + $modelLines = str_replace($tmpModelClass, $extModelClass, $modelLines); + file_put_contents($mergedModelFile, $modelLines); + unlink($tmpModelFile); + } + + /** + * Remove tags of PHP + * + * @param string $fileName + * @static + * @access public + * @return string + */ + static public function removePHPTAG($fileName) + { + $code = trim(file_get_contents($fileName)); + if(strpos($code, '') !== false) $code = rtrim($code, '?>'); + return trim($code); + } + + //-------------------- 路由相关方法(Routing related methods) --------------------// + + /** + * 设置路由(PATH_INFO 方式): + * 1.设置模块名; + * 2.设置方法名; + * 3.设置控制器文件。 + * + * Set the route according to PATH_INFO. + * 1. set the module name. + * 2. set the method name. + * 3. set the control file. + * + * @access public + * @return void + */ + public function setRouteByPathInfo() + { + if(!empty($this->URI)) + { + /* + * 根据$requestFix分割符,分割网址。 + * There's the request seperator, split the URI by it. + **/ + if(strpos($this->URI, $this->config->requestFix) !== false) + { + $items = explode($this->config->requestFix, $this->URI); + $this->setModuleName($items[0]); + $this->setMethodName($items[1]); + } + /* + * 如果网址中没有分隔符,使用默认的方法。 + * No reqeust seperator, use the default method name. + **/ + else + { + $this->setModuleName($this->URI); + $this->setMethodName($this->config->default->method); + } + } + else + { + $this->setModuleName($this->config->default->module); // 使用默认模块 use the default module. + $this->setMethodName($this->config->default->method); // 使用默认方法 use the default method. + } + $this->setControlFile(); + } + + /** + * 设置路由(GET 方式): + * 1.设置模块名; + * 2.设置方法名; + * 3.设置控制器文件。 + * + * Set the route according to GET. + * 1. set the module name. + * 2. set the method name. + * 3. set the control file. + * + * @access public + * @return void + */ + public function setRouteByGET() + { + $moduleName = isset($_GET[$this->config->moduleVar]) ? strtolower($_GET[$this->config->moduleVar]) : $this->config->default->module; + $methodName = isset($_GET[$this->config->methodVar]) ? strtolower($_GET[$this->config->methodVar]) : $this->config->default->method; + $this->setModuleName($moduleName); + $this->setControlFile(); + $this->setMethodName($methodName); + } + + /** + * 加载一个模块: + * 1. 引入控制器文件或扩展的方法文件; + * 2. 创建control对象; + * 3. 解析url,得到请求的参数; + * 4. 使用call_user_function_array调用相应的方法。 + * + * Load a module. + * 1. include the control file or the extension action file. + * 2. create the control object. + * 3. set the params passed in through url. + * 4. call the method by call_user_function_array + * + * @access public + * @return bool|object if the module object of die. + */ + public function loadModule() + { + $appName = $this->appName; + $moduleName = $this->moduleName; + $methodName = $this->methodName; + + /* + * 引入该模块的control文件。 + * Include the control file of the module. + **/ + $file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile; + chdir(dirname($file2Included)); + include $file2Included; + + /* + * 设置control的类名。 + * Set the class name of the control. + **/ + $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; + if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true); + + /* + * 创建control类的实例。 + * Create a instance of the control. + **/ + $module = new $className(); + if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true); + $this->control = $module; + + /* include default value for module*/ + $defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php"); + if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file; + + /* + * 使用反射机制获取函数参数的默认值。 + * Get the default settings of the method to be called using the reflecting. + * + * */ + $defaultParams = array(); + $methodReflect = new reflectionMethod($className, $methodName); + foreach($methodReflect->getParameters() as $param) + { + $name = $param->getName(); + + $default = '_NOT_SET'; + if(isset($paramDefaultValue[$appName][$className][$methodName][$name])) + { + $default = $paramDefaultValue[$appName][$className][$methodName][$name]; + } + elseif(isset($paramDefaultValue[$className][$methodName][$name])) + { + $default = $paramDefaultValue[$className][$methodName][$name]; + } + elseif($param->isDefaultValueAvailable()) + { + $default = $param->getDefaultValue(); + } + + $defaultParams[$name] = $default; + } + + /** + * 根据PATH_INFO或者GET方式设置请求的参数。 + * Set params according PATH_INFO or GET. + */ + if($this->config->requestType != 'GET') + { + $this->setParamsByPathInfo($defaultParams); + } + else + { + $this->setParamsByGET($defaultParams); + } + + if($this->config->framework->filterParam == 2) + { + $_GET = validater::filterParam($_GET, 'get'); + $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); + } + + /* 调用该方法 Call the method. */ + call_user_func_array(array($module, $methodName), $this->params); + return $module; + } + + /** + * 设置请求的参数(PATH_INFO 方式)。 + * Set the params by PATH_INFO. + * + * @param array $defaultParams the default settings of the params. + * @param string $type + * @access public + * @return void + */ + public function setParamsByPathInfo($defaultParams = array(), $type = '') + { + $params = array(); + if($type != 'fetch') + { + /* 分割URI。 Spit the URI. */ + $items = explode($this->config->requestFix, $this->URI); + $itemCount = count($items); + + /** + * 前两项为模块名和方法名,参数从下标2开始。 + * The first two item is moduleName and methodName. So the params should begin at 2. + **/ + for($i = 2; $i < $itemCount; $i ++) + { + $key = key($defaultParams); // Get key from the $defaultParams. + if(empty($key)) continue; + + $params[$key] = $items[$i]; + next($defaultParams); + } + } + + $this->params = $this->mergeParams($defaultParams, $params); + } + + /** + * 设置请求的参数(GET 方式)。 + * Set the params by GET. + * + * @param array $defaultParams the default settings of the params. + * @param string $type + * @access public + * @return void + */ + public function setParamsByGET($defaultParams, $type = '') + { + $params = array(); + if($type != 'fetch') + { + /* Unset moduleVar, methodVar, viewVar and session 变量, 剩下的作为参数。 */ + /* Unset the moduleVar, methodVar, viewVar and session var, all the left are the params. */ + unset($_GET[$this->config->moduleVar]); + unset($_GET[$this->config->methodVar]); + unset($_GET[$this->config->viewVar]); + unset($_GET[$this->config->sessionVar]); + $params = $_GET; + } + + $this->params = $this->mergeParams($defaultParams, $params); + } + + /** + * 合并请求的参数和默认参数,这样就可以省略已经有默认值的参数了。 + * Merge the params passed in and the default params. Thus the params which have default values needn't pass value, just like a function. + * + * @param array $defaultParams the default params defined by the method. + * @param array $passedParams the params passed in through url. + * @access public + * @return array the merged params. + */ + public function mergeParams($defaultParams, $passedParams) + { + global $filter; + + /* Remove these two params. */ + unset($passedParams['onlybody']); + unset($passedParams['HTTP_X_REQUESTED_WITH']); + + /* Check params from URL. */ + $nameRule = isset($filter->{$this->moduleName}->{$this->methodName}->paramName) ? $filter->{$this->moduleName}->{$this->methodName}->paramName : $filter->default->paramName; + foreach($passedParams as $param => $value) + { + if(!validater::checkByRule($param, $nameRule)) die('Bad Request!'); + $valueRule = $filter->default->paramValue; + if(isset($filter->{$this->moduleName}->{$this->methodName}->paramValue[$param])) + { + $valueRule = $filter->{$this->moduleName}->{$this->methodName}->paramValue[$param]; + } + + if($value and !validater::checkByRule($value, $valueRule)) die('Bad Request!'); + } + + $passedParams = array_values($passedParams); + $i = 0; + foreach($defaultParams as $key => $defaultValue) + { + if(isset($passedParams[$i])) + { + $defaultParams[$key] = strip_tags($passedParams[$i]); + } + else + { + if($defaultValue === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true); + } + $i ++; + } + + return $defaultParams; + } + + /** + * 获取$moduleName变量。 + * Get the $moduleName var. + * + * @access public + * @return string + */ + public function getModuleName() + { + return $this->moduleName; + } + + /** + * 获取$controlFile变量。 + * Get the $controlFile var. + * + * @access public + * @return string + */ + public function getControlFile() + { + return $this->controlFile; + } + + /** + * 获取$methodName变量。 + * Get the $methodName var. + * + * @access public + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * 获取$param变量。 + * Get the $param var. + * + * @access public + * @return string + */ + public function getParams() + { + return $this->params; + } + + //-------------------- 常用的工具方法(Tool methods) ------------------// + + /** + * 从类库中加载一个类文件。 + * + * Load a class file. + * + * @param string $className the class name + * @param bool $static statis class or not + * @access public + * @return object|bool the instance of the class or just true. + */ + public function loadClass($className, $static = false) + { + $className = strtolower($className); + + /* 搜索$coreLibRoot(Search in $coreLibRoot) */ + $classFile = $this->coreLibRoot . $className; + if(is_dir($classFile)) $classFile .= DS . $className; + $classFile .= '.class.php'; + if(!helper::import($classFile)) $this->triggerError("class file $classFile not found", __FILE__, __LINE__, $exit = true); + + /* 如果是静态调用,则返回(If staitc, return) */ + if($static) return true; + + /* 实例化该类(Instance it) */ + global $$className; + if(!class_exists($className)) $this->triggerError("the class $className not found in $classFile", __FILE__, __LINE__, $exit = true); + if(!is_object($$className)) $$className = new $className(); + return $$className; + } + + /** + * 加载整个应用公共的配置文件。 + * Load the common config files for the app. + * + * @access public + * @return void + */ + public function loadMainConfig() + { + /* 初始化$config对象。Init the $config object. */ + global $config, $filter; + if(!is_object($config)) $config = new config(); + $this->config = $config; + + /* 加载主配置文件。 Load the main config file. */ + $mainConfigFile = $this->configRoot . 'config.php'; + if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true); + include $mainConfigFile; + } + + /** + * 当multiSite功能打开的时候,加载额外的配置文件。 + * When multiSite feature enabled, load extra config file. + * + * @access public + * @return void + */ + public function loadExtraConfig() + { + global $config; + $multiConfigFile = $this->configRoot . 'multi.php'; + if(file_exists($multiConfigFile)) include $multiConfigFile; + + $siteConfigFile = $this->configRoot . "sites/{$this->siteCode}.php"; + if(file_exists($siteConfigFile)) include $siteConfigFile; + } + + /** + * 加载模块的config文件,返回全局$config对象。 + * 如果该模块是common,加载$configRoot的配置文件,其他模块则加载其模块的配置文件。 + * + * Load config and return it as the global config object. + * If the module is common, search in $configRoot, else in $modulePath. + * + * @param string $moduleName module name + * @param string $appName app name + * @param bool $exitIfNone exit or not + * @access public + * @return object|bool the config object or false. + */ + public function loadModuleConfig($moduleName, $appName = '') + { + global $config; + + if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass(); + + /* 初始化数组。Init the variables. */ + $extConfigFiles = array(); + $commonExtConfigFiles = array(); + $siteExtConfigFiles = array(); + + /* 先获得模块的主配置文件。Get the main config file for current module first. */ + $mainConfigFile = $this->getModulePath($appName, $moduleName) . 'config.php'; + + /* 查找扩展配置文件。Get extension config files. */ + if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($appName, $moduleName, 'config'); + if($config->framework->extensionLevel >= 1 and !empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php'); + if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php'); + $extConfigFiles = array_merge($commonExtConfigFiles, $siteExtConfigFiles); + + /* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */ + $configFiles = array_merge(array($mainConfigFile), $extConfigFiles); + + /* 加载每一个配置文件。Load every config file. */ + static $loadedConfigs = array(); + foreach($configFiles as $configFile) + { + if(in_array($configFile, $loadedConfigs)) continue; + if(file_exists($configFile)) include $configFile; + $loadedConfigs[] = $configFile; + } + + /* 加载数据库中与本模块相关的配置项。Merge from the db configs. */ + if($moduleName != 'common') + { + if(isset($config->system->$moduleName)) $this->mergeConfig($config->system->$moduleName, $moduleName); + if(isset($config->personal->$moduleName)) $this->mergeConfig($config->personal->$moduleName, $moduleName); + } + } + + /** + * Merge db config. + * + * @param array $dbConfig + * @param string $moduleName + * @access public + * @return void + */ + public function mergeConfig($dbConfig, $moduleName = 'common') + { + global $config; + + /* 如果没有设置本模块配置,则首先进行初始化。Init the $config->$moduleName if not set.*/ + if($moduleName != 'common' and !isset($config->$moduleName)) $config->$moduleName = new stdclass(); + + $config2Merge = $config; + if($moduleName != 'common') $config2Merge = $config->$moduleName; + + foreach($dbConfig as $item) + { + if($item->section) + { + if(!isset($config2Merge->{$item->section})) $config2Merge->{$item->section} = new stdclass(); + if(is_object($config2Merge->{$item->section})) + { + $config2Merge->{$item->section}->{$item->key} = $item->value; + } + } + else + { + $config2Merge->{$item->key} = $item->value; + } + } + } + + /** + * 向客户端输出配置参数,客户端可以根据这些参数实现和调整请求的逻辑。 + * Export the config params to the client, thus the client can adjust it's logic according the config. + * + * @access public + * @return void + */ + public function exportConfig() + { + $view = new stdclass(); + $view->version = $this->config->version; + $view->requestType = $this->config->requestType; + $view->requestFix = $this->config->requestFix; + $view->moduleVar = $this->config->moduleVar; + $view->methodVar = $this->config->methodVar; + $view->viewVar = $this->config->viewVar; + $view->sessionVar = $this->config->sessionVar; + + $this->session->set('random', mt_rand(0, 10000)); + $view->sessionName = session_name(); + $view->sessionID = session_id(); + $view->random = $this->session->random; + $view->expiredTime = ini_get('session.gc_maxlifetime'); + $view->serverTime = time(); + + echo json_encode($view); + } + + /** + * 加载语言文件,返回全局$lang对象。 + * Load lang and return it as the global lang object. + * + * @param string $moduleName the module name + * @param string $appName the app name + * @access public + * @return bool|ojbect the lang object or false. + */ + public function loadLang($moduleName, $appName = '') + { + /* 初始化变量。Init vars. */ + $modulePath = $this->getModulePath($appName, $moduleName); + $extLangFiles = array(); + $langFilesToLoad = array(); + + /* 判断主语言文件是否存在。Whether the main lang file exists or not. */ + $mainLangFile = $modulePath . 'lang' . DS . $this->clientLang . '.php'; + if(file_exists($mainLangFile)) $langFilesToLoad[] = $mainLangFile; + + /* 获取扩展语言文件。If extensionLevel > 0, get extension lang files. */ + if($this->config->framework->extensionLevel > 0) + { + $commonExtLangFiles = array(); + $siteExtLangFiles = array(); + + $extLangPath = $this->getModuleExtPath($appName, $moduleName, 'lang'); + if($this->config->framework->extensionLevel >= 1 and !empty($extLangPath['common'])) $commonExtLangFiles = helper::ls($extLangPath['common'] . $this->clientLang, '.php'); + if($this->config->framework->extensionLevel == 2 and !empty($extLangPath['site'])) $siteExtLangFiles = helper::ls($extLangPath['site'] . $this->clientLang, '.php'); + $extLangFiles = array_merge($commonExtLangFiles, $siteExtLangFiles); + } + + /* 计算最终要加载的语言文件。 Get the lang files to be loaded. */ + $langFilesToLoad = array_merge($langFilesToLoad, $extLangFiles); + if(empty($langFilesToLoad)) return false; + + /* 加载语言文件。Load lang files. */ + global $lang; + if(!is_object($lang)) $lang = new language(); + if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass(); + + static $loadedLangs = array(); + foreach($langFilesToLoad as $langFile) + { + if(in_array($langFile, $loadedLangs)) continue; + include $langFile; + $loadedLangs[] = $langFile; + } + + $this->lang = $lang; + return $lang; + } + + /** + * 连接数据库。 + * Connect to database. + * + * @access public + * @return void + */ + public function connectDB() + { + global $config, $dbh, $slaveDBH; + if(!isset($config->installed) or !$config->installed) return; + + if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db); + if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB); + } + + /** + * 使用PDO连接数据库。 + * Connect database by PDO. + * + * @param object $params the database params. + * @access public + * @return object|bool + */ + public function connectByPDO($params) + { + if(!isset($params->driver)) self::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, $exit = true); + if(!isset($params->user)) return false; + if($params->driver == 'mysql') + { + $dsn = "mysql:host={$params->host}; port={$params->port}; dbname={$params->name}"; + } + try + { + $dbh = new PDO($dsn, $params->user, $params->password, array(PDO::ATTR_PERSISTENT => $params->persistant)); + $dbh->exec("SET NAMES {$params->encoding}"); + + /* + * 如果系统是Linux,开启仿真预处理和缓冲查询。 + * If run on linux, set emulatePrepare and bufferQuery to true. + **/ + if(!isset($params->emulatePrepare) and PHP_OS == 'Linux') $params->emulatePrepare = true; + if(!isset($params->bufferQuery) and PHP_OS == 'Linux') $params->bufferQuery = true; + + $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + if(isset($params->strictMode) and $params->strictMode == false) $dbh->exec("SET @@sql_mode= ''"); + if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare); + if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery); + + return $dbh; + } + catch (PDOException $exception) + { + self::triggerError($exception->getMessage(), __FILE__, __LINE__, $exit = true); + } + } + + //-------------------- 错误处理方法(Error methods) ------------------// + + /** + * 程序停止时执行的函数。 + * The shutdown handler. + * + * @access public + * @return void + */ + public function shutdown() + { + /* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */ + if(!empty($this->config->debug)) $this->saveSQL(); + + /* + * 发现错误,保存到日志中。 + * If any error occers, save it. + * */ + if(!function_exists('error_get_last')) return; + $error = error_get_last(); + if($error) $this->saveError($error['type'], $error['message'], $error['file'], $error['line']); + } + + /** + * 触发一个错误。 + * Trigger an error. + * + * @param string $message 错误信息 error message + * @param string $file 所在文件 the file error occers + * @param int $line 错误行 the line error occers + * @param bool $exit 是否停止程序 exit the program or not + * @access public + * @return void + */ + public function triggerError($message, $file, $line, $exit = false) + { + /* 设置错误信息(Set the error info) */ + $message = htmlspecialchars($message); + $log = "ERROR: $message in $file on line $line"; + if(isset($_SERVER['SCRIPT_URI'])) $log .= ", request: $_SERVER[SCRIPT_URI]";; + $trace = debug_backtrace(); + extract($trace[0]); + extract($trace[1]); + $log .= ", last called by $file on line $line through function $function.\n"; + + /* 触发错误(Trigger the error) */ + trigger_error($log, $exit ? E_USER_ERROR : E_USER_WARNING); + } + + /** + * 保存错误信息。 + * Save error info. + * + * @param int $level + * @param string $message + * @param string $file + * @param int $line + * @access public + * @return void + */ + public function saveError($level, $message, $file, $line) + { + if(empty($this->config->debug)) return true; + if(!is_dir($this->logRoot)) return true; + if(!is_writable($this->logRoot)) return true; + + /* + * 删除设定时间之前的日志。 + * Delete the log before the set time. + **/ + if(mt_rand(0, 10) == 1) + { + $logDays = isset($this->config->framework->logDays) ? $this->config->framework->logDays : 14; + $dayTime = time() - $logDays * 24 * 3600; + foreach(glob($this->getLogRoot() . '*') as $logFile) + { + if(filemtime($logFile) <= $dayTime) unlink($logFile); + } + } + + /* + * 忽略该错误:Redefining already defined constructor。 + * Skip the error: Redefining already defined constructor. + **/ + if(strpos($message, 'Redefining') !== false) return true; + + /* + * 设置错误信息。 + * Set the error info. + **/ + $errorLog = "\n" . date('H:i:s') . " $message in $file on line $line "; + $errorLog .= "when visiting " . htmlspecialchars($this->getURI()) . "\n"; + + /* + * 为了安全起见,对公网环境隐藏脚本路径。 + * If the ip is pulic, hidden the full path of scripts. + */ + if(!defined('IN_SHELL') and !($this->server->remote_addr == '127.0.0.1' or filter_var($this->server->remote_addr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)) + { + $errorLog = str_replace($this->getBasePath(), '', $errorLog); + } + + /* 保存到日志文件(Save to log file) */ + $errorFile = $this->logRoot . 'php.' . date('Ymd') . '.log.php'; + if(!is_file($errorFile)) file_put_contents($errorFile, "\n"); + + $fh = fopen($errorFile, 'a'); + if($fh) fwrite($fh, strip_tags($errorLog)) and fclose($fh); + + /* + * 如果debug > 1,显示warning, notice级别的错误。 + * If the debug > 1, show warning, notice error. + **/ + if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192) // 8192: E_DEPRECATED + { + if(!empty($this->config->debug) and $this->config->debug > 1) + { + $cmd = "vim +$line $file"; + $size = strlen($cmd); + echo "
$message: ";
+                echo "
"; + } + } + + /* + * 如果是严重错误,停止程序。 + * If error level is serious, die. + * */ + if($level == E_ERROR or $level == E_PARSE or $level == E_CORE_ERROR or $level == E_COMPILE_ERROR or $level == E_USER_ERROR) + { + if(empty($this->config->debug)) die(); + if(PHP_SAPI == 'cli') die($errorLog); + + $htmlError = ""; + $htmlError .= "" . nl2br($errorLog) . ""; + die($htmlError); + } + } + + /** + * 保存sql语句。 + * Save the sql. + * + * @access public + * @return void + */ + public function saveSQL() + { + if(!$this->config->debug) return true; + if(!class_exists('dao')) return; + + $sqlLog = $this->getLogRoot() . 'sql.' . date('Ymd') . '.log.php'; + if(!is_file($sqlLog)) file_put_contents($sqlLog, "\n"); + + $fh = @fopen($sqlLog, 'a'); + if(!$fh) return false; + fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n"); + foreach(dao::$querys as $query) fwrite($fh, " $query\n"); + fwrite($fh, "\n"); + fclose($fh); + } +} + +/** + * config类。 + * The config class. + * + * @package framework + */ +class config +{ + /** + * 设置成员变量,成员可以是'db.user'类似的格式。 + * Set the value of a member. the member can be the format like db.user. + * + * + * set('db.user', 'wwccss'); + * ?> + * + * @param string $key the key of the member + * @param mixed $value the value + * @access public + * @return void + */ + public function set($key, $value) + { + helper::setMember('config', $key, $value); + } +} + +/** + * lang类。 + * The lang class. + * + * @package framework + */ +class language +{ + /** + * 设置成员变量,成员可以是'db.user'类似的格式。 + * Set the value of a member. the member can be the foramt like db.user. + * + * + * set('version', '1.0); + * ?> + * + * @param string $key 成员的键名,可以是father.child的形式。 + * the key of the member, can be father.child + * @param mixed $value the value + * @access public + * @return void + */ + public function set($key, $value) + { + helper::setMember('lang', $key, $value); + } + + /** + * 显示一个成员的值。 + * Show a member. + * + * @param object $obj the object + * @param string $key the key + * @access public + * @return void + */ + public function show($obj, $key) + { + $obj = (array)$obj; + echo isset($obj[$key]) ? $obj[$key] : ''; + } +} + +/** + * 超级对象类,转化超级全局变量。 + * The super object class. + * + * @package framework + */ +class super +{ + /** + * 构造函数,设置超级变量名。 + * Construct, set the var scope. + * + * @param string $scope the scope, can be server, post, get, cookie, session, global + * @access public + * @return void + */ + public function __construct($scope) + { + $this->scope = $scope; + } + + /** + * 设置超级变量的成员值。 + * Set one member value. + * + * @param string the key + * @param mixed $value the value + * @access public + * @return void + */ + public function set($key, $value) + { + if($this->scope == 'post') + { + $_POST[$key] = $value; + } + elseif($this->scope == 'get') + { + $_GET[$key] = $value; + } + elseif($this->scope == 'server') + { + $_SERVER[$key] = $value; + } + elseif($this->scope == 'cookie') + { + $_COOKIE[$key] = $value; + } + elseif($this->scope == 'session') + { + $_SESSION[$key] = $value; + } + elseif($this->scope == 'env') + { + $_ENV[$key] = $value; + } + elseif($this->scope == 'global') + { + $GLOBALS[$key] = $value; + } + } + + /** + * 超级变量的魔术方法,比如用$post->key访问$_POST['key']。 + * The magic get method. + * + * @param string $key the key + * @access public + * @return mixed|bool return the value of the key or false. + */ + public function __get($key) + { + if($this->scope == 'post') + { + if(isset($_POST[$key])) return $_POST[$key]; + return false; + } + elseif($this->scope == 'get') + { + if(isset($_GET[$key])) return $_GET[$key]; + return false; + } + elseif($this->scope == 'server') + { + if($key == 'ajax') return isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? true : false; + if(isset($_SERVER[$key])) return $_SERVER[$key]; + $key = strtoupper($key); + if(isset($_SERVER[$key])) return $_SERVER[$key]; + return false; + } + elseif($this->scope == 'cookie') + { + if(isset($_COOKIE[$key])) return $_COOKIE[$key]; + return false; + } + elseif($this->scope == 'session') + { + if(isset($_SESSION[$key])) return $_SESSION[$key]; + return false; + } + elseif($this->scope == 'env') + { + if(isset($_ENV[$key])) return $_ENV[$key]; + return false; + } + elseif($this->scope == 'global') + { + if(isset($GLOBALS[$key])) return $GLOBALS[$key]; + return false; + } + else + { + return false; + } + } + + /** + * 打印变量的详细结构。 + * Print the structure. + * + * @access public + * @return void + */ + public function a() + { + if($this->scope == 'post') a($_POST); + if($this->scope == 'get') a($_GET); + if($this->scope == 'server') a($_SERVER); + if($this->scope == 'cookie') a($_COOKIE); + if($this->scope == 'session') a($_SESSION); + if($this->scope == 'env') a($_ENV); + if($this->scope == 'global') a($GLOBALS); + } +} diff --git a/framework/control.class.php b/framework/control.class.php new file mode 100644 index 0000000000..848194f79b --- /dev/null +++ b/framework/control.class.php @@ -0,0 +1,23 @@ +requestType != 'GET') + { + $pathInfo = $app->getPathInfo(); + if(!empty($pathInfo)) + { + $dotPos = strrpos($pathInfo, '.'); + if($dotPos) + { + $viewType = substr($pathInfo, $dotPos + 1); + } + else + { + $config->default->view = $config->default->view == 'mhtml' ? 'html' : $config->default->view; + } + } + } + elseif($config->requestType == 'GET') + { + if(isset($_GET[$config->viewVar])) + { + $viewType = $_GET[$config->viewVar]; + } + else + { + /* Set default view when url has not module name. such as only domain. */ + $config->default->view = ($config->default->view == 'mhtml' and isset($_GET[$config->moduleVar])) ? 'html' : $config->default->view; + } + } + if($source and isset($viewType)) return $viewType; + + if(isset($viewType) and strpos($config->views, ',' . $viewType . ',') === false) $viewType = $config->default->view; + return isset($viewType) ? $viewType : $config->default->view; + } + + /** + * Encode json for $.parseJSON + * + * @param array $data + * @param int $options + * @static + * @access public + * @return string + */ + static public function jsonEncode4Parse($data, $options = 0) + { + $json = json_encode($data); + if($options) $json = str_replace(array("'", '"'), array('\u0027', '\u0022'), $json); + + $escapers = array("\\", "/", "\"", "'", "\n", "\r", "\t", "\x08", "\x0c", "\\\\u"); + $replacements = array("\\\\", "\\/", "\\\"", "\'", "\\n", "\\r", "\\t", "\\f", "\\b", "\\u"); + return str_replace($escapers, $replacements, $json); + } + + /** + * Convert encoding. + * + * @param string $string + * @param string $fromEncoding + * @param string $toEncoding + * @static + * @access public + * @return string + */ + static public function convertEncoding($string, $fromEncoding, $toEncoding = 'utf-8') + { + $toEncoding = str_replace('utf8', 'utf-8', $toEncoding); + if(function_exists('mb_convert_encoding')) + { + /* Remove like utf-8//TRANSLIT. */ + $position = strpos($toEncoding, '//'); + if($position !== false) $toEncoding = substr($toEncoding, 0, $position); + + /* Check string encoding. */ + $encoding = strtolower(mb_detect_encoding($string, array('ASCII','UTF-8','GB2312','GBK','BIG5'))); + if($encoding == $toEncoding) return $string; + return mb_convert_encoding($string, $toEncoding, $encoding); + } + elseif(function_exists('iconv')) + { + if($fromEncoding == $toEncoding) return $string; + $convertString = @iconv($fromEncoding, $toEncoding, $string); + /* iconv error then return original. */ + if(!$convertString) return $string; + return $convertString; + } + + return $string; + } +} + +/** + * 检查是否是onlybody模式。 + * Check exist onlybody param. + * + * @access public + * @return void + */ +function isonlybody() +{ + return helper::inOnlyBodyMode(); +} + +/** + * Format time. + * + * @param int $time + * @param string $format + * @access public + * @return void + */ +function formatTime($time, $format = '') +{ + $time = str_replace('0000-00-00', '', $time); + $time = str_replace('00:00:00', '', $time); + if(trim($time) == '') return ; + if($format) return date($format, strtotime($time)); + return trim($time); +} \ No newline at end of file diff --git a/framework/model.class.php b/framework/model.class.php new file mode 100644 index 0000000000..2335d5dd18 --- /dev/null +++ b/framework/model.class.php @@ -0,0 +1,38 @@ +dao->update($table)->set('deleted')->eq(1)->where('id')->eq($id)->exec(); + $object = preg_replace('/^' . preg_quote($this->config->db->prefix) . '/', '', trim($table, '`')); + $this->loadModel('action')->create($object, $id, 'deleted', '', $extra = ACTIONMODEL::CAN_UNDELETED); + } +} diff --git a/framework/router.class.php b/framework/router.class.php new file mode 100755 index 0000000000..536dddf8dd --- /dev/null +++ b/framework/router.class.php @@ -0,0 +1,159 @@ +dbh and !empty($this->config->db->name)) + { + global $config; + if(!isset($config->global)) $config->global = new stdclass(); + $flow = $this->dbh->query('SELECT value FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='common' AND `key`='flow'")->fetch(); + $config->global->flow = $flow ? $flow->value : 'full'; + + try + { + $productProject = $this->dbh->query('SELECT value FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); + } + catch (PDOException $exception) + { + $repairCode = '|1034|1035|1194|1195|1459|'; + $errorInfo = $exception->errorInfo; + $errorCode = $errorInfo[1]; + $errorMsg = $errorInfo[2]; + $message = $exception->getMessage(); + if(strpos($repairCode, "|$errorCode|") !== false or ($errorCode == '1016' and strpos($errorMsg, 'errno: 145') !== false) or strpos($message, 'repair') !== false) + { + if(isset($config->framework->autoRepairTable) and $config->framework->autoRepairTable) + { + header("location: " . $config->webRoot . 'checktable.php'); + exit; + } + } + } + } + + $productCommon = $projectCommon = 0; + if($productProject) + { + $productProject = $productProject->value; + list($productCommon, $projectCommon) = explode('_', $productProject); + } + $lang->productCommon = isset($this->config->productCommonList[$this->clientLang][(int)$productCommon]) ? $this->config->productCommonList[$this->clientLang][(int)$productCommon] : $this->config->productCommonList['zh-cn'][0]; + $lang->projectCommon = isset($this->config->projectCommonList[$this->clientLang][(int)$projectCommon]) ? $this->config->projectCommonList[$this->clientLang][(int)$projectCommon] : $this->config->projectCommonList['zh-cn'][0]; + } + + parent::loadLang($moduleName, $appName); + + /* Merge from the db lang. */ + if($moduleName != 'common' and isset($lang->db->custom[$moduleName])) + { + foreach($lang->db->custom[$moduleName] as $section => $fields) + { + if(isset($lang->{$moduleName}->{$section}[''])) + { + $nullKey = ''; + $nullValue = $lang->{$moduleName}->{$section}[$nullKey]; + } + elseif(isset($lang->{$moduleName}->{$section}[0])) + { + $nullKey = 0; + $nullValue = $lang->{$moduleName}->{$section}[0]; + } + unset($lang->{$moduleName}->{$section}); + + if(isset($nullKey))$lang->{$moduleName}->{$section}[$nullKey] = $nullValue; + foreach($fields as $key => $value) $lang->{$moduleName}->{$section}[$key] = $value; + unset($nullKey); + unset($nullValue); + } + } + return $lang; + } + + /** + * Save error info. + * + * @param int $level + * @param string $message + * @param string $file + * @param int $line + * @access public + * @return void + */ + public function saveError($level, $message, $file, $line) + { + $fatalLevel[E_ERROR] = E_ERROR; + $fatalLevel[E_PARSE] = E_PARSE; + $fatalLevel[E_CORE_ERROR] = E_CORE_ERROR; + $fatalLevel[E_USER_ERROR] = E_USER_ERROR; + if(isset($fatalLevel[$level])) $this->config->debug = true; + parent::saveError($level, $message, $file, $line); + } + + /** + * Alias load module config. + * + * @param string $moduleName + * @param string $appName + * @access public + * @return void + */ + public function loadConfig($moduleName, $appName = '') + { + return parent::loadModuleConfig($moduleName, $appName); + } + + /** + * Export config. + * + * @access public + * @return void + */ + public function exportConfig() + { + ob_start(); + parent::exportConfig(); + $view = ob_get_contents(); + ob_end_clean(); + + $view = json_decode($view); + $view->rand = $this->session->random; + $this->session->set('rand', $this->session->random); + echo json_encode($view); + } +} diff --git a/framework/tests/helper/.101.setmember.onelevel.et b/framework/tests/helper/.101.setmember.onelevel.et new file mode 100644 index 0000000000..5b184f35fa --- /dev/null +++ b/framework/tests/helper/.101.setmember.onelevel.et @@ -0,0 +1,13 @@ +value1.1 +value2 +3 +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) +stdClass Object +( + [key1] => value2.1 +) diff --git a/framework/tests/helper/.110.setmember.multilevel.et b/framework/tests/helper/.110.setmember.multilevel.et new file mode 100644 index 0000000000..ee73e72ede --- /dev/null +++ b/framework/tests/helper/.110.setmember.multilevel.et @@ -0,0 +1,6 @@ +Mary +20 +stdClass Object +( + [postcode] => 10000 +) diff --git a/framework/tests/helper/.201.getwebroot.inbrowse.et b/framework/tests/helper/.201.getwebroot.inbrowse.et new file mode 100644 index 0000000000..618380792d --- /dev/null +++ b/framework/tests/helper/.201.getwebroot.inbrowse.et @@ -0,0 +1,2 @@ +/ +/zentao/ diff --git a/framework/tests/helper/.202.getwebroot.inshell.et b/framework/tests/helper/.202.getwebroot.inshell.et new file mode 100644 index 0000000000..3de39a7763 --- /dev/null +++ b/framework/tests/helper/.202.getwebroot.inshell.et @@ -0,0 +1,8 @@ +http://localhost webRoot is / +http://localhost/ webRoot is / +http://localhost/my/ webRoot is / +http://localhost/my-todo.html webRoot is / +http://localhost/index.php?m=my&f=index webRoot is / +http://localhost/zentao/my/ webRoot is /zentao/ +http://localhost/zentao/my-todo.html webRoot is /zentao/ +http://localhost/zentao/index.php?m=my&f=index webRoot is /zentao/ diff --git a/framework/tests/helper/101.setmember.onelevel.php b/framework/tests/helper/101.setmember.onelevel.php new file mode 100755 index 0000000000..cb7a794639 --- /dev/null +++ b/framework/tests/helper/101.setmember.onelevel.php @@ -0,0 +1,27 @@ +#!/usr/bin/env php +key1 = 'value1'; + +$obj2 = new stdclass(); +$obj2->key1 = 'value2.1'; + +helper::setMember('obj', 'key1', 'value1.1'); // overide the exists key. +helper::setMember('obj', 'key2', 'value2'); // add a new key. +helper::setMember('obj', 'key3', 3); // set an int value. +helper::setMember('obj', 'key4', array(1, 2, 3)); // set an array value. +helper::setMember('obj', 'key5', $obj2); // set an object value. + +echo $obj->key1 . "\n"; +echo $obj->key2 . "\n"; +echo $obj->key3 . "\n"; +print_r($obj->key4); +print_r($obj->key5); diff --git a/framework/tests/helper/110.setmember.multilevel.php b/framework/tests/helper/110.setmember.multilevel.php new file mode 100755 index 0000000000..14d3baf0cd --- /dev/null +++ b/framework/tests/helper/110.setmember.multilevel.php @@ -0,0 +1,22 @@ +#!/usr/bin/env php +user = new stdclass(); +$obj->user->name = 'Tom'; + +helper::setMember('obj', 'user.name', 'Mary'); // overide the exists key. +helper::setMember('obj', 'user.age', 20); // add a child key to an existing key. +helper::setMember('obj', 'home.address', new stdclass()); // add a child key even the parent doesn't exist. +helper::setMember('obj', 'home.address.postcode', '10000'); // three level. + +echo $obj->user->name . "\n"; +echo $obj->user->age . "\n"; +print_r($obj->home->address); diff --git a/framework/tests/helper/201.getwebroot.inbrowse.php b/framework/tests/helper/201.getwebroot.inbrowse.php new file mode 100755 index 0000000000..a630da9b4d --- /dev/null +++ b/framework/tests/helper/201.getwebroot.inbrowse.php @@ -0,0 +1,17 @@ +#!/usr/bin/env php + Date: Fri, 20 Oct 2017 16:24:50 +0800 Subject: [PATCH 12/40] "fix debug level 2" --- module/project/js/common.js | 2 +- module/project/view/task.html.php | 2 +- module/task/js/common.js | 2 +- module/task/js/create.js | 2 +- module/task/js/edit.js | 2 +- module/task/model.php | 16 ++++++++-------- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/module/project/js/common.js b/module/project/js/common.js index 5f3298ac06..24c3c6b85f 100644 --- a/module/project/js/common.js +++ b/module/project/js/common.js @@ -190,4 +190,4 @@ $('.task-toggle').click(function() obj.removeClass('icon-minus').addClass('icon-plus'); } return false; -}); \ No newline at end of file +}); diff --git a/module/project/view/task.html.php b/module/project/view/task.html.php index f9ce51581f..2f2022c87c 100644 --- a/module/project/view/task.html.php +++ b/module/project/view/task.html.php @@ -70,7 +70,7 @@ js::set('browseType', $browseType);
$value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> - children):?> + children)):?> children as $child):?> $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs);?> diff --git a/module/task/js/common.js b/module/task/js/common.js index 9363ddb004..140db4051e 100644 --- a/module/task/js/common.js +++ b/module/task/js/common.js @@ -56,4 +56,4 @@ $('#modalTeam .btn').click(function() $('#teamMember').val(team); $('#estimate').val(time); }) -}) \ No newline at end of file +}) diff --git a/module/task/js/create.js b/module/task/js/create.js index bdb27f8b2f..b9b90827af 100644 --- a/module/task/js/create.js +++ b/module/task/js/create.js @@ -224,4 +224,4 @@ $(".btn[data-toggle='modalTeam']").click(function() { $('#modalTeam').modal('show'); adjustSortBtn(); -}); \ No newline at end of file +}); diff --git a/module/task/js/edit.js b/module/task/js/edit.js index 401c03d80e..72e23de445 100644 --- a/module/task/js/edit.js +++ b/module/task/js/edit.js @@ -88,4 +88,4 @@ $(document).ready(function() $('#teamTr').addClass('hidden'); } }); -}); \ No newline at end of file +}); diff --git a/module/task/model.php b/module/task/model.php index 1dc1000479..e33cbc5b2b 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1082,7 +1082,7 @@ class taskModel extends model $taskList = array_keys($tasks); $children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->in($taskList)->orderBy('id_desc')->fetchGroup('parent'); - if($children) foreach($children as $key => $child) $tasks[$key]->children = $child; + if(!empty($children)) foreach($children as $key => $child) $tasks[$key]->children = $child; $teams = $this->dao->select('*')->from(TABLE_TEAM)->where('task')->in($taskList)->fetchGroup('task'); if($teams) foreach($teams as $key => $team) if(!empty($team)) $tasks[$key]->team = $team; @@ -1362,7 +1362,7 @@ class taskModel extends model foreach($tasks as $task) { $task = $this->processTask($task); - if($task->children) foreach($task->children as $child) + if(!empty($task->children)) foreach($task->children as $child) { $task = $this->processTask($child); } @@ -1393,7 +1393,7 @@ class taskModel extends model /* Story changed or not. */ $task->needConfirm = false; - if($task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion) $task->needConfirm = true; + if(!empty($task->storyStatus) and $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion) $task->needConfirm = true; /* Set product type for task. */ if(isset($task->product)) @@ -1832,17 +1832,17 @@ class taskModel extends model echo ""; break; case 'name': - if(isset($branchGroups[$task->product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . ' '; + if(!empty($task->product) && isset($branchGroups[$task->product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . ' '; if($modulePairs and $task->module) echo "" . $modulePairs[$task->module] . ' '; echo html::a($taskLink, $task->name, null, "style='color: $task->color'"); if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); - if($task->children) echo '    '; + if(!empty($task->children)) echo '    '; break; case 'type': echo $this->lang->task->typeList[$task->type]; break; case 'status': - $storyChanged = ($task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion); + $storyChanged = (!empty($task->storyStatus) and $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion); $storyChanged ? print("{$this->lang->story->changed} ") : print($this->lang->task->statusList[$task->status]); break; case 'estimate': @@ -1900,7 +1900,7 @@ class taskModel extends model echo $this->lang->task->reasonList[$task->closedReason]; break; case 'story': - if($task->storyID) + if(!empty($task->storyID)) { if(!common::printLink('story', 'view', "storyid=$task->storyID", $task->storyTitle)) print $task->storyTitle; } @@ -1933,7 +1933,7 @@ class taskModel extends model common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'edit',"taskID=$task->id", $task, 'list'); - if(!$task->team or !$task->children) common::printIcon('task', 'batchCreate', "project=$task->project&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'list','plus','','','','',$this->lang->task->children); + if(empty($task->team) or empty($task->children)) common::printIcon('task', 'batchCreate', "project=$task->project&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'list','plus','','','','',$this->lang->task->children); break; } echo ''; From c312b64220222a56eb7c67cbdb4ac270cbbbfaf7 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Fri, 20 Oct 2017 16:27:17 +0800 Subject: [PATCH 13/40] =?UTF-8?q?Finish=20task#3253=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=9C=8B=E6=9D=BF=E5=8D=A1=E7=89=87=E5=BC=B9=E5=87=BA=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=9A=84=E4=BD=8D=E7=BD=AE,cost:1=20left:0=20Finish?= =?UTF-8?q?=20task#3243=20=E7=9C=8B=E6=9D=BF=E5=A2=9E=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=AE=BE=E7=BD=AE=E6=98=BE=E7=A4=BA=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E5=8A=9F=E8=83=BD,cost:3=20left:0=20Finish?= =?UTF-8?q?=20task#3242=20=E7=9C=8B=E6=9D=BF=E5=A2=9E=E5=8A=A0=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=B1=95=E5=BC=80/=E6=8A=98=E5=8F=A0=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E9=80=89=E9=A1=B9=E5=8D=A1=E7=9A=84=E5=8A=9F=E8=83=BD?= =?UTF-8?q?,cost:1=20left:0=20Finish=20task#3241=20=E7=9C=8B=E6=9D=BF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=BE=E7=A4=BA/=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E5=B7=B2=E5=85=B3=E9=97=AD=E5=B7=B2=E5=8F=96=E6=B6=88=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=9A=84=E5=8A=9F=E8=83=BD,cost:2=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/group/lang/resource.php | 4 + module/project/config.php | 8 ++ module/project/control.php | 78 +++++++++++++++++++ module/project/css/kanban.css | 40 +++++----- module/project/lang/en.php | 17 +++- module/project/lang/zh-cn.php | 17 +++- module/project/model.php | 33 ++++++++ .../project/view/ajaxkanbansetting.html.php | 62 +++++++++++++++ module/project/view/kanban.html.php | 33 ++++++-- module/task/model.php | 4 +- module/testreport/model.php | 2 +- 11 files changed, 261 insertions(+), 37 deletions(-) create mode 100644 module/project/view/ajaxkanbansetting.html.php diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index a4a188e929..23ef7835c1 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -321,6 +321,8 @@ $lang->resource->project->kanban = 'kanban'; $lang->resource->project->printKanban = 'printKanban'; $lang->resource->project->tree = 'tree'; $lang->resource->project->all = 'all'; +$lang->resource->project->kanbanHideCols = 'kanbanHideCols'; +$lang->resource->project->kanbanColsColor = 'kanbanColsColor'; $lang->project->methodOrder[0] = 'index'; $lang->project->methodOrder[5] = 'view'; @@ -360,6 +362,8 @@ $lang->project->methodOrder[175] = 'kanban'; $lang->project->methodOrder[180] = 'printKanban'; $lang->project->methodOrder[185] = 'tree'; $lang->project->methodOrder[190] = 'all'; +$lang->project->methodOrder[195] = 'kanbanHideCols'; +$lang->project->methodOrder[200] = 'kanbanColsColor'; /* Task. */ $lang->resource->task = new stdclass(); diff --git a/module/project/config.php b/module/project/config.php index ddd4c26299..471dc898f3 100644 --- a/module/project/config.php +++ b/module/project/config.php @@ -99,3 +99,11 @@ $config->printKanban->col['wait'] = 2; $config->printKanban->col['doing'] = 3; $config->printKanban->col['done'] = 4; $config->printKanban->col['closed'] = 5; + +$config->project->kanbanSetting = new stdclass(); +$config->project->kanbanSetting->colorList['wait'] = '#2b529c'; +$config->project->kanbanSetting->colorList['doing'] = '#d2323d'; +$config->project->kanbanSetting->colorList['pause'] = '#e48600'; +$config->project->kanbanSetting->colorList['done'] = '#229f24'; +$config->project->kanbanSetting->colorList['cancel'] = '#333'; +$config->project->kanbanSetting->colorList['closed'] = '#777'; diff --git a/module/project/control.php b/module/project/control.php index 32f1389fab..f4eb10402b 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -1273,6 +1273,13 @@ class project extends control $this->view->orderBy = $orderBy; $this->view->projectID = $projectID; $this->view->project = $project; + + $kanbanSetting = $this->project->getKanbanSetting($projectID); + + $this->view->allCols = $kanbanSetting->allCols; + $this->view->showOption = $kanbanSetting->showOption; + $this->view->colorList = $kanbanSetting->colorList; + $this->display(); } @@ -1992,4 +1999,75 @@ class project extends control { $this->locate($this->createLink('doc', 'objectLibs', "type=project&objectID=$projectID&from=project")); } + + /** + * Kanban setting. + * + * @param int $projectID + * @access public + * @return void + */ + public function ajaxKanbanSetting($projectID) + { + if($_POST) + { + $this->loadModel('setting'); + $data = fixer::input('post')->get(); + if(common::hasPriv('project', 'kanbanHideCols')) + { + $allCols = array(); + if(isset($this->config->project->kanbanSetting->allCols)) + { + $allCols = json_decode($this->config->project->kanbanSetting->allCols, true); + } + $allCols[$projectID] = $data->allCols; + $this->setting->setItem("system.project.kanbanSetting.allCols", json_encode($allCols)); + } + + $account = $this->app->user->account; + $this->setting->setItem("{$account}.project.kanbanSetting.showOption", $data->showOption); + + if(common::hasPriv('project', 'kanbanColsColor')) $this->setting->setItem("system.project.kanbanSetting.colorList", json_encode($data->colorList)); + + die(js::reload('parent.parent')); + } + + $this->app->loadLang('task'); + $kanbanSetting = $this->project->getKanbanSetting($projectID); + + $this->view->allCols = $kanbanSetting->allCols; + $this->view->showOption = $kanbanSetting->showOption; + $this->view->colorList = $kanbanSetting->colorList; + $this->view->projectID = $projectID; + $this->display(); + } + + /** + * Ajax reset kanban setting + * + * @param int $projectID + * @param string $confirm + * @access public + * @return void + */ + public function ajaxResetKanban($projectID, $confirm = 'no') + { + if($confirm != 'yes')die(js::confirm($this->lang->kanbanSetting->noticeReset, inlink('ajaxResetKanban', "projectID=$projectID&confirm=yes"))); + + $this->loadModel('setting'); + + if(common::hasPriv('project', 'kanbanHideCols') and isset($this->config->project->kanbanSetting->allCols)) + { + $allCols = json_decode($this->config->project->kanbanSetting->allCols, true); + unset($allCols[$projectID]); + $this->setting->setItem("system.project.kanbanSetting.allCols", json_encode($allCols)); + } + + $account = $this->app->user->account; + $this->setting->deleteItems("owner={$account}&module=project§ion=kanbanSetting&key=showOption"); + + if(common::hasPriv('project', 'kanbanColsColor')) $this->setting->deleteItems("owner=system&module=project§ion=kanbanSetting&key=colorList"); + + die(js::reload('parent.parent')); + } } diff --git a/module/project/css/kanban.css b/module/project/css/kanban.css index 2e20477f54..cae429cd04 100644 --- a/module/project/css/kanban.css +++ b/module/project/css/kanban.css @@ -1,8 +1,8 @@ -table th.col-closed {width:12%} +table th.col-closed {width:14%} #kanbanHeader th{border: 1px solid #ddd; padding:8px;} -#kanbanHeader .dropdown {display: inline-block; margin-left: 10px;} +#kanbanHeader .dropdown {display: inline-block;} #kanbanHeader .dropdown > a {color: #036} -#kanbanHeader th.col-closed {position: relative} +#kanbanHeader th{position: relative} #kanbanHeader .actions {position: absolute; right: 0; top: 0; padding: 0px; background: #f1f1f1; border-left: 1px solid #ccc} #kanbanHeader .actions .btn {display: block; border-radius: 0; width: 34px; height: 36px; position: relative; text-align: center; padding: 5px 0; color: #036} #kanbanHeader .actions .btn-text {display: none} @@ -39,7 +39,8 @@ table th.col-closed {width:12%} .board .dropdown-menu > a {display: block; padding: 3px 15px; color: #333; font-size: 12px} .board .dropdown-menu > a:hover {background: #f1f1f1; color: #1a53ff; text-decoration: none} .board .dropdown-menu > a.disabled, .board .dropdown-menu > a.disabled:hover, .board .dropdown-menu > a.active {color: #aaa; background: none} -.board .board-footer {display: none; opacity: 0.9} +.board .board-footer {display: none; opacity: 0.6} +.board.inverse .board-footer {display: none; opacity: 0.9} .board.show-info .board-footer {display: block; margin: 0 -6px -3px -6px; padding: 5px; background: rgba(0,0,0,0.07);} .board-footer .board-id {display: inline-block; border: 1px solid #aaa; font-weight: normal; padding: 0px 4px; text-align: center; min-width: 20px; line-height: 16px; height: 17px; margin-right: 10px; font-size: 12px;} .board-bug-closed .board-footer .bug-pri, .board-task-closed .board-footer .task-pri {border-color: #666} @@ -60,13 +61,21 @@ table th.col-closed {width:12%} .story-pri.pri-3 {background-color:#b56a00;} .story-pri.pri-4 {background-color:#795523;} -.board {background: #ddd; color: #444; outline: 1px solid rgba(0,0,0,.1); outline-offset: -1px;} -.board-bug-wait,.board-task-wait {color: #2b529c; background-color: #E8EAF6} -.board-bug-doing,.board-task-doing {color: #D2322D; background-color: #FFEBEE;} -.board-task-pause {color: #fff; background-color: #E48600;} -.board-bug-done,.board-task-done {color: #229F24; background-color: #E8F5E9;} -.board-bug-closed,.board-task-closed {color: #666; background-color: #ccc;} -.board:hover {outline: 1px solid rgba(0,0,0,.3);} +.board {background: #333; color: #f1f1f1; outline: 1px solid rgba(0,0,0,.1); outline-offset: -1px;} +.board:hover {outline: 1px solid rgba(0,0,0,.3); color:#fff} +.board-bug-cancel, .board-task-cancel {background: #333; opacity: 0.7;} +.board-bug-wait, .board-task-wait {background: #2b529c;opacity: 0.7;} +.board-task-pause {background: #E48600;opacity: 0.7;} +.board-bug-doing, .board-task-doing {background: #D2322D;opacity: 0.7;} +.board-bug-done, .board-task-done {background: #229F24;opacity: 0.7;} +.board-bug-closed, .board-task-closed {background: #777; opacity: 0.7;} + +.board-bug-cancel.inverse, .board-task-cancel.inverse {opacity: 1;} +.board-bug-wait.inverse, .board-task-wait.inverse {opacity: 1} +.board-task-pause.inverse {opacity: 1} +.board-bug-doing.inverse, .board-task-doing.inverse {opacity: 1} +.board-bug-done.inverse, .board-task-done.inverse {opacity: 1} +.board-bug-closed.inverse, .board-task-closed.inverse {opacity: 1} .board-story {color: #dd7503; background-color: #FFF3E0;} .board-story.stage-projected {color:#777;} @@ -76,15 +85,6 @@ table th.col-closed {width:12%} .board-story.stage-tested {color:#827717; background-color: #F9FBE7} .board-story.stage-released {color:#9C27B0; background-color: #F3E5F5} -.board.inverse {background: #333; color: #f1f1f1;} -.board.inverse:hover {color: #fff;} -.board-bug-cancel.inverse, .board-task-cancel.inverse {background: #333;} -.board-bug-wait.inverse, .board-task-wait.inverse {background: #2b529c} -.board-task-pause.inverse {background: #E48600} -.board-bug-doing.inverse, .board-task-doing.inverse {background: #D2322D} -.board-bug-done.inverse, .board-task-done.inverse {background: #229F24} -.board-bug-closed.inverse, .board-task-closed.inverse {background: #777;} - .board-story.inverse {background: #dd7503} .board-story.stage-projected.inverse {background-color:#777;} .board-story.stage-developing.inverse {background-color:#D2322D;} diff --git a/module/project/lang/en.php b/module/project/lang/en.php index 449372822c..415eb80747 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -242,9 +242,20 @@ $lang->project->orderList['id_desc'] = "ID Desc"; $lang->project->orderList['stage_asc'] = "Stage Asc"; $lang->project->orderList['stage_desc'] = "Stage Desc"; -$lang->project->kanban = "Kanban"; -$lang->project->printKanban = "Print Kanban"; -$lang->project->bugList = "Bugs"; +$lang->project->kanban = "Kanban"; +$lang->project->kanbanSetting = "Kanban Setting"; +$lang->project->resetKanban = "Reset Setting"; +$lang->project->printKanban = "Print Kanban"; +$lang->project->bugList = "Bugs"; + +$lang->project->kanbanHideCols = 'Kanban hidden has been closed and canceled columns'; +$lang->project->kanbanShowOption = 'Display folding information'; +$lang->project->kanbanColsColor = 'Custom color for Kanban column'; + +$lang->kanbanSetting = new stdclass(); +$lang->kanbanSetting->noticeReset = 'Do you want to restore the Kanban default settings?'; +$lang->kanbanSetting->optionList['0'] = 'Hidden'; +$lang->kanbanSetting->optionList['1'] = 'Show'; $lang->printKanban = new stdclass(); $lang->printKanban->common = 'Print Kanban'; diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index 832032b3a9..f35ba6f90a 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -242,9 +242,20 @@ $lang->project->orderList['id_desc'] = "需求ID倒序"; $lang->project->orderList['stage_asc'] = "需求阶段正序"; $lang->project->orderList['stage_desc'] = "需求阶段倒序"; -$lang->project->kanban = "看板"; -$lang->project->printKanban = "打印看板"; -$lang->project->bugList = "Bug列表"; +$lang->project->kanban = "看板"; +$lang->project->kanbanSetting = "看板设置"; +$lang->project->resetKanban = "恢复默认"; +$lang->project->printKanban = "打印看板"; +$lang->project->bugList = "Bug列表"; + +$lang->project->kanbanHideCols = '看板隐藏已关闭、已取消列'; +$lang->project->kanbanShowOption = '显示折叠信息'; +$lang->project->kanbanColsColor = '看板列自定义颜色'; + +$lang->kanbanSetting = new stdclass(); +$lang->kanbanSetting->noticeReset = '是否恢复看板默认设置?'; +$lang->kanbanSetting->optionList['0'] = '隐藏'; +$lang->kanbanSetting->optionList['1'] = '显示'; $lang->printKanban = new stdclass(); $lang->printKanban->common = '看板打印'; diff --git a/module/project/model.php b/module/project/model.php index 9bdb76f892..fe0ffdee9b 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -2124,6 +2124,39 @@ class projectModel extends model return json_decode($prevKanbans, true); } + /** + * Get kanban setting. + * + * @param int $projectID + * @access public + * @return object + */ + public function getKanbanSetting($projectID) + { + $allCols = '1'; + $showOption = '0'; + if(isset($this->config->project->kanbanSetting->allCols)) + { + $allColPairs = json_decode($this->config->project->kanbanSetting->allCols, true); + if(isset($allColPairs[$projectID])) $allCols = $allColPairs[$projectID]; + } + + if(isset($this->config->project->kanbanSetting->showOption)) + { + $showOption = $this->config->project->kanbanSetting->showOption; + } + + $colorList = $this->config->project->kanbanSetting->colorList; + if(!is_array($colorList)) $colorList = json_decode($colorList, true); + + $kanbanSetting = new stdclass(); + $kanbanSetting->allCols = $allCols; + $kanbanSetting->showOption = $showOption; + $kanbanSetting->colorList = $colorList; + + return $kanbanSetting; + } + /** * Build burn data. * diff --git a/module/project/view/ajaxkanbansetting.html.php b/module/project/view/ajaxkanbansetting.html.php new file mode 100644 index 0000000000..00d64aa0d9 --- /dev/null +++ b/module/project/view/ajaxkanbansetting.html.php @@ -0,0 +1,62 @@ + + * @package project + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+ project->kanbanSetting;?> +
+
+
+
+
diff --git a/module/product/view/browsedata.html.php b/module/product/view/browsedata.html.php deleted file mode 100644 index fe73a5445a..0000000000 --- a/module/product/view/browsedata.html.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @package product - * @version $Id$ - * @link http://www.zentao.net - */ -?> - - - - - - - - - - - - - - - - - - - - - - $story):?> - createLink('story', 'view', "storyID=$story->id"); - $canView = common::hasPriv('story', 'view'); - ?> - - - - - - - - - - - - - - - - - - diff --git a/module/product/view/datatabledata.html.php b/module/product/view/datatabledata.html.php deleted file mode 100644 index 5d1aacadda..0000000000 --- a/module/product/view/datatabledata.html.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @package product - * @version $Id$ - * @link http://www.zentao.net - */ -?> -datatable->getSetting('product'); -$widths = $this->datatable->setFixedFieldWidth($setting); -extract($widths); -?> -
idAB);?> priAB);?> story->title);?>story->planAB);?>openedByAB);?>assignedToAB);?>story->estimateAB);?>statusAB);?>story->stageAB);?>story->taskCountAB;?>story->bugCountAB;?>story->caseCountAB;?>actions;?>
- - id)): sprintf('%03d', $story->id);?> - story->priList, $story->pri, $story->pri);?>'>story->priList, $story->pri, $story->pri)?> - branch) echo "{$branches[$story->branch]}"?> - module) echo "{$modulePairs[$story->module]} "?> - title, null, "style='color: $story->color'");?> - planTitle;?>openedBy, $story->openedBy);?>assignedTo, $story->assignedTo);?>estimate;?>story->statusList[$story->status];?> - id]) ? " class='popoverStage' data-toggle='popover' data-placement='bottom' data-target='\$next'" : '') . "'>"; - echo $lang->story->stageList[$story->stage]; - if(isset($storyStages[$story->id])) echo ""; - echo ''; - if(isset($storyStages[$story->id])) - { - echo "
"; - foreach($storyStages[$story->id] as $storyBranch => $storyStage) echo $branches[$storyBranch] . ": " . $lang->story->stageList[$storyStage->stage] . '
'; - echo "
"; - } - ?> -
- id}"; - common::printIcon('story', 'change', $vars, $story, 'list', 'random'); - common::printIcon('story', 'review', $vars, $story, 'list', 'review'); - common::printIcon('story', 'close', $vars, $story, 'list', 'off', '', 'iframe', true); - common::printIcon('story', 'edit', $vars, $story, 'list', 'pencil'); - if($this->config->global->flow != 'onlyStory') - { - common::printIcon('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap'); - } - ?> -
- - $value) $this->datatable->printHead($value, $orderBy, $vars);?> - - - - - $value) $this->story->printCell($value, $story, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases);?> - - - diff --git a/module/project/view/datatabledata.html.php b/module/project/view/datatabledata.html.php deleted file mode 100644 index ad92507c5d..0000000000 --- a/module/project/view/datatabledata.html.php +++ /dev/null @@ -1,39 +0,0 @@ - - * @package project - * @version $Id$ - * @link http://www.zentao.net - */ -?> -datatable->getSetting('project'); -$widths = $this->datatable->setFixedFieldWidth($setting); -$columns = 0; -extract($widths); -?> -
- - $value) - { - if($value->show) - { - $this->datatable->printHead($value, $orderBy, $vars); - $columns++; - } - } - ?> - - - - - $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> - - - diff --git a/module/project/view/task.html.php b/module/project/view/task.html.php index 7dc32f6935..fead5bc024 100644 --- a/module/project/view/task.html.php +++ b/module/project/view/task.html.php @@ -45,10 +45,33 @@ js::set('browseType', $browseType); moduleName . ucfirst($this->methodName); $useDatatable = (isset($this->config->datatable->$datatableId->mode) and $this->config->datatable->$datatableId->mode == 'datatable'); - $file2Include = $useDatatable ? dirname(__FILE__) . '/datatabledata.html.php' : dirname(__FILE__) . '/taskdata.html.php'; $vars = "projectID=$project->id&status=$status&parma=$param&orderBy=%s&recTotal=$recTotal&recPerPage=$recPerPage"; - include $file2Include; + + if($useDatatable) include '../../common/view/datatable.html.php'; + $setting = $this->datatable->getSetting('project'); + $widths = $this->datatable->setFixedFieldWidth($setting); + $columns = 0; ?> +
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='taskIDList[]'> + + $value) + { + if($value->show) + { + $this->datatable->printHead($value, $orderBy, $vars); + $columns++; + } + } + ?> + + + + + $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> + + + cookie->windowWidth > $this->config->wideSize ? 15 : 13) - ($project->type == 'sprint' ? 0 : 1);?> diff --git a/module/project/view/taskdata.html.php b/module/project/view/taskdata.html.php deleted file mode 100644 index 152b51c3b2..0000000000 --- a/module/project/view/taskdata.html.php +++ /dev/null @@ -1,130 +0,0 @@ - - * @package project - * @version $Id$ - * @link http://www.zentao.net - */ -?> - - -
- - - - - - - - - cookie->windowWidth > $this->config->wideSize):?> - - - - - - - - cookie->windowWidth > $this->config->wideSize):?> - - - - - - - - - - type == 'sprint'): ?> - - - - - - - - - - - assignedTo == $app->user->account ? 'style=color:red' : ''; ?> - - - - - - - - cookie->windowWidth > $this->config->wideSize):?> - - - - - - - cookie->windowWidth > $this->config->wideSize):?> - - - - - - - - type == 'sprint') - { - echo ''; - } - ?> - - - - - diff --git a/module/story/config.php b/module/story/config.php index 5da07f186a..b8ced4326f 100644 --- a/module/story/config.php +++ b/module/story/config.php @@ -47,7 +47,7 @@ $config->story->custom->batchCreateFields = 'module,plan,spec,pri,estimate,revie $config->story->custom->batchEditFields = 'branch,module,plan,estimate,pri,source,stage,closedBy,closedReason'; $config->story->datatable = new stdclass(); -$config->story->datatable->defaultField = array('id', 'pri', 'title', 'plan', 'source', 'openedBy', 'assignedTo', 'estimate', 'status', 'stage', 'taskCount', 'bugCount', 'caseCount', 'actions'); +$config->story->datatable->defaultField = array('id', 'pri', 'title', 'plan', 'openedBy', 'assignedTo', 'estimate', 'status', 'stage', 'taskCount', 'bugCount', 'caseCount', 'actions'); $config->story->datatable->fieldList['id']['title'] = 'idAB'; $config->story->datatable->fieldList['id']['fixed'] = 'left'; @@ -91,37 +91,19 @@ $config->story->datatable->fieldList['sourceNote']['required'] = 'no'; $config->story->datatable->fieldList['status']['title'] = 'statusAB'; $config->story->datatable->fieldList['status']['fixed'] = 'no'; -$config->story->datatable->fieldList['status']['width'] = '80'; +$config->story->datatable->fieldList['status']['width'] = '50'; $config->story->datatable->fieldList['status']['required'] = 'no'; $config->story->datatable->fieldList['estimate']['title'] = 'estimateAB'; $config->story->datatable->fieldList['estimate']['fixed'] = 'no'; -$config->story->datatable->fieldList['estimate']['width'] = '80'; +$config->story->datatable->fieldList['estimate']['width'] = '50'; $config->story->datatable->fieldList['estimate']['required'] = 'no'; $config->story->datatable->fieldList['stage']['title'] = 'stageAB'; $config->story->datatable->fieldList['stage']['fixed'] = 'no'; -$config->story->datatable->fieldList['stage']['width'] = '90'; +$config->story->datatable->fieldList['stage']['width'] = '70'; $config->story->datatable->fieldList['stage']['required'] = 'no'; -$config->story->datatable->fieldList['taskCount']['title'] = 'taskCount'; -$config->story->datatable->fieldList['taskCount']['fixed'] = 'no'; -$config->story->datatable->fieldList['taskCount']['width'] = '90'; -$config->story->datatable->fieldList['taskCount']['required'] = 'no'; -$config->story->datatable->fieldList['taskCount']['sort'] = 'no'; - -$config->story->datatable->fieldList['bugCount']['title'] = 'bugCount'; -$config->story->datatable->fieldList['bugCount']['fixed'] = 'no'; -$config->story->datatable->fieldList['bugCount']['width'] = '90'; -$config->story->datatable->fieldList['bugCount']['required'] = 'no'; -$config->story->datatable->fieldList['bugCount']['sort'] = 'no'; - -$config->story->datatable->fieldList['caseCount']['title'] = 'caseCount'; -$config->story->datatable->fieldList['caseCount']['fixed'] = 'no'; -$config->story->datatable->fieldList['caseCount']['width'] = '90'; -$config->story->datatable->fieldList['caseCount']['required'] = 'no'; -$config->story->datatable->fieldList['caseCount']['sort'] = 'no'; - $config->story->datatable->fieldList['openedBy']['title'] = 'openedByAB'; $config->story->datatable->fieldList['openedBy']['fixed'] = 'no'; $config->story->datatable->fieldList['openedBy']['width'] = '80'; @@ -187,6 +169,28 @@ $config->story->datatable->fieldList['version']['fixed'] = 'no'; $config->story->datatable->fieldList['version']['width'] = '60'; $config->story->datatable->fieldList['version']['required'] = 'no'; +global $lang; +$config->story->datatable->fieldList['taskCount']['title'] = 'T'; +$config->story->datatable->fieldList['taskCount']['fixed'] = 'no'; +$config->story->datatable->fieldList['taskCount']['width'] = '30'; +$config->story->datatable->fieldList['taskCount']['required'] = 'no'; +$config->story->datatable->fieldList['taskCount']['sort'] = 'no'; +$config->story->datatable->fieldList['taskCount']['name'] = $lang->story->taskCount; + +$config->story->datatable->fieldList['bugCount']['title'] = 'B'; +$config->story->datatable->fieldList['bugCount']['fixed'] = 'no'; +$config->story->datatable->fieldList['bugCount']['width'] = '30'; +$config->story->datatable->fieldList['bugCount']['required'] = 'no'; +$config->story->datatable->fieldList['bugCount']['sort'] = 'no'; +$config->story->datatable->fieldList['bugCount']['name'] = $lang->story->bugCount; + +$config->story->datatable->fieldList['caseCount']['title'] = 'C'; +$config->story->datatable->fieldList['caseCount']['fixed'] = 'no'; +$config->story->datatable->fieldList['caseCount']['width'] = '30'; +$config->story->datatable->fieldList['caseCount']['required'] = 'no'; +$config->story->datatable->fieldList['caseCount']['sort'] = 'no'; +$config->story->datatable->fieldList['caseCount']['name'] = $lang->story->caseCount; + $config->story->datatable->fieldList['actions']['title'] = 'actions'; $config->story->datatable->fieldList['actions']['fixed'] = 'right'; $config->story->datatable->fieldList['actions']['width'] = '140'; diff --git a/module/task/config.php b/module/task/config.php index eb91526364..7e9ca2a596 100644 --- a/module/task/config.php +++ b/module/task/config.php @@ -58,7 +58,7 @@ if($config->global->flow == 'onlyTask') } $config->task->datatable = new stdclass(); -$config->task->datatable->defaultField = array('id', 'pri', 'name', 'status', 'deadline', 'openedDate', 'assignedTo', 'finishedBy', 'finishedDate', 'estimate', 'consumed', 'left', 'story', 'actions'); +$config->task->datatable->defaultField = array('id', 'pri', 'name', 'status', 'deadline', 'openedDate', 'assignedTo', 'finishedBy', 'estimate', 'consumed', 'left', 'story', 'actions'); $config->task->datatable->fieldList['id']['title'] = 'idAB'; $config->task->datatable->fieldList['id']['fixed'] = 'left'; diff --git a/module/testcase/config.php b/module/testcase/config.php index d0bd0f68e2..40c08e6a09 100644 --- a/module/testcase/config.php +++ b/module/testcase/config.php @@ -91,9 +91,14 @@ $config->testcase->datatable->fieldList['title']['fixed'] = 'left'; $config->testcase->datatable->fieldList['title']['width'] = 'auto'; $config->testcase->datatable->fieldList['title']['required'] = 'yes'; +$config->testcase->datatable->fieldList['branch']['title'] = 'branch'; +$config->testcase->datatable->fieldList['branch']['fixed'] = 'left'; +$config->testcase->datatable->fieldList['branch']['width'] = '100'; +$config->testcase->datatable->fieldList['branch']['required'] = 'no'; + $config->testcase->datatable->fieldList['type']['title'] = 'type'; $config->testcase->datatable->fieldList['type']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['type']['width'] = '90'; +$config->testcase->datatable->fieldList['type']['width'] = '80'; $config->testcase->datatable->fieldList['type']['required'] = 'no'; $config->testcase->datatable->fieldList['stage']['title'] = 'stage'; @@ -101,11 +106,6 @@ $config->testcase->datatable->fieldList['stage']['fixed'] = 'no'; $config->testcase->datatable->fieldList['stage']['width'] = '110'; $config->testcase->datatable->fieldList['stage']['required'] = 'no'; -$config->testcase->datatable->fieldList['status']['title'] = 'statusAB'; -$config->testcase->datatable->fieldList['status']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['status']['width'] = '80'; -$config->testcase->datatable->fieldList['status']['required'] = 'no'; - $config->testcase->datatable->fieldList['precondition']['title'] = 'precondition'; $config->testcase->datatable->fieldList['precondition']['fixed'] = 'no'; $config->testcase->datatable->fieldList['precondition']['width'] = '120'; @@ -116,6 +116,11 @@ $config->testcase->datatable->fieldList['keywords']['fixed'] = 'no'; $config->testcase->datatable->fieldList['keywords']['width'] = '100'; $config->testcase->datatable->fieldList['keywords']['required'] = 'no'; +$config->testcase->datatable->fieldList['assignedTo']['title'] = 'assignedTo'; +$config->testcase->datatable->fieldList['assignedTo']['fixed'] = 'no'; +$config->testcase->datatable->fieldList['assignedTo']['width'] = '80'; +$config->testcase->datatable->fieldList['assignedTo']['required'] = 'no'; + $config->testcase->datatable->fieldList['openedBy']['title'] = 'openedByAB'; $config->testcase->datatable->fieldList['openedBy']['fixed'] = 'no'; $config->testcase->datatable->fieldList['openedBy']['width'] = '80'; @@ -143,7 +148,7 @@ $config->testcase->datatable->fieldList['lastRunner']['required'] = 'no'; $config->testcase->datatable->fieldList['lastRunDate']['title'] = 'lastRunDate'; $config->testcase->datatable->fieldList['lastRunDate']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['lastRunDate']['width'] = '90'; +$config->testcase->datatable->fieldList['lastRunDate']['width'] = '120'; $config->testcase->datatable->fieldList['lastRunDate']['required'] = 'no'; $config->testcase->datatable->fieldList['lastRunResult']['title'] = 'lastRunResult'; @@ -151,6 +156,11 @@ $config->testcase->datatable->fieldList['lastRunResult']['fixed'] = 'no'; $config->testcase->datatable->fieldList['lastRunResult']['width'] = '80'; $config->testcase->datatable->fieldList['lastRunResult']['required'] = 'no'; +$config->testcase->datatable->fieldList['status']['title'] = 'statusAB'; +$config->testcase->datatable->fieldList['status']['fixed'] = 'no'; +$config->testcase->datatable->fieldList['status']['width'] = '100'; +$config->testcase->datatable->fieldList['status']['required'] = 'no'; + $config->testcase->datatable->fieldList['lastEditedBy']['title'] = 'lastEditedBy'; $config->testcase->datatable->fieldList['lastEditedBy']['fixed'] = 'no'; $config->testcase->datatable->fieldList['lastEditedBy']['width'] = '80'; @@ -171,31 +181,29 @@ $config->testcase->datatable->fieldList['story']['fixed'] = 'no'; $config->testcase->datatable->fieldList['story']['width'] = '90'; $config->testcase->datatable->fieldList['story']['required'] = 'no'; +$config->testcase->datatable->fieldList['bugs']['title'] = 'B'; +$config->testcase->datatable->fieldList['bugs']['fixed'] = 'no'; +$config->testcase->datatable->fieldList['bugs']['width'] = '30'; +$config->testcase->datatable->fieldList['bugs']['required'] = 'no'; +$config->testcase->datatable->fieldList['bugs']['sort'] = 'no'; +$config->testcase->datatable->fieldList['bugs']['name'] = $lang->testcase->bugs; + +$config->testcase->datatable->fieldList['results']['title'] = 'R'; +$config->testcase->datatable->fieldList['results']['fixed'] = 'no'; +$config->testcase->datatable->fieldList['results']['width'] = '30'; +$config->testcase->datatable->fieldList['results']['required'] = 'no'; +$config->testcase->datatable->fieldList['results']['sort'] = 'no'; +$config->testcase->datatable->fieldList['results']['name'] = $lang->testcase->results; + +$config->testcase->datatable->fieldList['stepNumber']['title'] = 'S'; +$config->testcase->datatable->fieldList['stepNumber']['fixed'] = 'no'; +$config->testcase->datatable->fieldList['stepNumber']['width'] = '30'; +$config->testcase->datatable->fieldList['stepNumber']['required'] = 'no'; +$config->testcase->datatable->fieldList['stepNumber']['sort'] = 'no'; +$config->testcase->datatable->fieldList['stepNumber']['name'] = $lang->testcase->stepNumber; + $config->testcase->datatable->fieldList['actions']['title'] = 'actions'; $config->testcase->datatable->fieldList['actions']['fixed'] = 'right'; $config->testcase->datatable->fieldList['actions']['width'] = '140'; $config->testcase->datatable->fieldList['actions']['required'] = 'yes'; $config->testcase->datatable->fieldList['actions']['sort'] = 'no'; - -$config->testcase->datatable->fieldList['branch']['title'] = 'branch'; -$config->testcase->datatable->fieldList['branch']['fixed'] = 'left'; -$config->testcase->datatable->fieldList['branch']['width'] = '100'; -$config->testcase->datatable->fieldList['branch']['required'] = 'no'; - -$config->testcase->datatable->fieldList['bugs']['title'] = 'bugs'; -$config->testcase->datatable->fieldList['bugs']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['bugs']['width'] = '80'; -$config->testcase->datatable->fieldList['bugs']['required'] = 'no'; -$config->testcase->datatable->fieldList['bugs']['sort'] = 'no'; - -$config->testcase->datatable->fieldList['results']['title'] = 'results'; -$config->testcase->datatable->fieldList['results']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['results']['width'] = '80'; -$config->testcase->datatable->fieldList['results']['required'] = 'no'; -$config->testcase->datatable->fieldList['results']['sort'] = 'no'; - -$config->testcase->datatable->fieldList['stepNumber']['title'] = 'stepNumber'; -$config->testcase->datatable->fieldList['stepNumber']['fixed'] = 'no'; -$config->testcase->datatable->fieldList['stepNumber']['width'] = '80'; -$config->testcase->datatable->fieldList['stepNumber']['required'] = 'no'; -$config->testcase->datatable->fieldList['stepNumber']['sort'] = 'no'; diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index 003eaa50a9..44fbe3fda3 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -43,14 +43,39 @@ js::set('branch', $branch); moduleName . ucfirst($this->methodName); $useDatatable = (isset($this->config->datatable->$datatableId->mode) and $this->config->datatable->$datatableId->mode == 'datatable'); - $file2Include = $useDatatable ? dirname(__FILE__) . '/datatabledata.html.php' : dirname(__FILE__) . '/browsedata.html.php'; $vars = "productID=$productID&branch=$branch&browseType=$browseType¶m=$param&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; - include $file2Include; + + if($useDatatable) include '../../common/view/datatable.html.php'; + if(!$useDatatable) include '../../common/view/tablesorter.html.php'; + $setting = $this->datatable->getSetting('testcase'); + $widths = $this->datatable->setFixedFieldWidth($setting); + $columns = 0; ?> +
idAB);?> priAB);?> task->name);?> statusAB);?> task->deadlineAB);?> task->openedDateAB);?> task->assignedToAB);?> task->finishedByAB);?> task->finishedDateAB);?> task->estimateAB);?> task->consumedAB);?> task->leftAB);?>task->progess;?>task->story); ?>actions;?>
- - id", sprintf('%03d', $task->id))) printf('%03d', $task->id);?> - task->priList, $task->pri, $task->pri)?>'>pri == '0' ? '' : zget($lang->task->priList, $task->pri, $task->pri);?> - product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . '';?> - module) echo "" . $modulePairs[$task->module] . '';?> - id", $task->name, null, "style='color: $task->color'")) echo $task->name; - if($task->fromBug) echo html::a($this->createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); - ?> - - storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion); - if($storyChanged) - { - echo "({$lang->story->changed} "; - echo html::a($this->createLink('task', 'confirmStoryChange', "taskID=$task->id"), $lang->confirm, 'hiddenwin'); - echo ")"; - } - else - { - echo $lang->task->statusList[$task->status]; - } - ?> - deadline, 0, 4) > 0) echo substr($task->deadline, 5, 6);?>openedDate, 5, 6);?>>assignedTo == 'closed' ? 'Closed' : zget($users, $task->assignedTo, $task->assignedTo);?>finishedBy, $task->finishedBy);?>finishedDate, 5, 6);?>estimate;?>consumed;?>left;?>progess?>%'; - if($task->storyID) - { - if(!common::printLink('story', 'view', "storyid=$task->storyID", $task->storyTitle)) print $task->storyTitle; - } - echo ' - project&taskID=$task->id", $task, 'list', '', '', 'iframe', true); - common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); - - common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true); - if($browseType == 'needconfirm') - { - $lang->task->confirmStoryChange = $lang->confirm; - common::printIcon('task', 'confirmStoryChange', "taskid=$task->id", '', 'list', '', 'hiddenwin'); - } - common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); - common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); - common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list'); - ?> -
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> + + + $value) + { + if($value->show) + { + $this->datatable->printHead($value, $orderBy, $vars); + $columns ++; + } + } + ?> + + + + + + $value) $this->testcase->printCell($value, $case, $users, $branches, $modulePairs, $browseType);?> + + + - - - + @@ -195,6 +195,29 @@
+
diff --git a/module/testcase/view/browsedata.html.php b/module/testcase/view/browsedata.html.php deleted file mode 100644 index 121fbd0548..0000000000 --- a/module/testcase/view/browsedata.html.php +++ /dev/null @@ -1,99 +0,0 @@ - - * @package testcase - * @version $Id$ - * @link http://www.zentao.net - */ -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - id&version=$case->version");?> - - - - - - - - - - - - - - - - - - - - - - diff --git a/module/testcase/view/datatabledata.html.php b/module/testcase/view/datatabledata.html.php deleted file mode 100644 index 01713084e1..0000000000 --- a/module/testcase/view/datatabledata.html.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @package testcase - * @version $Id$ - * @link http://www.zentao.net - */ -?> -datatable->getSetting('testcase'); -$widths = $this->datatable->setFixedFieldWidth($setting); -extract($widths); -?> -
idAB);?> priAB);?> testcase->title);?> testcase->story);?> actions;?> typeAB);?> openedByAB);?> testtask->lastRunAccount);?> testtask->lastRunTime);?> testtask->lastRunResult);?> statusAB);?> testcase->bugsAB;?> testcase->resultsAB;?> testcase->stepNumberAB;?> {sorter:false}'>actions;?>
- - id));?> - testcase->priList, $case->pri, $case->pri)?>'>testcase->priList, $case->pri, $case->pri);?> - branch) echo "{$branches[$case->branch]} "?> - module) echo "{$modulePairs[$case->module]} "?> - title, null, "style='color: $case->color'");?> - createLink('story', 'view', "storyID=$case->story"), $case->storyTitle, '_blank');?>testcase->confirmStoryChange = $lang->confirm; common::printIcon('testcase', 'confirmStoryChange', "caseID=$case->id", '', 'list', '', 'hiddenwin');?>testcase->typeList[$case->type];?>openedBy];?>lastRunner];?>lastRunDate)) echo date(DT_MONTHTIME1, strtotime($case->lastRunDate));?>lastRunResult) echo $lang->testcase->resultList[$case->lastRunResult];?> - needconfirm) - { - echo "({$lang->story->changed} "; - echo html::a($this->createLink('testcase', 'confirmStoryChange', "caseID=$case->id"), $lang->confirm, 'hiddenwin'); - echo ")"; - } - else - { - echo $lang->testcase->statusList[$case->status]; - } - ?> - bugs) ? html::a(inlink('bugs', "runID=0&caseID={$case->id}"), $case->bugs, '', "class='iframe'") : $case->bugs;?>results) ? html::a($this->createLink('testtask', 'results', "runID=0&caseID={$case->id}"), $case->results, '', "class='iframe'") : $case->results;?>stepNumber;?> - id&version=$case->version", $case, 'list', 'play', '', 'runCase iframe', false, "data-width='95%'"); - common::printIcon('testtask', 'results', "runID=0&caseID=$case->id", $case, 'list', 'list-alt', '', 'results iframe', false, "data-width='95%'"); - if($config->testcase->needReview or !empty($config->testcase->forceReview)) common::printIcon('testcase', 'review', "caseID=$case->id", $case, 'list', 'review', '', 'iframe'); - common::printIcon('testcase', 'edit', "caseID=$case->id", $case, 'list'); - common::printIcon('testcase', 'create', "productID=$case->product&branch=$case->branch&moduleID=$case->module&from=testcase¶m=$case->id", $case, 'list', 'copy'); - - if(common::hasPriv('testcase', 'delete', $case)) - { - $deleteURL = $this->createLink('testcase', 'delete', "caseID=$case->id&confirm=yes"); - echo html::a("javascript:ajaxDelete(\"$deleteURL\",\"caseList\",confirmDelete)", '', '', "title='{$lang->testcase->delete}' class='btn-icon'"); - } - - common::printIcon('testcase', 'createBug', "product=$case->product&branch=$case->branch&extra=caseID=$case->id,version=$case->version,runID=", $case, 'list', 'bug', '', 'iframe',false,"data-width='95%'"); - ?> -
- - $value) $this->datatable->printHead($value, $orderBy, $vars);?> - - - - - $value) $this->testcase->printCell($value, $case, $users, $branches, $modulePairs, $browseType);?> - - - diff --git a/module/testtask/config.php b/module/testtask/config.php index e56adeac74..130f031c8d 100644 --- a/module/testtask/config.php +++ b/module/testtask/config.php @@ -15,4 +15,4 @@ $config->testtask->editor->block = array('id' => 'comment', 'tools' => 'simple $config->testtask->editor->activate= array('id' => 'comment', 'tools' => 'simpleTools'); $config->testtask->datatable = new stdclass(); -$config->testtask->datatable->defaultField = array('id', 'pri', 'title', 'type', 'assignedTo', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'actions'); +$config->testtask->datatable->defaultField = array('id', 'pri', 'title', 'type', 'assignedTo', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'stepNumber','actions'); diff --git a/module/testtask/view/cases.html.php b/module/testtask/view/cases.html.php index 997fee9970..05b6d970a6 100644 --- a/module/testtask/view/cases.html.php +++ b/module/testtask/view/cases.html.php @@ -37,17 +37,47 @@ var moduleID = ''; $vars = "taskID=$task->id&browseType=$browseType¶m=$param&orderBy=%s&recToal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; $datatableId = $this->moduleName . ucfirst($this->methodName); $useDatatable = (isset($this->config->datatable->$datatableId->mode) and $this->config->datatable->$datatableId->mode == 'datatable'); - $file2Include = $useDatatable ? dirname(__FILE__) . '/datatabledata.html.php' : dirname(__FILE__) . '/casesdata.html.php'; $canBatchEdit = common::hasPriv('testcase', 'batchEdit'); $canBatchAssign = common::hasPriv('testtask', 'batchAssign'); $canBatchRun = common::hasPriv('testtask', 'batchRun'); $hasCheckbox = ($canBatchEdit or $canBatchAssign or $canBatchRun); - include $file2Include; + + if($useDatatable) include '../../common/view/datatable.html.php'; + if(!$useDatatable) include '../../common/view/tablesorter.html.php'; + + $this->config->testcase->datatable->defaultField = $this->config->testtask->datatable->defaultField; + $this->config->testcase->datatable->fieldList['actions']['width'] = '100'; + + $setting = $this->datatable->getSetting('testtask'); + $widths = $this->datatable->setFixedFieldWidth($setting); + $columns = 0; ?> +
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> + + + $value) + { + if($value->show) + { + $this->datatable->printHead($value, $orderBy, $vars); + $columns ++; + } + } + ?> + + + + + + $value) $this->testtask->printCell($value, $run, $users, $task, $branches);?> + + + - + ' id='teamTr'> + + + @@ -168,5 +180,56 @@ +
+
diff --git a/module/testtask/view/casesdata.html.php b/module/testtask/view/casesdata.html.php deleted file mode 100644 index a172a05b70..0000000000 --- a/module/testtask/view/casesdata.html.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @package testtask - * @version $Id$ - * @link http://www.zentao.net - */ -?> - - id&browseType=$browseType¶m=$param&orderBy=%s&recToal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/module/testtask/view/datatabledata.html.php b/module/testtask/view/datatabledata.html.php deleted file mode 100644 index f4264a1116..0000000000 --- a/module/testtask/view/datatabledata.html.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @package testtask - * @version $Id$ - * @link http://www.zentao.net - */ -?> -config->testcase->datatable->defaultField = $this->config->testtask->datatable->defaultField; -$this->config->testcase->datatable->fieldList['assignedTo']['title'] = 'assignedTo'; -$this->config->testcase->datatable->fieldList['assignedTo']['fixed'] = 'no'; -$this->config->testcase->datatable->fieldList['assignedTo']['width'] = '80'; -$this->config->testcase->datatable->fieldList['assignedTo']['required'] = 'no'; -$this->config->testcase->datatable->fieldList['actions']['width'] = '100'; - -$setting = $this->datatable->getSetting('testtask'); -$widths = $this->datatable->setFixedFieldWidth($setting); -extract($widths); -?> -
idAB);?> priAB);?> testcase->title);?> testcase->type);?> testtask->assignedTo);?> testtask->lastRunAccount);?> testtask->lastRunTime);?> testtask->lastRunResult);?> statusAB);?> testcase->bugsAB;?> testcase->resultsAB;?> testcase->stepNumberAB;?>actions;?>
- - - - case);?> - testcase->priList, $run->pri, $run->pri)?>'>testcase->priList, $run->pri, $run->pri)?> - branch) echo "{$branches[$run->branch]}"?> - createLink('testcase', 'view', "caseID=$run->case&version=$run->version&from=testtask&taskID=$run->task"), $run->title);?> - testcase->typeList[$run->type];?>assignedTo]; echo substr($assignedTo, strpos($assignedTo, ':') + 1);?>lastRunner]; echo substr($lastRunner, strpos($lastRunner, ':') + 1);?>lastRunDate)) echo date(DT_MONTHTIME1, strtotime($run->lastRunDate));?>lastRunResult) echo $lang->testcase->resultList[$run->lastRunResult];?>version < $run->caseVersion) ? "{$lang->testcase->changed}" : $lang->testtask->statusList[$run->status];?>bugs) ? html::a($this->createLink('testcase', 'bugs', "runID={$run->id}&caseID={$run->case}"), $run->bugs, '', "class='iframe'") : $run->bugs;?>results) ? html::a($this->createLink('testtask', 'results', "runID={$run->id}&caseID={$run->case}"), $run->results, '', "class='iframe'") : $run->results;?>stepNumber;?> - id", $run, 'list', '', '', 'runCase iframe', false, "data-width='95%'"); - common::printIcon('testtask', 'results', "id=$run->id", $run, 'list', '', '', 'iframe', '', "data-width='90%'"); - - if(common::hasPriv('testtask', 'unlinkCase', $run)) - { - $unlinkURL = $this->createLink('testtask', 'unlinkCase', "caseID=$run->id&confirm=yes"); - echo html::a("javascript:ajaxDelete(\"$unlinkURL\",\"caseList\",confirmUnlink)", '', '', "title='{$lang->testtask->unlinkCase}' class='btn-icon'"); - } - - common::printIcon('testcase', 'createBug', "product=$productID&branch=$task->branch&extra=projectID=$task->project,buildID=$task->build,caseID=$run->case,version=$run->version,runID=$run->id,testtask=$taskID", $run, 'list', 'bug', '', 'iframe', '', "data-width='90%'"); - ?> -
- - $value) $this->datatable->printHead($value, $orderBy, $vars);?> - - - - - $value) $this->testtask->printCell($value, $run, $users, $task, $branches);?> - - - From b965b706a86a26814b45239a6bd9c23255bd2355 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Thu, 19 Oct 2017 14:31:03 +0800 Subject: [PATCH 02/40] =?UTF-8?q?Finish=20task#3252=20=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E5=8F=AF=E4=BB=A5=E5=B0=86=E8=87=AA=E5=B7=B1=E7=9A=84?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E9=85=8D=E7=BD=AE=E4=BD=9C=E4=B8=BA=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E7=9A=84=E9=85=8D=E7=BD=AE,cost:3=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/common/model.php | 2 +- module/custom/control.php | 10 ++++++---- module/custom/js/ajaxmenu.js | 11 +++++++++-- module/custom/lang/en.php | 1 + module/custom/lang/zh-cn.php | 1 + module/custom/model.php | 2 ++ module/custom/view/ajaxmenu.html.php | 3 ++- module/doc/model.php | 13 +++++++++++-- module/group/lang/resource.php | 20 +++++++++++++++----- 9 files changed, 48 insertions(+), 15 deletions(-) diff --git a/module/common/model.php b/module/common/model.php index 797fdfdd13..941b993d29 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -817,7 +817,7 @@ class commonModel extends model if(strtolower($key) == 'closeddate' && $value == '') continue; if($magicQuote) $value = stripslashes($value); - if($value != stripslashes($old->$key)) + if(isset($old->$key) and $value != stripslashes($old->$key)) { $diff = ''; if(substr_count($value, "\n") > 1 or diff --git a/module/custom/control.php b/module/custom/control.php index bede50f426..c57bd58301 100644 --- a/module/custom/control.php +++ b/module/custom/control.php @@ -310,7 +310,7 @@ class custom extends control if(is_array($menus)) { - foreach ($menus as $menu) + foreach($menus as $menu) { $menu = json_decode($menu); $this->custom->saveCustomMenu($menu->value, $menu->module, isset($menu->method) ? $menu->method : ''); @@ -374,11 +374,13 @@ class custom extends control * @access public * @return void */ - public function ajaxRestoreMenu($confirm = 'no') + public function ajaxRestoreMenu($setPublic = 0, $confirm = 'no') { - if($confirm == 'no') die(js::confirm($this->lang->custom->confirmRestore, inlink('ajaxRestoreMenu', "confirm=yes"))); + if($confirm == 'no') die(js::confirm($this->lang->custom->confirmRestore, inlink('ajaxRestoreMenu', "setPublic=$setPublic&confirm=yes"))); - $this->loadModel('setting')->deleteItems("owner={$this->app->user->account}&module=common§ion=customMenu"); + $account = $this->app->user->account; + if($setPublic) $account = 'system'; + $this->loadModel('setting')->deleteItems("owner={$account}&module=common§ion=customMenu"); die(js::reload('parent.parent')); } } diff --git a/module/custom/js/ajaxmenu.js b/module/custom/js/ajaxmenu.js index 5d28e83f53..44655c78a8 100644 --- a/module/custom/js/ajaxmenu.js +++ b/module/custom/js/ajaxmenu.js @@ -217,9 +217,10 @@ $(function() }); }); - postData = $.map(postData, JSON.stringify); + postData = $.map(postData, JSON.stringify); + setPublic = $('#setPublic1').prop('checked') ? 1 : 0; - $.post(createLink('custom', 'ajaxSetMenu'), {menus: postData}, function(data) + $.post(createLink('custom', 'ajaxSetMenu'), {menus: postData, setPublic: setPublic}, function(data) { if(data.result === 'success') window.parent.location.reload() if(data.message) alert(data.message); @@ -242,4 +243,10 @@ $(function() { window.parent.$.closeModal(); }); + + $('#resetMenuBtn').click(function() + { + var setPublic = $('#setPublic1').prop('checked') ? 1 : 0; + hiddenwin.location.href = createLink('custom', 'ajaxRestoreMenu', "public=" + setPublic); + }); }); diff --git a/module/custom/lang/en.php b/module/custom/lang/en.php index 0e45826aab..5fe8a27fe1 100644 --- a/module/custom/lang/en.php +++ b/module/custom/lang/en.php @@ -13,6 +13,7 @@ $lang->custom->owner = 'Owner'; $lang->custom->module = 'Module'; $lang->custom->section = 'Section'; $lang->custom->lang = 'Language'; +$lang->custom->setPublic = 'Set Public'; $lang->custom->object['story'] = 'Story'; $lang->custom->object['task'] = 'Task'; diff --git a/module/custom/lang/zh-cn.php b/module/custom/lang/zh-cn.php index 914abf963f..b443b578a0 100644 --- a/module/custom/lang/zh-cn.php +++ b/module/custom/lang/zh-cn.php @@ -13,6 +13,7 @@ $lang->custom->owner = '所有者'; $lang->custom->module = '模块'; $lang->custom->section = '附加部分'; $lang->custom->lang = '所属语言'; +$lang->custom->setPublic = '设为公共'; $lang->custom->object['story'] = '需求'; $lang->custom->object['task'] = '任务'; diff --git a/module/custom/model.php b/module/custom/model.php index dae69c8b3f..1d10e5b9f8 100644 --- a/module/custom/model.php +++ b/module/custom/model.php @@ -359,6 +359,8 @@ class customModel extends model $account = $this->app->user->account; $settingKey = ''; + $setPublic = $this->post->setPublic; + if($setPublic) $account = 'system'; if(!is_string($menu)) $menu = json_encode($menu); $flow = $this->config->global->flow; diff --git a/module/custom/view/ajaxmenu.html.php b/module/custom/view/ajaxmenu.html.php index 3377f6b564..fdb196964d 100644 --- a/module/custom/view/ajaxmenu.html.php +++ b/module/custom/view/ajaxmenu.html.php @@ -32,9 +32,10 @@
+ $lang->custom->setPublic));?>     - custom->restore, 'hiddenwin', "class='btn'")?> +     custom->menuTip ?>
diff --git a/module/doc/model.php b/module/doc/model.php index b28231f121..499aeb7f9e 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -457,8 +457,17 @@ class docModel extends model ->remove('comment,files,labels,uid') ->get(); if($doc->acl == 'private') $doc->users = $oldDoc->addedBy; + $oldDocContent = $this->dao->select('files,type')->from(TABLE_DOCCONTENT)->where('doc')->eq($docID)->andWhere('version')->eq($oldDoc->version)->fetch(); - if($oldDocContent->type == 'markdown') $doc->content = str_replace('>', '>', $doc->content); + if($oldDocContent) + { + $oldDoc->title = $oldDocContent->title; + $oldDoc->digest = $oldDocContent->digest; + $oldDoc->content = $oldDocContent->content; + $oldDoc->contentType = $oldDocContent->type; + + if($oldDocContent->type == 'markdown') $doc->content = str_replace('>', '>', $doc->content); + } $lib = $this->getLibByID($doc->lib); $doc = $this->loadModel('file')->processImgURL($doc, $this->config->doc->editor->edit['id'], $this->post->uid); @@ -484,9 +493,9 @@ class docModel extends model $docContent->title = $doc->title; $docContent->content = $doc->content; $docContent->version = $doc->version; - $docContent->digest = $doc->digest; $docContent->type = $oldDocContent->type; $docContent->files = $oldDocContent->files; + if(isset($doc->digest)) $docContent->digest = $doc->digest; if($files) $docContent->files .= ',' . join(',', array_keys($files)); $docContent->files = trim($docContent->files, ','); $this->dao->insert(TABLE_DOCCONTENT)->data($docContent)->exec(); diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 05bde960eb..a4a188e929 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -714,17 +714,24 @@ $lang->mail->methodOrder[60] = 'sendcloudUser'; /* custom. */ $lang->resource->custom = new stdclass(); -$lang->resource->custom->index = 'index'; -$lang->resource->custom->set = 'set'; -$lang->resource->custom->restore = 'restore'; -$lang->resource->custom->flow = 'flow'; -$lang->resource->custom->working = 'working'; +$lang->resource->custom->index = 'index'; +$lang->resource->custom->set = 'set'; +$lang->resource->custom->restore = 'restore'; +$lang->resource->custom->flow = 'flow'; +$lang->resource->custom->working = 'working'; +$lang->resource->custom->setPublic = 'setPublic'; $lang->custom->methodOrder[5] = 'index'; $lang->custom->methodOrder[10] = 'set'; $lang->custom->methodOrder[15] = 'restore'; $lang->custom->methodOrder[20] = 'flow'; $lang->custom->methodOrder[25] = 'working'; +$lang->custom->methodOrder[30] = 'setPublic'; + +$lang->resource->datatable = new stdclass(); +$lang->resource->datatable->setGlobal = 'setGlobal'; + +$lang->datatable->methodOrder[5] = 'setGlobal'; /* Subversion. */ $lang->resource->svn = new stdclass(); @@ -1327,6 +1334,9 @@ $lang->changelog['9.2'][] = 'testsuite-import'; $lang->changelog['9.2'][] = 'testsuite-showImport'; $lang->changelog['9.5'][] = 'bug-batchActivate'; +$lang->changelog['9.6'][] = 'custom-setPublic'; +$lang->changelog['9.6'][] = 'datatable-setGlobal'; + global $config; if($config->global->flow != 'full') { From 4b52f18cbfc0266e2c82e849f482e79c8c4937bf Mon Sep 17 00:00:00 2001 From: wangyidong Date: Thu, 19 Oct 2017 16:12:13 +0800 Subject: [PATCH 03/40] =?UTF-8?q?Finish=20task#3240=20=E7=9C=8B=E6=9D=BF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=BB=E5=8A=A1=E7=9A=84=E6=9A=82=E5=81=9C?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=A0=8F=E6=98=BE=E7=A4=BA,cost:1=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/project/css/kanban.css | 4 +++- module/project/js/kanban.js | 3 ++- module/project/view/kanban.html.php | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/module/project/css/kanban.css b/module/project/css/kanban.css index 5a13b4fbe6..2e20477f54 100644 --- a/module/project/css/kanban.css +++ b/module/project/css/kanban.css @@ -1,4 +1,4 @@ -table th.col-closed {width:15%} +table th.col-closed {width:12%} #kanbanHeader th{border: 1px solid #ddd; padding:8px;} #kanbanHeader .dropdown {display: inline-block; margin-left: 10px;} #kanbanHeader .dropdown > a {color: #036} @@ -63,6 +63,7 @@ table th.col-closed {width:15%} .board {background: #ddd; color: #444; outline: 1px solid rgba(0,0,0,.1); outline-offset: -1px;} .board-bug-wait,.board-task-wait {color: #2b529c; background-color: #E8EAF6} .board-bug-doing,.board-task-doing {color: #D2322D; background-color: #FFEBEE;} +.board-task-pause {color: #fff; background-color: #E48600;} .board-bug-done,.board-task-done {color: #229F24; background-color: #E8F5E9;} .board-bug-closed,.board-task-closed {color: #666; background-color: #ccc;} .board:hover {outline: 1px solid rgba(0,0,0,.3);} @@ -79,6 +80,7 @@ table th.col-closed {width:15%} .board.inverse:hover {color: #fff;} .board-bug-cancel.inverse, .board-task-cancel.inverse {background: #333;} .board-bug-wait.inverse, .board-task-wait.inverse {background: #2b529c} +.board-task-pause.inverse {background: #E48600} .board-bug-doing.inverse, .board-task-doing.inverse {background: #D2322D} .board-bug-done.inverse, .board-task-done.inverse {background: #229F24} .board-bug-closed.inverse, .board-task-closed.inverse {background: #777;} diff --git a/module/project/js/kanban.js b/module/project/js/kanban.js index 91b0dcd287..85e1e007e5 100644 --- a/module/project/js/kanban.js +++ b/module/project/js/kanban.js @@ -17,7 +17,8 @@ $(function() task: { wait : {doing: 'start', done: 'finish', cancel: 'cancel'}, - doing : {done: 'finish'}, + doing : {done: 'finish', pause: 'pause'}, + pause : {doing: 'activate', done: 'finish', cancel: 'cancel'}, done : {doing: 'activate', closed: 'close'}, cancel : {doing: 'activate', closed: 'close'}, closed : {doing: 'activate'} diff --git a/module/project/view/kanban.html.php b/module/project/view/kanban.html.php index 90c02ea77b..f6f689c425 100644 --- a/module/project/view/kanban.html.php +++ b/module/project/view/kanban.html.php @@ -10,7 +10,7 @@ ?> - +
@@ -47,7 +47,7 @@ - + From 129834b30fae3ceb2b8fb87a118e018d15eb2321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 13:50:48 +0800 Subject: [PATCH 04/40] * childTask and multiTask --- .gitignore | 4 + .idea/vcs.xml | 6 + framework/helper.class.php | 8 + module/project/css/task.css | 1 + module/project/view/datatabledata.html.php | 5 + module/project/view/taskdata.html.php | 83 +++- module/task/config.php | 2 +- module/task/control.php | 28 +- module/task/js/common.js | 43 ++ module/task/js/create.js | 23 + module/task/js/edit.js | 22 + module/task/lang/en.php | 9 + module/task/lang/zh-cn.php | 157 +++--- module/task/lang/zh-tw.php | 9 + module/task/model.php | 536 ++++++++++++++------- module/task/view/assignto.html.php | 6 +- module/task/view/batchcreate.html.php | 2 +- module/task/view/create.html.php | 52 +- module/task/view/edit.html.php | 63 +++ module/task/view/view.html.php | 27 +- 20 files changed, 824 insertions(+), 262 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 5529453cfe..1a682f8be0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ tmp/cache/* tmp/extension/* .gitkeep .bak +.idea +.gitignore +_log +framework/ \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..94a25f7f4c --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/framework/helper.class.php b/framework/helper.class.php index 165ba1bab1..dc33d2ffa0 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -141,3 +141,11 @@ function formatTime($time, $format = '') if($format) return date($format, strtotime($time)); return trim($time); } + +function b($val) +{ + echo '
';
+    print_r($val);
+    echo '
'; + exit; +} \ No newline at end of file diff --git a/module/project/css/task.css b/module/project/css/task.css index 12b48fcf8a..050b253d51 100644 --- a/module/project/css/task.css +++ b/module/project/css/task.css @@ -24,3 +24,4 @@ .pl-5px{padding-left:5px;} tbody > tr > td > .btn-icon {margin-right: 2px} +.table-children td{background-color:#fff0f5!important;} \ No newline at end of file diff --git a/module/project/view/datatabledata.html.php b/module/project/view/datatabledata.html.php index ad92507c5d..31ac138adf 100644 --- a/module/project/view/datatabledata.html.php +++ b/module/project/view/datatabledata.html.php @@ -35,5 +35,10 @@ extract($widths);
$value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> + children) foreach($task->children as $child):?> + + $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs);?> + + diff --git a/module/project/view/taskdata.html.php b/module/project/view/taskdata.html.php index 152b51c3b2..1401800464 100644 --- a/module/project/view/taskdata.html.php +++ b/module/project/view/taskdata.html.php @@ -44,7 +44,7 @@ - + @@ -122,9 +122,90 @@ common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list'); + common::printIcon('task', 'batchCreate', "project=$projectID&storyID=&moduleID=$moduleID&taskID=$task->id", $task, 'list','plus','','','','',$lang->task->children); ?> + children):?> + children as $child):?> + assignedTo == $app->user->account ? 'style=color:red' : ''; ?> + + + + + + + + cookie->windowWidth > $this->config->wideSize):?> + + + + + + + cookie->windowWidth > $this->config->wideSize):?> + + + + + + + + type == 'sprint') + { + echo ''; + } + ?> + + + + diff --git a/module/task/config.php b/module/task/config.php index b5bbbc3481..ca4fc78573 100644 --- a/module/task/config.php +++ b/module/task/config.php @@ -183,5 +183,5 @@ $config->task->datatable->fieldList['story']['required'] = 'no'; $config->task->datatable->fieldList['actions']['title'] = 'actions'; $config->task->datatable->fieldList['actions']['fixed'] = 'right'; -$config->task->datatable->fieldList['actions']['width'] = '140'; +$config->task->datatable->fieldList['actions']['width'] = '180'; $config->task->datatable->fieldList['actions']['required'] = 'yes'; diff --git a/module/task/control.php b/module/task/control.php index 4beb48389d..002e5c1db6 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -169,12 +169,15 @@ class task extends control /** * Batch create task. * - * @param int $projectID - * @param int $storyID + * @param int $projectID + * @param int $storyID + * @param int $iframe + * @param int $taskID + * * @access public - * @return void + * @return mixed */ - public function batchCreate($projectID = 0, $storyID = 0, $iframe = 0) + public function batchCreate($projectID = 0, $storyID = 0, $iframe = 0, $taskID = 0) { $project = $this->project->getById($projectID); $taskLink = $this->createLink('project', 'browse', "projectID=$projectID&tab=task"); @@ -213,6 +216,7 @@ class task extends control $this->view->project = $project; $this->view->stories = $stories; $this->view->modules = $modules; + $this->view->parent = $taskID; $this->view->storyID = $storyID; $this->view->story = $this->story->getByID($storyID); $this->view->storyTasks = $this->task->getStoryTaskCounts(array_keys($stories), $projectID); @@ -426,10 +430,20 @@ class task extends control die(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent')); } + $task = $this->task->getByID($taskID); + + $members = $this->project->getTeamMemberPairs($projectID, 'nodeleted'); + /* Compute next assignedTo. */ + if(!empty($task->team)) + { + $task->assignedTo = $this->task->getNextUser(array_keys($task->team), $task->assignedTo); + $members = $this->task->getMemberPairs($task); + } + $this->view->title = $this->view->project->name . $this->lang->colon . $this->lang->task->assign; $this->view->position[] = $this->lang->task->assign; - - $this->view->users = $this->project->getTeamMemberPairs($projectID, 'nodeleted'); + $this->view->task = $task; + $this->view->users = $members; $this->display(); } @@ -518,6 +532,8 @@ class task extends control $task->storyFiles = $this->loadModel('file')->getByObject('story', $task->story); } + if($task->team) $this->lang->task->assign = $this->lang->task->transmit; + /* Update action. */ if($task->assignedTo == $this->app->user->account) $this->loadModel('action')->read('task', $taskID); diff --git a/module/task/js/common.js b/module/task/js/common.js index f6b1f07a1c..140db4051e 100644 --- a/module/task/js/common.js +++ b/module/task/js/common.js @@ -14,3 +14,46 @@ function checkLeft() } } } + +$('.btn-move-up, .btn-move-down').click(function() +{ + var $this = $(this); + if($this.hasClass('btn-move-up')) + { + $(this).parents('tr').prev().before($(this).parents('tr')); + } + else + { + $this.parents('tr').next().after($(this).parents('tr')); + } + $('.btn-move-up, .btn-move-down').removeClass('disabled').removeAttr('disabled'); + + adjustSortBtn(); +}); +function adjustSortBtn() +{ + $('.btn-move-up:first').addClass('disabled').attr('disabled', 'disabled'); + $('.btn-move-down:last').addClass('disabled').attr('disabled', 'disabled'); +} + +$('#modalTeam .btn').click(function() +{ + var team = ''; + var time = 0; + $('[name*=team]').each(function() + { + if($(this).find('option:selected').text() != '') + { + team += ' ' + $(this).find('option:selected').text(); + } + + estimate = parseFloat($(this).parents('td').next('td').find('[name*=teamEstimate]').val()); + if(!isNaN(estimate)) + { + time += estimate; + } + + $('#teamMember').val(team); + $('#estimate').val(time); + }) +}) diff --git a/module/task/js/create.js b/module/task/js/create.js index e50bb46e83..bdb27f8b2f 100644 --- a/module/task/js/create.js +++ b/module/task/js/create.js @@ -202,3 +202,26 @@ $(document).ready(function() return false; }); }); + +/* show team menu. */ +$('[name^=multiple]').change(function() +{ + if($(this).prop('checked')) + { + $('#assignedTo, #assignedTo_chosen').addClass('hidden'); + $('.team-group').removeClass('hidden'); + $('#estimate').attr('readonly', true); + } + else + { + $('#assignedTo, #assignedTo_chosen').removeClass('hidden'); + $('.team-group').addClass('hidden'); + $('#estimate').attr('readonly', false); + } +}); + +$(".btn[data-toggle='modalTeam']").click(function() +{ + $('#modalTeam').modal('show'); + adjustSortBtn(); +}); \ No newline at end of file diff --git a/module/task/js/edit.js b/module/task/js/edit.js index 6ed08f4209..401c03d80e 100644 --- a/module/task/js/edit.js +++ b/module/task/js/edit.js @@ -67,3 +67,25 @@ function loadProjectMembers(projectID) /* empty function. */ function setPreview(){} +$(".btn[data-toggle='modalTeam']").click(function() +{ + $('#modalTeam').modal('show') + adjustSortBtn(); +}); + +$(document).ready(function() +{ + /* show team menu. */ + $('[name=multiple]').change(function() + { + var checked = $(this).prop('checked'); + if(checked) + { + $('#teamTr').removeClass('hidden'); + } + else + { + $('#teamTr').addClass('hidden'); + } + }); +}); \ No newline at end of file diff --git a/module/task/lang/en.php b/module/task/lang/en.php index 94cbe81964..c863c97ed8 100644 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -95,6 +95,15 @@ $lang->task->deleteEstimate = 'Delete Man-Hour'; $lang->task->colorTag = 'Color Tag'; $lang->task->files = 'Files'; $lang->task->hasConsumed = 'Consumed'; +$lang->task->multiple = 'Multiple Tasks'; +$lang->task->multipleAB = 'Multiple'; +$lang->task->team = 'Team'; +$lang->task->transmit = 'Transfer'; +$lang->task->transmitTo = 'Transfer To'; +$lang->task->children = 'Child'; +$lang->task->childrenAB = 'Child'; +$lang->task->parent = 'Parent Task'; +$lang->task->unfinished = 'Unfinished'; $lang->task->ditto = 'Ditto'; $lang->task->dittoNotice = "This Task does not belong to the Project as the previous one does!"; diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 60e5fd64a1..54e7d352d0 100644 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -39,62 +39,71 @@ $lang->task->progess = '进度'; $lang->task->progessTips = '已消耗/(已消耗+剩余)'; $lang->task->copy = '复制任务'; -$lang->task->common = '任务'; -$lang->task->id = '编号'; -$lang->task->project = '所属' . $lang->projectCommon; -$lang->task->module = '所属模块'; -$lang->task->moduleAB = '模块'; -$lang->task->story = '相关需求'; -$lang->task->storySpec = '需求描述'; -$lang->task->storyVerify = '验收标准'; -$lang->task->name = '任务名称'; -$lang->task->type = '任务类型'; -$lang->task->pri = '优先级'; -$lang->task->mailto = '抄送给'; -$lang->task->estimate = '最初预计'; -$lang->task->estimateAB = '预'; -$lang->task->left = '预计剩余'; -$lang->task->leftAB = '剩'; -$lang->task->consumed = '总消耗'; -$lang->task->consumedAB = '消耗'; -$lang->task->hour = '小时'; -$lang->task->consumedThisTime = '工时'; -$lang->task->leftThisTime = '剩余'; -$lang->task->datePlan = '日程规划'; -$lang->task->estStarted = '预计开始'; -$lang->task->realStarted = '实际开始'; -$lang->task->date = '日期'; -$lang->task->deadline = '截止日期'; -$lang->task->deadlineAB = '截止'; -$lang->task->status = '任务状态'; -$lang->task->desc = '任务描述'; -$lang->task->assign = '指派'; -$lang->task->assignTo = $lang->task->assign; -$lang->task->batchAssignTo = '批量指派'; -$lang->task->assignedTo = '指派给'; -$lang->task->assignedToAB = '指派给'; -$lang->task->assignedDate = '指派日期'; -$lang->task->openedBy = '由谁创建'; -$lang->task->openedDate = '创建日期'; -$lang->task->openedDateAB = '创建'; -$lang->task->finishedBy = '由谁完成'; -$lang->task->finishedByAB = '完成者'; -$lang->task->finishedDate = '完成时间'; -$lang->task->finishedDateAB = '完成'; -$lang->task->canceledBy = '由谁取消'; -$lang->task->canceledDate = '取消时间'; -$lang->task->closedBy = '由谁关闭'; -$lang->task->closedDate = '关闭时间'; -$lang->task->closedReason = '关闭原因'; -$lang->task->lastEditedBy = '最后修改'; -$lang->task->lastEditedDate = '最后修改日期'; -$lang->task->lastEdited = '最后编辑'; -$lang->task->recordEstimate = '工时'; -$lang->task->editEstimate = '编辑工时'; -$lang->task->deleteEstimate = '删除工时'; -$lang->task->colorTag = '颜色标签'; -$lang->task->files = '附件'; -$lang->task->hasConsumed = '已消耗'; +$lang->task->common = '任务'; +$lang->task->id = '编号'; +$lang->task->project = '所属' . $lang->projectCommon; +$lang->task->module = '所属模块'; +$lang->task->moduleAB = '模块'; +$lang->task->story = '相关需求'; +$lang->task->storySpec = '需求描述'; +$lang->task->storyVerify = '验收标准'; +$lang->task->name = '任务名称'; +$lang->task->type = '任务类型'; +$lang->task->pri = '优先级'; +$lang->task->mailto = '抄送给'; +$lang->task->estimate = '最初预计'; +$lang->task->estimateAB = '预'; +$lang->task->left = '预计剩余'; +$lang->task->leftAB = '剩'; +$lang->task->consumed = '总消耗'; +$lang->task->consumedAB = '消耗'; +$lang->task->hour = '小时'; +$lang->task->consumedThisTime = '工时'; +$lang->task->leftThisTime = '剩余'; +$lang->task->datePlan = '日程规划'; +$lang->task->estStarted = '预计开始'; +$lang->task->realStarted = '实际开始'; +$lang->task->date = '日期'; +$lang->task->deadline = '截止日期'; +$lang->task->deadlineAB = '截止'; +$lang->task->status = '任务状态'; +$lang->task->desc = '任务描述'; +$lang->task->assign = '指派'; +$lang->task->assignTo = $lang->task->assign; +$lang->task->batchAssignTo = '批量指派'; +$lang->task->assignedTo = '指派给'; +$lang->task->assignedToAB = '指派给'; +$lang->task->assignedDate = '指派日期'; +$lang->task->openedBy = '由谁创建'; +$lang->task->openedDate = '创建日期'; +$lang->task->openedDateAB = '创建'; +$lang->task->finishedBy = '由谁完成'; +$lang->task->finishedByAB = '完成者'; +$lang->task->finishedDate = '完成时间'; +$lang->task->finishedDateAB = '完成'; +$lang->task->canceledBy = '由谁取消'; +$lang->task->canceledDate = '取消时间'; +$lang->task->closedBy = '由谁关闭'; +$lang->task->closedDate = '关闭时间'; +$lang->task->closedReason = '关闭原因'; +$lang->task->lastEditedBy = '最后修改'; +$lang->task->lastEditedDate = '最后修改日期'; +$lang->task->lastEdited = '最后编辑'; +$lang->task->recordEstimate = '工时'; +$lang->task->editEstimate = '编辑工时'; +$lang->task->deleteEstimate = '删除工时'; +$lang->task->colorTag = '颜色标签'; +$lang->task->files = '附件'; +$lang->task->hasConsumed = '已消耗'; +$lang->task->multiple = '多人任务'; +$lang->task->multipleAB = '多人'; +$lang->task->team = '团队'; +$lang->task->transmit = '转交'; +$lang->task->transmitTo = '转交给'; +$lang->task->children = '子任务'; +$lang->task->childrenAB = '子'; +$lang->task->parent = '父任务'; +$lang->task->unfinished = '未完成'; $lang->task->ditto = '同上'; $lang->task->dittoNotice = "该任务与上一任务不属于同一项目!"; @@ -104,13 +113,13 @@ $lang->task->noAssigned = '未指派'; $lang->task->noFinished = '未完成'; $lang->task->noClosed = '未关闭'; -$lang->task->statusList[''] = ''; -$lang->task->statusList['wait'] = '未开始'; -$lang->task->statusList['doing'] = '进行中'; -$lang->task->statusList['done'] = '已完成'; -$lang->task->statusList['pause'] = '已暂停'; -$lang->task->statusList['cancel'] = '已取消'; -$lang->task->statusList['closed'] = '已关闭'; +$lang->task->statusList[''] = ''; +$lang->task->statusList['wait'] = '未开始'; +$lang->task->statusList['doing'] = '进行中'; +$lang->task->statusList['done'] = '已完成'; +$lang->task->statusList['pause'] = '已暂停'; +$lang->task->statusList['cancel'] = '已取消'; +$lang->task->statusList['closed'] = '已关闭'; $lang->task->typeList[''] = ''; $lang->task->typeList['design'] = '设计'; @@ -122,11 +131,11 @@ $lang->task->typeList['ui'] = '界面'; $lang->task->typeList['affair'] = '事务'; $lang->task->typeList['misc'] = '其他'; -$lang->task->priList[0] = ''; -$lang->task->priList[3] = '3'; -$lang->task->priList[1] = '1'; -$lang->task->priList[2] = '2'; -$lang->task->priList[4] = '4'; +$lang->task->priList[0] = ''; +$lang->task->priList[3] = '3'; +$lang->task->priList[1] = '1'; +$lang->task->priList[2] = '2'; +$lang->task->priList[4] = '4'; $lang->task->reasonList[''] = ''; $lang->task->reasonList['done'] = '已完成'; @@ -155,7 +164,7 @@ $lang->task->noticeLinkStory = "没有可关联的相关需求,您可以 $lang->task->noticeSaveRecord = '您有尚未保存的工时记录,请先将其保存。'; $lang->task->commentActions = '%s. %s, 由 %s 添加备注。'; -$lang->task->error = new stdclass(); +$lang->task->error = new stdclass(); $lang->task->error->consumedNumber = '"已经消耗"必须为数字'; $lang->task->error->estimateNumber = '"预计剩余"必须为数字'; $lang->task->error->consumedSmall = '"已经消耗"必须大于之前消耗'; @@ -166,7 +175,7 @@ $lang->task->error->skipClose = '任务:%s 不是“已完成”或“ $lang->task->error->consumed = '任务:%s工时不能小于0,忽略该任务工时的改动'; /* 统计报表。*/ -$lang->task->report = new stdclass(); +$lang->task->report = new stdclass(); $lang->task->report->common = '报表'; $lang->task->report->select = '请选择报表类型'; $lang->task->report->create = '生成报表'; @@ -186,8 +195,8 @@ $lang->task->report->charts['tasksPerFinishedBy'] = '由谁完成统计'; $lang->task->report->charts['tasksPerClosedReason'] = '关闭原因统计'; $lang->task->report->charts['finishedTasksPerDay'] = '每天完成统计'; -$lang->task->report->options = new stdclass(); -$lang->task->report->options->graph = new stdclass(); +$lang->task->report->options = new stdclass(); +$lang->task->report->options->graph = new stdclass(); $lang->task->report->options->type = 'pie'; $lang->task->report->options->width = 500; $lang->task->report->options->height = 140; @@ -247,5 +256,5 @@ $lang->task->report->tasksPerConsumed->graph->xAxisName = '时间'; $lang->task->report->tasksPerFinishedBy->graph->xAxisName = '用户'; $lang->task->report->tasksPerClosedReason->graph->xAxisName = '关闭原因'; -$lang->task->report->finishedTasksPerDay->type = 'bar'; -$lang->task->report->finishedTasksPerDay->graph->xAxisName = '日期'; +$lang->task->report->finishedTasksPerDay->type = 'bar'; +$lang->task->report->finishedTasksPerDay->graph->xAxisName = '日期'; diff --git a/module/task/lang/zh-tw.php b/module/task/lang/zh-tw.php index 3533c2bc56..d75d0e2d61 100644 --- a/module/task/lang/zh-tw.php +++ b/module/task/lang/zh-tw.php @@ -95,6 +95,15 @@ $lang->task->deleteEstimate = '刪除工時'; $lang->task->colorTag = '顏色標籤'; $lang->task->files = '附件'; $lang->task->hasConsumed = '已消耗'; +$lang->task->multiple = '多人任務'; +$lang->task->multipleAB = '多人'; +$lang->task->team = '團隊'; +$lang->task->transmit = '轉交'; +$lang->task->transmitTo = '轉交給'; +$lang->task->children = '子任務'; +$lang->task->childrenAB = '子'; +$lang->task->parent = '父任務'; +$lang->task->unfinished = '未完成'; $lang->task->ditto = '同上'; $lang->task->dittoNotice = "該任務與上一任務不屬於同一項目!"; diff --git a/module/task/model.php b/module/task/model.php index b74b5e646c..fc0599a781 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -15,8 +15,8 @@ class taskModel extends model { /** * Create a task. - * - * @param int $projectID + * + * @param int $projectID * @access public * @return void */ @@ -37,9 +37,11 @@ class taskModel extends model ->setDefault('openedDate', helper::now()) ->stripTags($this->config->task->editor->create['id'], $this->config->allowedTags) ->join('mailto', ',') - ->remove('after,files,labels,assignedTo,uid,storyEstimate,storyDesc,storyPri') + ->remove('after,files,labels,assignedTo,uid,storyEstimate,storyDesc,storyPri,team,teamEstimate,teamMember,multiple,teams') ->get(); + + foreach($this->post->assignedTo as $assignedTo) { /* When type is affair and has assigned then ignore none. */ @@ -48,6 +50,37 @@ class taskModel extends model $task->assignedTo = $assignedTo; if($assignedTo) $task->assignedDate = helper::now(); + $teams = array(); + if($this->post->multiple) + { + $estimate = 0; + $left = 0; + foreach($this->post->team as $row => $account) + { + if(empty($account) or isset($team[$account])) continue; + $member = new stdClass(); + $member->project = 0; + $member->account = $account; + $member->role = $assignedTo; + $member->join = helper::today(); + $member->estimate = $this->post->teamEstimate[$row] ? $this->post->teamEstimate[$row] : 0; + $member->left = $member->estimate; + $member->order = $row; + + $teams[$account] = $member; + $estimate += (float)$member->eatimate; + $left += (float)$member->left; + } + + if(!empty($teams)) + { + $firstMember = reset($teams); + $task->assignedTo = $firstMember->account; + $task->estimate = $estimate; + $task->left = $left; + } + } + /* Check duplicate task. */ if($task->type != 'affair') { @@ -67,31 +100,33 @@ class taskModel extends model ->checkIF($task->deadline != '0000-00-00', 'deadline', 'ge', $task->estStarted) ->exec(); - if(!dao::isError()) + if(dao::isError()) return false; + + $taskID = $this->dao->lastInsertID(); + if($this->post->story) $this->loadModel('story')->setStage($this->post->story); + $this->file->updateObjectID($this->post->uid, $taskID, 'task'); + if(!empty($taskFiles)) { - $taskID = $this->dao->lastInsertID(); - if($this->post->story) $this->loadModel('story')->setStage($this->post->story); - $this->file->updateObjectID($this->post->uid, $taskID, 'task'); - if(!empty($taskFiles)) + foreach($taskFiles as $taskFile) { - foreach($taskFiles as $taskFile) - { - $taskFile->objectID = $taskID; - $this->dao->insert(TABLE_FILE)->data($taskFile)->exec(); - } + $taskFile->objectID = $taskID; + $this->dao->insert(TABLE_FILE)->data($taskFile)->exec(); } - else - { - $taskFileTitle = $this->file->saveUpload('task', $taskID); - $taskFiles = $this->dao->select('*')->from(TABLE_FILE)->where('id')->in(array_keys($taskFileTitle))->fetchAll('id'); - foreach($taskFiles as $fileID => $taskFile) unset($taskFiles[$fileID]->id); - } - $tasksID[$assignedTo] = array('status' => 'created', 'id' => $taskID); } else { - return false; + $taskFileTitle = $this->file->saveUpload('task', $taskID); + $taskFiles = $this->dao->select('*')->from(TABLE_FILE)->where('id')->in(array_keys($taskFileTitle))->fetchAll('id'); + foreach($taskFiles as $fileID => $taskFile) unset($taskFiles[$fileID]->id); } + + if(!empty($teams)) foreach($teams as $team) + { + $team->task = $taskID; + $this->dao->insert(TABLE_TEAM)->data($team)->autoCheck()->exec(); + } + + $tasksID[$assignedTo] = array('status' => 'created', 'id' => $taskID); } return $tasksID; } @@ -111,7 +146,7 @@ class taskModel extends model $tasks = fixer::input('post')->get(); $batchNum = count(reset($tasks)); - $storyIDs = array(); + $storyIDs = array(); $taskNames = array(); foreach($tasks->story as $key => $storyID) { @@ -120,7 +155,7 @@ class taskModel extends model $inNames = in_array($tasks->name[$key], $taskNames); if(!$inNames || $inNames && !in_array($storyID, $storyIDs)) { - $storyIDs[] = $storyID; + $storyIDs[] = $storyID; $taskNames[] = $tasks->name[$key]; } else @@ -141,7 +176,7 @@ class taskModel extends model { die(js::alert($this->lang->task->error->estimateNumber)); } - if(!empty($tasks->name[$i]) and empty($tasks->type[$i]))die(js::alert(sprintf($this->lang->error->notempty, $this->lang->task->type))); + if(!empty($tasks->name[$i]) and empty($tasks->type[$i])) die(js::alert(sprintf($this->lang->error->notempty, $this->lang->task->type))); } $story = 0; @@ -165,23 +200,24 @@ class taskModel extends model { if(empty($tasks->name[$i])) continue; - $data[$i] = new stdclass(); - $data[$i]->story = $tasks->story[$i]; - $data[$i]->type = $tasks->type[$i]; - $data[$i]->module = $tasks->module[$i]; - $data[$i]->assignedTo = $tasks->assignedTo[$i]; - $data[$i]->color = $tasks->color[$i]; - $data[$i]->name = $tasks->name[$i]; - $data[$i]->desc = nl2br($tasks->desc[$i]); - $data[$i]->pri = $tasks->pri[$i]; - $data[$i]->estimate = $tasks->estimate[$i]; - $data[$i]->left = $tasks->estimate[$i]; - $data[$i]->project = $projectID; - $data[$i]->estStarted = empty($tasks->estStarted[$i]) ? '0000-00-00' : $tasks->estStarted[$i]; - $data[$i]->deadline = empty($tasks->deadline[$i]) ? '0000-00-00' : $tasks->deadline[$i]; - $data[$i]->status = 'wait'; - $data[$i]->openedBy = $this->app->user->account; - $data[$i]->openedDate = $now; + $data[$i] = new stdclass(); + $data[$i]->story = $tasks->story[$i]; + $data[$i]->type = $tasks->type[$i]; + $data[$i]->module = $tasks->module[$i]; + $data[$i]->assignedTo = $tasks->assignedTo[$i]; + $data[$i]->color = $tasks->color[$i]; + $data[$i]->name = $tasks->name[$i]; + $data[$i]->desc = nl2br($tasks->desc[$i]); + $data[$i]->pri = $tasks->pri[$i]; + $data[$i]->estimate = $tasks->estimate[$i]; + $data[$i]->left = $tasks->estimate[$i]; + $data[$i]->project = $projectID; + $data[$i]->estStarted = empty($tasks->estStarted[$i]) ? '0000-00-00' : $tasks->estStarted[$i]; + $data[$i]->deadline = empty($tasks->deadline[$i]) ? '0000-00-00' : $tasks->deadline[$i]; + $data[$i]->status = 'wait'; + $data[$i]->openedBy = $this->app->user->account; + $data[$i]->openedDate = $now; + $data[$i]->parent = $tasks->parent[$i]; if($tasks->story[$i] != '') $data[$i]->storyVersion = $this->loadModel('story')->getVersion($data[$i]->story); if($tasks->assignedTo[$i] != '') $data[$i]->assignedDate = $now; @@ -202,13 +238,49 @@ class taskModel extends model $mails[$i]->actionID = $actionID; } + $this->countTime($tasks->parent[0]); + return $mails; } + /** + * count parent task estimate and left value + * + * @param $taskID + * + * @access public + * @return void + */ + public function countTime($taskID) + { + if(!$taskID) return true; + + $tasks = $this->dao->select('`id`,`estimate`,`consumed`,`left`')->from(TABLE_TASK)->where('parent')->eq($taskID)->fetchAll('id'); + + if(empty($tasks)) return true; + + $estimate = 0; + $consumed = 0; + $left = 0; + foreach($tasks as $task) + { + $estimate += $task->estimate; + $consumed += $task->consumed; + $left += $task->left; + } + + $newTask = new stdClass(); + $newTask->estimate = $estimate; + $newTask->consumed = $consumed; + $newTask->left = $left; + + $this->dao->update(TABLE_TASK)->data($newTask)->autoCheck()->where('id')->eq($taskID)->exec(); + } + /** * Update a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -248,10 +320,46 @@ class taskModel extends model ->add('lastEditedDate', $now) ->stripTags($this->config->task->editor->edit['id'], $this->config->allowedTags) ->join('mailto', ',') - ->remove('comment,files,labels,uid') + ->remove('comment,files,labels,uid,multiple,team,teamEstimate,teamConsumed,teamLeft') ->get(); - if($task->consumed < $oldTask->consumed) + $teams = array(); + if($this->post->multiple) + { + $estimate = 0; + $left = 0; + foreach($this->post->team as $row => $account) + { + if(empty($account) or isset($team[$account])) continue; + $member = new stdClass(); + $member->project = 0; + $member->account = $account; + $member->role = $task->assignedTo; + $member->join = helper::today(); + $member->task = $taskID; + $member->estimate = $this->post->teamEstimate[$row] ? $this->post->teamEstimate[$row] : 0; + $member->consumed = $this->post->teamConsumed[$row] ? $this->post->teamConsumed[$row] : 0; + $member->left = $member->estimate - $member->consumed; + $member->order = $row; + + $teams[$account] = $member; + $estimate += (float)$member->eatimate; + $left += (float)$member->left; + } + + if(!empty($teams)) + { + $task->estimate = $estimate; + $task->left = $left; + if(!isset($task->assignedTo)) + { + $firstMember = reset($teams); + $task->assignedTo = $firstMember->account; + } + } + } + + if($task->consumed < $oldTask->consumed) { die(js::error($this->lang->task->error->consumedSmall)); } @@ -287,6 +395,12 @@ class taskModel extends model ->batchCheckIF($task->closedReason == 'cancel', 'finishedBy, finishedDate', 'empty') ->where('id')->eq((int)$taskID)->exec(); + $this->countTime($oldTask->parent); + + /* Save team. */ + $this->dao->delete()->from(TABLE_TEAM)->where('task')->eq($taskID)->exec(); + if(!empty($teams)) foreach($teams as $member) $this->dao->insert(TABLE_TEAM)->data($member)->autoCheck()->exec(); + if($this->post->story != false) $this->loadModel('story')->setStage($this->post->story); if(!dao::isError()) { @@ -297,7 +411,7 @@ class taskModel extends model /** * Batch update task. - * + * * @access public * @return void */ @@ -432,7 +546,7 @@ class taskModel extends model break; case 'pause': { - $task->finishedDate = ''; + $task->finishedDate = ''; } default:break; @@ -462,7 +576,7 @@ class taskModel extends model if($task->status == 'done' and $task->closedReason) $this->dao->update(TABLE_TASK)->set('status')->eq('closed')->where('id')->eq($taskID)->exec(); if($oldTask->story != false) $this->loadModel('story')->setStage($oldTask->story); - if(!dao::isError()) + if(!dao::isError()) { $allChanges[$taskID] = common::createChanges($oldTask, $task); } @@ -506,8 +620,8 @@ class taskModel extends model /** * Assign a task to a user again. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -527,15 +641,15 @@ class taskModel extends model ->data($task) ->autoCheck() ->check('left', 'float') - ->where('id')->eq($taskID)->exec(); + ->where('id')->eq($taskID)->exec(); if(!dao::isError()) return common::createChanges($oldTask, $task); } /** * Start a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -547,12 +661,12 @@ class taskModel extends model $task = fixer::input('post') ->setDefault('assignedTo', $this->app->user->account) ->setDefault('lastEditedBy', $this->app->user->account) - ->setDefault('lastEditedDate', $now) + ->setDefault('lastEditedDate', $now) ->setIF($oldTask->assignedTo != $this->app->user->account, 'assignedDate', $now) ->remove('comment')->get(); if($this->post->left == 0) { - $task->status = 'done'; + $task->status = 'done'; $task->finishedBy = $this->app->user->account; $task->finishedDate = helper::now(); } @@ -563,11 +677,11 @@ class taskModel extends model /* Record consumed and left. */ $estimate = fixer::input('post') - ->setDefault('account', $this->app->user->account) - ->setDefault('task', $taskID) - ->setDefault('date', $task->realStarted) + ->setDefault('account', $this->app->user->account) + ->setDefault('task', $taskID) + ->setDefault('date', $task->realStarted) ->remove('realStarted,comment')->get(); - $estimate->consumed = $estimate->consumed - $oldTask->consumed; + $estimate->consumed = $estimate->consumed - $oldTask->consumed; $this->addTaskEstimate($estimate); $this->dao->update(TABLE_TASK)->data($task) @@ -580,9 +694,9 @@ class taskModel extends model } /** - * Record estimate and left of task. - * - * @param int $taskID + * Record estimate and left of task. + * + * @param int $taskID * @access public * @return void */ @@ -649,7 +763,7 @@ class taskModel extends model $data->lastEditedDate = $now; if($left == 0) { - $task->status = 'done'; + $task->status = 'done'; $data->status = $task->status; $data->assignedTo = $task->openedBy; $data->assignedDate = $now; @@ -658,7 +772,7 @@ class taskModel extends model } else if($task->status == 'wait') { - $task->status = 'doing'; + $task->status = 'doing'; $data->status = $task->status; $data->assignedTo = $this->app->user->account; $data->assignedDate = $now; @@ -666,7 +780,7 @@ class taskModel extends model } else if($task->status == 'pause') { - $task->status = 'doing'; + $task->status = 'doing'; $data->status = $task->status; $data->assignedTo = $this->app->user->account; $data->assignedDate = $now; @@ -687,13 +801,15 @@ class taskModel extends model if(!empty($actionID)) $this->action->logHistory($actionID, $changes); if($task->story) $this->loadModel('story')->setStage($task->story); + $this->countTime($task->parent); + return $changes; } /** * Finish a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -707,7 +823,7 @@ class taskModel extends model ->setDefault('assignedDate', $now) ->setDefault('status', 'done') ->setDefault('finishedBy, lastEditedBy', $this->app->user->account) - ->setDefault('finishedDate, lastEditedDate', $now) + ->setDefault('finishedDate, lastEditedDate', $now) ->remove('comment,files,labels') ->get(); if($task->finishedDate == substr($now, 0, 10)) $task->finishedDate = $now; @@ -718,13 +834,13 @@ class taskModel extends model $consumed = $task->consumed - $oldTask->consumed; if($consumed < 0) die(js::error($this->lang->task->error->consumedSmall)); $estimate = fixer::input('post') - ->setDefault('account', $this->app->user->account) - ->setDefault('task', $taskID) - ->setDefault('date', date(DT_DATE1)) + ->setDefault('account', $this->app->user->account) + ->setDefault('task', $taskID) + ->setDefault('date', date(DT_DATE1)) ->setDefault('left', 0) ->remove('finishedDate,comment,assignedTo,files,labels,consumed') ->get(); - $estimate->consumed = $consumed; + $estimate->consumed = $consumed; if($estimate->consumed) $this->addTaskEstimate($estimate); $this->dao->update(TABLE_TASK)->data($task) @@ -737,9 +853,9 @@ class taskModel extends model } /** - * Pause task - * - * @param int $taskID + * Pause task + * + * @param int $taskID * @access public * @return array */ @@ -750,18 +866,18 @@ class taskModel extends model $task = fixer::input('post') ->setDefault('status', 'pause') ->setDefault('lastEditedBy', $this->app->user->account) - ->setDefault('lastEditedDate', $now) + ->setDefault('lastEditedDate', $now) ->remove('comment')->get(); $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec(); if(!dao::isError()) return common::createChanges($oldTask, $task); } - + /** * Close a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -774,9 +890,9 @@ class taskModel extends model ->setDefault('assignedTo', 'closed') ->setDefault('assignedDate', $now) ->setDefault('closedBy, lastEditedBy', $this->app->user->account) - ->setDefault('closedDate, lastEditedDate', $now) - ->setIF($oldTask->status == 'done', 'closedReason', 'done') - ->setIF($oldTask->status == 'cancel', 'closedReason', 'cancel') + ->setDefault('closedDate, lastEditedDate', $now) + ->setIF($oldTask->status == 'done', 'closedReason', 'done') + ->setIF($oldTask->status == 'cancel', 'closedReason', 'cancel') ->remove('_recPerPage') ->remove('comment')->get(); @@ -788,8 +904,8 @@ class taskModel extends model /** * Cancel a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -804,7 +920,7 @@ class taskModel extends model ->setDefault('finishedBy', '') ->setDefault('finishedDate', '0000-00-00') ->setDefault('canceledBy, lastEditedBy', $this->app->user->account) - ->setDefault('canceledDate, lastEditedDate', $now) + ->setDefault('canceledDate, lastEditedDate', $now) ->remove('comment')->get(); $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec(); @@ -815,8 +931,8 @@ class taskModel extends model /** * Activate a task. - * - * @param int $taskID + * + * @param int $taskID * @access public * @return void */ @@ -844,8 +960,8 @@ class taskModel extends model /** * Get task info by Id. - * - * @param int $taskID + * + * @param int $taskID * @param bool $setImgSize * @access public * @return object|bool @@ -862,6 +978,14 @@ class taskModel extends model ->fetch(); if(!$task) return false; + $children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->eq($taskID)->andWhere('deleted')->eq(0)->fetchAll('id'); + $task->children = $children; + + $teams = $this->dao->select('*')->from(TABLE_TEAM)->where('task')->eq($taskID)->orderBy('order_desc')->fetchGroup('task', 'account'); + foreach($teams as $key => $team) $teams[$key] = array_reverse($team); + $task->team = isset($teams[$taskID]) ? $teams[$taskID] : array(); + foreach($children as $child) $child->team = isset($teams[$child->id]) ? $teams[$child->id] : array(); + $task = $this->loadModel('file')->replaceImgURL($task, 'desc'); if($setImgSize) $task->desc = $this->file->setImgSize($task->desc); if($task->assignedTo == 'closed') $task->assignedToRealName = 'Closed'; @@ -876,8 +1000,8 @@ class taskModel extends model /** * Get task list. - * - * @param int|array|string $taskIDList + * + * @param int|array|string $taskIDList * @access public * @return array */ @@ -922,13 +1046,15 @@ class taskModel extends model /** * Get tasks of a project. * - * @param int $projectID - * @param string $status all|needConfirm|wait|doing|done|cancel - * @param string $type - * @param array|string $modules - * @param object $pager + * @param $projectID + * @param int $productID + * @param string $type + * @param int $modules + * @param string $orderBy + * @param null $pager + * * @access public - * @return array + * @return array|void */ public function getProjectTasks($projectID, $productID = 0, $type = 'all', $modules = 0, $orderBy = 'status_asc, id_desc', $pager = null) { @@ -939,6 +1065,7 @@ class taskModel extends model ->leftJoin(TABLE_USER)->alias('t3')->on('t1.assignedTo = t3.account') ->where('t1.project')->eq((int)$projectID) ->andWhere('t1.deleted')->eq(0) + ->andWhere('t1.parent')->eq(0) ->beginIF($productID)->andWhere('t2.product')->eq((int)$productID)->fi() ->beginIF($type == 'undone')->andWhere("(t1.status = 'wait' or t1.status ='doing')")->fi() ->beginIF($type == 'needconfirm')->andWhere('t2.version > t1.storyVersion')->andWhere("t2.status = 'active'")->fi() @@ -949,10 +1076,16 @@ class taskModel extends model ->beginIF($modules)->andWhere('t1.module')->in($modules)->fi() ->orderBy($orderBy) ->page($pager) - ->fetchAll(); + ->fetchAll('id'); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'task', ($productID or $type == 'needconfirm') ? false : true); + $taskList = array_keys($tasks); + $children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->in($taskList)->orderBy('id_desc')->fetchGroup('parent'); + if($children) foreach($children as $key => $child) $tasks[$key]->children = $child; + $teams = $this->dao->select('*')->from(TABLE_TEAM)->where('task')->in($taskList)->fetchGroup('task'); + if($teams) foreach($teams as $key => $team) if(!empty($team)) $tasks[$key]->team = $team; + if($tasks) return $this->processTasks($tasks); return array(); } @@ -960,9 +1093,9 @@ class taskModel extends model /** * Get project tasks pairs. * - * @param int $projectID + * @param int $projectID * @param string $status - * @param string $orderBy + * @param string $orderBy * @access public * @return array */ @@ -983,11 +1116,11 @@ class taskModel extends model /** * Get tasks of a user. - * - * @param string $account - * @param string $type the query type - * @param int $limit - * @param object $pager + * + * @param string $account + * @param string $type the query type + * @param int $limit + * @param object $pager * @access public * @return array */ @@ -1013,9 +1146,9 @@ class taskModel extends model /** * Get tasks pairs of a user. - * - * @param string $account - * @param string $status + * + * @param string $account + * @param string $status * @access public * @return array */ @@ -1038,9 +1171,9 @@ class taskModel extends model /** * Get task pairs of a story. - * - * @param int $storyID - * @param int $projectID + * + * @param int $storyID + * @param int $projectID * @access public * @return array */ @@ -1075,9 +1208,9 @@ class taskModel extends model /** * Get counts of some stories' tasks. - * - * @param array $stories - * @param int $projectID + * + * @param array $stories + * @param int $projectID * @access public * @return int */ @@ -1095,30 +1228,30 @@ class taskModel extends model } /** - * Get task estimate. - * - * @param int $taskID + * Get task estimate. + * + * @param int $taskID * @access public - * @return object + * @return object */ public function getTaskEstimate($taskID) { - return $this->dao->select('*')->from(TABLE_TASKESTIMATE) + return $this->dao->select('*')->from(TABLE_TASKESTIMATE) ->where('task')->eq($taskID) ->orderBy('date,id') ->fetchAll(); } /** - * Get estimate by id. - * - * @param int $estimateID + * Get estimate by id. + * + * @param int $estimateID * @access public - * @return object. + * @return object. */ public function getEstimateById($estimateID) { - $estimate = $this->dao->select('*')->from(TABLE_TASKESTIMATE) + $estimate = $this->dao->select('*')->from(TABLE_TASKESTIMATE) ->where('id')->eq($estimateID) ->fetch(); @@ -1127,14 +1260,14 @@ class taskModel extends model ->where('task')->eq($estimate->task) ->andWhere('id')->gt($estimate->id) ->fetch('id'); - $estimate->isLast = $lastID ? false :true; + $estimate->isLast = $lastID ? false :true; return $estimate; } /** - * Update estimate. - * - * @param int $estimateID + * Update estimate. + * + * @param int $estimateID * @access public * @return void */ @@ -1152,7 +1285,7 @@ class taskModel extends model $consumed = $task->consumed + $estimate->consumed - $oldEstimate->consumed; $lastEstimate = $this->dao->select('*')->from(TABLE_TASKESTIMATE)->where('task')->eq($task->id)->orderBy('id desc')->fetch(); - $left = ($lastEstimate and $estimateID == $lastEstimate->id) ? $estimate->left : $task->left; + $left = ($lastEstimate and $estimateID == $lastEstimate->id) ? $estimate->left : $task->left; if($left == 0) $task->status = 'done'; $now = helper::now(); @@ -1162,7 +1295,7 @@ class taskModel extends model $data->status = $task->status; $data->lastEditedBy = $this->app->user->account; $data->lastEditedDate = $now; - if(!$left) + if(!$left) { $data->finishedBy = $this->app->user->account; $data->finishedDate = $now; @@ -1183,9 +1316,9 @@ class taskModel extends model } /** - * Delete estimate. - * - * @param int $estimateID + * Delete estimate. + * + * @param int $estimateID * @access public * @return void */ @@ -1198,7 +1331,7 @@ class taskModel extends model $consumed = $task->consumed - $estimate->consumed; $left = $lastEstimate->left ? $lastEstimate->left : $estimate->left; $oldStatus = $task->status; - if($left == 0 and $consumed != 0) $task->status = 'done'; + if($left == 0 and $consumed != 0) $task->status = 'done'; $this->dao->update(TABLE_TASK) ->set("consumed")->eq($consumed) ->set('`left`')->eq($left) @@ -1219,8 +1352,8 @@ class taskModel extends model /** * Batch process tasks. - * - * @param int $tasks + * + * @param int $tasks * @access private * @return void */ @@ -1229,6 +1362,10 @@ class taskModel extends model foreach($tasks as $task) { $task = $this->processTask($task); + if($task->children) foreach($task->children as $child) + { + $task = $this->processTask($child); + } } return $tasks; } @@ -1303,7 +1440,7 @@ class taskModel extends model return true; } - + /** * Get story comments. * @@ -1322,8 +1459,8 @@ class taskModel extends model /** * Merge the default chart settings and the settings of current chart. - * - * @param string $chartType + * + * @param string $chartType * @access public * @return void */ @@ -1340,10 +1477,10 @@ class taskModel extends model /* merge configuration */ foreach($commonOption->graph as $key => $value) if(!isset($chartOption->graph->$key)) $chartOption->graph->$key = $value; } - + /** - * Get report data of tasks per project - * + * Get report data of tasks per project + * * @access public * @return array */ @@ -1362,8 +1499,8 @@ class taskModel extends model } /** - * Get report data of tasks per module - * + * Get report data of tasks per module + * * @access public * @return array */ @@ -1383,8 +1520,8 @@ class taskModel extends model } /** - * Get report data of tasks per assignedTo - * + * Get report data of tasks per assignedTo + * * @access public * @return array */ @@ -1403,8 +1540,8 @@ class taskModel extends model } /** - * Get report data of tasks per type - * + * Get report data of tasks per type + * * @access public * @return array */ @@ -1423,7 +1560,7 @@ class taskModel extends model /** * Get report data of tasks per priority - * + * * @access public * @return array */ @@ -1445,8 +1582,8 @@ class taskModel extends model } /** - * Get report data of tasks per deadline - * + * Get report data of tasks per deadline + * * @access public * @return array */ @@ -1461,8 +1598,8 @@ class taskModel extends model } /** - * Get report data of tasks per estimate - * + * Get report data of tasks per estimate + * * @access public * @return array */ @@ -1478,7 +1615,7 @@ class taskModel extends model /** * Get report data of tasks per left - * + * * @access public * @return array */ @@ -1494,7 +1631,7 @@ class taskModel extends model /** * Get report data of tasks per consumed - * + * * @access public * @return array */ @@ -1509,8 +1646,8 @@ class taskModel extends model } /** - * Get report data of tasks per finishedBy - * + * Get report data of tasks per finishedBy + * * @access public * @return array */ @@ -1530,8 +1667,8 @@ class taskModel extends model } /** - * Get report data of tasks per closed reason - * + * Get report data of tasks per closed reason + * * @access public * @return array */ @@ -1556,7 +1693,7 @@ class taskModel extends model /** * Get report data of finished tasks per day - * + * * @access public * @return array */ @@ -1581,7 +1718,7 @@ class taskModel extends model /** * Get report data of status - * + * * @access public * @return array */ @@ -1613,6 +1750,11 @@ class taskModel extends model { $action = strtolower($action); + if($action == 'start' and !empty($task->children)) return false; + if($action == 'recordestimate' and !empty($task->children)) return false; + if($action == 'batchcreate' and !empty($task->team)) return false; + if($action == 'batchcreate' and $task->parent) return false; + if($action == 'assignto') return $task->status != 'closed' and $task->status != 'cancel'; if($action == 'start') return $task->status == 'wait'; if($action == 'restart') return $task->status == 'pause'; @@ -1622,12 +1764,14 @@ class taskModel extends model if($action == 'cancel') return $task->status != 'done' and $task->status != 'closed' and $task->status != 'cancel'; if($action == 'pause') return $task->status == 'doing'; + + return true; } /** * Get report condition from session. - * + * * @access public * @return void */ @@ -1643,8 +1787,8 @@ class taskModel extends model /** * Add task estimate. - * - * @param object $data + * + * @param object $data * @access public * @return void */ @@ -1655,11 +1799,11 @@ class taskModel extends model /** * Print cell data. - * - * @param object $col - * @param object $task - * @param array $users - * @param string $browseType + * + * @param object $col + * @param object $task + * @param array $users + * @param string $browseType * @access public * @return void */ @@ -1773,6 +1917,7 @@ class taskModel extends model common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'edit',"taskID=$task->id", $task, 'list'); + if(!$task->team or !$task->children) common::printIcon('task', 'batchCreate', "project=$task->project&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'list','plus','','','','',$this->lang->task->children); break; } echo ''; @@ -1781,9 +1926,9 @@ class taskModel extends model /** * Send mail. - * - * @param int $taskID - * @param int $actionID + * + * @param int $taskID + * @param int $actionID * @access public * @return void */ @@ -1844,4 +1989,51 @@ class taskModel extends model $this->mail->send($toList, 'TASK#' . $task->id . ' ' . $task->name . ' - ' . $projectName, $mailContent, $ccList); if($this->mail->isError()) trigger_error(join("\n", $this->mail->getError())); } + + /** + * Get next user. + * + * @param string $users + * @param string $current + * @access public + * @return void + */ + public function getNextUser($users, $current) + { + /* Process user */ + if(!is_array($users)) $users = explode(',', trim($users, ',')); + if(!$current) return reset($users); + $hit = false; + $next = ''; + foreach($users as $key => $account) + { + if($hit) + { + $next = $account; + break; + } + + if($account == $current) $hit = true; + } + if($next == '') return reset($users); + return $next; + } + + /** + * Get task's team member pairs. + * + * @param object $task + * @access public + * @return array + */ + public function getMemberPairs($task) + { + $users = $this->loadModel('user')->getPairs('noletter'); + $members = array(); + foreach($task->team as $member) + { + $members[$member->account] = $users[$member->account]; + } + return array('' => '') + $members; + } } diff --git a/module/task/view/assignto.html.php b/module/task/view/assignto.html.php index 166f610599..2b80014f08 100644 --- a/module/task/view/assignto.html.php +++ b/module/task/view/assignto.html.php @@ -16,14 +16,14 @@
icons['task']);?> id;?> createLink('task', 'view', 'task=' . $task->id), $task->name, '_blank');?> - task->assign;?> + team) ? $lang->task->assign : $lang->task->transmit;?>
actions;?>actions;?>
+ + id", sprintf('%03d', $child->id))) printf('%03d', $child->id);?> + task->priList, $child->pri, $child->pri)?>'>pri == '0' ? '' : zget($lang->task->priList, $child->pri, $child->pri);?> + product][$child->branch])) echo "" . $branchGroups[$child->product][$child->branch] . '';?> + module) echo "" . $modulePairs[$child->module] . '';?> + id", $child->name, null, "style='color: $child->color'")) echo $child->name; + if($child->fromBug) echo html::a($this->createLink('bug', 'view', "id=$child->fromBug"), "[BUG#$child->fromBug]", '_blank', "class='bug'"); + ?> + + storyStatus == 'active' and $child->latestStoryVersion > $child->storyVersion); + if($storyChanged) + { + echo "({$lang->story->changed} "; + echo html::a($this->createLink('task', 'confirmStoryChange', "taskID=$child->id"), $lang->confirm, 'hiddenwin'); + echo ")"; + } + else + { + echo $lang->task->statusList[$child->status]; + } + ?> + deadline, 0, 4) > 0) echo substr($child->deadline, 5, 6);?>openedDate, 5, 6);?>>assignedTo == 'closed' ? 'Closed' : zget($users, $child->assignedTo, $child->assignedTo);?>finishedBy, $child->finishedBy);?>finishedDate, 5, 6);?>estimate;?>consumed;?>left;?>progess?>%'; + if($child->storyID) + { + if(!common::printLink('story', 'view', "storyid=$child->storyID", $child->storyTitle)) print $child->storyTitle; + } + echo ' + project&taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'start', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + + common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true); + if($browseType == 'needconfirm') + { + $lang->task->confirmStoryChange = $lang->confirm; + common::printIcon('task', 'confirmStoryChange', "taskid=$child->id", '', 'list', '', 'hiddenwin'); + } + common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list'); + common::printIcon('task', 'batchCreate', "", $child, 'list','plus','','','','',$lang->task->children); + ?> +
- - + + diff --git a/module/task/view/batchcreate.html.php b/module/task/view/batchcreate.html.php index f58f2670f5..af9b4e682c 100644 --- a/module/task/view/batchcreate.html.php +++ b/module/task/view/batchcreate.html.php @@ -73,7 +73,7 @@ if($hiddenStory and isset($visibleFields['story'])) $colspan -= 1; ?> - + - + config->global->flow != 'onlyTask'):?> @@ -175,6 +192,37 @@
task->assignedTo;?>team) ? $lang->task->assign : $lang->task->transmitTo;?>assignedTo, "class='form-control chosen'");?>
task->left;?>
style='overflow:visible'>id, $i)'")?> style='overflow: visible'>
diff --git a/module/task/view/create.html.php b/module/task/view/create.html.php index b36c445810..d88374d6e0 100644 --- a/module/task/view/create.html.php +++ b/module/task/view/create.html.php @@ -38,9 +38,26 @@
task->assignedTo;?>assignedTo, "class='form-control chosen'");?> - +
+
+ assignedTo, "class='form-control chosen'");?> +
+ type == 'ops') :?> +
+ +
+ +
+ +
+ +
+
+ type != 'ops') :?> + +
+
createLink('custom', 'ajaxSaveCustomFields', 'module=task§ion=custom&key=createFields')?> diff --git a/module/task/view/edit.html.php b/module/task/view/edit.html.php index 04e7086b73..bf6af02834 100644 --- a/module/task/view/edit.html.php +++ b/module/task/view/edit.html.php @@ -35,6 +35,14 @@
name, 'class="form-control" autocomplete="off" placeholder="' . $lang->task->name . '"');?> + children) and empty($task->parent)):?> + + + +
@@ -81,6 +89,10 @@
task->assignedTo;?> assignedTo, "class='form-control chosen'");?>
task->team;?>task->team,'', "class='form-control btn' data-toggle='modalTeam'");?>
task->type;?> task->typeList, $task->type, 'class=form-control');?>
+ team as $member):?> + + + + + + + + + + + + + + +
account, "class='form-control chosen'")?> +
+ task->estimate?> + estimate, "class='form-control text-center' placeholder='{$lang->task->hour}'")?> + task->consumed?> + consumed, "class='form-control text-center' placeholder='{$lang->task->hour}'")?> + task->left?> + left, "class='form-control text-center' placeholder='{$lang->task->hour}'")?> +
+
+ + +
+
+ task->estimate?> + task->hour}'")?> + task->consumed?> + task->hour}'")?> + task->left?> + task->hour}'")?> +
+
+ + +
confirm, '',"class='btn btn-primary' data-dismiss='modal'") ?>
+
+ + diff --git a/module/task/view/view.html.php b/module/task/view/view.html.php index e7cec9645a..3decac4ae3 100644 --- a/module/task/view/view.html.php +++ b/module/task/view/view.html.php @@ -31,7 +31,7 @@ { ob_start(); echo "
"; - common::printIcon('task', 'assignTo', "projectID=$task->project&taskID=$task->id", $task, 'button', '', '', 'iframe', true); + common::printIcon('task', 'assignTo', "projectID=$task->project&taskID=$task->id", $task, 'button', '', '', 'iframe', true); common::printIcon('task', 'start', "taskID=$task->id", $task, 'button', '', '', 'iframe', true); common::printIcon('task', 'restart', "taskID=$task->id", $task, 'button', '', '', 'iframe', true); common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'button', '', '', 'iframe', true); @@ -175,7 +175,7 @@
task->assignedTo;?>assignedTo ? $task->assignedToRealName . $lang->at . $task->assignedDate : '';?>assignedTo ? $task->assignedToRealName . $lang->at . $task->assignedDate : '';?>
task->type;?>
+ team)) :?> +
+ task->team;?> + + + + + + + + + + team as $member):?> + + + + + + + +
task->team?>task->estimate?>task->consumed?>task->left?>
account)?>estimate?>consumed?>left?>
+
+
task->legendEffort;?> From 9ba2bbdb9e4e3154324982ec83ec3866806c0581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 14:12:39 +0800 Subject: [PATCH 05/40] add database change --- db/update9.5.1.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/db/update9.5.1.sql b/db/update9.5.1.sql index 88c7cd1fdb..789f54ab2e 100644 --- a/db/update9.5.1.sql +++ b/db/update9.5.1.sql @@ -27,3 +27,15 @@ CREATE TABLE IF NOT EXISTS `zt_webhook` ( `editedDate` datetime NOT NULL, PRIMARY KEY `id` (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_task` ADD `parent` INT(11) NULL DEFAULT '0' AFTER `deleted`; +ALTER TABLE `zt_team` ADD `task` INT(11) NULL DEFAULT '0' AFTER `project`; +ALTER TABLE `zt_team` ADD `estimate` DECIMAL(12,2) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `zt_team` ADD `consumed` DECIMAL(12,2) UNSIGNED NOT NULL DEFAULT '0' AFTER `estimate`; +ALTER TABLE `zt_team` ADD `left` DECIMAL(12,2) UNSIGNED NOT NULL DEFAULT '0' AFTER `consumed`; +ALTER TABLE `zt_team` ADD `order` TINYINT(3) NOT NULL DEFAULT '0' AFTER `left`; +ALTER TABLE `zt_team` CHANGE `days` `days` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0'; + + +ALTER TABLE `zt_team` DROP PRIMARY KEY; +ALTER TABLE `zt_team` ADD PRIMARY KEY (`project`, `task`, `account`); \ No newline at end of file From 2cf44d57e6354f0f32ce5ef97e75703d4ad2f9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 14:18:41 +0800 Subject: [PATCH 06/40] (gitignore commit and remove .idea) --- .idea/vcs.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4c..0000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 303ad1cb7da34d7c4d3615f804c7069177e95deb Mon Sep 17 00:00:00 2001 From: lvtao320 Date: Fri, 20 Oct 2017 14:26:43 +0800 Subject: [PATCH 07/40] Update helper.class.php --- framework/helper.class.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/framework/helper.class.php b/framework/helper.class.php index dc33d2ffa0..165ba1bab1 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -141,11 +141,3 @@ function formatTime($time, $format = '') if($format) return date($format, strtotime($time)); return trim($time); } - -function b($val) -{ - echo '
';
-    print_r($val);
-    echo '
'; - exit; -} \ No newline at end of file From 984f3940c9b8a966633351a9b7e491c46242a822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 14:31:55 +0800 Subject: [PATCH 08/40] change .gitignore --- .gitignore | 19 - framework/base/control.class.php | 887 ------ framework/base/helper.class.php | 747 ----- framework/base/model.class.php | 302 -- framework/base/router.class.php | 2517 ----------------- framework/control.class.php | 23 - framework/helper.class.php | 143 - framework/model.class.php | 38 - framework/router.class.php | 159 -- .../tests/helper/.101.setmember.onelevel.et | 13 - .../tests/helper/.110.setmember.multilevel.et | 6 - .../tests/helper/.201.getwebroot.inbrowse.et | 2 - .../tests/helper/.202.getwebroot.inshell.et | 8 - .../tests/helper/101.setmember.onelevel.php | 27 - .../tests/helper/110.setmember.multilevel.php | 22 - .../tests/helper/201.getwebroot.inbrowse.php | 17 - .../tests/helper/202.getwebroot.inshell.php | 43 - module/action/ext/config/.gitkeep | 0 module/action/ext/control/.gitkeep | 0 module/action/ext/css/.gitkeep | 0 module/action/ext/js/.gitkeep | 0 module/action/ext/lang/en/.gitkeep | 0 module/action/ext/lang/zh-cn/.gitkeep | 0 module/action/ext/lang/zh-tw/.gitkeep | 0 module/action/ext/model/.gitkeep | 0 module/action/ext/view/.gitkeep | 0 module/admin/ext/config/.gitkeep | 0 module/admin/ext/control/.gitkeep | 0 module/admin/ext/css/.gitkeep | 0 module/admin/ext/js/.gitkeep | 0 module/admin/ext/lang/en/.gitkeep | 0 module/admin/ext/lang/zh-cn/.gitkeep | 0 module/admin/ext/lang/zh-tw/.gitkeep | 0 module/admin/ext/model/.gitkeep | 0 module/admin/ext/view/.gitkeep | 0 module/api/ext/config/.gitkeep | 0 module/api/ext/control/.gitkeep | 0 module/api/ext/css/.gitkeep | 0 module/api/ext/js/.gitkeep | 0 module/api/ext/lang/en/.gitkeep | 0 module/api/ext/lang/zh-cn/.gitkeep | 0 module/api/ext/lang/zh-tw/.gitkeep | 0 module/api/ext/model/.gitkeep | 0 module/api/ext/view/.gitkeep | 0 module/build/ext/config/.gitkeep | 0 module/build/ext/control/.gitkeep | 0 module/build/ext/css/.gitkeep | 0 module/build/ext/js/.gitkeep | 0 module/build/ext/lang/en/.gitkeep | 0 module/build/ext/lang/zh-cn/.gitkeep | 0 module/build/ext/lang/zh-tw/.gitkeep | 0 module/build/ext/model/.gitkeep | 0 module/build/ext/view/.gitkeep | 0 module/common/ext/config/.gitkeep | 0 module/common/ext/control/.gitkeep | 0 module/common/ext/css/.gitkeep | 0 module/common/ext/js/.gitkeep | 0 module/common/ext/lang/en/.gitkeep | 0 module/common/ext/lang/zh-cn/.gitkeep | 0 module/common/ext/lang/zh-tw/.gitkeep | 0 module/common/ext/model/.gitkeep | 0 module/common/ext/view/.gitkeep | 0 module/company/ext/config/.gitkeep | 0 module/company/ext/control/.gitkeep | 0 module/company/ext/css/.gitkeep | 0 module/company/ext/js/.gitkeep | 0 module/company/ext/lang/en/.gitkeep | 0 module/company/ext/lang/zh-cn/.gitkeep | 0 module/company/ext/lang/zh-tw/.gitkeep | 0 module/company/ext/model/.gitkeep | 0 module/company/ext/view/.gitkeep | 0 module/convert/ext/config/.gitkeep | 0 module/convert/ext/control/.gitkeep | 0 module/convert/ext/css/.gitkeep | 0 module/convert/ext/js/.gitkeep | 0 module/convert/ext/lang/en/.gitkeep | 0 module/convert/ext/lang/zh-cn/.gitkeep | 0 module/convert/ext/lang/zh-tw/.gitkeep | 0 module/convert/ext/model/.gitkeep | 0 module/convert/ext/view/.gitkeep | 0 module/dept/ext/config/.gitkeep | 0 module/dept/ext/control/.gitkeep | 0 module/dept/ext/css/.gitkeep | 0 module/dept/ext/js/.gitkeep | 0 module/dept/ext/lang/en/.gitkeep | 0 module/dept/ext/lang/zh-cn/.gitkeep | 0 module/dept/ext/lang/zh-tw/.gitkeep | 0 module/dept/ext/model/.gitkeep | 0 module/dept/ext/view/.gitkeep | 0 module/doc/ext/config/.gitkeep | 0 module/doc/ext/control/.gitkeep | 0 module/doc/ext/css/.gitkeep | 0 module/doc/ext/js/.gitkeep | 0 module/doc/ext/lang/en/.gitkeep | 0 module/doc/ext/lang/zh-cn/.gitkeep | 0 module/doc/ext/lang/zh-tw/.gitkeep | 0 module/doc/ext/model/.gitkeep | 0 module/doc/ext/view/.gitkeep | 0 module/editor/ext/config/.gitkeep | 0 module/editor/ext/control/.gitkeep | 0 module/editor/ext/css/.gitkeep | 0 module/editor/ext/js/.gitkeep | 0 module/editor/ext/lang/en/.gitkeep | 0 module/editor/ext/lang/zh-cn/.gitkeep | 0 module/editor/ext/lang/zh-tw/.gitkeep | 0 module/editor/ext/model/.gitkeep | 0 module/editor/ext/view/.gitkeep | 0 module/extension/ext/config/.gitkeep | 0 module/extension/ext/control/.gitkeep | 0 module/extension/ext/css/.gitkeep | 0 module/extension/ext/js/.gitkeep | 0 module/extension/ext/lang/en/.gitkeep | 0 module/extension/ext/lang/zh-cn/.gitkeep | 0 module/extension/ext/lang/zh-tw/.gitkeep | 0 module/extension/ext/model/.gitkeep | 0 module/extension/ext/view/.gitkeep | 0 module/file/ext/config/.gitkeep | 0 module/file/ext/control/.gitkeep | 0 module/file/ext/css/.gitkeep | 0 module/file/ext/js/.gitkeep | 0 module/file/ext/lang/en/.gitkeep | 0 module/file/ext/lang/zh-cn/.gitkeep | 0 module/file/ext/lang/zh-tw/.gitkeep | 0 module/file/ext/model/.gitkeep | 0 module/file/ext/view/.gitkeep | 0 module/git/ext/config/.gitkeep | 0 module/git/ext/control/.gitkeep | 0 module/git/ext/css/.gitkeep | 0 module/git/ext/js/.gitkeep | 0 module/git/ext/lang/.gitkeep | 0 module/git/ext/model/.gitkeep | 0 module/git/ext/view/.gitkeep | 0 module/group/ext/config/.gitkeep | 0 module/group/ext/control/.gitkeep | 0 module/group/ext/css/.gitkeep | 0 module/group/ext/js/.gitkeep | 0 module/group/ext/lang/en/.gitkeep | 0 module/group/ext/lang/zh-cn/.gitkeep | 0 module/group/ext/lang/zh-tw/.gitkeep | 0 module/group/ext/model/.gitkeep | 0 module/group/ext/view/.gitkeep | 0 module/index/ext/config/.gitkeep | 0 module/index/ext/control/.gitkeep | 0 module/index/ext/css/.gitkeep | 0 module/index/ext/js/.gitkeep | 0 module/index/ext/lang/en/.gitkeep | 0 module/index/ext/lang/zh-cn/.gitkeep | 0 module/index/ext/lang/zh-tw/.gitkeep | 0 module/index/ext/model/.gitkeep | 0 module/index/ext/view/.gitkeep | 0 module/install/ext/config/.gitkeep | 0 module/install/ext/control/.gitkeep | 0 module/install/ext/css/.gitkeep | 0 module/install/ext/js/.gitkeep | 0 module/install/ext/lang/en/.gitkeep | 0 module/install/ext/lang/zh-cn/.gitkeep | 0 module/install/ext/lang/zh-tw/.gitkeep | 0 module/install/ext/model/.gitkeep | 0 module/install/ext/view/.gitkeep | 0 module/mail/ext/config/.gitkeep | 0 module/mail/ext/control/.gitkeep | 0 module/mail/ext/css/.gitkeep | 0 module/mail/ext/js/.gitkeep | 0 module/mail/ext/lang/en/.gitkeep | 0 module/mail/ext/lang/zh-cn/.gitkeep | 0 module/mail/ext/lang/zh-tw/.gitkeep | 0 module/mail/ext/model/.gitkeep | 0 module/mail/ext/view/.gitkeep | 0 module/misc/ext/config/.gitkeep | 0 module/misc/ext/control/.gitkeep | 0 module/misc/ext/css/.gitkeep | 0 module/misc/ext/js/.gitkeep | 0 module/misc/ext/lang/en/.gitkeep | 0 module/misc/ext/lang/zh-cn/.gitkeep | 0 module/misc/ext/lang/zh-tw/.gitkeep | 0 module/misc/ext/model/.gitkeep | 0 module/misc/ext/view/.gitkeep | 0 module/my/ext/config/.gitkeep | 0 module/my/ext/control/.gitkeep | 0 module/my/ext/css/.gitkeep | 0 module/my/ext/js/.gitkeep | 0 module/my/ext/lang/en/.gitkeep | 0 module/my/ext/lang/zh-cn/.gitkeep | 0 module/my/ext/lang/zh-tw/.gitkeep | 0 module/my/ext/model/.gitkeep | 0 module/my/ext/view/.gitkeep | 0 module/product/ext/config/.gitkeep | 0 module/product/ext/control/.gitkeep | 0 module/product/ext/css/.gitkeep | 0 module/product/ext/js/.gitkeep | 0 module/product/ext/lang/en/.gitkeep | 0 module/product/ext/lang/zh-cn/.gitkeep | 0 module/product/ext/lang/zh-tw/.gitkeep | 0 module/product/ext/model/.gitkeep | 0 module/product/ext/view/.gitkeep | 0 module/productplan/ext/config/.gitkeep | 0 module/productplan/ext/control/.gitkeep | 0 module/productplan/ext/css/.gitkeep | 0 module/productplan/ext/js/.gitkeep | 0 module/productplan/ext/lang/en/.gitkeep | 0 module/productplan/ext/lang/zh-cn/.gitkeep | 0 module/productplan/ext/lang/zh-tw/.gitkeep | 0 module/productplan/ext/model/.gitkeep | 0 module/productplan/ext/view/.gitkeep | 0 module/project/ext/config/.gitkeep | 0 module/project/ext/control/.gitkeep | 0 module/project/ext/css/.gitkeep | 0 module/project/ext/js/.gitkeep | 0 module/project/ext/lang/en/.gitkeep | 0 module/project/ext/lang/zh-cn/.gitkeep | 0 module/project/ext/lang/zh-tw/.gitkeep | 0 module/project/ext/model/.gitkeep | 0 module/project/ext/view/.gitkeep | 0 module/qa/ext/config/.gitkeep | 0 module/qa/ext/control/.gitkeep | 0 module/qa/ext/css/.gitkeep | 0 module/qa/ext/js/.gitkeep | 0 module/qa/ext/lang/en/.gitkeep | 0 module/qa/ext/lang/zh-cn/.gitkeep | 0 module/qa/ext/lang/zh-tw/.gitkeep | 0 module/qa/ext/model/.gitkeep | 0 module/qa/ext/view/.gitkeep | 0 module/report/ext/config/.gitkeep | 0 module/report/ext/control/.gitkeep | 0 module/report/ext/css/.gitkeep | 0 module/report/ext/js/.gitkeep | 0 module/report/ext/lang/en/.gitkeep | 0 module/report/ext/lang/zh-cn/.gitkeep | 0 module/report/ext/lang/zh-tw/.gitkeep | 0 module/report/ext/model/.gitkeep | 0 module/report/ext/view/.gitkeep | 0 module/search/ext/config/.gitkeep | 0 module/search/ext/control/.gitkeep | 0 module/search/ext/css/.gitkeep | 0 module/search/ext/js/.gitkeep | 0 module/search/ext/lang/en/.gitkeep | 0 module/search/ext/lang/zh-cn/.gitkeep | 0 module/search/ext/lang/zh-tw/.gitkeep | 0 module/search/ext/model/.gitkeep | 0 module/search/ext/view/.gitkeep | 0 module/setting/ext/config/.gitkeep | 0 module/setting/ext/control/.gitkeep | 0 module/setting/ext/css/.gitkeep | 0 module/setting/ext/js/.gitkeep | 0 module/setting/ext/lang/en/.gitkeep | 0 module/setting/ext/lang/zh-cn/.gitkeep | 0 module/setting/ext/lang/zh-tw/.gitkeep | 0 module/setting/ext/model/.gitkeep | 0 module/setting/ext/view/.gitkeep | 0 module/story/ext/config/.gitkeep | 0 module/story/ext/control/.gitkeep | 0 module/story/ext/css/.gitkeep | 0 module/story/ext/js/.gitkeep | 0 module/story/ext/lang/en/.gitkeep | 0 module/story/ext/lang/zh-cn/.gitkeep | 0 module/story/ext/lang/zh-tw/.gitkeep | 0 module/story/ext/model/.gitkeep | 0 module/story/ext/view/.gitkeep | 0 module/svn/ext/config/.gitkeep | 0 module/svn/ext/control/.gitkeep | 0 module/svn/ext/css/.gitkeep | 0 module/svn/ext/js/.gitkeep | 0 module/svn/ext/lang/en/.gitkeep | 0 module/svn/ext/lang/zh-cn/.gitkeep | 0 module/svn/ext/lang/zh-tw/.gitkeep | 0 module/svn/ext/model/.gitkeep | 0 module/svn/ext/view/.gitkeep | 0 module/testcase/ext/config/.gitkeep | 0 module/testcase/ext/control/.gitkeep | 0 module/testcase/ext/css/.gitkeep | 0 module/testcase/ext/js/.gitkeep | 0 module/testcase/ext/lang/en/.gitkeep | 0 module/testcase/ext/lang/zh-cn/.gitkeep | 0 module/testcase/ext/lang/zh-tw/.gitkeep | 0 module/testcase/ext/model/.gitkeep | 0 module/testcase/ext/view/.gitkeep | 0 module/testtask/ext/config/.gitkeep | 0 module/testtask/ext/control/.gitkeep | 0 module/testtask/ext/css/.gitkeep | 0 module/testtask/ext/js/.gitkeep | 0 module/testtask/ext/lang/en/.gitkeep | 0 module/testtask/ext/lang/zh-cn/.gitkeep | 0 module/testtask/ext/lang/zh-tw/.gitkeep | 0 module/testtask/ext/model/.gitkeep | 0 module/testtask/ext/view/.gitkeep | 0 module/todo/ext/config/.gitkeep | 0 module/todo/ext/control/.gitkeep | 0 module/todo/ext/css/.gitkeep | 0 module/todo/ext/js/.gitkeep | 0 module/todo/ext/lang/en/.gitkeep | 0 module/todo/ext/lang/zh-cn/.gitkeep | 0 module/todo/ext/lang/zh-tw/.gitkeep | 0 module/todo/ext/model/.gitkeep | 0 module/todo/ext/view/.gitkeep | 0 module/tree/ext/config/.gitkeep | 0 module/tree/ext/control/.gitkeep | 0 module/tree/ext/css/.gitkeep | 0 module/tree/ext/js/.gitkeep | 0 module/tree/ext/lang/en/.gitkeep | 0 module/tree/ext/lang/zh-cn/.gitkeep | 0 module/tree/ext/lang/zh-tw/.gitkeep | 0 module/tree/ext/model/.gitkeep | 0 module/tree/ext/view/.gitkeep | 0 module/upgrade/ext/config/.gitkeep | 0 module/upgrade/ext/control/.gitkeep | 0 module/upgrade/ext/css/.gitkeep | 0 module/upgrade/ext/js/.gitkeep | 0 module/upgrade/ext/lang/en/.gitkeep | 0 module/upgrade/ext/lang/zh-cn/.gitkeep | 0 module/upgrade/ext/lang/zh-tw/.gitkeep | 0 module/upgrade/ext/model/.gitkeep | 0 module/upgrade/ext/view/.gitkeep | 0 module/user/ext/config/.gitkeep | 0 module/user/ext/control/.gitkeep | 0 module/user/ext/css/.gitkeep | 0 module/user/ext/js/.gitkeep | 0 module/user/ext/lang/en/.gitkeep | 0 module/user/ext/lang/zh-cn/.gitkeep | 0 module/user/ext/lang/zh-tw/.gitkeep | 0 module/user/ext/model/.gitkeep | 0 module/user/ext/view/.gitkeep | 0 tmp/cache/.gitkeep | 0 tmp/extension/.gitkeep | 0 tmp/log/.gitkeep | 0 tmp/model/.gitkeep | 0 tools/check.php | 259 -- tools/cn2tw.php | 18 - tools/copylang.php | 17 - tools/exportactions.php | 29 - tools/getallcommon.php | 28 - tools/initext.php | 73 - tools/minifyfront.php | 74 - tools/preparetest.php | 60 - tools/replace.sh | 17 - tools/syncext.php | 100 - tools/syncext.sh | 62 - tools/zdog.sh | 39 - 337 files changed, 5749 deletions(-) delete mode 100644 .gitignore delete mode 100644 framework/base/control.class.php delete mode 100644 framework/base/helper.class.php delete mode 100644 framework/base/model.class.php delete mode 100644 framework/base/router.class.php delete mode 100644 framework/control.class.php delete mode 100644 framework/helper.class.php delete mode 100644 framework/model.class.php delete mode 100755 framework/router.class.php delete mode 100644 framework/tests/helper/.101.setmember.onelevel.et delete mode 100644 framework/tests/helper/.110.setmember.multilevel.et delete mode 100644 framework/tests/helper/.201.getwebroot.inbrowse.et delete mode 100644 framework/tests/helper/.202.getwebroot.inshell.et delete mode 100755 framework/tests/helper/101.setmember.onelevel.php delete mode 100755 framework/tests/helper/110.setmember.multilevel.php delete mode 100755 framework/tests/helper/201.getwebroot.inbrowse.php delete mode 100755 framework/tests/helper/202.getwebroot.inshell.php delete mode 100755 module/action/ext/config/.gitkeep delete mode 100755 module/action/ext/control/.gitkeep delete mode 100755 module/action/ext/css/.gitkeep delete mode 100755 module/action/ext/js/.gitkeep delete mode 100755 module/action/ext/lang/en/.gitkeep delete mode 100755 module/action/ext/lang/zh-cn/.gitkeep delete mode 100755 module/action/ext/lang/zh-tw/.gitkeep delete mode 100755 module/action/ext/model/.gitkeep delete mode 100755 module/action/ext/view/.gitkeep delete mode 100644 module/admin/ext/config/.gitkeep delete mode 100644 module/admin/ext/control/.gitkeep delete mode 100644 module/admin/ext/css/.gitkeep delete mode 100644 module/admin/ext/js/.gitkeep delete mode 100644 module/admin/ext/lang/en/.gitkeep delete mode 100644 module/admin/ext/lang/zh-cn/.gitkeep delete mode 100644 module/admin/ext/lang/zh-tw/.gitkeep delete mode 100644 module/admin/ext/model/.gitkeep delete mode 100644 module/admin/ext/view/.gitkeep delete mode 100644 module/api/ext/config/.gitkeep delete mode 100644 module/api/ext/control/.gitkeep delete mode 100644 module/api/ext/css/.gitkeep delete mode 100644 module/api/ext/js/.gitkeep delete mode 100644 module/api/ext/lang/en/.gitkeep delete mode 100644 module/api/ext/lang/zh-cn/.gitkeep delete mode 100644 module/api/ext/lang/zh-tw/.gitkeep delete mode 100644 module/api/ext/model/.gitkeep delete mode 100644 module/api/ext/view/.gitkeep delete mode 100644 module/build/ext/config/.gitkeep delete mode 100755 module/build/ext/control/.gitkeep delete mode 100644 module/build/ext/css/.gitkeep delete mode 100644 module/build/ext/js/.gitkeep delete mode 100755 module/build/ext/lang/en/.gitkeep delete mode 100755 module/build/ext/lang/zh-cn/.gitkeep delete mode 100644 module/build/ext/lang/zh-tw/.gitkeep delete mode 100755 module/build/ext/model/.gitkeep delete mode 100755 module/build/ext/view/.gitkeep delete mode 100644 module/common/ext/config/.gitkeep delete mode 100644 module/common/ext/control/.gitkeep delete mode 100644 module/common/ext/css/.gitkeep delete mode 100644 module/common/ext/js/.gitkeep delete mode 100755 module/common/ext/lang/en/.gitkeep delete mode 100755 module/common/ext/lang/zh-cn/.gitkeep delete mode 100644 module/common/ext/lang/zh-tw/.gitkeep delete mode 100644 module/common/ext/model/.gitkeep delete mode 100644 module/common/ext/view/.gitkeep delete mode 100644 module/company/ext/config/.gitkeep delete mode 100644 module/company/ext/control/.gitkeep delete mode 100644 module/company/ext/css/.gitkeep delete mode 100644 module/company/ext/js/.gitkeep delete mode 100644 module/company/ext/lang/en/.gitkeep delete mode 100644 module/company/ext/lang/zh-cn/.gitkeep delete mode 100644 module/company/ext/lang/zh-tw/.gitkeep delete mode 100644 module/company/ext/model/.gitkeep delete mode 100644 module/company/ext/view/.gitkeep delete mode 100644 module/convert/ext/config/.gitkeep delete mode 100644 module/convert/ext/control/.gitkeep delete mode 100644 module/convert/ext/css/.gitkeep delete mode 100644 module/convert/ext/js/.gitkeep delete mode 100644 module/convert/ext/lang/en/.gitkeep delete mode 100644 module/convert/ext/lang/zh-cn/.gitkeep delete mode 100644 module/convert/ext/lang/zh-tw/.gitkeep delete mode 100644 module/convert/ext/model/.gitkeep delete mode 100644 module/convert/ext/view/.gitkeep delete mode 100644 module/dept/ext/config/.gitkeep delete mode 100644 module/dept/ext/control/.gitkeep delete mode 100644 module/dept/ext/css/.gitkeep delete mode 100644 module/dept/ext/js/.gitkeep delete mode 100644 module/dept/ext/lang/en/.gitkeep delete mode 100644 module/dept/ext/lang/zh-cn/.gitkeep delete mode 100644 module/dept/ext/lang/zh-tw/.gitkeep delete mode 100644 module/dept/ext/model/.gitkeep delete mode 100644 module/dept/ext/view/.gitkeep delete mode 100644 module/doc/ext/config/.gitkeep delete mode 100644 module/doc/ext/control/.gitkeep delete mode 100644 module/doc/ext/css/.gitkeep delete mode 100644 module/doc/ext/js/.gitkeep delete mode 100755 module/doc/ext/lang/en/.gitkeep delete mode 100755 module/doc/ext/lang/zh-cn/.gitkeep delete mode 100644 module/doc/ext/lang/zh-tw/.gitkeep delete mode 100644 module/doc/ext/model/.gitkeep delete mode 100644 module/doc/ext/view/.gitkeep delete mode 100644 module/editor/ext/config/.gitkeep delete mode 100644 module/editor/ext/control/.gitkeep delete mode 100644 module/editor/ext/css/.gitkeep delete mode 100644 module/editor/ext/js/.gitkeep delete mode 100644 module/editor/ext/lang/en/.gitkeep delete mode 100644 module/editor/ext/lang/zh-cn/.gitkeep delete mode 100644 module/editor/ext/lang/zh-tw/.gitkeep delete mode 100644 module/editor/ext/model/.gitkeep delete mode 100644 module/editor/ext/view/.gitkeep delete mode 100755 module/extension/ext/config/.gitkeep delete mode 100755 module/extension/ext/control/.gitkeep delete mode 100755 module/extension/ext/css/.gitkeep delete mode 100755 module/extension/ext/js/.gitkeep delete mode 100755 module/extension/ext/lang/en/.gitkeep delete mode 100755 module/extension/ext/lang/zh-cn/.gitkeep delete mode 100755 module/extension/ext/lang/zh-tw/.gitkeep delete mode 100755 module/extension/ext/model/.gitkeep delete mode 100755 module/extension/ext/view/.gitkeep delete mode 100644 module/file/ext/config/.gitkeep delete mode 100644 module/file/ext/control/.gitkeep delete mode 100644 module/file/ext/css/.gitkeep delete mode 100644 module/file/ext/js/.gitkeep delete mode 100644 module/file/ext/lang/en/.gitkeep delete mode 100644 module/file/ext/lang/zh-cn/.gitkeep delete mode 100644 module/file/ext/lang/zh-tw/.gitkeep delete mode 100644 module/file/ext/model/.gitkeep delete mode 100644 module/file/ext/view/.gitkeep delete mode 100644 module/git/ext/config/.gitkeep delete mode 100644 module/git/ext/control/.gitkeep delete mode 100644 module/git/ext/css/.gitkeep delete mode 100644 module/git/ext/js/.gitkeep delete mode 100644 module/git/ext/lang/.gitkeep delete mode 100644 module/git/ext/model/.gitkeep delete mode 100644 module/git/ext/view/.gitkeep delete mode 100644 module/group/ext/config/.gitkeep delete mode 100644 module/group/ext/control/.gitkeep delete mode 100644 module/group/ext/css/.gitkeep delete mode 100644 module/group/ext/js/.gitkeep delete mode 100755 module/group/ext/lang/en/.gitkeep delete mode 100755 module/group/ext/lang/zh-cn/.gitkeep delete mode 100644 module/group/ext/lang/zh-tw/.gitkeep delete mode 100644 module/group/ext/model/.gitkeep delete mode 100644 module/group/ext/view/.gitkeep delete mode 100644 module/index/ext/config/.gitkeep delete mode 100644 module/index/ext/control/.gitkeep delete mode 100644 module/index/ext/css/.gitkeep delete mode 100644 module/index/ext/js/.gitkeep delete mode 100644 module/index/ext/lang/en/.gitkeep delete mode 100644 module/index/ext/lang/zh-cn/.gitkeep delete mode 100644 module/index/ext/lang/zh-tw/.gitkeep delete mode 100644 module/index/ext/model/.gitkeep delete mode 100644 module/index/ext/view/.gitkeep delete mode 100644 module/install/ext/config/.gitkeep delete mode 100644 module/install/ext/control/.gitkeep delete mode 100644 module/install/ext/css/.gitkeep delete mode 100644 module/install/ext/js/.gitkeep delete mode 100644 module/install/ext/lang/en/.gitkeep delete mode 100644 module/install/ext/lang/zh-cn/.gitkeep delete mode 100644 module/install/ext/lang/zh-tw/.gitkeep delete mode 100644 module/install/ext/model/.gitkeep delete mode 100644 module/install/ext/view/.gitkeep delete mode 100644 module/mail/ext/config/.gitkeep delete mode 100644 module/mail/ext/control/.gitkeep delete mode 100644 module/mail/ext/css/.gitkeep delete mode 100644 module/mail/ext/js/.gitkeep delete mode 100644 module/mail/ext/lang/en/.gitkeep delete mode 100644 module/mail/ext/lang/zh-cn/.gitkeep delete mode 100644 module/mail/ext/lang/zh-tw/.gitkeep delete mode 100644 module/mail/ext/model/.gitkeep delete mode 100644 module/mail/ext/view/.gitkeep delete mode 100644 module/misc/ext/config/.gitkeep delete mode 100644 module/misc/ext/control/.gitkeep delete mode 100644 module/misc/ext/css/.gitkeep delete mode 100644 module/misc/ext/js/.gitkeep delete mode 100644 module/misc/ext/lang/en/.gitkeep delete mode 100644 module/misc/ext/lang/zh-cn/.gitkeep delete mode 100644 module/misc/ext/lang/zh-tw/.gitkeep delete mode 100644 module/misc/ext/model/.gitkeep delete mode 100644 module/misc/ext/view/.gitkeep delete mode 100755 module/my/ext/config/.gitkeep delete mode 100755 module/my/ext/control/.gitkeep delete mode 100644 module/my/ext/css/.gitkeep delete mode 100644 module/my/ext/js/.gitkeep delete mode 100755 module/my/ext/lang/en/.gitkeep delete mode 100755 module/my/ext/lang/zh-cn/.gitkeep delete mode 100644 module/my/ext/lang/zh-tw/.gitkeep delete mode 100644 module/my/ext/model/.gitkeep delete mode 100755 module/my/ext/view/.gitkeep delete mode 100644 module/product/ext/config/.gitkeep delete mode 100755 module/product/ext/control/.gitkeep delete mode 100644 module/product/ext/css/.gitkeep delete mode 100644 module/product/ext/js/.gitkeep delete mode 100755 module/product/ext/lang/en/.gitkeep delete mode 100755 module/product/ext/lang/zh-cn/.gitkeep delete mode 100644 module/product/ext/lang/zh-tw/.gitkeep delete mode 100755 module/product/ext/model/.gitkeep delete mode 100755 module/product/ext/view/.gitkeep delete mode 100644 module/productplan/ext/config/.gitkeep delete mode 100644 module/productplan/ext/control/.gitkeep delete mode 100644 module/productplan/ext/css/.gitkeep delete mode 100644 module/productplan/ext/js/.gitkeep delete mode 100644 module/productplan/ext/lang/en/.gitkeep delete mode 100644 module/productplan/ext/lang/zh-cn/.gitkeep delete mode 100644 module/productplan/ext/lang/zh-tw/.gitkeep delete mode 100644 module/productplan/ext/model/.gitkeep delete mode 100644 module/productplan/ext/view/.gitkeep delete mode 100644 module/project/ext/config/.gitkeep delete mode 100644 module/project/ext/control/.gitkeep delete mode 100644 module/project/ext/css/.gitkeep delete mode 100644 module/project/ext/js/.gitkeep delete mode 100644 module/project/ext/lang/en/.gitkeep delete mode 100644 module/project/ext/lang/zh-cn/.gitkeep delete mode 100644 module/project/ext/lang/zh-tw/.gitkeep delete mode 100644 module/project/ext/model/.gitkeep delete mode 100644 module/project/ext/view/.gitkeep delete mode 100644 module/qa/ext/config/.gitkeep delete mode 100644 module/qa/ext/control/.gitkeep delete mode 100644 module/qa/ext/css/.gitkeep delete mode 100644 module/qa/ext/js/.gitkeep delete mode 100644 module/qa/ext/lang/en/.gitkeep delete mode 100644 module/qa/ext/lang/zh-cn/.gitkeep delete mode 100644 module/qa/ext/lang/zh-tw/.gitkeep delete mode 100644 module/qa/ext/model/.gitkeep delete mode 100644 module/qa/ext/view/.gitkeep delete mode 100644 module/report/ext/config/.gitkeep delete mode 100755 module/report/ext/control/.gitkeep delete mode 100644 module/report/ext/css/.gitkeep delete mode 100644 module/report/ext/js/.gitkeep delete mode 100644 module/report/ext/lang/en/.gitkeep delete mode 100755 module/report/ext/lang/zh-cn/.gitkeep delete mode 100644 module/report/ext/lang/zh-tw/.gitkeep delete mode 100755 module/report/ext/model/.gitkeep delete mode 100755 module/report/ext/view/.gitkeep delete mode 100644 module/search/ext/config/.gitkeep delete mode 100644 module/search/ext/control/.gitkeep delete mode 100644 module/search/ext/css/.gitkeep delete mode 100644 module/search/ext/js/.gitkeep delete mode 100644 module/search/ext/lang/en/.gitkeep delete mode 100644 module/search/ext/lang/zh-cn/.gitkeep delete mode 100644 module/search/ext/lang/zh-tw/.gitkeep delete mode 100644 module/search/ext/model/.gitkeep delete mode 100644 module/search/ext/view/.gitkeep delete mode 100644 module/setting/ext/config/.gitkeep delete mode 100644 module/setting/ext/control/.gitkeep delete mode 100644 module/setting/ext/css/.gitkeep delete mode 100644 module/setting/ext/js/.gitkeep delete mode 100644 module/setting/ext/lang/en/.gitkeep delete mode 100644 module/setting/ext/lang/zh-cn/.gitkeep delete mode 100644 module/setting/ext/lang/zh-tw/.gitkeep delete mode 100644 module/setting/ext/model/.gitkeep delete mode 100644 module/setting/ext/view/.gitkeep delete mode 100644 module/story/ext/config/.gitkeep delete mode 100644 module/story/ext/control/.gitkeep delete mode 100644 module/story/ext/css/.gitkeep delete mode 100644 module/story/ext/js/.gitkeep delete mode 100644 module/story/ext/lang/en/.gitkeep delete mode 100644 module/story/ext/lang/zh-cn/.gitkeep delete mode 100644 module/story/ext/lang/zh-tw/.gitkeep delete mode 100644 module/story/ext/model/.gitkeep delete mode 100644 module/story/ext/view/.gitkeep delete mode 100644 module/svn/ext/config/.gitkeep delete mode 100644 module/svn/ext/control/.gitkeep delete mode 100644 module/svn/ext/css/.gitkeep delete mode 100644 module/svn/ext/js/.gitkeep delete mode 100644 module/svn/ext/lang/en/.gitkeep delete mode 100644 module/svn/ext/lang/zh-cn/.gitkeep delete mode 100644 module/svn/ext/lang/zh-tw/.gitkeep delete mode 100644 module/svn/ext/model/.gitkeep delete mode 100644 module/svn/ext/view/.gitkeep delete mode 100644 module/testcase/ext/config/.gitkeep delete mode 100644 module/testcase/ext/control/.gitkeep delete mode 100644 module/testcase/ext/css/.gitkeep delete mode 100644 module/testcase/ext/js/.gitkeep delete mode 100644 module/testcase/ext/lang/en/.gitkeep delete mode 100644 module/testcase/ext/lang/zh-cn/.gitkeep delete mode 100644 module/testcase/ext/lang/zh-tw/.gitkeep delete mode 100644 module/testcase/ext/model/.gitkeep delete mode 100755 module/testcase/ext/view/.gitkeep delete mode 100644 module/testtask/ext/config/.gitkeep delete mode 100644 module/testtask/ext/control/.gitkeep delete mode 100644 module/testtask/ext/css/.gitkeep delete mode 100644 module/testtask/ext/js/.gitkeep delete mode 100644 module/testtask/ext/lang/en/.gitkeep delete mode 100644 module/testtask/ext/lang/zh-cn/.gitkeep delete mode 100644 module/testtask/ext/lang/zh-tw/.gitkeep delete mode 100644 module/testtask/ext/model/.gitkeep delete mode 100755 module/testtask/ext/view/.gitkeep delete mode 100644 module/todo/ext/config/.gitkeep delete mode 100644 module/todo/ext/control/.gitkeep delete mode 100644 module/todo/ext/css/.gitkeep delete mode 100644 module/todo/ext/js/.gitkeep delete mode 100644 module/todo/ext/lang/en/.gitkeep delete mode 100644 module/todo/ext/lang/zh-cn/.gitkeep delete mode 100644 module/todo/ext/lang/zh-tw/.gitkeep delete mode 100644 module/todo/ext/model/.gitkeep delete mode 100644 module/todo/ext/view/.gitkeep delete mode 100644 module/tree/ext/config/.gitkeep delete mode 100644 module/tree/ext/control/.gitkeep delete mode 100644 module/tree/ext/css/.gitkeep delete mode 100644 module/tree/ext/js/.gitkeep delete mode 100644 module/tree/ext/lang/en/.gitkeep delete mode 100644 module/tree/ext/lang/zh-cn/.gitkeep delete mode 100644 module/tree/ext/lang/zh-tw/.gitkeep delete mode 100644 module/tree/ext/model/.gitkeep delete mode 100755 module/tree/ext/view/.gitkeep delete mode 100644 module/upgrade/ext/config/.gitkeep delete mode 100644 module/upgrade/ext/control/.gitkeep delete mode 100644 module/upgrade/ext/css/.gitkeep delete mode 100644 module/upgrade/ext/js/.gitkeep delete mode 100644 module/upgrade/ext/lang/en/.gitkeep delete mode 100644 module/upgrade/ext/lang/zh-cn/.gitkeep delete mode 100644 module/upgrade/ext/lang/zh-tw/.gitkeep delete mode 100644 module/upgrade/ext/model/.gitkeep delete mode 100644 module/upgrade/ext/view/.gitkeep delete mode 100644 module/user/ext/config/.gitkeep delete mode 100755 module/user/ext/control/.gitkeep delete mode 100644 module/user/ext/css/.gitkeep delete mode 100644 module/user/ext/js/.gitkeep delete mode 100644 module/user/ext/lang/en/.gitkeep delete mode 100644 module/user/ext/lang/zh-cn/.gitkeep delete mode 100644 module/user/ext/lang/zh-tw/.gitkeep delete mode 100644 module/user/ext/model/.gitkeep delete mode 100755 module/user/ext/view/.gitkeep delete mode 100755 tmp/cache/.gitkeep delete mode 100755 tmp/extension/.gitkeep delete mode 100755 tmp/log/.gitkeep delete mode 100755 tmp/model/.gitkeep delete mode 100755 tools/check.php delete mode 100755 tools/cn2tw.php delete mode 100755 tools/copylang.php delete mode 100755 tools/exportactions.php delete mode 100755 tools/getallcommon.php delete mode 100755 tools/initext.php delete mode 100755 tools/minifyfront.php delete mode 100755 tools/preparetest.php delete mode 100755 tools/replace.sh delete mode 100755 tools/syncext.php delete mode 100755 tools/syncext.sh delete mode 100755 tools/zdog.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1a682f8be0..0000000000 --- a/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -config/my.php -www/data/ -www/install.php -www/upgrade.php -www/ok.txt -tools/* -release/* -tmp/* -tmp/extensions/* -tmp/log/* -tmp/model/* -tmp/cache/* -tmp/extension/* -.gitkeep -.bak -.idea -.gitignore -_log -framework/ \ No newline at end of file diff --git a/framework/base/control.class.php b/framework/base/control.class.php deleted file mode 100644 index 23dde557c2..0000000000 --- a/framework/base/control.class.php +++ /dev/null @@ -1,887 +0,0 @@ -post->key来引用$_POST变量。 - * The $post object, useer can access a post var by $this->post->key. - * - * @var ojbect - * @access public - */ - public $post; - - /** - * $get对象,用户可以通过$this->get->key来引用$_GET变量。 - * The $get object, useer can access a get var by $this->get->key. - * - * @var ojbect - * @access public - */ - public $get; - - /** - * $session对象,用户可以通过$this->session->key来引用$_SESSION变量。 - * The $session object, useer can access a session var by $this->session->key. - * - * @var ojbect - * @access public - */ - public $session; - - /** - * $server对象,用户可以通过$this->server->key来引用$_SERVER变量。 - * The $server object, useer can access a server var by $this->server->key. - * - * @var ojbect - * @access public - */ - public $server; - - /** - * $cookie对象,用户可以通过$this->cookie->key来引用$_COOKIE变量。 - * The $cookie object, useer can access a cookie var by $this->cookie->key. - * - * @var ojbect - * @access public - */ - public $cookie; - - /** - * 当前模块的名称。 - * The name of current module. - * - * @var string - * @access public - */ - public $moduleName; - - /** - * $view用于存放从control传到view视图的数据。 - * The vars assigned to the view page. - * - * @var object - * @access public - */ - public $view; - - /** - * 视图的类型,比如html, json。 - * The type of the view, such html, json. - * - * @var string - * @access public - */ - public $viewType; - - /** - * 输出到浏览器的内容。 - * The content to display. - * - * @var string - * @access public - */ - public $output; - - /** - * 客户端设备。 - * The client device. - * - * @var string - * @access public - */ - public $clientDevice; - - /** - * 不同设备下视图文件的前缀。 - * The prefix of view file for mobile or PC. - * - * @var string - * @access public - */ - public $devicePrefix; - - /** - * 构造方法。 - * - * 1. 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用; - * 2. 设置当前模块,读取该模块的model类; - * 3. 初始化$view视图类。 - * - * The construct function. - * - * 1. global the global vars, refer them by the class member such as $this->app. - * 2. set the pathes of current module, and load it's model class. - * 3. auto assign the $lang and $config to the view. - * - * @param string $moduleName - * @param string $methodName - * @param string $appName - * @access public - * @return void - */ - public function __construct($moduleName = '', $methodName = '', $appName = '') - { - /* - * 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用。 - * Global the globals, and refer them as a class member. - */ - global $app, $config, $lang, $dbh, $common; - $this->app = $app; - $this->config = $config; - $this->lang = $lang; - $this->dbh = $dbh; - $this->viewType = $this->app->getViewType(); - $this->appName = $appName ? $appName : $this->app->getAppName(); - - /** - * 设置当前模块,读取该模块的model类。 - * Load the model file auto. - */ - $this->setModuleName($moduleName); - $this->setMethodName($methodName); - $this->loadModel($this->moduleName, $appName); - - /** - * 如果客户端是手机的话,视图文件增加m.前缀。 - * If the clent is mobile, add m. as prefix for view file. - */ - $this->setClientDevice(); - $this->setDevicePrefix(); - - /** - * 初始化$view视图类。 - * Init the view vars. - */ - $this->view = new stdclass(); - $this->view->app = $app; - $this->view->lang = $lang; - $this->view->config = $config; - $this->view->common = $common; - $this->view->title = ''; - - /** - * 设置超级变量,从$app引用过来。 - * Set super vars. - */ - $this->setSuperVars(); - } - - //-------------------- Model相关方法(Model related methods) --------------------// - - /* - * 设置模块名。 - * Set the module name. - * - * @param string $moduleName 模块名,如果为空,则从$app中获取. The module name, if empty, get it from $app. - * @access public - * @return void - */ - public function setModuleName($moduleName = '') - { - $this->moduleName = $moduleName ? strtolower($moduleName) : $this->app->getModuleName(); - } - - /** - * 设置方法名。 - * Set the method name. - * - * @param string $methodName 方法名,如果为空,则从$app中获取。The method name, if empty, get it from $app. - * @access public - * @return void - */ - public function setMethodName($methodName = '') - { - $this->methodName = $methodName ? strtolower($methodName) : $this->app->getMethodName(); - } - - /** - * 加载指定模块的model文件。 - * Load the model file of one module. - * - * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. - * @param string $appName The app name, if empty, use current app's name. - * @access public - * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. - */ - public function loadModel($moduleName = '', $appName = '') - { - if(empty($moduleName)) $moduleName = $this->moduleName; - if(empty($appName)) $appName = $this->appName; - - global $loadedModels; - if(isset($loadedModels[$appName][$moduleName])) - { - $this->$moduleName = $loadedModels[$appName][$moduleName]; - $this->dao = $this->$moduleName->dao; - return $this->$moduleName; - } - - $modelFile = $this->app->setModelFile($moduleName, $appName); - - /** - * 如果没有model文件,尝试加载config配置信息。 - * If no model file, try load config. - */ - if(!helper::import($modelFile)) - { - $this->app->loadModuleConfig($moduleName, $appName); - $this->app->loadLang($moduleName, $appName); - $this->dao = new dao(); - return false; - } - - /** - * 如果没有扩展文件,model类名是$moduleName + 'model',如果有扩展,还需要增加ext前缀。 - * If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix. - */ - $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; - if(!class_exists($modelClass)) - { - $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; - if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); - } - - /** - * 初始化model对象,在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量,方便引用。 - * Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object. - */ - $loadedModels[$appName][$moduleName] = new $modelClass($appName); - $this->$moduleName = $loadedModels[$appName][$moduleName]; - $this->dao = $this->$moduleName->dao; - return $this->$moduleName; - } - - /** - * 设置超级全局变量,方便直接引用。 - * Set the super vars. - * - * @access public - * @return void - */ - public function setSuperVars() - { - $this->post = $this->app->post; - $this->get = $this->app->get; - $this->server = $this->app->server; - $this->session = $this->app->session; - $this->cookie = $this->app->cookie; - } - - /** - * 设置客户端的设备类型。 - * Set client device. - * - * @access public - * @return void - */ - public function setClientDevice() - { - $this->clientDevice = $this->app->clientDevice; - } - - /** - * 如果客户端是手机的话,视图文件增加m.前缀。 - * If the clent is mobile, add m. as prefix for view file. - * - * @access public - * @return void - */ - public function setDevicePrefix() - { - $this->devicePrefix = zget($this->config->devicePrefix, $this->viewType, ''); - } - - //-------------------- 视图相关方法(View related methods) --------------------// - - /** - * 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。 - * Set view files: the main file, extension view file, site extension view file and hook files. - * - * @param string $moduleName module name - * @param string $methodName method name - * @access public - * @return string the view file - */ - public function setViewFile($moduleName, $methodName) - { - $moduleName = strtolower(trim($moduleName)); - $methodName = strtolower(trim($methodName)); - - $modulePath = $this->app->getModulePath($this->appName, $moduleName); - $viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view'); - - $viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType; - $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php'; - $viewFile = $mainViewFile; - - if(!empty($viewExtPath)) - { - $commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; - $siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; - - $viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile; - $viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile; - if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true); - - $commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); - $siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); - $extHookFiles = array_merge((array) $commonExtHookFiles, (array) $siteExtHookFiles); - } - - if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles); - return $viewFile; - } - - /** - * 获取某一个视图文件的扩展。 - * Get the extension file of an view. - * - * @param string $viewFile - * @access public - * @return string|bool If extension view file exists, return the path. Else return fasle. - */ - public function getExtViewFile($viewFile) - { - /** - * 首先找sitecode下的扩展文件,如果没有,再找ext下的扩展文件。 - * Find extViewFile in ext/_$siteCode/view first, then try ext/view/. - */ - if($this->app->siteCode) - { - $extPath = dirname(dirname(realpath($viewFile))) . "/ext/_{$this->app->siteCode}/view"; - $extViewFile = $extPath . basename($viewFile); - - if(file_exists($extViewFile)) - { - helper::cd($extPath); - return $extViewFile; - } - } - - $extPath = dirname(dirname(realpath($viewFile))) . '/ext/view/'; - $extViewFile = $extPath . basename($viewFile); - if(file_exists($extViewFile)) - { - helper::cd($extPath); - return $extViewFile; - } - return false; - } - - /** - * 获取适用于当前方法的css:该模块公用的css + 当前方法的css + 扩展的css。 - * Get css codes applied to current method: module common css + method css + extension css. - * - * @param string $moduleName - * @param string $methodName - * @access public - * @return string - */ - public function getCSS($moduleName, $methodName) - { - $moduleName = strtolower(trim($moduleName)); - $methodName = strtolower(trim($methodName)); - - $modulePath = $this->app->getModulePath($this->appName, $moduleName); - $cssExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'css') ; - - $css = ''; - $mainCssFile = $modulePath . 'css' . DS . $this->devicePrefix . 'common.css'; - $methodCssFile = $modulePath . 'css' . DS . $this->devicePrefix . $methodName . '.css'; - if(file_exists($mainCssFile)) $css .= file_get_contents($mainCssFile); - if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile); - - if(!empty($cssExtPath)) - { - $cssMethodExt = $cssExtPath['common'] . $methodName . DS; - $cssCommonExt = $cssExtPath['common'] . 'common' . DS; - - $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); - if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); - - $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); - if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); - - if(!empty($cssExtPath['site'])) - { - $cssMethodExt = $cssExtPath['site'] . $methodName . DS; - $cssCommonExt = $cssExtPath['site'] . 'common' . DS; - $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); - if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); - - $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); - if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); - } - } - - return $css; - } - - /** - * 获取适用于当前方法的js:该模块公用的js + 当前方法的js + 扩展的js。 - * Get js codes applied to current method: module common js + method js + extension js. - * - * @param string $moduleName - * @param string $methodName - * @access public - * @return string - */ - public function getJS($moduleName, $methodName) - { - $moduleName = strtolower(trim($moduleName)); - $methodName = strtolower(trim($methodName)); - - $modulePath = $this->app->getModulePath($this->appName, $moduleName); - $jsExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'js'); - - $js = ''; - $mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . 'common.js'; - $methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . '.js'; - if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile); - if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile); - - if(!empty($jsExtPath)) - { - $jsMethodExt = $jsExtPath['common'] . $methodName . DS; - $jsCommonExt = $jsExtPath['common'] . 'common' . DS; - - $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); - if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); - - $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); - if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); - - if(!empty($jsExtPath['site'])) - { - $jsMethodExt = $jsExtPath['site'] . $methodName . DS; - $jsCommonExt = $jsExtPath['site'] . 'common' . DS; - - $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); - if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); - - $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); - if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); - } - } - - return $js; - } - - /** - * 向$view传递一个变量。 - * Assign one var to the view vars. - * - * @param string $name the name. - * @param mixed $value the value. - * @access public - * @return void - */ - public function assign($name, $value) - { - $this->view->$name = $value; - } - - /** - * 清空$output。 - * Clear the output. - * - * @access public - * @return void - */ - public function clear() - { - $this->output = ''; - } - - /** - * 渲染视图文件。 - * Parse view file. - * - * @param string $moduleName module name, if empty, use current module. - * @param string $methodName method name, if empty, use current method. - * @access public - * @return string the parsed result. - */ - public function parse($moduleName = '', $methodName = '') - { - if(empty($moduleName)) $moduleName = $this->moduleName; - if(empty($methodName)) $methodName = $this->methodName; - - if($this->viewType == 'json') $this->parseJSON($moduleName, $methodName); - if($this->viewType != 'json') $this->parseDefault($moduleName, $methodName); - - return $this->output; - } - - /** - * 渲染json格式。 - * Parse json format. - * - * @param string $moduleName module name - * @param string $methodName method name - * @access public - * @return void - */ - public function parseJSON($moduleName, $methodName) - { - unset($this->view->app); - unset($this->view->config); - unset($this->view->lang); - unset($this->view->header); - unset($this->view->position); - unset($this->view->moduleTree); - unset($this->view->common); - unset($this->view->pager->app); - unset($this->view->pager->lang); - - $output['status'] = is_object($this->view) ? 'success' : 'fail'; - $output['data'] = json_encode($this->view); - $output['md5'] = md5(json_encode($this->view)); - - $this->output = json_encode($output); - } - - /** - * 默认渲染方法,适用于viewType = html的时候。 - * Default parse method when viewType != json, like html. - * - * @param string $moduleName module name - * @param string $methodName method name - * @access public - * @return void - */ - public function parseDefault($moduleName, $methodName) - { - /** - * 设置视图文件。(PHP7有一个bug,不能直接$viewFile = $this->setViewFile())。 - * Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.) - */ - $results = $this->setViewFile($moduleName, $methodName); - $viewFile = $results; - if(is_array($results)) extract($results); - - /** - * 获得当前页面的CSS和JS。 - * Get css and js codes for current method. - */ - $css = $this->getCSS($moduleName, $methodName); - $js = $this->getJS($moduleName, $methodName); - if($css) $this->view->pageCSS = $css; - if($js) $this->view->pageJS = $js; - - /** - * 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。 - * Change the dir to the view file to keep the relative pathes work. - */ - $currentPWD = getcwd(); - chdir(dirname($viewFile)); - - /** - * 使用extract安定ob方法渲染$viewFile里面的代码。 - * Use extract and ob functions to eval the codes in $viewFile. - */ - extract((array)$this->view); - ob_start(); - include $viewFile; - if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile; - $this->output .= ob_get_contents(); - ob_end_clean(); - - /** - * 渲染完毕后,再切换回之前的路径。 - * At the end, chang the dir to the previous. - */ - chdir($currentPWD); - } - - /** - * 获取一个方法的输出内容,这样我们可以在一个方法里获取其他模块方法的内容。 - * 如果模块名为空,则调用该模块、该方法;如果设置了模块名,调用指定模块指定方法。 - * - * Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content. - * If the module name is empty, then use the current module and method. If set, use the user defined module and method. - * - * @param string $moduleName module name. - * @param string $methodName method name. - * @param array $params params. - * @access public - * @return string the parsed html. - */ - public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '') - { - /** - * 如果模块名为空,则调用该模块、该方法。 - * If the module name is empty, then use the current module and method. - */ - if($moduleName == '') $moduleName = $this->moduleName; - if($methodName == '') $methodName = $this->methodName; - if($appName == '') $appName = $this->appName; - if($moduleName == $this->moduleName and $methodName == $this->methodName) - { - $this->parse($moduleName, $methodName); - return $this->output; - } - - $currentModuleName = $this->moduleName; - $currentMethodName = $this->methodName; - $currentAppName = $this->appName; - $currentParams = $this->app->getParams(); - - /** - * 设置调用指定模块的指定方法。 - * chang the dir to the previous. - */ - $this->app->setModuleName($moduleName); - $this->app->setMethodName($methodName); - - if(!is_array($params)) parse_str($params, $params); - $this->app->params = $params; - - $currentPWD = getcwd(); - - /** - * 设置引用的文件和路径。 - * Set the pathes and files to included. - */ - $modulePath = $this->app->getModulePath($appName, $moduleName); - $moduleControlFile = $modulePath . 'control.php'; - $actionExtPath = $this->app->getModuleExtPath($appName, $moduleName, 'control'); - $file2Included = $moduleControlFile; - - if(!empty($actionExtPath)) - { - /** - * 设置公共扩展。 - * set common extension. - */ - $commonActionExtFile = $actionExtPath['common'] . strtolower($methodName) . '.php'; - $file2Included = file_exists($commonActionExtFile) ? $commonActionExtFile : $moduleControlFile; - - if(!empty($actionExtPath['site'])) - { - /** - * 设置站点扩展。 - * every site has it's extension. - */ - $siteActionExtFile = $actionExtPath['site'] . strtolower($methodName) . '.php'; - $file2Included = file_exists($siteActionExtFile) ? $siteActionExtFile : $file2Included; - } - } - - /** - * 加载控制器文件。 - * Load the control file. - */ - if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true); - chdir(dirname($file2Included)); - if($moduleName != $this->moduleName) helper::import($file2Included); - - /** - * 设置调用的类名。 - * Set the name of the class to be called. - */ - $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; - if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true); - - /** - * 解析参数,创建模块control对象。 - * Parse the params, create the $module control object. - */ - $module = new $className($moduleName, $methodName, $appName); - - /** - * 调用对应方法,使用ob方法获取输出内容。 - * Call the method and use ob function to get the output. - */ - ob_start(); - call_user_func_array(array($module, $methodName), $params); - $output = ob_get_contents(); - ob_end_clean(); - - unset($module); - - /** - * 切换回之前的模块和方法。 - * Chang the module、method to the previous. - */ - $this->app->setModuleName($currentModuleName); - $this->app->setMethodName($currentMethodName); - $this->app->params = $currentParams; - - chdir($currentPWD); - - /** - * 返回内容。 - * Return the content. - */ - return $output; - } - - /** - * 向浏览器输出内容。 - * Print the content of the view. - * - * @param string $moduleName module name - * @param string $methodName method name - * @access public - * @return void - */ - public function display($moduleName = '', $methodName = '') - { - if(empty($this->output)) $this->parse($moduleName, $methodName); - echo $this->output; - } - - /** - * 直接输出data数据,通常用于ajax请求中。 - * Send data directly, for ajax requests. - * - * @param misc $data - * @param string $type - * @access public - * @return void - */ - public function send($data, $type = 'json') - { - if($type != 'json') die(); - - $data = (array) $data; - if(helper::isAjaxRequest() or $this->viewType == 'json') print(json_encode($data)) and die(helper::removeUTF8Bom(ob_get_clean())); - - /** - * 响应非ajax的请求。 - * Response request not ajax. - */ - if(isset($data['result']) and $data['result'] == 'success') - { - if(!empty($data['message'])) echo js::alert($data['message']); - $locate = isset($data['locate']) ? $data['locate'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''); - if(!empty($locate)) die(js::locate($locate)); - die(isset($data['message']) ? $data['message'] : 'success'); - } - - if(isset($data['result']) and $data['result'] == 'fail') - { - if(!empty($data['message'])) - { - $message = json_decode(json_encode((array)$data['message'])); - foreach((array)$message as $item => $errors) $message->$item = implode(',', $errors); - echo js::alert(strip_tags(implode(" ", (array) $message))); - die(js::locate('back')); - } - die('fail'); - } - } - - /** - * 创建一个模块方法的链接。 - * Create a link to one method of one module. - * - * @param string $moduleName module name - * @param string $methodName method name - * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 - * @param string $viewType the view type - * @access public - * @return string the link string. - */ - public function createLink($moduleName, $methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) - { - if(empty($moduleName)) $moduleName = $this->moduleName; - return helper::createLink($moduleName, $methodName, $vars, $viewType, $onlybody); - } - - /** - * 创建当前模块的一个方法链接。 - * Create a link to the inner method of current module. - * - * @param string $methodName method name - * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 - * @param string $viewType the view type - * @access public - * @return string the link string. - */ - public function inlink($methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) - { - return helper::createLink($this->moduleName, $methodName, $vars, $viewType, $onlybody); - } - - /** - * 重定向到另一个页面。 - * Location to another page. - * - * @param string $url the target url. - * @access public - * @return void - */ - public function locate($url) - { - header("location: $url"); - exit; - } -} diff --git a/framework/base/helper.class.php b/framework/base/helper.class.php deleted file mode 100644 index 9066ccc455..0000000000 --- a/framework/base/helper.class.php +++ /dev/null @@ -1,747 +0,0 @@ - - * db->user = 'wwccss'; - * helper::setMember('lang', 'db.user', 'chunsheng.wang'); - * ?> - * - * @param string $objName the var name of the object. - * @param string $key the key of the member, can be parent.child. - * @param mixed $value the value to be set. - * @static - * @access public - * @return bool - */ - static public function setMember($objName, $key, $value) - { - global $$objName; - if(!is_object($$objName) or empty($key)) return false; - $key = str_replace('.', '->', $key); - $value = serialize($value); - $code = ("\$${objName}->{$key}=unserialize(<< - * 'value1', 'var2' => 'value2'); - * ?> - * - * @param string $moduleName module name, can pass appName like app.module. - * @param string $methodName method name - * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) or key1=value1&key2=value2 - * @param string $viewType the view type - * @param bool $onlyBody pass onlyBody=yes to the link thus the app can control the header and footer hide or show.. - * @static - * @access public - * @return string the link string. - */ - static public function createLink($moduleName, $methodName = 'index', $vars = '', $viewType = '', $onlyBody = false) - { - /* 设置$appName和$moduleName。Set appName and moduleName. */ - global $app, $config; - if(strpos($moduleName, '.') !== false) - { - list($appName, $moduleName) = explode('.', $moduleName); - } - else - { - $appName = $app->getAppName(); - } - if(!empty($appName)) $appName .= '/'; - - /* 处理$viewType和$vars。Set $viewType and $vars. */ - if(empty($viewType)) $viewType = $app->getViewType(); - if(!is_array($vars)) parse_str($vars, $vars); - - /* 生成url链接的开始部分。Set the begin parts of the link. */ - if($config->requestType == 'PATH_INFO') $link = $config->webRoot . $appName; - if($config->requestType != 'PATH_INFO') $link = $config->webRoot . $appName . basename($_SERVER['SCRIPT_NAME']); - if($config->requestType == 'PATH_INFO2') $link .= '/'; - - /** - * #1: RequestType为GET。When the requestType is GET. - * Input: moduleName=article&methodName=index&var1=value1. Output: ?m=article&f=index&var1=value1. - * - */ - if($config->requestType == 'GET') - { - $link .= "?{$config->moduleVar}=$moduleName&{$config->methodVar}=$methodName"; - if($viewType != 'html') $link .= "&{$config->viewVar}=" . $viewType; - foreach($vars as $key => $value) $link .= "&$key=$value"; - return self::processOnlyBodyParam($link, $onlyBody); - } - - /** - * #2: 方法名不是默认值或者是默认值,但有传参。MethodName equals the default method or vars not empty. - * Input: moduleName=article&methodName=view. Output: article-view.html - * Input: moduleName=article&methodName=view. Output: article-index-abc.html - * - */ - if($methodName != $config->default->method or !empty($vars)) - { - $link .= "$moduleName{$config->requestFix}$methodName"; - foreach($vars as $value) $link .= "{$config->requestFix}$value"; - $link .= '.' . $viewType; - - return self::processOnlyBodyParam($link, $onlyBody); - } - - /** - * #3: 方法名为默认值且没有传参且模块名为默认值。MethodName is the default and moduleName is default and vars empty. - * Input: moduleName=index&methodName=index. Output: index.html - * - */ - if($moduleName == $config->default->module) - { - $link .= $config->default->method . '.' . $viewType; - return self::processOnlyBodyParam($link, $onlyBody); - } - - /** - * #4: 方法名为默认值且没有传参且模块名不为默认值,viewType和app指定的相等。MethodName is default but moduleName not and viewType equal app's viewType.. - * Input: moduleName=article&methodName=index&viewType=html. Output: /article/ - * - */ - if($viewType == $app->getViewType()) - { - $link .= $moduleName . '/'; - return self::processOnlyBodyParam($link, $onlyBody); - } - - /** - * #5: 方法名为默认值且没有传参且模块名不为默认值,viewType有另外指定。MethodName is default but moduleName not and viewType no equls app's viewType. - * Input: moduleName=article&methodName=index&viewType=json. Output: /article.json - * - */ - $link .= $moduleName . '.' . $viewType; - return self::processOnlyBodyParam($link, $onlyBody); - } - - /** - * 处理onlyBody 参数。 - * Process the onlyBody param in url. - * - * 如果传参的时候设定了$onlyBody为真,或者当前页面请求中包含了onlybody=yes,在生成链接的时候继续追加。 - * If $onlyBody set to true or onlybody=yes in the url, append onlyBody param to the link. - * - * @param string $link - * @param bool $onlyBody - * @static - * @access public - * @return string - */ - public static function processOnlyBodyParam($link, $onlyBody = false) - { - global $config; - if(!$onlyBody and !self::inOnlyBodyMode()) return $link; - $onlybodyString = $config->requestType != 'GET' ? "?onlybody=yes" : "&onlybody=yes"; - return $link . $onlybodyString; - } - - /** - * 检查是否是onlybody模式。 - * Check in only body mode or not. - * - * @access public - * @return void - */ - public static function inOnlyBodyMode() - { - return (isset($_GET['onlybody']) and $_GET['onlybody'] == 'yes'); - } - - /** - * 使用helper::import()来引入文件,不要直接使用include或者require. - * Using helper::import() to import a file, instead of include or require. - * - * @param string $file the file to be imported. - * @static - * @access public - * @return bool - */ - static public function import($file) - { - if(!is_file($file)) return false; - - static $includedFiles = array(); - if(!isset($includedFiles[$file])) - { - include $file; - $includedFiles[$file] = true; - return true; - } - - return true; - } - - /** - * 将数组或者列表转化成 IN( 'a', 'b') 的形式。 - * Convert a list to IN('a', 'b') string. - * - * @param string|array $idList 列表,可以是数组或者用逗号隔开的列表。The id lists, can be a array or a string joined with comma. - * @static - * @access public - * @return string the string like IN('a', 'b'). - */ - static public function dbIN($idList) - { - if(is_array($idList)) - { - if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) - { - foreach ($idList as $key=>$value) $idList[$key] = addslashes($value); - } - return "IN ('" . join("','", $idList) . "')"; - } - - if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) $idList = addslashes($idList); - return "IN ('" . str_replace(',', "','", str_replace(' ', '', $idList)) . "')"; - } - - /** - * 安全的Base64编码,框架对'/'字符比较敏感,转换为'.'。 - * Create safe base64 encoded string for the framework. - * - * @param string $string the string to encode. - * @static - * @access public - * @return string encoded string. - */ - static public function safe64Encode($string) - { - return strtr(base64_encode($string), '/', '.'); - } - - /** - * 解码base64,先将之前的'.' 转换回'/' - * Decode the string encoded by safe64Encode. - * - * @param string $string the string to decode - * @static - * @access public - * @return string decoded string. - */ - static public function safe64Decode($string) - { - return base64_decode(strtr($string, '.', '/')); - } - - /** - * JSON编码,自动处理转义的问题。 - * JSON encode, process the slashes. - * - * @param mixed $data the object to encode - * @static - * @access public - * @return string decoded string. - */ - static public function jsonEncode($data) - { - return (function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc()) ? addslashes(json_encode($data)) : json_encode($data); - } - - /** - * 判断是否是utf8编码 - * Judge a string is utf-8 or not. - * - * @author hmdker@gmail.com - * @param string $string - * @see http://php.net/manual/en/function.mb-detect-encoding.php - * @static - * @access public - * @return bool - */ - static public function isUTF8($string) - { - $c = 0; - $b = 0; - $bits = 0; - $len = strlen($string); - for($i=0; $i<$len; $i++) - { - $c = ord($string[$i]); - if($c > 128) - { - if(($c >= 254)) return false; - elseif($c >= 252) $bits=6; - elseif($c >= 248) $bits=5; - elseif($c >= 240) $bits=4; - elseif($c >= 224) $bits=3; - elseif($c >= 192) $bits=2; - else return false; - if(($i+$bits) > $len) return false; - while($bits > 1) - { - $i++; - $b=ord($string[$i]); - if($b < 128 || $b > 191) return false; - $bits--; - } - } - } - return true; - } - - /** - * 去掉UTF-8 Bom头。 - * Remove UTF-8 Bom. - * - * @param string $string - * @access public - * @return string - */ - public static function removeUTF8Bom($string) - { - if(substr($string, 0, 3) == pack('CCC', 239, 187, 191)) return substr($string, 3); - return $string; - } - - /** - * 增强substr方法:支持多字节语言,比如中文。 - * Enhanced substr version: support multibyte languages like Chinese. - * - * @param string $string - * @param int $length - * @param string $append - * @return string - */ - public static function substr($string, $length, $append = '') - { - $rawString = $string; - - if(function_exists('mb_substr')) $string = mb_substr($string, 0, $length, 'utf-8'); - - preg_match_all("/./su", $string, $data); - $string = join("", array_slice($data[0], 0, $length)); - - return ($string != $rawString) ? $string . $append : $string; - } - - /** - * Get browser name and version. - * - * @access public - * @return array - */ - public static function getBrowser() - { - $browser = array('name'=>'unknown', 'version'=>'unknown'); - - if(empty($_SERVER['HTTP_USER_AGENT'])) return $browser; - - $agent = $_SERVER["HTTP_USER_AGENT"]; - - /* Chrome should checked before safari.*/ - if(strpos($agent, 'Firefox') !== false) $browser['name'] = "firefox"; - if(strpos($agent, 'Opera') !== false) $browser['name'] = 'opera'; - if(strpos($agent, 'Safari') !== false) $browser['name'] = 'safari'; - if(strpos($agent, 'Chrome') !== false) $browser['name'] = "chrome"; - - // Check the name of browser - if(strpos($agent, 'MSIE') !== false || strpos($agent, 'rv:11.0')) $browser['name'] = 'ie'; - if(strpos($agent, 'Edge') !== false) $browser['name'] = 'edge'; - - // Check the version of browser - if(preg_match('/MSIE\s(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - if(preg_match('/FireFox\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - if(preg_match('/Opera[\s|\/](\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - if(preg_match('/Chrome\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - - if((strpos($agent, 'Chrome') == false) && preg_match('/Safari\/(\d+)\..*$/i', $agent, $regs)) $browser['version'] = $regs[1]; - if(preg_match('/rv:(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - if(preg_match('/Edge\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; - - return $browser; - } - - /** - * Get client os from agent info. - * - * @static - * @access public - * @return string - */ - public static function getOS() - { - if(empty($_SERVER['HTTP_USER_AGENT'])) return 'unknow'; - - $osList = array(); - $osList['/windows nt 10/i'] = 'Windows 10'; - $osList['/windows nt 6.3/i'] = 'Windows 8.1'; - $osList['/windows nt 6.2/i'] = 'Windows 8'; - $osList['/windows nt 6.1/i'] = 'Windows 7'; - $osList['/windows nt 6.0/i'] = 'Windows Vista'; - $osList['/windows nt 5.2/i'] = 'Windows Server 2003/XP x64'; - $osList['/windows nt 5.1/i'] = 'Windows XP'; - $osList['/windows xp/i'] = 'Windows XP'; - $osList['/windows nt 5.0/i'] = 'Windows 2000'; - $osList['/windows me/i'] = 'Windows ME'; - $osList['/win98/i'] = 'Windows 98'; - $osList['/win95/i'] = 'Windows 95'; - $osList['/win16/i'] = 'Windows 3.11'; - $osList['/macintosh|mac os x/i'] = 'Mac OS X'; - $osList['/mac_powerpc/i'] = 'Mac OS 9'; - $osList['/linux/i'] = 'Linux'; - $osList['/ubuntu/i'] = 'Ubuntu'; - $osList['/iphone/i'] = 'iPhone'; - $osList['/ipod/i'] = 'iPod'; - $osList['/ipad/i'] = 'iPad'; - $osList['/android/i'] = 'Android'; - $osList['/blackberry/i'] = 'BlackBerry'; - $osList['/webos/i'] = 'Mobile'; - - foreach ($osList as $regex => $value) - { - if(preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) return $value; - } - - return 'unknown'; - } - - /** - * 计算两个日期相差的天数,取整。 - * Compute the diff days of two date. - * - * @param string $date1 the first date. - * @param string $date2 the sencond date. - * @access public - * @return int the diff of the two days. - */ - static public function diffDate($date1, $date2) - { - return round((strtotime($date1) - strtotime($date2)) / 86400, 0); - } - - /** - * 获取当前时间,使用common语言文件定义的DT_DATETIME1常量。 - * Get now time use the DT_DATETIME1 constant defined in the lang file. - * - * @access public - * @return datetime now - */ - static public function now() - { - return date(DT_DATETIME1); - } - - /** - * 获取当前日期,使用common语言文件定义的DT_DATE1常量。 - * Get today according to the DT_DATE1 constant defined in the lang file. - * - * @access public - * @return date today - */ - static public function today() - { - return date(DT_DATE1); - } - - /** - * 获取当前日期,使用common语言文件定义的DT_TIME1常量。 - * Get now time use the DT_TIME1 constant defined in the lang file. - * - * @access public - * @return date today - */ - static public function time() - { - return date(DT_TIME1); - } - - /** - * 判断日期是不是零。 - * Judge a date is zero or not. - * - * @access public - * @return bool - */ - static public function isZeroDate($date) - { - return substr($date, 0, 4) == '0000'; - } - - /** - * 列出目录中符合该正则表达式的文件。 - * Get files match the pattern under a directory. - * - * @access public - * @return array the files match the pattern - */ - static public function ls($dir, $pattern = '') - { - if(empty($dir)) return array(); - - $files = array(); - $dir = realpath($dir); - if(is_dir($dir)) $files = glob($dir . DIRECTORY_SEPARATOR . '*' . $pattern); - return empty($files) ? array() : $files; - } - - /** - * 切换目录。第一次调用的时候记录当前的路径,再次调用的时候切换回之前的路径。 - * Change directory: first call, save the $cwd, secend call, change to $cwd. - * - * @param string $path - * @static - * @access public - * @return void - */ - static function cd($path = '') - { - static $cwd = ''; - if($path) $cwd = getcwd(); - !empty($path) ? chdir($path) : chdir($cwd); - } - - /** - * 通过域名获取站点代号。 - * Get siteCode for a domain. - * - * www.xirang.com => xirang - * xirang.com => xirang - * xirang.com.cn => xirang - * xirang.cn => xirang - * xirang => xirang - * - * @param string $domain - * @return string $siteCode - **/ - public static function parseSiteCode($domain) - { - global $config; - - /* 去除域名中的端口部分。Remove the port part of the domain. */ - if(strpos($domain, ':') !== false) $domain = substr($domain, 0, strpos($domain, ':')); - $domain = strtolower($domain); - - /* $config里面有定义或者是localhost,直接返回。 Return directly if defined in $config or is localhost. */ - if(isset($config->siteCodeList[$domain])) return $config->siteCodeList[$domain]; - if($domain == 'localhost') return $domain; - - /* 将域名中的-改为_。Replace '-' with '_' in the domain. */ - $domain = str_replace('-', '_', $domain); - $items = explode('.', $domain); - - /* 类似a.com的形式。 Domain like a.com. */ - $postfix = str_replace($items[0] . '.', '', $domain); - if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; - - /* 类似www.a.com的形式。 Domain like www.a.com. */ - $postfix = str_replace($items[0] . '.' . $items[1] . '.', '', $domain); - if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[1]; - - /* 类似xxx.sub.a.com的形式。 Domain like xxx.sub.a.com. */ - $postfix = str_replace($items[0] . '.' . $items[1] . '.' . $items[2] . '.', '', $domain); - if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; - - return ''; - } - - /** - * 检查是否是AJAX请求。 - * Check is ajax request. - * - * @static - * @access public - * @return bool - */ - public static function isAjaxRequest() - { - if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; - if(isset($_GET['HTTP_X_REQUESTED_WITH']) && $_GET['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; - return false; - } - - /** - * 301跳转。 - * Header 301 Moved Permanently. - * - * @param string $locate - * @access public - * @return void - */ - public static function header301($locate) - { - header('HTTP/1.1 301 Moved Permanently'); - die(header('Location:' . $locate)); - } - - /** - * 获取远程IP。 - * Get remote ip. - * - * @access public - * @return string - */ - public static function getRemoteIp() - { - $ip = ''; - if(!empty($_SERVER["REMOTE_ADDR"])) $ip = $_SERVER["REMOTE_ADDR"]; - if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; - if(!empty($_SERVER['HTTP_CLIENT_IP'])) $ip = $_SERVER['HTTP_CLIENT_IP']; - - return $ip; - } -} - -//------------------------------- 常用函数。Some tool functions.-------------------------------// - -/** - * helper::createLink()的别名,方便创建本模块方法的链接。 - * The short alias of helper::createLink() method to create link to control method of current module. - * - * @param string $methodName the method name - * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) - * @param string $viewType - * @return string the link string. - */ -function inLink($methodName = 'index', $vars = '', $viewType = '') -{ - global $app; - return helper::createLink($app->getModuleName(), $methodName, $vars, $viewType); -} - -/** - * 通过一个静态游标,可以遍历数组。 - * Static cycle a array. - * - * @param array $items the array to be cycled. - * @return mixed - */ -function cycle($items) -{ - static $i = 0; - if(!is_array($items)) $items = explode(',', $items); - if(!isset($items[$i])) $i = 0; - return $items[$i++]; -} - -/** - * 获取当前时间的Unix时间戳,精确到微妙。 - * Get current microtime. - * - * @access public - * @return float current time. - */ -function getTime() -{ - list($usec, $sec) = explode(" ", microtime()); - return ((float)$usec + (float)$sec); -} - -/** - * 打印变量的信息 - * dump a var. - * - * @param mixed $var - * @access public - * @return void - */ -function a($var) -{ - echo ""; - print_r($var); - echo ""; -} - -/** - * 判断是否内外IP。 - * Judge the server ip is local or not. - * - * @access public - * @return void - */ -function isLocalIP() -{ - global $config; - if(isset($config->islocalIP)) return $config->isLocalIP; - $serverIP = $_SERVER['SERVER_ADDR']; - if($serverIP == '127.0.0.1') return true; - if(strpos($serverIP, '10.70') !== false) return false; - return !filter_var($serverIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); -} - -/** - * 获取webRoot。 - * Get web root. - * - * @access public - * @return string - */ -function getWebRoot($full = false) -{ - $path = $_SERVER['SCRIPT_NAME']; - - if(PHP_SAPI == 'cli') - { - if(isset($_SERVER['argv'][1])) - { - $url = parse_url($_SERVER['argv'][1]); - $path = empty($url['path']) ? '/' : rtrim($url['path'], '/'); - } - $path = empty($path) ? '/' : preg_replace('/\/www$/', '/www/', $path); - } - - if($full) - { - $http = (isset($_SERVER['HTTPS']) and strtolower($_SERVER['HTTPS']) != 'off') ? 'https://' : 'http://'; - return $http . $_SERVER['HTTP_HOST'] . substr($path, 0, (strrpos($path, '/') + 1)); - } - - $path = substr($path, 0, (strrpos($path, '/') + 1)); - $path = str_replace('\\', '/', $path); - return $path; -} - -/** - * 当数组/对象变量$var存在$key项时,返回存在的对应值或设定值,否则返回$key或不存在的设定值。 - * When the $var has the $key, return it, esle result one default value. - * - * @param array|object $var - * @param string|int $key - * @param mixed $valueWhenNone value when the key not exits. - * @param mixed $valueWhenExists value when the key exits. - * @access public - * @return string - */ -function zget($var, $key, $valueWhenNone = false, $valueWhenExists = false) -{ - if(!is_array($var) and !is_object($var)) return false; - - $type = is_array($var) ? 'array' : 'object'; - $checkExists = $type == 'array' ? isset($var[$key]) : isset($var->$key); - - if($checkExists) - { - if($valueWhenExists !== false) return $valueWhenExists; - return $type == 'array' ? $var[$key] : $var->$key; - } - - if($valueWhenNone !== false) return $valueWhenNone; - return $key; -} diff --git a/framework/base/model.class.php b/framework/base/model.class.php deleted file mode 100644 index 0b84a41ee2..0000000000 --- a/framework/base/model.class.php +++ /dev/null @@ -1,302 +0,0 @@ -app. - * 2. set the pathes, config, lang of current module - * - * @param string $appName - * @access public - * @return void - */ - public function __construct($appName = '') - { - global $app, $config, $lang, $dbh; - $this->app = $app; - $this->config = $config; - $this->lang = $lang; - $this->dbh = $dbh; - $this->appName = empty($appName) ? $this->app->getAppName() : $appName; - - $moduleName = $this->getModuleName(); - if($this->config->framework->multiLanguage) $this->app->loadLang($moduleName, $this->appName); - if($moduleName != 'common') $this->app->loadModuleConfig($moduleName, $this->appName); - - $this->loadDAO(); - $this->setSuperVars(); - } - - /** - * 获取该model的模块名,而不是用户请求的模块名。 - * - * 这个方法通过去掉该model类名的'ext'和'model'字符串,来获取当前模块名。 - * 不要使用$app->getModuleName(),因为其返回的是用户请求的模块名。 - * 另一个model可以通过loadModel()加载进来,与请求的模块名不一致。 - * - * Get the module name of this model. Not the module user visiting. - * - * This method replace the 'ext' and 'model' string from the model class name, thus get the module name. - * Not using $app->getModuleName() because it return the module user is visiting. But one module can be - * loaded by loadModel() so we must get the module name of this model. - * - * @access public - * @return string the module name. - */ - public function getModuleName() - { - $parentClass = get_parent_class($this); - $selfClass = get_class($this); - $className = $parentClass == 'model' ? $selfClass : $parentClass; - if($className == 'extensionModel') return 'extension'; - return strtolower(str_ireplace(array('ext', 'Model'), '', $className)); - } - - /** - * 设置全局超级变量。 - * Set the super vars. - * - * @access public - * @return void - */ - public function setSuperVars() - { - $this->post = $this->app->post; - $this->get = $this->app->get; - $this->server = $this->app->server; - $this->cookie = $this->app->cookie; - $this->session = $this->app->session; - } - - /** - * 加载一个模块的model。加载完成后,使用$this->$moduleName来访问这个model对象。 - * 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。 - * - * Load the model of one module. After loaded, can use $this->$moduleName to visit the model object. - * - * @param string $moduleName - * @access public - * @return object|bool the model object or false if model file not exists. - */ - public function loadModel($moduleName, $appName = '') - { - if(empty($moduleName)) return false; - if(empty($appName)) $appName = $this->appName; - - global $loadedModels; - if(isset($loadedModels[$appName][$moduleName])) - { - $this->$moduleName = $loadedModels[$appName][$moduleName]; - return $this->$moduleName; - } - - $modelFile = $this->app->setModelFile($moduleName, $appName); - - if(!helper::import($modelFile)) return false; - $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; - if(!class_exists($modelClass)) - { - $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; - if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); - } - - $loadedModels[$appName][$moduleName] = new $modelClass($appName); - $this->$moduleName = $loadedModels[$appName][$moduleName]; - return $this->$moduleName; - } - - /** - * 加载model的class扩展,主要是为了开发加密代码使用。 - * 可以将主要的逻辑存放到$moduleName/ext/model/class/$extensionName.class.php中。 - * 然后在ext/model/$extension.php的扩展里面使用$this->loadExtension()来调用相应的方法。 - * ext/model/class/*.class.php代码可以加密。而ext/model/*.php可以不用加密。 - * 因为框架对model的扩展是采取合并文件的方式,ext/model/*.php文件不能加密。 - * - * Load extension class of a model thus user can encrypt the code. - * You can put the main extension logic codes in $moduleName/ext/model/class/$extensionName.class.php. - * And call them by the ext/model/$extension.php like this: $this->loadExtension('myextension')->method(). - * You can encrypt the code in ext/model/class/*.class.php. - * Because the framework will merge the extension files in ext/model/*.php to the module/model.php. - * - * @param string $extensionName - * @param string $moduleName - * @access public - * @return void - */ - public function loadExtension($extensionName, $moduleName = '') - { - if(empty($extensionName)) return false; - - /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */ - $extensionName = strtolower($extensionName); - $moduleName = $moduleName ? $moduleName : $this->getModuleName(); - $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model'); - if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php'; - if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php'; - - /* 载入父类。Try to import parent model file auto and then import the extension file. */ - if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($this->appName, $moduleName) . 'model.php'); - if(!helper::import($extensionFile)) return false; - - /* 设置扩展类的名字。Set the extension class name. */ - $extensionClass = $extensionName . ucfirst($moduleName); - if(!class_exists($extensionClass)) return false; - - /* 实例化扩展类。Create an instance of the extension class and return it. */ - $extensionObject = new $extensionClass; - $extensionClass = str_replace('Model', '', $extensionClass); - $this->$extensionClass = $extensionObject; - return $extensionObject; - } - - /** - * 加载DAO。 - * Load DAO. - * - * @access public - * @return void - */ - public function loadDAO() - { - $this->dao = $this->app->loadClass('dao'); - } - - /** - * 删除记录。 - * Delete one record. - * - * @param string $table the table name - * @param string $id the id value of the record to be deleted - * @access public - * @return void - */ - public function delete($table, $id) - { - $this->dao->delete()->from($table)->where('id')->eq($id)->exec(); - } -} diff --git a/framework/base/router.class.php b/framework/base/router.class.php deleted file mode 100644 index d700da5fc4..0000000000 --- a/framework/base/router.class.php +++ /dev/null @@ -1,2517 +0,0 @@ -basePath/framework) - * - * @var string - * @access public - */ - public $frameRoot; - - /** - * 类库的根目录。{$this->basePath/lib} - * The root directory of the library($this->basePath/lib). - * - * @var string - * @access public - */ - public $coreLibRoot; - - /** - * 应用名称 - * The appName. - * - * @var string - * @access public - */ - public $appName = ''; - - /** - * 应用程序的根目录。 - * The root directory of the app. - * - * @var string - * @access public - */ - public $appRoot; - - /** - * 临时文件的根目录。 - * The root directory of temp. - * - * @var string - * @access public - */ - public $tmpRoot; - - /** - * 缓存的根目录。 - * The root directory of cache. - * - * @var string - * @access public - */ - public $cacheRoot; - - /** - * WWW目录。 - * The root directory of www. - * - * @var string - * @access public - */ - public $wwwRoot; - - /** - * 附件存放目录。 - * The root directory of data. - * - * @var string - * @access public - */ - public $dataRoot; - - /** - * 日志文件的根目录。 - * The root directory of log. - * - * @var string - * @access public - */ - public $logRoot; - - /** - * 配置文件的根目录。 - * The root directory of config. - * - * @var string - * @access public - */ - public $configRoot; - - /** - * 模块的根目录。 - * The root directory of module. - * - * @var string - * @access public - */ - public $moduleRoot; - - /** - * 主题的根目录。 - * The root directory of theme. - * - * @var string - * @access public - */ - public $themeRoot; - - /** - * 用户使用的语言。 - * The lang of the client user. - * - * @var string - * @access public - */ - public $clientLang; - - /** - * 用户使用的主题。 - * The theme of the client user. - * - * @var string - * @access public - */ - public $clientTheme; - - /** - * 客户端设备类型。 - * The device type of client. - * - * @var string - * @access public - */ - public $clientDevice; - - /** - * 当前模块的control对象。 - * The control object of current module. - * - * @var object - * @access public - */ - public $control; - - /** - * 模块名。 - * The module name - * - * @var string - * @access public - */ - public $moduleName; - - /** - * 当前访问模块的control文件。 - * The control file of the module current visiting. - * - * @var string - * @access public - */ - public $controlFile; - - /** - * 当前访问的方法名。 - * The name of the method current visiting. - * - * @var string - * @access public - */ - public $methodName; - - /** - * 当前方法的扩展文件。 - * The action extension file of current method. - * - * @var string - * @access public - */ - public $extActionFile; - - /** - * 访问的URI。 - * The URI. - * - * @var string - * @access public - */ - public $URI; - - /** - * url地址传递的参数。 - * The params passed in through url. - * - * @var array - * @access public - */ - public $params; - - /** - * 视图类型。 - * The view type. - * - * @var string - * @access public - */ - public $viewType; - - /** - * 全局$config对象。 - * The global $config object. - * - * @var object - * @access public - */ - public $config; - - /** - * 全局$lang对象。 - * The global $lang object. - * - * @var object - * @access public - */ - public $lang; - - /** - * 全局$dbh对象,数据库连接句柄。 - * The global $dbh object, the database connection handler. - * - * @var object - * @access public - */ - public $dbh; - - /** - * 从数据库的句柄。 - * The slave database handler. - * - * @var object - * @access public - */ - public $slaveDBH; - - /** - * $post对象,用于访问$_POST变量。 - * The $post object, used to access the $_POST var. - * - * @var ojbect - * @access public - */ - public $post; - - /** - * $get对象,用于访问$_GET变量。 - * The $get object, used to access the $_GET var. - * - * @var ojbect - * @access public - */ - public $get; - - /** - * $session对象,用于访问$_SESSION变量。 - * The $session object, used to access the $_SESSION var. - * - * @var ojbect - * @access public - */ - public $session; - - /** - * $server对象,用于访问$_SERVER变量。 - * The $server object, used to access the $_SERVER var. - * - * @var ojbect - * @access public - */ - public $server; - - /** - * $cookie对象,用于访问$_COOKIE变量。 - * The $cookie object, used to access the $_COOKIE var. - * - * @var ojbect - * @access public - */ - public $cookie; - - /** - * 网站代号。 - * The code of current site. - * - * @var string - * @access public - */ - public $siteCode; - - /** - * 构造方法, 设置路径,类,超级变量等。注意: - * 1.应该使用createApp()方法实例化router类; - * 2.如果$appRoot为空,框架会根据$appName计算应用路径。 - * - * The construct function. - * Prepare all the paths, classes, super objects and so on. - * Notice: - * 1. You should use the createApp() method to get an instance of the router. - * 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName - * - * @param string $appName the name of the app - * @param string $appRoot the root path of the app - * @access public - * @return void - */ - public function __construct($appName = 'demo', $appRoot = '') - { - $this->setPathFix(); - $this->setBasePath(); - $this->setFrameRoot(); - $this->setCoreLibRoot(); - $this->setAppRoot($appName, $appRoot); - $this->setTmpRoot(); - $this->setCacheRoot(); - $this->setLogRoot(); - $this->setConfigRoot(); - $this->setModuleRoot(); - $this->setWwwRoot(); - $this->setThemeRoot(); - $this->setDataRoot(); - $this->loadMainConfig(); - - $this->loadClass('front', $static = true); - $this->loadClass('filter', $static = true); - $this->loadClass('dao', $static = true); - $this->loadClass('mobile', $static = true); - - $this->setSuperVars(); - $this->setDebug(); - $this->setErrorHandler(); - $this->setTimezone(); - $this->startSession(); - - if($this->config->framework->multiSite) $this->setSiteCode() && $this->loadExtraConfig(); - if($this->config->framework->autoConnectDB) $this->connectDB(); - if($this->config->framework->multiLanguage) $this->setClientLang(); - - $needDetectDevice = zget($this->config->framework->detectDevice, $this->clientLang, false); - $this->clientDevice = $needDetectDevice ? $this->setClientDevice() : 'desktop'; - - if($this->config->framework->multiLanguage) $this->loadLang('common'); - if($this->config->framework->multiTheme) $this->setClientTheme(); - } - - /** - * 创建一个应用。 - * Create an application. - * - * @param string $appName 应用名称。 The name of the app. - * @param string $appRoot 应用根路径。The root path of the app. - * @param string $className 应用类名,如果对router类做了扩展,需要指定类名。When extends router class, you should pass in the child router class name. - * @static - * @access public - * @return object the app object - */ - public static function createApp($appName = 'demo', $appRoot = '', $className = '') - { - if(empty($className)) $className = __CLASS__; - return new $className($appName, $appRoot); - } - - //-------------------- 路径相关方法(Path related methods)--------------------// - - /** - * 设置应用名称。 - * Set app name. - * - * @param string $appName - * @access public - * @return void - */ - public function setAppName($appName) - { - $this->appName = $appName; - } - - /** - * 设置目录分隔符。 - * Set the path directory separator. - * - * @access public - * @return void - */ - public function setPathFix() - { - define('DS', DIRECTORY_SEPARATOR); - } - - /** - * 设置基础目录。 - * Set the base path. - * - * @access public - * @return void - */ - public function setBasePath() - { - $this->basePath = realpath(dirname(dirname(dirname(__FILE__)))) . DS; - } - - /** - * 设置框架根目录。 - * Set the frame root. - * - * @access public - * @return void - */ - public function setFrameRoot() - { - $this->frameRoot = $this->basePath . 'framework' . DS; - } - - /** - * 设置类库的根目录。 - * Set the app lib root. - * - * @access public - * @return void - */ - public function setCoreLibRoot() - { - $this->coreLibRoot = $this->basePath . 'lib' . DS; - } - - /** - * 设置应用的根目录。 - * Set the app root. - * - * @param string $appName - * @param string $appRoot - * @access public - * @return void - */ - public function setAppRoot($appName = 'demo', $appRoot = '') - { - if(empty($appRoot)) $this->appRoot = $this->basePath . 'app' . DS . $appName . DS; - if(!empty($appRoot)) $this->appRoot = realpath($appRoot) . DS; - if(!is_dir($this->appRoot)) $this->triggerError("The app you call not found in {$this->appRoot}", __FILE__, __LINE__, $exit = true); - } - - /** - * 设置临时文件的根目录。 - * Set the tmp root. - * - * @access public - * @return void - */ - public function setTmpRoot() - { - $this->tmpRoot = $this->basePath . 'tmp' . DS; - } - - /** - * 设置缓存的根目录。 - * Set the cache root. - * - * @access public - * @return void - */ - public function setCacheRoot() - { - $this->cacheRoot = $this->tmpRoot . 'cache' . DS; - } - - /** - * 设置log的根目录。 - * Set the log root. - * - * @access public - * @return void - */ - public function setLogRoot() - { - $this->logRoot = $this->tmpRoot . 'log' . DS; - } - - /** - * 设置config配置文件的根目录。 - * Set the config root. - * - * @access public - * @return void - */ - public function setConfigRoot() - { - $this->configRoot = $this->basePath . 'config' . DS; - } - - /** - * 设置模块的根目录。 - * Set the module root. - * - * @access public - * @return void - */ - public function setModuleRoot() - { - $this->moduleRoot = $this->basePath . 'module' . DS; - } - - /** - * 设置www的根目录。 - * Set the www root. - * - * @access public - * @return void - */ - public function setWwwRoot() - { - $this->wwwRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), DS) . DS; - } - - /** - * 设置主题根目录。 - * Set the theme root. - * - * @access public - * @return void - */ - public function setThemeRoot() - { - $this->themeRoot = $this->wwwRoot . 'theme' . DS; - } - - /** - * 设置data根目录。 - * Set the data root. - * - * @access public - * @return void - */ - public function setDataRoot() - { - $this->dataRoot = $this->wwwRoot . 'data' . DS; - } - - /** - * 设置超级变量。 - * Set the super vars. - * - * @access public - * @return void - */ - public function setSuperVars() - { - $this->post = new super('post'); - $this->get = new super('get'); - $this->server = new super('server'); - $this->cookie = new super('cookie'); - $this->session = new super('session'); - - unset($GLOBALS); - unset($_REQUEST); - - $_FILES = validater::filterFiles(); - $_POST = validater::filterSuper($_POST); - $_GET = validater::filterSuper($_GET); - $_COOKIE = validater::filterSuper($_COOKIE); - } - - /** - * 设置Debug模式。 - * set Debug. - * - * @access public - * @return void - */ - public function setDebug() - { - if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT); - } - - /** - * 设置错误处理句柄。 - * Set the error handler. - * - * @access public - * @return void - */ - public function setErrorHandler() - { - set_error_handler(array($this, 'saveError')); - register_shutdown_function(array($this, 'shutdown')); - } - - /** - * 获取应用名称 - * Get app name - * - * @access public - * @return string - */ - public function getAppName() - { - return $this->appName; - } - - /** - * 获取$basePath,即基础路径。 - * Get the $basePath var. - * - * @access public - * @return string - */ - public function getBasePath() - { - return $this->basePath; - } - - /** - * 获取$frameRoot,即框架根目录。 - * Get the $frameRoot var. - * - * @access public - * @return string - */ - public function getFrameRoot() - { - return $this->frameRoot; - } - - /** - * 获取$appRoot变量,即应用的根目录。 - * Get the $appRoot var. - * - * @access public - * @return string - */ - public function getAppRoot() - { - return $this->appRoot; - } - - /** - * 获取$wwwRoot变量。 - * Get the $wwwRoot var - * - * @access public - * @return string - */ - public function getWwwRoot() - { - return $this->wwwRoot; - } - - /** - * 获取$coreLibRoot变量,即应用类库的根目录。 - * Get the $coreLibRoot var. - * - * @access public - * @return string - */ - public function getCoreLibRoot() - { - return $this->coreLibRoot; - } - - /** - * 获取$tmpRoot变量,即临时文件的根目录。 - * Get the $tmpRoot var. - * - * @access public - * @return string - */ - public function getTmpRoot() - { - return $this->tmpRoot; - } - - /** - * 获取$cacheRoot变量,即缓存文件的根目录。 - * Get the $cacheRoot var. - * - * @access public - * @return string - */ - public function getCacheRoot() - { - return $this->cacheRoot; - } - - /** - * 获取$logRoot变量,即日志文件的根目录。 - * Get the $logRoot var. - * - * @access public - * @return string - */ - public function getLogRoot() - { - return $this->logRoot; - } - - /** - * 获取$configRoot变量,即配置文件的根目录。 - * Get the $configRoot var. - * - * @access public - * @return string - */ - public function getConfigRoot() - { - return $this->configRoot; - } - - /** - * 获取$moduleRoot变量,即应用模块的根目录。 - * Get the $moduleRoot var. - * - * @param string $appName - * @access public - * @return string - */ - public function getModuleRoot($appName = '') - { - if($appName == '') return $this->moduleRoot; - return dirname($this->moduleRoot) . DS . $appName . DS; - } - - /** - * 获取$webRoot,即应用的路径。 - * Get the $webRoot var. - * - * @access public - * @return string - */ - public function getWebRoot() - { - return $this->config->webRoot; - } - - /** - * 获取$themeRoot变量,即主题的根目录。 - * Get the $themeRoot var. - * - * @access public - * @return string - */ - public function getThemeRoot() - { - return $this->themeRoot; - } - - /** - * 获取$dataRoot目录 - * Get the $dataRoot var - * - * @access public - * @return string - */ - public function getDataRoot() - { - return $this->dataRoot; - } - - //------ 客户端环境有关的函数(Client environment related functions) ------// - - /** - * 根据配置设置当前时区。 - * Set the time zone according to the config. - * - * @access public - * @return void - */ - public function setTimezone() - { - if(isset($this->config->timezone)) date_default_timezone_set($this->config->timezone); - } - - /** - * 开启 session - * Start the session. - * - * @access public - * @return void - */ - public function startSession() - { - if(!defined('SESSION_STARTED')) - { - $sessionName = $this->config->sessionVar; - session_name($sessionName); - if(isset($_GET[$this->config->sessionVar])) session_id($_GET[$this->config->sessionVar]); - session_start(); - define('SESSION_STARTED', true); - } - } - - /** - * 根据用户浏览器的语言设置和服务器配置,选择显示的语言。 - * 优先级:$lang参数 > session > cookie > 浏览器 > 配置文件。 - * - * Set the language. - * Using the order of method $lang param, session, cookie, browser and the default lang. - * - * @param string $lang zh-cn|zh-tw|zh-hk|en - * @access public - * @return void - */ - public function setClientLang($lang = '') - { - if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $this->clientLang = $this->parseHttpAcceptLang(); - if(isset($_COOKIE['lang'])) $this->clientLang = $_COOKIE['lang']; - if(isset($_SESSION['lang'])) $this->clientLang = $_SESSION['lang']; - if(!empty($lang)) $this->clientLang = $lang; - - if(!empty($this->clientLang)) - { - $this->clientLang = strtolower($this->clientLang); - if(!isset($this->config->langs[$this->clientLang])) $this->clientLang = $this->config->default->lang; - } - else - { - $this->clientLang = $this->config->default->lang; - } - - setcookie('lang', $this->clientLang, $this->config->cookieLife, $this->config->webRoot); - if(!isset($_COOKIE['lang'])) $_COOKIE['lang'] = $this->clientLang; - - return true; - } - - /** - * 从HTTP_ACCEPT_LANGUAGE中提出去支持的语言。 - * Parse the lang str from HTTP_ACCEPT_LANGUAGE header. - * - * @access public - * @return string - */ - public function parseHttpAcceptLang() - { - if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return ''; - - $raw = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - $pos = strpos($raw, ','); - $lang = $pos === false ? $raw : substr($raw, 0, $pos); - - /* Fix clientLang for ie >= 10. https://www.drupal.org/node/365615. */ - if(stripos($lang, 'hans')) $lang = 'zh-cn'; - if(stripos($lang, 'hant')) $lang = 'zh-tw'; - return $lang; - } - - /** - * 设置客户端使用的主题,判断逻辑与客户端的语言相同。 - * 主题的css和图片文件应该存放在www/theme/$themeName路径。 - * - * Set the theme the client user using. The logic is same as the clientLang. - * The css and images files of an theme should saved at www/theme/$themeName - * - * @param string $theme - * @access public - * @return void - */ - public function setClientTheme($theme = '') - { - if(isset($this->config->client->theme)) $this->clientTheme = $this->config->client->theme; - if(isset($_COOKIE['theme'])) $this->clientTheme = $_COOKIE['theme']; - if(!empty($theme)) $this->clientTheme = $theme; - - if(!empty($this->clientTheme)) - { - $this->clientTheme = strtolower($this->clientTheme); - if(!isset($this->lang->themes[$this->clientTheme])) $this->clientTheme = $this->config->default->theme; - } - else - { - $this->clientTheme = $this->config->default->theme; - } - - setcookie('theme', $this->clientTheme, $this->config->cookieLife, $this->config->webRoot); - if(!isset($_COOKIE['theme'])) $_COOKIE['theme'] = $this->clientTheme; - - return true; - } - - /** - * 设置客户端的设备类型。 - * Set client device. - * - * @access public - * @return void - */ - public function setClientDevice() - { - $this->clientDevice = 'desktop'; - - if($this->cookie->device == 'mobile') $this->clientDevice = 'mobile'; - if($this->cookie->device == 'desktop') $this->clientDevice = 'desktop'; - - if(strpos('mobile,desktop', $this->cookie->device) === false) - { - $mobile = new mobile(); - $this->clientDevice = ($mobile->isMobile() and !$mobile->isTablet()) ? 'mobile' : 'desktop'; - } - - setcookie('device', $this->clientDevice, $this->config->cookieLife, $this->config->webRoot); - if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice; - - return $this->clientDevice; - } - - /** - * 设置站点代号,可以针对不同的站点来加载不同的扩展。 - * Set the code of current site, thus can load diffrent extension of diffrent site. - * - * @access public - * @return void - */ - public function setSiteCode() - { - return $this->siteCode = helper::parseSiteCode($this->server->http_host); - } - - /** - * 获取$clientLang变量,即客户端的语言。 - * Get the $clientLang var. - * - * @access public - * @return string - */ - public function getClientLang() - { - return $this->clientLang; - } - - /** - * 获取$clientTheme变量。 - * Get the $clientTheme var. - * - * @access public - * @return string - */ - public function getClientTheme() - { - return $this->config->webRoot . 'theme/' . $this->clientTheme . '/'; - } - - /** - * 获得客户端的终端设备。 - * Get the client device. - * - * @access public - * @return void - */ - public function getClientDevice() - { - return $this->clientDevice; - } - - //-------------------- 请求相关的方法(Request related methods) --------------------// - - /** - * 解析本次请求的入口方法,根据请求的类型(PATH_INFO GET),调用相应的方法。 - * The entrance of parseing request. According to the requestType, call related methods. - * - * @access public - * @return void - */ - public function parseRequest() - { - if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2') - { - $this->parsePathInfo(); - $this->setRouteByPathInfo(); - } - elseif($this->config->requestType == 'GET') - { - $this->parseGET(); - $this->setRouteByGET(); - } - else - { - $this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true); - } - } - - /** - * PATH_INFO方式解析,获取$URI和$viewType。 - * Parse PATH_INFO, get the $URI and $viewType. - * - * @access public - * @return void - */ - public function parsePathInfo() - { - $pathInfo = $this->getPathInfo(); - if(!empty($pathInfo)) - { - $dotPos = strrpos($pathInfo, '.'); - if($dotPos) - { - $this->URI = substr($pathInfo, 0, $dotPos); - $this->viewType = substr($pathInfo, $dotPos + 1); - if(strpos($this->config->views, ',' . $this->viewType . ',') === false) - { - $this->viewType = $this->config->default->view; - } - } - else - { - $this->URI = $pathInfo; - $this->viewType = $this->config->default->view; - } - } - else - { - $this->viewType = $this->config->default->view; - } - } - - /** - * 从$_SERVER或者$_ENV全局变量根据pathinfo变量名获取$PATH_INFO值。 - * PATH_INFO的变量名几乎都是'PATH_INFO',但也有可能是ORIG_PATH_INFO。 - * - * Get $PATH_INFO from $_SERVER or $_ENV by the pathinfo var name. - * Mostly, the var name of PATH_INFO is PATH_INFO, but may be ORIG_PATH_INFO. - * - * @access public - * @return string the PATH_INFO - */ - public function getPathInfo() - { - if(isset($_SERVER['PATH_INFO'])) - { - $value = $_SERVER['PATH_INFO']; - } - elseif(isset($_SERVER['ORIG_PATH_INFO'])) - { - $value = $_SERVER['ORIG_PATH_INFO']; - } - elseif(isset($this->URI)) - { - $value = $this->URI; - $subpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', dirname($_SERVER['SCRIPT_FILENAME'])); - if($subpath != '/') $subpath = '/' . $subpath; - if($subpath != '' and $subpath != '/' and strpos($value, $subpath) === 0) $value = substr($value, strlen($subpath)); - } - else - { - $value = @getenv('PATH_INFO'); - if(empty($value)) $value = @getenv('ORIG_PATH_INFO'); - } - - if(strpos($value, $_SERVER['SCRIPT_NAME']) !== false) $value = str_replace($_SERVER['SCRIPT_NAME'], '', $value); - if(strpos($value, '?') === false) return trim($value, '/'); - - $value = parse_url($value); - $pathInfo = trim(zget($value, 'path', ''), '/'); - if(trim($pathInfo, '/') == trim($this->config->webRoot, '/')) $pathInfo = ''; - - return $pathInfo; - } - - /** - * GET请求方式解析,获取$URI和$viewType。 - * Parse GET, get $URI and $viewType. - * - * @access public - * @return void - */ - public function parseGET() - { - if(isset($_GET[$this->config->viewVar])) - { - $this->viewType = $_GET[$this->config->viewVar]; - if(strpos($this->config->views, ',' . $this->viewType . ',') === false) $this->viewType = $this->config->default->view; - } - else - { - $this->viewType = $this->config->default->view; - } - $this->URI = $_SERVER['REQUEST_URI']; - } - - /** - * 获取$URL。 - * Get the $URL. - * - * @param bool $full true, the URI contains the webRoot, else only hte URI. - * @access public - * @return string - */ - public function getURI($full = false) - { - if($full and $this->config->requestType == 'PATH_INFO') - { - if($this->URI) return $this->config->webRoot . $this->URI . '.' . $this->viewType; - return $this->config->webRoot; - } - return $this->URI; - } - - /** - * 获取$vewType变量。 - * Get the $viewType var. - * - * @access public - * @return string - */ - public function getViewType() - { - return $this->viewType; - } - - //-------------------- 模块及扩展设置(Module and extension) --------------------// - - /** - * 加载common模块。 - * - * common模块比较特别,它会执行几乎每次请求都需要执行的操作,例如: - * 打开session,检查权限等等。 - * 加载完$lang, $config, $dbh后,需要在入口文件(www/index.php)中手动调用该方法。 - * - * Load the common module - * - * The common module is a special module, which can be used to do some common things. For examle: - * start session, check priviledge and so on. - * This method should called manually in the router file(www/index.php) after the $lang, $config, $dbh loaded. - * - * @access public - * @return object|bool the common model object or false if not exits. - */ - public function loadCommon() - { - $this->setModuleName('common'); - $commonModelFile = $this->setModelFile('common'); - if(!file_exists($commonModelFile)) return false; - - helper::import($commonModelFile); - - if($this->config->framework->extensionLevel == 0 and class_exists('commonModel')) return new commonModel(); - if($this->config->framework->extensionLevel > 0 and class_exists('extCommonModel')) return new extCommonModel(); - - if(class_exists('commonModel')) return new commonModel(); - return false; - } - - /** - * 设置要被调用的模块名。 - * Set the name of the module to be called. - * - * @param string $moduleName the module name - * @access public - * @return void - */ - public function setModuleName($moduleName = '') - { - if($this->checkModuleName($moduleName)) $this->moduleName = strtolower($moduleName); - } - - /** - * 设置要被调用的控制器文件。 - * Set the control file of the module to be called. - * - * @param bool $exitIfNone 没有找到该控制器文件的情况:如果该参数为true,则终止程序;如果为false,则打印错误日志 - * If control file not foundde, how to do. True, die the whole app. false, log error. - * @access public - * @return bool - */ - public function setControlFile($exitIfNone = true) - { - $this->controlFile = $this->moduleRoot . $this->moduleName . DS . 'control.php'; - if(file_exists($this->controlFile)) return true; - $this->triggerError("the control file $this->controlFile not found.", __FILE__, __LINE__, $exitIfNone); - } - - /** - * 设置要被调用的方法名。 - * Set the name of the method calling. - * - * @param string $methodName - * @access public - * @return void - */ - public function setMethodName($methodName = '') - { - if($this->checkMethodName($methodName)) $this->methodName = strtolower($methodName); - } - - /** - * 获取一个模块的路径。 - * Get the path of one module. - * - * @param string $appName the app name - * @param string $moduleName the module name - * @access public - * @return string the module path - */ - public function getModulePath($appName = '', $moduleName = '') - { - if($moduleName == '') $moduleName = $this->moduleName; - - if($this->checkModuleName($moduleName)) - { - $modulePath = $this->getModuleRoot($appName) . strtolower($moduleName) . DS; - return $modulePath; - } - } - - /** - * 获取一个模块的扩展路径。 Get extension path of one module. - * - * If the extensionLevel == 0, return empty array. - * If the extensionLevel == 1, return the common extension directory. - * If the extensionLevel == 2, return the common and site extension directories. - * - * @param string $appName the app name - * @param string $moduleName the module name - * @param string $ext the extension type, can be control|model|view|lang|config - * @access public - * @return string the extension path. - */ - public function getModuleExtPath($appName, $moduleName, $ext) - { - /* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */ - if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array(); - - /* When extensionLevel == 1. */ - $modulePath = $this->getModulePath($appName, $moduleName); - $paths = array(); - $paths['common'] = $modulePath . 'ext' . DS . $ext . DS; - if($this->config->framework->extensionLevel == 1) return $paths; - - /* When extensionLevel == 2. */ - $paths['site'] = empty($this->siteCode) ? '' : $modulePath . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS; - return $paths; - } - - /** - * 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii. - * - * @param string $var - * @access public - * @return bool - */ - public function checkModuleName($var) - { - global $filter; - $rule = $filter->default->moduleName; - if(validater::checkByRule($var, $rule)) return true; - $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); - } - - /** - * 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii. - * - * @param string $var - * @access public - * @return bool - */ - public function checkMethodName($var) - { - global $filter; - $rule = $filter->default->methodName; - if($this->config->framework->filterParam == 2 and isset($filter->{$this->moduleName}->methodName)) $rule = $filter->{$this->moduleName}->methodName; - - if(validater::checkByRule($var, $rule)) return true; - $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); - } - - /** - * 设置Action的扩展文件。 Set the action extension file. - * - * @access public - * @return bool - */ - public function setActionExtFile() - { - $moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control'); - - /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext pathes return false.*/ - if(empty($moduleExtPaths)) return false; - - /* 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。If extensionLevel == 2 and site extensionFile exists, return it. */ - if($this->config->framework->extensionLevel == 2 and !empty( $moduleExtPaths['site'])) - { - $this->extActionFile = $moduleExtPaths['site'] . $this->methodName . '.php'; - if(file_exists($this->extActionFile)) return true; - } - - /* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */ - $this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php'; - return file_exists($this->extActionFile); - } - - /** - * 设置一个模块的model文件,如果存在model扩展,一起合并。 - * Set the model file of one module. If there's an extension file, merge it with the main model file. - * - * @param string $moduleName the module name - * @param string $appName the app name - * @static - * @access public - * @return string the model file - */ - public function setModelFile($moduleName, $appName = '') - { - if($appName == '') $appName = $this->getAppName(); - - /* 设置主model文件。 Set the main model file. */ - $mainModelFile = $this->getModulePath($appName, $moduleName) . 'model.php'; - if($this->config->framework->extensionLevel == 0) return $mainModelFile; - - /* 计算扩展的文件和hook文件。Compute the extension files and hook files. */ - $hookFiles = array(); - $extFiles = array(); - $siteExtended = false; - - $modelExtPaths = $this->getModuleExtPath($appName, $moduleName, 'model'); - foreach($modelExtPaths as $extType => $modelExtPath) - { - if(empty($modelExtPath)) continue; - - $tmpHookFiles = helper::ls($modelExtPath . 'hook/', '.php'); - $tmpExtFiles = helper::ls($modelExtPath, '.php'); - $hookFiles = array_merge($hookFiles, $tmpHookFiles); - $extFiles = array_merge($extFiles, $tmpExtFiles); - - if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles))) $siteExtended = true; - } - - /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */ - if(empty($extFiles) and empty($hookFiles)) return $mainModelFile; - - /* 计算合并之后的modelFile路径。Compute the merged model file path. */ - $extModelPrefix = ($siteExtended and !empty($this->siteCode)) ? $this->siteCode{0} . DS . $this->siteCode : ''; - $mergedModelDir = $this->getTmpRoot() . 'model' . DS . ($extModelPrefix ? $extModelPrefix . DS : ''); - $mergedModelFile = $mergedModelDir . $moduleName . '.php'; - if(!is_dir($mergedModelDir)) mkdir($mergedModelDir, 0755, true); - - /* 判断生成的缓存文件是否需要更新。 Judge whether the merged model file needed update or not. */ - if(!$this->needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile)) return $mergedModelFile; - - /* 合并扩展和hook文件。Merge the extension and hook files. */ - $modelLines = $this->mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir); - $this->mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile); - - return $mergedModelFile; - } - - /** - * 检查合并之后的model文件是否需要更新。Check whether the merged model file need update or not. - * - * @param string $mainModelFile - * @param string $mergedModelFile - * @param string $modelExtPaths - * @param array $extFiles - * @param array $hookFiles - * @access public - * @return bool - */ - public function needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile) - { - $lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0; - - foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true; - foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) return true; - - $modelExtPath = $modelExtPaths['common']; - $modelHookPath = $modelExtPaths['common'] . 'hook/'; - if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; - if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; - - if(!empty($modelExtPaths['site'])) - { - $modelExtPath = $modelExtPaths['site']; - $modelHookPath = $modelExtPaths['site'] . 'hook/'; - if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; - if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; - } - - if(filemtime($mainModelFile) > $lastTime) return true; - - return false; - } - - /** - * 将model的扩展文件合并在一起。Merge model ext files. - * - * @param string $moduleName - * @param string $mainModelFile - * @param array $extFiles - * @param string $mergedModelDir - * @access public - * @return void - */ - public function mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir) - { - /* 设置类名。Set the class names. */ - $modelClass = "{$moduleName}Model"; - $tmpModelClass = "tmpExt$modelClass"; - - /* 开始拼装代码。Prepare the codes. */ - $modelLines = "getBasePath());\n"; - $modelLines .= "helper::import('" . str_replace($this->getBasePath(), '', $mainModelFile) . "');\n"; - $modelLines .= "helper::cd();\n"; - $modelLines .= "class $tmpModelClass extends $modelClass \n{\n"; - - /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into model lines. */ - foreach($extFiles as $extFile) $modelLines .= self::removePHPTAG($extFile); - - /* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */ - $replaceMark = '//**//'; - $modelLines .= "\n$replaceMark\n}"; - - /* 生成一个临时的model扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged model file and import it for merge hook codes using. */ - $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; - if(@file_put_contents($tmpModelFile, $modelLines)) - { - if(!class_exists($tmpModelClass)) include $tmpModelFile; - return $modelLines; - } - - $this->triggerError("ERROR: $tmpModelFile not writable.", __FILE__, __LINE__, true); - } - - /** - * 合并model的hook脚本。Merge hook files for a model. - * - * @access public - * @return void - */ - public function mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile) - { - /* 定义相关变量。Init vars. */ - $modelClass = $moduleName . 'Model'; - $extModelClass = 'ext' . $modelClass; - $tmpModelClass = 'tmpExt' . $modelClass; - $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; - $replaceMark = '//**//'; - - /* 读取hook文件。Get hook codes need to merge. */ - $hookCodes = array(); - foreach($hookFiles as $hookFile) - { - /* 通过文件名获得其对应的方法名。Get methods according it's filename. */ - $fileName = baseName($hookFile); - list($method) = explode('.', $fileName); - $hookCodes[$method][] = self::removePHPTAG($hookFile); - } - - /* 合并Hook文件。Cycle the hook methods and merge hook codes. */ - $hookedMethods = array_keys($hookCodes); - $mainModelCodes = file($mainModelFile); - $mergedModelCodes = file($tmpModelFile); - foreach($hookedMethods as $method) - { - /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */ - if(!method_exists($tmpModelClass, $method)) continue; - $methodRelfection = new reflectionMethod($tmpModelClass, $method); - $definedFile = $methodRelfection->getFileName(); - $startLine = $methodRelfection->getStartLine() . ' '; - $endLine = $methodRelfection->getEndLine() . ' '; - - /* 将Hook脚本和老的代码合并在一起,并替换原来的定义。Merge hook codes with old codes and replace back. */ - $oldCodes = $definedFile == $tmpModelFile ? $mergedModelCodes : $mainModelCodes; - $oldCodes = join("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1)); - $openBrace = strpos($oldCodes, '{'); - $newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . join("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1); - - if($definedFile == $tmpModelFile) $modelLines = str_replace($oldCodes, $newCodes, $modelLines); - if($definedFile != $tmpModelFile) $modelLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $modelLines); - } - - /* 保存最终的Model文件。Save the last merged model file. */ - $modelLines = str_replace($tmpModelClass, $extModelClass, $modelLines); - file_put_contents($mergedModelFile, $modelLines); - unlink($tmpModelFile); - } - - /** - * Remove tags of PHP - * - * @param string $fileName - * @static - * @access public - * @return string - */ - static public function removePHPTAG($fileName) - { - $code = trim(file_get_contents($fileName)); - if(strpos($code, '') !== false) $code = rtrim($code, '?>'); - return trim($code); - } - - //-------------------- 路由相关方法(Routing related methods) --------------------// - - /** - * 设置路由(PATH_INFO 方式): - * 1.设置模块名; - * 2.设置方法名; - * 3.设置控制器文件。 - * - * Set the route according to PATH_INFO. - * 1. set the module name. - * 2. set the method name. - * 3. set the control file. - * - * @access public - * @return void - */ - public function setRouteByPathInfo() - { - if(!empty($this->URI)) - { - /* - * 根据$requestFix分割符,分割网址。 - * There's the request seperator, split the URI by it. - **/ - if(strpos($this->URI, $this->config->requestFix) !== false) - { - $items = explode($this->config->requestFix, $this->URI); - $this->setModuleName($items[0]); - $this->setMethodName($items[1]); - } - /* - * 如果网址中没有分隔符,使用默认的方法。 - * No reqeust seperator, use the default method name. - **/ - else - { - $this->setModuleName($this->URI); - $this->setMethodName($this->config->default->method); - } - } - else - { - $this->setModuleName($this->config->default->module); // 使用默认模块 use the default module. - $this->setMethodName($this->config->default->method); // 使用默认方法 use the default method. - } - $this->setControlFile(); - } - - /** - * 设置路由(GET 方式): - * 1.设置模块名; - * 2.设置方法名; - * 3.设置控制器文件。 - * - * Set the route according to GET. - * 1. set the module name. - * 2. set the method name. - * 3. set the control file. - * - * @access public - * @return void - */ - public function setRouteByGET() - { - $moduleName = isset($_GET[$this->config->moduleVar]) ? strtolower($_GET[$this->config->moduleVar]) : $this->config->default->module; - $methodName = isset($_GET[$this->config->methodVar]) ? strtolower($_GET[$this->config->methodVar]) : $this->config->default->method; - $this->setModuleName($moduleName); - $this->setControlFile(); - $this->setMethodName($methodName); - } - - /** - * 加载一个模块: - * 1. 引入控制器文件或扩展的方法文件; - * 2. 创建control对象; - * 3. 解析url,得到请求的参数; - * 4. 使用call_user_function_array调用相应的方法。 - * - * Load a module. - * 1. include the control file or the extension action file. - * 2. create the control object. - * 3. set the params passed in through url. - * 4. call the method by call_user_function_array - * - * @access public - * @return bool|object if the module object of die. - */ - public function loadModule() - { - $appName = $this->appName; - $moduleName = $this->moduleName; - $methodName = $this->methodName; - - /* - * 引入该模块的control文件。 - * Include the control file of the module. - **/ - $file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile; - chdir(dirname($file2Included)); - include $file2Included; - - /* - * 设置control的类名。 - * Set the class name of the control. - **/ - $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; - if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true); - - /* - * 创建control类的实例。 - * Create a instance of the control. - **/ - $module = new $className(); - if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true); - $this->control = $module; - - /* include default value for module*/ - $defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php"); - if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file; - - /* - * 使用反射机制获取函数参数的默认值。 - * Get the default settings of the method to be called using the reflecting. - * - * */ - $defaultParams = array(); - $methodReflect = new reflectionMethod($className, $methodName); - foreach($methodReflect->getParameters() as $param) - { - $name = $param->getName(); - - $default = '_NOT_SET'; - if(isset($paramDefaultValue[$appName][$className][$methodName][$name])) - { - $default = $paramDefaultValue[$appName][$className][$methodName][$name]; - } - elseif(isset($paramDefaultValue[$className][$methodName][$name])) - { - $default = $paramDefaultValue[$className][$methodName][$name]; - } - elseif($param->isDefaultValueAvailable()) - { - $default = $param->getDefaultValue(); - } - - $defaultParams[$name] = $default; - } - - /** - * 根据PATH_INFO或者GET方式设置请求的参数。 - * Set params according PATH_INFO or GET. - */ - if($this->config->requestType != 'GET') - { - $this->setParamsByPathInfo($defaultParams); - } - else - { - $this->setParamsByGET($defaultParams); - } - - if($this->config->framework->filterParam == 2) - { - $_GET = validater::filterParam($_GET, 'get'); - $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); - } - - /* 调用该方法 Call the method. */ - call_user_func_array(array($module, $methodName), $this->params); - return $module; - } - - /** - * 设置请求的参数(PATH_INFO 方式)。 - * Set the params by PATH_INFO. - * - * @param array $defaultParams the default settings of the params. - * @param string $type - * @access public - * @return void - */ - public function setParamsByPathInfo($defaultParams = array(), $type = '') - { - $params = array(); - if($type != 'fetch') - { - /* 分割URI。 Spit the URI. */ - $items = explode($this->config->requestFix, $this->URI); - $itemCount = count($items); - - /** - * 前两项为模块名和方法名,参数从下标2开始。 - * The first two item is moduleName and methodName. So the params should begin at 2. - **/ - for($i = 2; $i < $itemCount; $i ++) - { - $key = key($defaultParams); // Get key from the $defaultParams. - if(empty($key)) continue; - - $params[$key] = $items[$i]; - next($defaultParams); - } - } - - $this->params = $this->mergeParams($defaultParams, $params); - } - - /** - * 设置请求的参数(GET 方式)。 - * Set the params by GET. - * - * @param array $defaultParams the default settings of the params. - * @param string $type - * @access public - * @return void - */ - public function setParamsByGET($defaultParams, $type = '') - { - $params = array(); - if($type != 'fetch') - { - /* Unset moduleVar, methodVar, viewVar and session 变量, 剩下的作为参数。 */ - /* Unset the moduleVar, methodVar, viewVar and session var, all the left are the params. */ - unset($_GET[$this->config->moduleVar]); - unset($_GET[$this->config->methodVar]); - unset($_GET[$this->config->viewVar]); - unset($_GET[$this->config->sessionVar]); - $params = $_GET; - } - - $this->params = $this->mergeParams($defaultParams, $params); - } - - /** - * 合并请求的参数和默认参数,这样就可以省略已经有默认值的参数了。 - * Merge the params passed in and the default params. Thus the params which have default values needn't pass value, just like a function. - * - * @param array $defaultParams the default params defined by the method. - * @param array $passedParams the params passed in through url. - * @access public - * @return array the merged params. - */ - public function mergeParams($defaultParams, $passedParams) - { - global $filter; - - /* Remove these two params. */ - unset($passedParams['onlybody']); - unset($passedParams['HTTP_X_REQUESTED_WITH']); - - /* Check params from URL. */ - $nameRule = isset($filter->{$this->moduleName}->{$this->methodName}->paramName) ? $filter->{$this->moduleName}->{$this->methodName}->paramName : $filter->default->paramName; - foreach($passedParams as $param => $value) - { - if(!validater::checkByRule($param, $nameRule)) die('Bad Request!'); - $valueRule = $filter->default->paramValue; - if(isset($filter->{$this->moduleName}->{$this->methodName}->paramValue[$param])) - { - $valueRule = $filter->{$this->moduleName}->{$this->methodName}->paramValue[$param]; - } - - if($value and !validater::checkByRule($value, $valueRule)) die('Bad Request!'); - } - - $passedParams = array_values($passedParams); - $i = 0; - foreach($defaultParams as $key => $defaultValue) - { - if(isset($passedParams[$i])) - { - $defaultParams[$key] = strip_tags($passedParams[$i]); - } - else - { - if($defaultValue === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true); - } - $i ++; - } - - return $defaultParams; - } - - /** - * 获取$moduleName变量。 - * Get the $moduleName var. - * - * @access public - * @return string - */ - public function getModuleName() - { - return $this->moduleName; - } - - /** - * 获取$controlFile变量。 - * Get the $controlFile var. - * - * @access public - * @return string - */ - public function getControlFile() - { - return $this->controlFile; - } - - /** - * 获取$methodName变量。 - * Get the $methodName var. - * - * @access public - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - - /** - * 获取$param变量。 - * Get the $param var. - * - * @access public - * @return string - */ - public function getParams() - { - return $this->params; - } - - //-------------------- 常用的工具方法(Tool methods) ------------------// - - /** - * 从类库中加载一个类文件。 - * - * Load a class file. - * - * @param string $className the class name - * @param bool $static statis class or not - * @access public - * @return object|bool the instance of the class or just true. - */ - public function loadClass($className, $static = false) - { - $className = strtolower($className); - - /* 搜索$coreLibRoot(Search in $coreLibRoot) */ - $classFile = $this->coreLibRoot . $className; - if(is_dir($classFile)) $classFile .= DS . $className; - $classFile .= '.class.php'; - if(!helper::import($classFile)) $this->triggerError("class file $classFile not found", __FILE__, __LINE__, $exit = true); - - /* 如果是静态调用,则返回(If staitc, return) */ - if($static) return true; - - /* 实例化该类(Instance it) */ - global $$className; - if(!class_exists($className)) $this->triggerError("the class $className not found in $classFile", __FILE__, __LINE__, $exit = true); - if(!is_object($$className)) $$className = new $className(); - return $$className; - } - - /** - * 加载整个应用公共的配置文件。 - * Load the common config files for the app. - * - * @access public - * @return void - */ - public function loadMainConfig() - { - /* 初始化$config对象。Init the $config object. */ - global $config, $filter; - if(!is_object($config)) $config = new config(); - $this->config = $config; - - /* 加载主配置文件。 Load the main config file. */ - $mainConfigFile = $this->configRoot . 'config.php'; - if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true); - include $mainConfigFile; - } - - /** - * 当multiSite功能打开的时候,加载额外的配置文件。 - * When multiSite feature enabled, load extra config file. - * - * @access public - * @return void - */ - public function loadExtraConfig() - { - global $config; - $multiConfigFile = $this->configRoot . 'multi.php'; - if(file_exists($multiConfigFile)) include $multiConfigFile; - - $siteConfigFile = $this->configRoot . "sites/{$this->siteCode}.php"; - if(file_exists($siteConfigFile)) include $siteConfigFile; - } - - /** - * 加载模块的config文件,返回全局$config对象。 - * 如果该模块是common,加载$configRoot的配置文件,其他模块则加载其模块的配置文件。 - * - * Load config and return it as the global config object. - * If the module is common, search in $configRoot, else in $modulePath. - * - * @param string $moduleName module name - * @param string $appName app name - * @param bool $exitIfNone exit or not - * @access public - * @return object|bool the config object or false. - */ - public function loadModuleConfig($moduleName, $appName = '') - { - global $config; - - if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass(); - - /* 初始化数组。Init the variables. */ - $extConfigFiles = array(); - $commonExtConfigFiles = array(); - $siteExtConfigFiles = array(); - - /* 先获得模块的主配置文件。Get the main config file for current module first. */ - $mainConfigFile = $this->getModulePath($appName, $moduleName) . 'config.php'; - - /* 查找扩展配置文件。Get extension config files. */ - if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($appName, $moduleName, 'config'); - if($config->framework->extensionLevel >= 1 and !empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php'); - if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php'); - $extConfigFiles = array_merge($commonExtConfigFiles, $siteExtConfigFiles); - - /* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */ - $configFiles = array_merge(array($mainConfigFile), $extConfigFiles); - - /* 加载每一个配置文件。Load every config file. */ - static $loadedConfigs = array(); - foreach($configFiles as $configFile) - { - if(in_array($configFile, $loadedConfigs)) continue; - if(file_exists($configFile)) include $configFile; - $loadedConfigs[] = $configFile; - } - - /* 加载数据库中与本模块相关的配置项。Merge from the db configs. */ - if($moduleName != 'common') - { - if(isset($config->system->$moduleName)) $this->mergeConfig($config->system->$moduleName, $moduleName); - if(isset($config->personal->$moduleName)) $this->mergeConfig($config->personal->$moduleName, $moduleName); - } - } - - /** - * Merge db config. - * - * @param array $dbConfig - * @param string $moduleName - * @access public - * @return void - */ - public function mergeConfig($dbConfig, $moduleName = 'common') - { - global $config; - - /* 如果没有设置本模块配置,则首先进行初始化。Init the $config->$moduleName if not set.*/ - if($moduleName != 'common' and !isset($config->$moduleName)) $config->$moduleName = new stdclass(); - - $config2Merge = $config; - if($moduleName != 'common') $config2Merge = $config->$moduleName; - - foreach($dbConfig as $item) - { - if($item->section) - { - if(!isset($config2Merge->{$item->section})) $config2Merge->{$item->section} = new stdclass(); - if(is_object($config2Merge->{$item->section})) - { - $config2Merge->{$item->section}->{$item->key} = $item->value; - } - } - else - { - $config2Merge->{$item->key} = $item->value; - } - } - } - - /** - * 向客户端输出配置参数,客户端可以根据这些参数实现和调整请求的逻辑。 - * Export the config params to the client, thus the client can adjust it's logic according the config. - * - * @access public - * @return void - */ - public function exportConfig() - { - $view = new stdclass(); - $view->version = $this->config->version; - $view->requestType = $this->config->requestType; - $view->requestFix = $this->config->requestFix; - $view->moduleVar = $this->config->moduleVar; - $view->methodVar = $this->config->methodVar; - $view->viewVar = $this->config->viewVar; - $view->sessionVar = $this->config->sessionVar; - - $this->session->set('random', mt_rand(0, 10000)); - $view->sessionName = session_name(); - $view->sessionID = session_id(); - $view->random = $this->session->random; - $view->expiredTime = ini_get('session.gc_maxlifetime'); - $view->serverTime = time(); - - echo json_encode($view); - } - - /** - * 加载语言文件,返回全局$lang对象。 - * Load lang and return it as the global lang object. - * - * @param string $moduleName the module name - * @param string $appName the app name - * @access public - * @return bool|ojbect the lang object or false. - */ - public function loadLang($moduleName, $appName = '') - { - /* 初始化变量。Init vars. */ - $modulePath = $this->getModulePath($appName, $moduleName); - $extLangFiles = array(); - $langFilesToLoad = array(); - - /* 判断主语言文件是否存在。Whether the main lang file exists or not. */ - $mainLangFile = $modulePath . 'lang' . DS . $this->clientLang . '.php'; - if(file_exists($mainLangFile)) $langFilesToLoad[] = $mainLangFile; - - /* 获取扩展语言文件。If extensionLevel > 0, get extension lang files. */ - if($this->config->framework->extensionLevel > 0) - { - $commonExtLangFiles = array(); - $siteExtLangFiles = array(); - - $extLangPath = $this->getModuleExtPath($appName, $moduleName, 'lang'); - if($this->config->framework->extensionLevel >= 1 and !empty($extLangPath['common'])) $commonExtLangFiles = helper::ls($extLangPath['common'] . $this->clientLang, '.php'); - if($this->config->framework->extensionLevel == 2 and !empty($extLangPath['site'])) $siteExtLangFiles = helper::ls($extLangPath['site'] . $this->clientLang, '.php'); - $extLangFiles = array_merge($commonExtLangFiles, $siteExtLangFiles); - } - - /* 计算最终要加载的语言文件。 Get the lang files to be loaded. */ - $langFilesToLoad = array_merge($langFilesToLoad, $extLangFiles); - if(empty($langFilesToLoad)) return false; - - /* 加载语言文件。Load lang files. */ - global $lang; - if(!is_object($lang)) $lang = new language(); - if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass(); - - static $loadedLangs = array(); - foreach($langFilesToLoad as $langFile) - { - if(in_array($langFile, $loadedLangs)) continue; - include $langFile; - $loadedLangs[] = $langFile; - } - - $this->lang = $lang; - return $lang; - } - - /** - * 连接数据库。 - * Connect to database. - * - * @access public - * @return void - */ - public function connectDB() - { - global $config, $dbh, $slaveDBH; - if(!isset($config->installed) or !$config->installed) return; - - if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db); - if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB); - } - - /** - * 使用PDO连接数据库。 - * Connect database by PDO. - * - * @param object $params the database params. - * @access public - * @return object|bool - */ - public function connectByPDO($params) - { - if(!isset($params->driver)) self::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, $exit = true); - if(!isset($params->user)) return false; - if($params->driver == 'mysql') - { - $dsn = "mysql:host={$params->host}; port={$params->port}; dbname={$params->name}"; - } - try - { - $dbh = new PDO($dsn, $params->user, $params->password, array(PDO::ATTR_PERSISTENT => $params->persistant)); - $dbh->exec("SET NAMES {$params->encoding}"); - - /* - * 如果系统是Linux,开启仿真预处理和缓冲查询。 - * If run on linux, set emulatePrepare and bufferQuery to true. - **/ - if(!isset($params->emulatePrepare) and PHP_OS == 'Linux') $params->emulatePrepare = true; - if(!isset($params->bufferQuery) and PHP_OS == 'Linux') $params->bufferQuery = true; - - $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); - $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - if(isset($params->strictMode) and $params->strictMode == false) $dbh->exec("SET @@sql_mode= ''"); - if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare); - if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery); - - return $dbh; - } - catch (PDOException $exception) - { - self::triggerError($exception->getMessage(), __FILE__, __LINE__, $exit = true); - } - } - - //-------------------- 错误处理方法(Error methods) ------------------// - - /** - * 程序停止时执行的函数。 - * The shutdown handler. - * - * @access public - * @return void - */ - public function shutdown() - { - /* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */ - if(!empty($this->config->debug)) $this->saveSQL(); - - /* - * 发现错误,保存到日志中。 - * If any error occers, save it. - * */ - if(!function_exists('error_get_last')) return; - $error = error_get_last(); - if($error) $this->saveError($error['type'], $error['message'], $error['file'], $error['line']); - } - - /** - * 触发一个错误。 - * Trigger an error. - * - * @param string $message 错误信息 error message - * @param string $file 所在文件 the file error occers - * @param int $line 错误行 the line error occers - * @param bool $exit 是否停止程序 exit the program or not - * @access public - * @return void - */ - public function triggerError($message, $file, $line, $exit = false) - { - /* 设置错误信息(Set the error info) */ - $message = htmlspecialchars($message); - $log = "ERROR: $message in $file on line $line"; - if(isset($_SERVER['SCRIPT_URI'])) $log .= ", request: $_SERVER[SCRIPT_URI]";; - $trace = debug_backtrace(); - extract($trace[0]); - extract($trace[1]); - $log .= ", last called by $file on line $line through function $function.\n"; - - /* 触发错误(Trigger the error) */ - trigger_error($log, $exit ? E_USER_ERROR : E_USER_WARNING); - } - - /** - * 保存错误信息。 - * Save error info. - * - * @param int $level - * @param string $message - * @param string $file - * @param int $line - * @access public - * @return void - */ - public function saveError($level, $message, $file, $line) - { - if(empty($this->config->debug)) return true; - if(!is_dir($this->logRoot)) return true; - if(!is_writable($this->logRoot)) return true; - - /* - * 删除设定时间之前的日志。 - * Delete the log before the set time. - **/ - if(mt_rand(0, 10) == 1) - { - $logDays = isset($this->config->framework->logDays) ? $this->config->framework->logDays : 14; - $dayTime = time() - $logDays * 24 * 3600; - foreach(glob($this->getLogRoot() . '*') as $logFile) - { - if(filemtime($logFile) <= $dayTime) unlink($logFile); - } - } - - /* - * 忽略该错误:Redefining already defined constructor。 - * Skip the error: Redefining already defined constructor. - **/ - if(strpos($message, 'Redefining') !== false) return true; - - /* - * 设置错误信息。 - * Set the error info. - **/ - $errorLog = "\n" . date('H:i:s') . " $message in $file on line $line "; - $errorLog .= "when visiting " . htmlspecialchars($this->getURI()) . "\n"; - - /* - * 为了安全起见,对公网环境隐藏脚本路径。 - * If the ip is pulic, hidden the full path of scripts. - */ - if(!defined('IN_SHELL') and !($this->server->remote_addr == '127.0.0.1' or filter_var($this->server->remote_addr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)) - { - $errorLog = str_replace($this->getBasePath(), '', $errorLog); - } - - /* 保存到日志文件(Save to log file) */ - $errorFile = $this->logRoot . 'php.' . date('Ymd') . '.log.php'; - if(!is_file($errorFile)) file_put_contents($errorFile, "\n"); - - $fh = fopen($errorFile, 'a'); - if($fh) fwrite($fh, strip_tags($errorLog)) and fclose($fh); - - /* - * 如果debug > 1,显示warning, notice级别的错误。 - * If the debug > 1, show warning, notice error. - **/ - if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192) // 8192: E_DEPRECATED - { - if(!empty($this->config->debug) and $this->config->debug > 1) - { - $cmd = "vim +$line $file"; - $size = strlen($cmd); - echo "
$message: ";
-                echo "
"; - } - } - - /* - * 如果是严重错误,停止程序。 - * If error level is serious, die. - * */ - if($level == E_ERROR or $level == E_PARSE or $level == E_CORE_ERROR or $level == E_COMPILE_ERROR or $level == E_USER_ERROR) - { - if(empty($this->config->debug)) die(); - if(PHP_SAPI == 'cli') die($errorLog); - - $htmlError = ""; - $htmlError .= "" . nl2br($errorLog) . ""; - die($htmlError); - } - } - - /** - * 保存sql语句。 - * Save the sql. - * - * @access public - * @return void - */ - public function saveSQL() - { - if(!$this->config->debug) return true; - if(!class_exists('dao')) return; - - $sqlLog = $this->getLogRoot() . 'sql.' . date('Ymd') . '.log.php'; - if(!is_file($sqlLog)) file_put_contents($sqlLog, "\n"); - - $fh = @fopen($sqlLog, 'a'); - if(!$fh) return false; - fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n"); - foreach(dao::$querys as $query) fwrite($fh, " $query\n"); - fwrite($fh, "\n"); - fclose($fh); - } -} - -/** - * config类。 - * The config class. - * - * @package framework - */ -class config -{ - /** - * 设置成员变量,成员可以是'db.user'类似的格式。 - * Set the value of a member. the member can be the format like db.user. - * - * - * set('db.user', 'wwccss'); - * ?> - * - * @param string $key the key of the member - * @param mixed $value the value - * @access public - * @return void - */ - public function set($key, $value) - { - helper::setMember('config', $key, $value); - } -} - -/** - * lang类。 - * The lang class. - * - * @package framework - */ -class language -{ - /** - * 设置成员变量,成员可以是'db.user'类似的格式。 - * Set the value of a member. the member can be the foramt like db.user. - * - * - * set('version', '1.0); - * ?> - * - * @param string $key 成员的键名,可以是father.child的形式。 - * the key of the member, can be father.child - * @param mixed $value the value - * @access public - * @return void - */ - public function set($key, $value) - { - helper::setMember('lang', $key, $value); - } - - /** - * 显示一个成员的值。 - * Show a member. - * - * @param object $obj the object - * @param string $key the key - * @access public - * @return void - */ - public function show($obj, $key) - { - $obj = (array)$obj; - echo isset($obj[$key]) ? $obj[$key] : ''; - } -} - -/** - * 超级对象类,转化超级全局变量。 - * The super object class. - * - * @package framework - */ -class super -{ - /** - * 构造函数,设置超级变量名。 - * Construct, set the var scope. - * - * @param string $scope the scope, can be server, post, get, cookie, session, global - * @access public - * @return void - */ - public function __construct($scope) - { - $this->scope = $scope; - } - - /** - * 设置超级变量的成员值。 - * Set one member value. - * - * @param string the key - * @param mixed $value the value - * @access public - * @return void - */ - public function set($key, $value) - { - if($this->scope == 'post') - { - $_POST[$key] = $value; - } - elseif($this->scope == 'get') - { - $_GET[$key] = $value; - } - elseif($this->scope == 'server') - { - $_SERVER[$key] = $value; - } - elseif($this->scope == 'cookie') - { - $_COOKIE[$key] = $value; - } - elseif($this->scope == 'session') - { - $_SESSION[$key] = $value; - } - elseif($this->scope == 'env') - { - $_ENV[$key] = $value; - } - elseif($this->scope == 'global') - { - $GLOBALS[$key] = $value; - } - } - - /** - * 超级变量的魔术方法,比如用$post->key访问$_POST['key']。 - * The magic get method. - * - * @param string $key the key - * @access public - * @return mixed|bool return the value of the key or false. - */ - public function __get($key) - { - if($this->scope == 'post') - { - if(isset($_POST[$key])) return $_POST[$key]; - return false; - } - elseif($this->scope == 'get') - { - if(isset($_GET[$key])) return $_GET[$key]; - return false; - } - elseif($this->scope == 'server') - { - if($key == 'ajax') return isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? true : false; - if(isset($_SERVER[$key])) return $_SERVER[$key]; - $key = strtoupper($key); - if(isset($_SERVER[$key])) return $_SERVER[$key]; - return false; - } - elseif($this->scope == 'cookie') - { - if(isset($_COOKIE[$key])) return $_COOKIE[$key]; - return false; - } - elseif($this->scope == 'session') - { - if(isset($_SESSION[$key])) return $_SESSION[$key]; - return false; - } - elseif($this->scope == 'env') - { - if(isset($_ENV[$key])) return $_ENV[$key]; - return false; - } - elseif($this->scope == 'global') - { - if(isset($GLOBALS[$key])) return $GLOBALS[$key]; - return false; - } - else - { - return false; - } - } - - /** - * 打印变量的详细结构。 - * Print the structure. - * - * @access public - * @return void - */ - public function a() - { - if($this->scope == 'post') a($_POST); - if($this->scope == 'get') a($_GET); - if($this->scope == 'server') a($_SERVER); - if($this->scope == 'cookie') a($_COOKIE); - if($this->scope == 'session') a($_SESSION); - if($this->scope == 'env') a($_ENV); - if($this->scope == 'global') a($GLOBALS); - } -} diff --git a/framework/control.class.php b/framework/control.class.php deleted file mode 100644 index 848194f79b..0000000000 --- a/framework/control.class.php +++ /dev/null @@ -1,23 +0,0 @@ -requestType != 'GET') - { - $pathInfo = $app->getPathInfo(); - if(!empty($pathInfo)) - { - $dotPos = strrpos($pathInfo, '.'); - if($dotPos) - { - $viewType = substr($pathInfo, $dotPos + 1); - } - else - { - $config->default->view = $config->default->view == 'mhtml' ? 'html' : $config->default->view; - } - } - } - elseif($config->requestType == 'GET') - { - if(isset($_GET[$config->viewVar])) - { - $viewType = $_GET[$config->viewVar]; - } - else - { - /* Set default view when url has not module name. such as only domain. */ - $config->default->view = ($config->default->view == 'mhtml' and isset($_GET[$config->moduleVar])) ? 'html' : $config->default->view; - } - } - if($source and isset($viewType)) return $viewType; - - if(isset($viewType) and strpos($config->views, ',' . $viewType . ',') === false) $viewType = $config->default->view; - return isset($viewType) ? $viewType : $config->default->view; - } - - /** - * Encode json for $.parseJSON - * - * @param array $data - * @param int $options - * @static - * @access public - * @return string - */ - static public function jsonEncode4Parse($data, $options = 0) - { - $json = json_encode($data); - if($options) $json = str_replace(array("'", '"'), array('\u0027', '\u0022'), $json); - - $escapers = array("\\", "/", "\"", "'", "\n", "\r", "\t", "\x08", "\x0c", "\\\\u"); - $replacements = array("\\\\", "\\/", "\\\"", "\'", "\\n", "\\r", "\\t", "\\f", "\\b", "\\u"); - return str_replace($escapers, $replacements, $json); - } - - /** - * Convert encoding. - * - * @param string $string - * @param string $fromEncoding - * @param string $toEncoding - * @static - * @access public - * @return string - */ - static public function convertEncoding($string, $fromEncoding, $toEncoding = 'utf-8') - { - $toEncoding = str_replace('utf8', 'utf-8', $toEncoding); - if(function_exists('mb_convert_encoding')) - { - /* Remove like utf-8//TRANSLIT. */ - $position = strpos($toEncoding, '//'); - if($position !== false) $toEncoding = substr($toEncoding, 0, $position); - - /* Check string encoding. */ - $encoding = strtolower(mb_detect_encoding($string, array('ASCII','UTF-8','GB2312','GBK','BIG5'))); - if($encoding == $toEncoding) return $string; - return mb_convert_encoding($string, $toEncoding, $encoding); - } - elseif(function_exists('iconv')) - { - if($fromEncoding == $toEncoding) return $string; - $convertString = @iconv($fromEncoding, $toEncoding, $string); - /* iconv error then return original. */ - if(!$convertString) return $string; - return $convertString; - } - - return $string; - } -} - -/** - * 检查是否是onlybody模式。 - * Check exist onlybody param. - * - * @access public - * @return void - */ -function isonlybody() -{ - return helper::inOnlyBodyMode(); -} - -/** - * Format time. - * - * @param int $time - * @param string $format - * @access public - * @return void - */ -function formatTime($time, $format = '') -{ - $time = str_replace('0000-00-00', '', $time); - $time = str_replace('00:00:00', '', $time); - if(trim($time) == '') return ; - if($format) return date($format, strtotime($time)); - return trim($time); -} diff --git a/framework/model.class.php b/framework/model.class.php deleted file mode 100644 index 2335d5dd18..0000000000 --- a/framework/model.class.php +++ /dev/null @@ -1,38 +0,0 @@ -dao->update($table)->set('deleted')->eq(1)->where('id')->eq($id)->exec(); - $object = preg_replace('/^' . preg_quote($this->config->db->prefix) . '/', '', trim($table, '`')); - $this->loadModel('action')->create($object, $id, 'deleted', '', $extra = ACTIONMODEL::CAN_UNDELETED); - } -} diff --git a/framework/router.class.php b/framework/router.class.php deleted file mode 100755 index 536dddf8dd..0000000000 --- a/framework/router.class.php +++ /dev/null @@ -1,159 +0,0 @@ -dbh and !empty($this->config->db->name)) - { - global $config; - if(!isset($config->global)) $config->global = new stdclass(); - $flow = $this->dbh->query('SELECT value FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='common' AND `key`='flow'")->fetch(); - $config->global->flow = $flow ? $flow->value : 'full'; - - try - { - $productProject = $this->dbh->query('SELECT value FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); - } - catch (PDOException $exception) - { - $repairCode = '|1034|1035|1194|1195|1459|'; - $errorInfo = $exception->errorInfo; - $errorCode = $errorInfo[1]; - $errorMsg = $errorInfo[2]; - $message = $exception->getMessage(); - if(strpos($repairCode, "|$errorCode|") !== false or ($errorCode == '1016' and strpos($errorMsg, 'errno: 145') !== false) or strpos($message, 'repair') !== false) - { - if(isset($config->framework->autoRepairTable) and $config->framework->autoRepairTable) - { - header("location: " . $config->webRoot . 'checktable.php'); - exit; - } - } - } - } - - $productCommon = $projectCommon = 0; - if($productProject) - { - $productProject = $productProject->value; - list($productCommon, $projectCommon) = explode('_', $productProject); - } - $lang->productCommon = isset($this->config->productCommonList[$this->clientLang][(int)$productCommon]) ? $this->config->productCommonList[$this->clientLang][(int)$productCommon] : $this->config->productCommonList['zh-cn'][0]; - $lang->projectCommon = isset($this->config->projectCommonList[$this->clientLang][(int)$projectCommon]) ? $this->config->projectCommonList[$this->clientLang][(int)$projectCommon] : $this->config->projectCommonList['zh-cn'][0]; - } - - parent::loadLang($moduleName, $appName); - - /* Merge from the db lang. */ - if($moduleName != 'common' and isset($lang->db->custom[$moduleName])) - { - foreach($lang->db->custom[$moduleName] as $section => $fields) - { - if(isset($lang->{$moduleName}->{$section}[''])) - { - $nullKey = ''; - $nullValue = $lang->{$moduleName}->{$section}[$nullKey]; - } - elseif(isset($lang->{$moduleName}->{$section}[0])) - { - $nullKey = 0; - $nullValue = $lang->{$moduleName}->{$section}[0]; - } - unset($lang->{$moduleName}->{$section}); - - if(isset($nullKey))$lang->{$moduleName}->{$section}[$nullKey] = $nullValue; - foreach($fields as $key => $value) $lang->{$moduleName}->{$section}[$key] = $value; - unset($nullKey); - unset($nullValue); - } - } - return $lang; - } - - /** - * Save error info. - * - * @param int $level - * @param string $message - * @param string $file - * @param int $line - * @access public - * @return void - */ - public function saveError($level, $message, $file, $line) - { - $fatalLevel[E_ERROR] = E_ERROR; - $fatalLevel[E_PARSE] = E_PARSE; - $fatalLevel[E_CORE_ERROR] = E_CORE_ERROR; - $fatalLevel[E_USER_ERROR] = E_USER_ERROR; - if(isset($fatalLevel[$level])) $this->config->debug = true; - parent::saveError($level, $message, $file, $line); - } - - /** - * Alias load module config. - * - * @param string $moduleName - * @param string $appName - * @access public - * @return void - */ - public function loadConfig($moduleName, $appName = '') - { - return parent::loadModuleConfig($moduleName, $appName); - } - - /** - * Export config. - * - * @access public - * @return void - */ - public function exportConfig() - { - ob_start(); - parent::exportConfig(); - $view = ob_get_contents(); - ob_end_clean(); - - $view = json_decode($view); - $view->rand = $this->session->random; - $this->session->set('rand', $this->session->random); - echo json_encode($view); - } -} diff --git a/framework/tests/helper/.101.setmember.onelevel.et b/framework/tests/helper/.101.setmember.onelevel.et deleted file mode 100644 index 5b184f35fa..0000000000 --- a/framework/tests/helper/.101.setmember.onelevel.et +++ /dev/null @@ -1,13 +0,0 @@ -value1.1 -value2 -3 -Array -( - [0] => 1 - [1] => 2 - [2] => 3 -) -stdClass Object -( - [key1] => value2.1 -) diff --git a/framework/tests/helper/.110.setmember.multilevel.et b/framework/tests/helper/.110.setmember.multilevel.et deleted file mode 100644 index ee73e72ede..0000000000 --- a/framework/tests/helper/.110.setmember.multilevel.et +++ /dev/null @@ -1,6 +0,0 @@ -Mary -20 -stdClass Object -( - [postcode] => 10000 -) diff --git a/framework/tests/helper/.201.getwebroot.inbrowse.et b/framework/tests/helper/.201.getwebroot.inbrowse.et deleted file mode 100644 index 618380792d..0000000000 --- a/framework/tests/helper/.201.getwebroot.inbrowse.et +++ /dev/null @@ -1,2 +0,0 @@ -/ -/zentao/ diff --git a/framework/tests/helper/.202.getwebroot.inshell.et b/framework/tests/helper/.202.getwebroot.inshell.et deleted file mode 100644 index 3de39a7763..0000000000 --- a/framework/tests/helper/.202.getwebroot.inshell.et +++ /dev/null @@ -1,8 +0,0 @@ -http://localhost webRoot is / -http://localhost/ webRoot is / -http://localhost/my/ webRoot is / -http://localhost/my-todo.html webRoot is / -http://localhost/index.php?m=my&f=index webRoot is / -http://localhost/zentao/my/ webRoot is /zentao/ -http://localhost/zentao/my-todo.html webRoot is /zentao/ -http://localhost/zentao/index.php?m=my&f=index webRoot is /zentao/ diff --git a/framework/tests/helper/101.setmember.onelevel.php b/framework/tests/helper/101.setmember.onelevel.php deleted file mode 100755 index cb7a794639..0000000000 --- a/framework/tests/helper/101.setmember.onelevel.php +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env php -key1 = 'value1'; - -$obj2 = new stdclass(); -$obj2->key1 = 'value2.1'; - -helper::setMember('obj', 'key1', 'value1.1'); // overide the exists key. -helper::setMember('obj', 'key2', 'value2'); // add a new key. -helper::setMember('obj', 'key3', 3); // set an int value. -helper::setMember('obj', 'key4', array(1, 2, 3)); // set an array value. -helper::setMember('obj', 'key5', $obj2); // set an object value. - -echo $obj->key1 . "\n"; -echo $obj->key2 . "\n"; -echo $obj->key3 . "\n"; -print_r($obj->key4); -print_r($obj->key5); diff --git a/framework/tests/helper/110.setmember.multilevel.php b/framework/tests/helper/110.setmember.multilevel.php deleted file mode 100755 index 14d3baf0cd..0000000000 --- a/framework/tests/helper/110.setmember.multilevel.php +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env php -user = new stdclass(); -$obj->user->name = 'Tom'; - -helper::setMember('obj', 'user.name', 'Mary'); // overide the exists key. -helper::setMember('obj', 'user.age', 20); // add a child key to an existing key. -helper::setMember('obj', 'home.address', new stdclass()); // add a child key even the parent doesn't exist. -helper::setMember('obj', 'home.address.postcode', '10000'); // three level. - -echo $obj->user->name . "\n"; -echo $obj->user->age . "\n"; -print_r($obj->home->address); diff --git a/framework/tests/helper/201.getwebroot.inbrowse.php b/framework/tests/helper/201.getwebroot.inbrowse.php deleted file mode 100755 index a630da9b4d..0000000000 --- a/framework/tests/helper/201.getwebroot.inbrowse.php +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env php -global = new stdclass(); -$config->global->flow = 'full'; - -/* set module root path and included the resource of group module. */ -$moduleRoot = '../module/'; -include $moduleRoot . '/group/lang/resource.php'; -foreach(glob($moduleRoot . '/group/ext/lang/zh-cn/*.php') as $resourceFile) -{ - include $resourceFile; -} - -$lang->productCommon = ''; -$lang->projectCommon = ''; - -$whiteList[] = 'api-getsessionid'; -$whiteList[] = 'admin-setflow'; -$whiteList[] = 'bug-buildtemplates'; -$whiteList[] = 'bug-sendmail'; -$whiteList[] = 'board-managechild'; -$whiteList[] = 'custom-menu'; -$whiteList[] = 'company-create'; -$whiteList[] = 'company-delete'; -$whiteList[] = 'file-buildexporttpl'; -$whiteList[] = 'file-buildform'; -$whiteList[] = 'file-printfiles'; -$whiteList[] = 'file-export2csv'; -$whiteList[] = 'file-export2xml'; -$whiteList[] = 'file-export2html'; -$whiteList[] = 'file-export2excel'; -$whiteList[] = 'file-export2word'; -$whiteList[] = 'file-senddownheader'; -$whiteList[] = 'file-read'; -$whiteList[] = 'help-field'; -$whiteList[] = 'index-testext'; -$whiteList[] = 'productplan-commonaction'; -$whiteList[] = 'project-managechilds'; -$whiteList[] = 'project-tips'; -$whiteList[] = 'project-commonaction'; -$whiteList[] = 'project-sendmail'; -$whiteList[] = 'release-commonaction'; -$whiteList[] = 'task-commonaction'; -$whiteList[] = 'task-sendmail'; -$whiteList[] = 'testtask-sendmail'; -$whiteList[] = 'user-login'; -$whiteList[] = 'user-deny'; -$whiteList[] = 'user-logout'; -$whiteList[] = 'user-setreferer'; -$whiteList[] = 'svn-run'; -$whiteList[] = 'git-run'; -$whiteList[] = 'admin-ignore'; -$whiteList[] = 'admin-register'; -$whiteList[] = 'admin-win2unix'; -$whiteList[] = 'admin-bind'; -$whiteList[] = 'admin-certifyztemail'; -$whiteList[] = 'admin-certifyztmobile'; -$whiteList[] = 'admin-ztcompany'; -$whiteList[] = 'story-commonaction'; -$whiteList[] = 'story-sendmail'; -$whiteList[] = 'webapp-ajaxaddview'; -$whiteList[] = 'report-remind'; -$whiteList[] = 'sso-auth'; -$whiteList[] = 'sso-depts'; -$whiteList[] = 'sso-users'; -$whiteList[] = 'sso-login'; -$whiteList[] = 'sso-logout'; -$whiteList[] = 'sso-bind'; -$whiteList[] = 'sso-getuserpairs'; -$whiteList[] = 'sso-getbindusers'; -$whiteList[] = 'sso-binduser'; -$whiteList[] = 'sso-createuser'; -$whiteList[] = 'sso-gettodolist'; -$whiteList[] = 'mail-asyncsend'; -$whiteList[] = 'user-reset'; -$whiteList[] = 'product-showerrornone'; -$whiteList[] = 'tutorial-start'; -$whiteList[] = 'tutorial-index'; -$whiteList[] = 'tutorial-quit'; -$whiteList[] = 'tutorial-wizard'; -$whiteList[] = 'my-buildcontactlists'; -$whiteList[] = 'mail-ztcloud'; -$whiteList[] = 'doc-diff'; -$whiteList[] = 'testreport-commonaction'; -$whiteList[] = 'testsuite-library'; -$whiteList[] = 'testsuite-createlib'; -$whiteList[] = 'testsuite-createcase'; -$whiteList[] = 'testsuite-libview'; - - -/* checking actions of every module. */ -echo '-------------action checking-----------------' . "\n"; -foreach(glob($moduleRoot . '*') as $modulePath) -{ - $moduleName = basename($modulePath); - if(strpos('install|upgrade|convert|common|misc|editor', $moduleName) !== false) continue; - $controlFile = $modulePath . '/control.php'; - if(file_exists($controlFile)) - { - include $controlFile; - if(class_exists($moduleName)) - { - if($moduleName == 'block') continue; - $class = new ReflectionClass($moduleName); - $methods = $class->getMethods(); - foreach($methods as $method) - { - $methodRef = new ReflectionMethod($method->class, $method->name); - if($methodRef->isPublic() and strpos($method->name, '__') === false) - { - $methodName = $method->name; - if(in_array($moduleName . '-' . strtolower($method->name), $whiteList)) continue; - if(strpos($methodName, 'ajax') !== false) continue; - - $exits = false; - if(isset($lang->resource->$moduleName)) - { - foreach($lang->resource->$moduleName as $key => $label) - { - if(strtolower($methodName) == strtolower($key)) $exits = true; - } - } - if(!$exits) echo $moduleName . "\t" . $methodName . " not in the list. \n"; - } - } - } - } - - /* Checking extension files. */ - $extControlFiles = glob($modulePath . '/ext/control/*.php'); - if($extControlFiles) - { - foreach($extControlFiles as $extControlFile) - { - $methodFile = substr($extControlFile, strrpos($extControlFile, '/') + 1); - $methodName = substr($methodFile, 0, strpos($methodFile, '.')); - if(in_array($moduleName . '-' . strtolower($methodName), $whiteList)) continue; - if(strpos($methodName, 'ajax') !== false) continue; - - $exits = false; - foreach($lang->resource->$moduleName as $key => $label) - { - if(strtolower($methodName) == strtolower($key)) $exits = true; - } - if(!$exits) echo $moduleName . "\t" . $methodName . " not in the list. \n"; - } - } -} - -/* checking actions of every module. */ -echo '-------------lang checking-----------------' . "\n"; -include '../module/common/lang/zh-cn.php'; -include '../config/config.php'; -foreach(glob($moduleRoot . '*') as $modulePath) -{ - unset($lang); - $moduleName = basename($modulePath); - $mainLangFile = $modulePath . '/lang/zh-cn.php'; - if(!file_exists($mainLangFile)) continue; - $mainLines = file($mainLangFile); - - foreach($config->langs as $langKey => $langName) - { - if($langKey == 'zh-cn' or $langKey == 'zh-tw') continue; - $langFile = $modulePath . '/lang/' . $langKey . '.php'; - if(!file_exists($langFile)) continue; - $lines = file($langFile); - foreach($mainLines as $lineNO => $line) - { - if(strpos($line, '$lang') === false) - { - //if($line != $lines[$lineNO]) echo $moduleName . ' ' . $langKey . ' ' . $lineNO . "\n"; - } - else - { - if(strpos($line, '=') !== false) - { - list($mainKey, $mainValue) = explode('=', $line); - list($key, $value) = explode('=', $lines[$lineNO]); - } - if((strpos($line, '=') === false and $line != $lines[$lineNO]) or trim($mainKey) != trim($key)) - { - $key = trim($key); - $lineNO = $lineNO + 1; - echo "module $moduleName need checking, command is:"; - echo " vim -O +$lineNO ../module/$moduleName/lang/zh-cn.php +$lineNO ../module/$moduleName/lang/en.php \n"; - break; - } - } - } - } - - foreach(glob($modulePath . '/ext/lang/zh-cn/*.php') as $extMainLangFile) - { - $extMainLines = file($extMainLangFile); - $extLangFile = basename($extMainLangFile); - $extEnFile = $modulePath . '/ext/lang/en/' . $extLangFile; - $extLines = file($extEnFile); - foreach($extMainLines as $lineNO => $line) - { - if(strpos($line, '$lang') === false) - { - //if($line != $lines[$lineNO]) echo $moduleName . ' ' . $langKey . ' ' . $lineNO . "\n"; - } - else - { - list($mainKey, $mainValue) = explode('=', $line); - list($key, $value) = explode('=', $extLines[$lineNO]); - if(trim($mainKey) != trim($key)) - { - $key = trim($key); - $lineNO = $lineNO + 1; - echo "module $moduleName need checking, command is:"; - echo " vim -O +$lineNO ../../module/$moduleName/ext/lang/zh-cn/$extLangFile +$lineNO ../../module/$moduleName/ext/lang/en/$extLangFile \n"; - break; - } - } - } - } -} - -echo '-------------php5.4 synatax checking-----------------' . "\n"; -class app {function loadLang() {}} -$app = new app; -$lang = new stdclass(); - -error_reporting(E_WARNING | E_STRICT ); -foreach(glob($moduleRoot . '*') as $modulePath) -{ - $moduleName = basename($modulePath); - $cnLangFile = $modulePath . '/lang/zh-cn.php'; - $enLangFile = $modulePath . '/lang/en.php'; - $configFile = $modulePath . '/config.php'; - - if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass(); - if(!isset($config->$moduleName)) $config->$moduleName = new stdclass(); - if(file_exists($cnLangFile)) include $cnLangFile; - if(file_exists($enLangFile)) include $enLangFile; - if(file_exists($configFile)) include $configFile; -} - -echo '-------------demo data checking. -----------------' . "\n"; -$demoSQL = file("../db/demo.sql"); -foreach($demoSQL as $line => $sql) -{ - if(strpos($sql, 'INSERT') === false) continue; - - if(strpos($sql, $config->db->prefix . 'config') !== false or - strpos($sql, $config->db->prefix . 'company') !== false or - strpos($sql, $config->db->prefix . 'group') !== false) - { - die('line ' . ($line + 1) . " has error\n"); - } -} diff --git a/tools/cn2tw.php b/tools/cn2tw.php deleted file mode 100755 index 8af43a45b4..0000000000 --- a/tools/cn2tw.php +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env php - $targetLangFile"); - $defaultLang = file_get_contents($targetLangFile); - $targetLang = str_replace('zh-cn', $langDesc, $defaultLang); - file_put_contents($targetLangFile, $targetLang); -} -?> diff --git a/tools/copylang.php b/tools/copylang.php deleted file mode 100755 index 8f7d1a34ce..0000000000 --- a/tools/copylang.php +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env php - diff --git a/tools/exportactions.php b/tools/exportactions.php deleted file mode 100755 index 3bf58bdd0b..0000000000 --- a/tools/exportactions.php +++ /dev/null @@ -1,29 +0,0 @@ -getMethods(); - foreach($methods as $method) - { - $methodRef = new ReflectionMethod($method->class, $method->name); - if($methodRef->isPublic() and strpos($method->name, '__') === false) - { - echo "\$lang['action']['$moduleName']['$method->name'] = '$method->name';\n"; - } - } - echo "\n"; - } - } -} -?> diff --git a/tools/getallcommon.php b/tools/getallcommon.php deleted file mode 100755 index 6bea6b1765..0000000000 --- a/tools/getallcommon.php +++ /dev/null @@ -1,28 +0,0 @@ - $maxLength) $maxLength = strlen($moduleName); -} - -foreach($modules as $modulePath) -{ - $moduleName = basename($modulePath); - if($moduleName == 'help' or $moduleName == 'editor') continue; - $langFile = $modulePath . "/lang/$langType.php"; - if(!file_exists($langFile)) continue; - include $langFile; - - $moduleTitle = ''; - if(isset($lang->$moduleName->common)) - { - $moduleTitle = $lang->$moduleName->common; - } - - echo "\$lang->editor->modules['$moduleName'] " . str_pad('', $maxLength - strlen($moduleName)) . "= '$moduleTitle';\n"; - -} diff --git a/tools/initext.php b/tools/initext.php deleted file mode 100755 index be9f06a725..0000000000 --- a/tools/initext.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @package common - * @version $Id$ - * @link http://www.zentao.net - */ -include '../config/config.php'; - -$modules = array(); -$moduleRoot = realpath('../module/') . '/'; - -if(is_dir($moduleRoot)) -{ - if($dh = opendir($moduleRoot)) - { - while($module = readdir($dh)) - { - if(strpos(basename($module), '.') === false) $modules[] = $module; - } - closedir($dh); - } -} -else -{ - die("The module you input does not exist. \n"); -} - -foreach($modules as $module) -{ - /* 设定各个目录。*/ - $extRoot = $moduleRoot . DIRECTORY_SEPARATOR. $module . DIRECTORY_SEPARATOR . 'ext'; - $extControl = $extRoot . DIRECTORY_SEPARATOR . 'control'; - $extModel = $extRoot . DIRECTORY_SEPARATOR . 'model'; - $extView = $extRoot . DIRECTORY_SEPARATOR . 'view'; - $extCSS = $extRoot . DIRECTORY_SEPARATOR . 'css'; - $extJS = $extRoot . DIRECTORY_SEPARATOR . 'js'; - $extConfig = $extRoot . DIRECTORY_SEPARATOR . 'config'; - $extLang = $extRoot . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR; - - /* 建立各个扩展目录 */ - if(!file_exists($extRoot)) mkdir($extRoot, 0777); - if(!file_exists($extControl)) mkdir($extControl, 0777); - if(!file_exists($extModel)) mkdir($extModel, 0777); - if(!file_exists($extView)) mkdir($extView, 0777); - if(!file_exists($extCSS)) mkdir($extCSS, 0777); - if(!file_exists($extJS)) mkdir($extJS, 0777); - if(!file_exists($extConfig)) mkdir($extConfig, 0777); - if(!file_exists($extLang)) mkdir($extLang, 0777); - - /* Touch .gitkeep file. */ - touch($extControl . '/.gitkeep'); - touch($extModel . '/.gitkeep'); - touch($extView . '/.gitkeep'); - touch($extCSS . '/.gitkeep'); - touch($extJS . '/.gitkeep'); - touch($extConfig . '/.gitkeep'); - - /* 创建语言目录。*/ - $langs = array_keys($config->langs); - foreach($langs as $lang) - { - $langPath = $extLang . $lang; - if(!file_exists($langPath)) mkdir($langPath, 0777); - touch($langPath . '/.gitkeep'); - } - - echo "init $module ... \n"; -} diff --git a/tools/minifyfront.php b/tools/minifyfront.php deleted file mode 100755 index d8eaa75ead..0000000000 --- a/tools/minifyfront.php +++ /dev/null @@ -1,74 +0,0 @@ -productCommon = ''; -$lang->projectCommon = ''; -include $baseDir . '/module/common/lang/zh-cn.php'; -$langs = array_keys($config->langs); -$themes = array_keys($lang->themes); - -/* Create css files for every them and every lang. */ -$zuiCode = str_replace('../fonts', '../zui/fonts', file_get_contents($themeRoot . 'zui/css/min.css')); -foreach($langs as $lang) -{ - foreach($themes as $theme) - { - /* Common css files. */ - $cssCode = $zuiCode; - $cssCode .= file_get_contents($themeRoot . 'default/style.css'); - $cssCode .= file_get_contents($jqueryRoot . 'chosen/min.css'); - $cssCode .= file_get_contents($themeRoot . 'default/treeview.css'); - $cssCode .= file_get_contents($jqueryRoot . 'datetimepicker/min.css'); - - /* Css file for current lang and current them. */ - $cssCode .= file_get_contents($themeRoot . "lang/$lang.css"); - if($theme != 'default') - { - $themCode = file_get_contents($themeRoot . $theme . '/style.css'); - $cssCode .= str_replace('./images', "../$theme/images", $themCode); - } - - /* Combine them. */ - $cssFile = $themeRoot . "default/$lang.$theme.css"; - file_put_contents($cssFile, $cssCode); - - /* Compress it. */ - `java -jar ~/bin/yuicompressor/build/yuicompressor.jar --type css $cssFile -o $cssFile`; - } -} diff --git a/tools/preparetest.php b/tools/preparetest.php deleted file mode 100755 index bce62e92d7..0000000000 --- a/tools/preparetest.php +++ /dev/null @@ -1,60 +0,0 @@ - $database.sql;\n"; - $cmd .= "mysql -uroot $backupDatabase < $database.sql;\n"; - $cmd .= "rm -fr $database.sql"; - system($cmd); - } -} - -/* Restore stored backup databases. */ -function restore() -{ - global $databases; - foreach($databases as $database) - { - $backupDatabase = "back_$database"; - mysql_query("DROP DATABASE $database"); - mysql_query("CREATE DATABASE $database"); - $cmd = "mysqldump -u root $backupDatabase > $database.sql;\n"; - $cmd .= "mysql -uroot $database < $database.sql;\n"; - $cmd .= "rm -fr $database.sql"; - system($cmd); - } -} - -/* Connect to database. */ -function connectDB() -{ - global $config; - mysql_connect($config->db->host, $config->db->user, $config->db->password); -} diff --git a/tools/replace.sh b/tools/replace.sh deleted file mode 100755 index 8536f5eb8b..0000000000 --- a/tools/replace.sh +++ /dev/null @@ -1,17 +0,0 @@ -#replace title -foreachd(){ - for file in `ls $1` - do - if [ -d $1/$file ] - then - echo "into dir $1/$file" - foreachd $1/$file - elif [ -f $1/$file ] - then -# sed -i '1,11s/\@license.*$/\@license ZPL \(http:\/\/zpl\.pub\/page\/zplv11\.html\)/g' $1/$file - sed -i '1,11s/\@copyright.*$/\@copyright Copyright 2009-2015 青岛易软天创网络科技有限公司\(QingDao Nature Easy Soft Network Technology Co,LTD, www\.cnezsoft\.com\)/g' $1/$file - fi - done -} - -foreachd $1 diff --git a/tools/syncext.php b/tools/syncext.php deleted file mode 100755 index cad41b07ce..0000000000 --- a/tools/syncext.php +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env php - -# @author yidong wang -# - -# Judge the params, must give $src and $dest. -if [ $# -lt 2 ]; then - echo './syncext.sh src dest' - exit 1 -fi - -# Judge inotifywait tool exists or not. -which inotifywait > /dev/null -if [ $? -ne 0 ]; then - echo 'No inotifywait. Please install inotify-tools, for debian or ubuntun run apt-get install inotify-tools.'; - exit 1 -fi - -# Get $src and $dest. -src=$1/ -dest=$2/ - -# Rsync when the shell load first. -rsync -aqP --exclude="db/" $src $dest -echo `date "+%H:%M:%S"` sync $src to $dest succssfully. - -# Watch the $src directory, and sync files to destination. -inotifywait -mrq --event create,modify,delete,move --format '%w %e %f' $src |\ -while read watcher event file ; do - - # compute the $path2process. - path2process=$watcher$file - path2process=${path2process/$src/$dest} - - # Delete a file or a directory. - if [ "$event" == "DELETE" -o "$event" == "DELETE,ISDIR" ]; then - - # If delete a file, use rm -fr, directory, use rmdir for security. - if [ "$event" == "DELETE" ]; then - rm -fr $path2process - else - rmdir $path2process - fi - - echo `date "+%H:%M:%S"` $path2process deleted.; - - # If event is ngnored, continue. - elif [ "$event" == "IGNORED" ]; then - - continue - - # Sync files. - else - - echo `date "+%H:%M:%S"` $path2process copied. - rsync -aqP --exclude='db/' $src/ $dest - fi - -done diff --git a/tools/zdog.sh b/tools/zdog.sh deleted file mode 100755 index e0e6354df7..0000000000 --- a/tools/zdog.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# Watch a upload directory and remove dangder files. -# -# @author chunsheng wang - -# Judge the params, must give $dir -if [ $# -lt 1 ]; then - echo './zdog.sh directory' - exit 1 -fi - -# Judge inotifywait tool exists or not. -which inotifywait > /dev/null -if [ $? -ne 0 ]; then - echo 'No inotifywait. Please install inotify-tools, for debian or ubuntun run apt-get install inotify-tools.'; - exit 1 -fi - -# Get the directory to watch. -dir=$@ - -# Watch the $dir directory -if [ ! -d "/tmp/zdog" ]; then - mkdir -v /tmp/zdog; -fi -echo `date "+%H:%M:%S"` begin watching $dir > /tmp/zdog/zdog.log -inotifywait -mrq --event create,modify --format '%w %e %f' $dir |\ -while read watcher event file ; do - - # compute the $path2process. - path2process=$watcher$file - targetfile=/tmp/zdog/$file; - echo $path2process | grep -q '.php'; - if [ $? -eq 0 ] && [ -s $path2process ]; then - sudo mv -f $path2process $targetfile; - echo `date "+%H:%M:%S"` $path2process moved to $targetfile >> /tmp/zdog/zdog.log 2>&1; - fi -done From 1f102d851de8264d3e4a05d44c06faf3d78c21ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 15:11:27 +0800 Subject: [PATCH 09/40] "Task list add btn" --- module/project/js/common.js | 17 ++++++++++++++ module/task/js/common.js | 2 +- module/task/model.php | 1 + module/task/view/view.html.php | 42 ++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/module/project/js/common.js b/module/project/js/common.js index dc3a24d66a..5f3298ac06 100644 --- a/module/project/js/common.js +++ b/module/project/js/common.js @@ -174,3 +174,20 @@ $(function() computeWorkDays(this.id); }) }); + +$('.task-toggle').click(function() +{ + var obj = $(this).find('i'); + var id = $(this).data('id'); + if(obj.hasClass('icon-plus')) + { + $('tr.parent-'+id).show(); + obj.removeClass('icon-plus').addClass('icon-minus'); + } + else if(obj.hasClass('icon-minus')) + { + $('tr.parent-'+id).hide(); + obj.removeClass('icon-minus').addClass('icon-plus'); + } + return false; +}); \ No newline at end of file diff --git a/module/task/js/common.js b/module/task/js/common.js index 140db4051e..9363ddb004 100644 --- a/module/task/js/common.js +++ b/module/task/js/common.js @@ -56,4 +56,4 @@ $('#modalTeam .btn').click(function() $('#teamMember').val(team); $('#estimate').val(time); }) -}) +}) \ No newline at end of file diff --git a/module/task/model.php b/module/task/model.php index b7643dc362..1dc1000479 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1836,6 +1836,7 @@ class taskModel extends model if($modulePairs and $task->module) echo "" . $modulePairs[$task->module] . ' '; echo html::a($taskLink, $task->name, null, "style='color: $task->color'"); if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); + if($task->children) echo '    '; break; case 'type': echo $this->lang->task->typeList[$task->type]; diff --git a/module/task/view/view.html.php b/module/task/view/view.html.php index 3decac4ae3..58ab441e40 100644 --- a/module/task/view/view.html.php +++ b/module/task/view/view.html.php @@ -87,7 +87,6 @@
storyVerify;?>
- cases) and $task->cases):?>
task->case;?> @@ -98,7 +97,46 @@
- + children)):?> +
+ lang->task->children;?> +
+ + + + + + + + + + + + children as $child):?> + + + + + + + + + + + + +
task->id;?> task->lblPri;?> task->name;?>task->deadline;?> task->assignedTo;?> task->status;?> task->consumedAB . $lang->task->lblHour;?>task->leftAB . $lang->task->lblHour;?>actions;?>
id;?>task->priList[$child->pri];?>id"); ?>">name;?>deadline;?>assignedTo])) echo $users[$child->assignedTo];?>task->statusList, $child->status);?>consumed;?>left;?> + project&taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'start', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true); + common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'edit',"taskID=$child->id", $child, 'list'); + ?> +
+
+ fetch('file', 'printFiles', array('files' => $task->files, 'fieldset' => 'true'));?>
deleted) echo $actionLinks;?>
From 421e046c930cb8b41990af585da9994d2759c50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Fri, 20 Oct 2017 15:17:24 +0800 Subject: [PATCH 10/40] "style change" --- module/task/view/view.html.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/module/task/view/view.html.php b/module/task/view/view.html.php index 58ab441e40..418625d699 100644 --- a/module/task/view/view.html.php +++ b/module/task/view/view.html.php @@ -115,7 +115,13 @@ children as $child):?>
id;?>task->priList[$child->pri];?> + lang->task->priList, $child->pri, $child->pri) . "'>"; + echo $child->pri == '0' ? '' : zget($this->lang->task->priList, $child->pri, $child->pri); + echo ""; + ?> + id"); ?>">name;?> deadline;?> assignedTo])) echo $users[$child->assignedTo];?>
+ + + + + + + + + + + + + + + + + + + +
project->kanbanHideCols?>kanbanSetting->optionList, $allCols)?>
project->kanbanShowOption?>kanbanSetting->optionList, $showOption)?>
project->kanbanColsColor?> +
+ $color):?> +
+ task->statusList[$status];?> + +
+ +
+
+ project->resetKanban, 'hiddenwin', "class='btn'"); + ?> +
+ + + diff --git a/module/project/view/kanban.html.php b/module/project/view/kanban.html.php index f6f689c425..a8e56ac739 100644 --- a/module/project/view/kanban.html.php +++ b/module/project/view/kanban.html.php @@ -10,7 +10,16 @@ ?> - + +app->user->account +?>
@@ -34,7 +43,15 @@ @@ -58,7 +75,7 @@ - $value) $this->bug->printCell($value, $bug, $users, $builds, $branches, $modulePairs, $projects, $plans, $stories, $tasks);?> + $value) $this->bug->printCell($value, $bug, $users, $builds, $branches, $modulePairs, $projects, $plans, $stories, $tasks, $useDatatable ? 'datatable' : 'table');?> diff --git a/module/product/view/browse.html.php b/module/product/view/browse.html.php index 8bb6c7f7b5..b7929f2910 100644 --- a/module/product/view/browse.html.php +++ b/module/product/view/browse.html.php @@ -130,7 +130,7 @@ - $value) $this->story->printCell($value, $story, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases);?> + $value) $this->story->printCell($value, $story, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases, $useDatatable ? 'datatable' : 'table');?> diff --git a/module/project/view/datatabledata.html.php b/module/project/view/datatabledata.html.php deleted file mode 100644 index 31ac138adf..0000000000 --- a/module/project/view/datatabledata.html.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @package project - * @version $Id$ - * @link http://www.zentao.net - */ -?> -datatable->getSetting('project'); -$widths = $this->datatable->setFixedFieldWidth($setting); -$columns = 0; -extract($widths); -?> -
task->statusList[$endCol];?>
- " . $lang->project->printKanban . '', "id='printKanban' title='" . $lang->project->printKanban . "'", 'btn btn-sm btn-link');?> +
id)):?> -
' data-id='id?>'> +
assignedTo == $account) echo ' inverse';?>' data-id='id?>'>
createLink('story', 'view', "storyID=$story->id", '', true), $story->title, '', 'class="kanbanFrame" title="' . $story->title . '"');?>
@@ -67,7 +84,7 @@ -
tasks[$col])):?> tasks[$col] as $task):?> -
' data-id='id?>' id='task-id?>'> +
assignedTo == $account) echo ' inverse';?>' data-id='id?>' id='task-id?>'>
delay)) @@ -109,7 +126,7 @@ -
"; - switch ($id) + switch($id) { case 'id': - echo html::a($bugLink, sprintf('%03d', $bug->id)); + if($mode == 'table') echo " "; + echo $canView ? html::a($bugLink, sprintf('%03d', $bug->id)) : sprintf('%03d', $bug->id); break; case 'severity': echo ""; @@ -2344,7 +2347,7 @@ class bugModel extends model echo "[{$this->lang->bug->confirmedList[$bug->confirmed]}] "; if($bug->branch)echo "{$branches[$bug->branch]} "; if($modulePairs and $bug->module)echo "{$modulePairs[$bug->module]} "; - echo html::a($bugLink, $bug->title, null, "style='color: $bug->color'"); + echo $canView ? html::a($bugLink, $bug->title, null, "style='color: $bug->color'") : "{$bug->title}"; break; case 'branch': echo $branches[$bug->branch]; diff --git a/module/bug/view/browse.html.php b/module/bug/view/browse.html.php index a761bba051..4d3af96683 100644 --- a/module/bug/view/browse.html.php +++ b/module/bug/view/browse.html.php @@ -193,7 +193,7 @@ js::set('branch', $branch);
- - $value) - { - if($value->show) - { - $this->datatable->printHead($value, $orderBy, $vars); - $columns++; - } - } - ?> - - - - - $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> - - children) foreach($task->children as $child):?> - - $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs);?> - - - - diff --git a/module/project/view/task.html.php b/module/project/view/task.html.php index 2f2022c87c..311b74f614 100644 --- a/module/project/view/task.html.php +++ b/module/project/view/task.html.php @@ -68,12 +68,12 @@ js::set('browseType', $browseType); - $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs);?> + $value) $this->task->printCell($value, $task, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table');?> children)):?> children as $child):?> - $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs);?> + $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table');?> diff --git a/module/story/model.php b/module/story/model.php index 402cc7fd5c..017dde597f 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -2247,8 +2247,9 @@ class storyModel extends model * @access public * @return void */ - public function printCell($col, $story, $users, $branches, $storyStages, $modulePairs = array(), $storyTasks, $storyBugs, $storyCases) + public function printCell($col, $story, $users, $branches, $storyStages, $modulePairs = array(), $storyTasks = array(), $storyBugs = array(), $storyCases = array(), $mode = 'datatable') { + $canView = common::hasPriv('story', 'view'); $storyLink = helper::createLink('story', 'view', "storyID=$story->id"); $account = $this->app->user->account; $id = $col->id; @@ -2256,7 +2257,8 @@ class storyModel extends model { $class = ''; if($id == 'status') $class .= ' story-' . $story->status; - if($id == 'title') $class .= ' text-left'; + if($id == 'title') $class .= ' text-left'; + if($id == 'id') $class .= ' cell-id'; if($id == 'assignedTo' && $story->assignedTo == $account) $class .= ' red'; $title = ''; @@ -2264,10 +2266,11 @@ class storyModel extends model if($id == 'plan') $title = $story->planTitle; echo " - $value) $this->testcase->printCell($value, $case, $users, $branches, $modulePairs, $browseType);?> + $value) $this->testcase->printCell($value, $case, $users, $branches, $modulePairs, $browseType, $useDatatable ? 'datatable' : 'table');?> diff --git a/module/testtask/model.php b/module/testtask/model.php index 200ec43d55..ad83c3fc21 100644 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -1118,8 +1118,9 @@ class testtaskModel extends model * @access public * @return void */ - public function printCell($col, $run, $users, $task, $branches) + public function printCell($col, $run, $users, $task, $branches, $mode = 'datatable') { + $canView = common::hasPriv('testcase', 'view'); $caseLink = helper::createLink('testcase', 'view', "caseID=$run->case&version=$run->version&from=testtask&taskID=$run->task"); $account = $this->app->user->account; $id = $col->id; @@ -1127,7 +1128,8 @@ class testtaskModel extends model { $class = ''; if($id == 'status') $class .= $run->status; - if($id == 'title') $class .= ' text-left'; + if($id == 'title') $class .= ' text-left'; + if($id == 'id') $class .= ' cell-id'; if($id == 'lastRunResult') $class .= " $run->lastRunResult"; if($id == 'assignedTo' && $run->assignedTo == $account) $class .= ' red'; @@ -1135,7 +1137,8 @@ class testtaskModel extends model switch ($id) { case 'id': - echo html::a($caseLink, sprintf('%03d', $run->case)); + if($mode == 'table') echo " "; + echo $canView ? html::a($caseLink, sprintf('%03d', $run->case)) : sprintf('%03d', $run->case); break; case 'pri': echo ""; @@ -1144,7 +1147,7 @@ class testtaskModel extends model break; case 'title': if($run->branch) echo "{$branches[$run->branch]}"; - echo html::a($caseLink, $run->title); + echo $canView ? html::a($caseLink, $run->title) : $run->title; break; case 'branch': echo $branches[$run->branch]; diff --git a/module/testtask/view/cases.html.php b/module/testtask/view/cases.html.php index 05b6d970a6..e1caa1c4ef 100644 --- a/module/testtask/view/cases.html.php +++ b/module/testtask/view/cases.html.php @@ -71,7 +71,7 @@ var moduleID = ''; - $value) $this->testtask->printCell($value, $run, $users, $task, $branches);?> + $value) $this->testtask->printCell($value, $run, $users, $task, $branches, $useDatatable ? 'datatable' : 'table');?> From 492bf6e956542f888ca176ee114903f2a6fba6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Mon, 23 Oct 2017 16:50:48 +0800 Subject: [PATCH 22/40] * Task Bug --- db/update9.5.1.sql | 21 +++++++++-- module/score/control.php | 10 ++++-- module/score/lang/zh-cn.php | 14 +++++++- module/score/model.php | 4 +++ module/score/view/browse.html.php | 50 ++++++--------------------- module/task/css/create.css | 1 + module/task/js/batchcreate.js | 8 +++++ module/task/js/create.js | 10 ++++-- module/task/view/batchcreate.html.php | 10 +++--- module/task/view/create.html.php | 20 ++++------- 10 files changed, 83 insertions(+), 65 deletions(-) diff --git a/db/update9.5.1.sql b/db/update9.5.1.sql index 789f54ab2e..e8021f50e2 100644 --- a/db/update9.5.1.sql +++ b/db/update9.5.1.sql @@ -36,6 +36,23 @@ ALTER TABLE `zt_team` ADD `left` DECIMAL(12,2) UNSIGNED NOT NULL DEFAULT '0' ALTER TABLE `zt_team` ADD `order` TINYINT(3) NOT NULL DEFAULT '0' AFTER `left`; ALTER TABLE `zt_team` CHANGE `days` `days` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0'; - ALTER TABLE `zt_team` DROP PRIMARY KEY; -ALTER TABLE `zt_team` ADD PRIMARY KEY (`project`, `task`, `account`); \ No newline at end of file +ALTER TABLE `zt_team` ADD PRIMARY KEY (`project`, `task`, `account`); + +ALTER TABLE `zt_user` ADD `score` DECIMAL(12,1) NOT NULL DEFAULT '0' AFTER `deleted`; +ALTER TABLE `zt_user` ADD `score_level` DECIMAL(12,1) NOT NULL DEFAULT '0' AFTER `score`; + +CREATE TABLE `zt_score` ( + `id` bigint(12) unsigned NOT NULL AUTO_INCREMENT, + `userID` int(11) NOT NULL DEFAULT '0', + `account` varchar(30) NOT NULL, + `model` varchar(30) NOT NULL, + `method` varchar(30) NOT NULL, + `score` decimal(12,1) NOT NULL DEFAULT '0.0', + `type` enum('repeat','one') NOT NULL DEFAULT 'repeat', + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `model` (`model`), + KEY `method` (`method`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/module/score/control.php b/module/score/control.php index 08c3586b4f..4cb167c4ec 100644 --- a/module/score/control.php +++ b/module/score/control.php @@ -16,9 +16,15 @@ class score extends control * @access public * @return mixed */ - public function browse() + public function browse($recTotal = 0, $recPerPage = 100) { - $this->view->title = $this->lang->score->common; + $this->app->loadClass('pager', $static = true); + $pager = new pager($recTotal, $recPerPage); + $scores = $this->score->getScores($pager); + + $this->view->title = $this->lang->score->common; + $this->view->pager = $pager; + $this->view->scores = $scores; $this->display(); } } \ No newline at end of file diff --git a/module/score/lang/zh-cn.php b/module/score/lang/zh-cn.php index 917048ec31..f090ab8c1b 100644 --- a/module/score/lang/zh-cn.php +++ b/module/score/lang/zh-cn.php @@ -2,4 +2,16 @@ $lang->score->common = '积分'; $lang->score->record = '积分记录'; $lang->score->reset = '重置积分'; -$lang->score->reset = '重置积分'; + +$lang->score->id = '编号'; +$lang->score->userID = '用户ID'; +$lang->score->account = '用户'; +$lang->score->model = '模块'; +$lang->score->method = '方式'; +$lang->score->type = '类型'; +$lang->score->score = '分值'; +$lang->score->time = '时间'; + +$lang->score->methods['login'] = '登录'; +$lang->score->methods['admin'] = ''; +$lang->score->methods['tutorial'] = '新手教程'; diff --git a/module/score/model.php b/module/score/model.php index 0f6f2ef07d..c418afc981 100644 --- a/module/score/model.php +++ b/module/score/model.php @@ -10,5 +10,9 @@ */ class scoreModel extends model { + public function getScores($pager) + { + + } } \ No newline at end of file diff --git a/module/score/view/browse.html.php b/module/score/view/browse.html.php index 6845415fc1..82f353c61c 100644 --- a/module/score/view/browse.html.php +++ b/module/score/view/browse.html.php @@ -18,57 +18,27 @@
"; - switch ($id) + switch($id) { case 'id': - echo html::a($storyLink, sprintf('%03d', $story->id)); + if($mode == 'table') echo " "; + echo $canView ? html::a($storyLink, sprintf('%03d', $story->id)) : sprintf('%03d', $story->id); break; case 'pri': echo ""; @@ -2277,7 +2280,7 @@ class storyModel extends model case 'title': if($story->branch) echo "{$branches[$story->branch]} "; if($modulePairs and $story->module) echo "{$modulePairs[$story->module]} "; - echo html::a($storyLink, $story->title, null, "style='color: $story->color'"); + echo $canView ? html::a($storyLink, $story->title, '', "style='color: $story->color'") : "{$story->title}"; break; case 'plan': echo $story->planTitle; diff --git a/module/task/model.php b/module/task/model.php index 6f69dce32d..ca53e028b5 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1807,8 +1807,9 @@ class taskModel extends model * @access public * @return void */ - public function printCell($col, $task, $users, $browseType, $branchGroups, $modulePairs = array()) + public function printCell($col, $task, $users, $browseType, $branchGroups, $modulePairs = array(), $mode = 'datatable') { + $canView = common::hasPriv('task', 'view'); $taskLink = helper::createLink('task', 'view', "taskID=$task->id"); $account = $this->app->user->account; $id = $col->id; @@ -1816,15 +1817,17 @@ class taskModel extends model { $class = ''; if($id == 'status') $class .= ' task-' . $task->status; + if($id == 'id') $class .= ' cell-id'; if($id == 'name' or $id == 'story') $class .= ' text-left'; if($id == 'deadline' and isset($task->delay)) $class .= ' delayed'; if($id == 'assignedTo' && $task->assignedTo == $account) $class .= ' red'; echo ""; - switch ($id) + switch($id) { case 'id': - echo html::a($taskLink, sprintf('%03d', $task->id)); + if($mode == 'table') echo " "; + echo $canView ? html::a($taskLink, sprintf('%03d', $task->id)) : sprintf('%03d', $task->id); break; case 'pri': echo ""; @@ -1834,7 +1837,7 @@ class taskModel extends model case 'name': if(!empty($task->product) && isset($branchGroups[$task->product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . ' '; if($modulePairs and $task->module) echo "" . $modulePairs[$task->module] . ' '; - echo html::a($taskLink, $task->name, null, "style='color: $task->color'"); + echo $canView ? html::a($taskLink, $task->name, null, "style='color: $task->color'") : "$task->name"; if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); if(!empty($task->children)) echo '    '; break; diff --git a/module/testcase/control.php b/module/testcase/control.php index bcbadf5123..c6ad633bcb 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -503,6 +503,7 @@ class testcase extends control $caseFails = $this->dao->select('COUNT(*) AS count')->from(TABLE_TESTRESULT)->where('caseResult')->eq('fail')->andwhere('`case`')->eq($caseID) ->beginIF($from == 'testtask')->andwhere('`run`')->eq($taskID)->fi() ->fetch('count'); + $case->caseFails = $caseFails; $this->view->position[] = $this->lang->testcase->common; $this->view->position[] = $this->lang->testcase->view; diff --git a/module/testcase/model.php b/module/testcase/model.php index d8970b9ff6..1d01119a47 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -1232,8 +1232,9 @@ class testcaseModel extends model * @access public * @return void */ - public function printCell($col, $case, $users, $branches, $modulePairs = array(), $browseType = '') + public function printCell($col, $case, $users, $branches, $modulePairs = array(), $browseType = '', $mode = 'datatable') { + $canView = common::hasPriv('testcase', 'view'); $caseLink = helper::createLink('testcase', 'view', "caseID=$case->id&version=$case->version"); $account = $this->app->user->account; $id = $col->id; @@ -1241,14 +1242,16 @@ class testcaseModel extends model { $class = ''; if($id == 'status') $class .= $case->status; - if($id == 'title') $class .= ' text-left'; + if($id == 'title') $class .= ' text-left'; + if($id == 'id') $class .= ' cell-id'; if($id == 'lastRunResult') $class .= $case->lastRunResult; echo ""; - switch ($id) + switch($id) { case 'id': - echo html::a($caseLink, sprintf('%03d', $case->id)); + if($mode == 'table') echo " "; + echo $canView ? html::a($caseLink, sprintf('%03d', $case->id)) : sprintf('%03d', $case->id); break; case 'pri': echo ""; @@ -1258,7 +1261,7 @@ class testcaseModel extends model case 'title': if($case->branch) echo "{$branches[$case->branch]} "; if($modulePairs and $case->module) echo "{$modulePairs[$case->module]} "; - echo html::a($caseLink, $case->title, null, "style='color: $case->color'"); + echo $canView ? html::a($caseLink, $case->title, null, "style='color: $case->color'") : "$case->title"; break; case 'branch': echo $branches[$case->branch]; diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index 44fbe3fda3..a2f5099c74 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -69,7 +69,7 @@ js::set('branch', $branch);
- recTotal}&recPerPage={$pager->recPerPage}"; ?> - - - - - - + + + + + + + - - objectType == 'case' ? 'testcase' : $action->objectType;?> + - - - - - - + - diff --git a/module/task/css/create.css b/module/task/css/create.css index 1e089a7841..21e7dcdaab 100644 --- a/module/task/css/create.css +++ b/module/task/css/create.css @@ -14,3 +14,4 @@ #taskPlanCol, #mailCol {width: 50%} #planAndMailCell.has-mail-col #taskPlanCol {padding-right: 5px;} #planAndMailCell.has-mail-col #taskPlanCol + #mailCol {padding-left: 5px;} +.input-group-addon input[type='checkBox']{} \ No newline at end of file diff --git a/module/task/js/batchcreate.js b/module/task/js/batchcreate.js index 97e0603d71..13ca2c9e8f 100755 --- a/module/task/js/batchcreate.js +++ b/module/task/js/batchcreate.js @@ -153,3 +153,11 @@ $(function() if(storyID != 0) setStoryRelated(0); }) + +// if(projectType == 'ops') +// { +// $("#tableBody th").eq(2).addClass('hidden'); +// $("#tableBody tr").each(function() { +// $(this).find('td').eq(2).addClass('hidden'); +// }); +// } diff --git a/module/task/js/create.js b/module/task/js/create.js index b9b90827af..0129013f0b 100644 --- a/module/task/js/create.js +++ b/module/task/js/create.js @@ -20,11 +20,16 @@ function copyStoryTitle() /* Set the assignedTos field. */ function setOwners(result) { + $("#multipleBox").removeAttr("checked"); + $('.team-group').addClass('hidden'); + $('#assignedTo, #assignedTo_chosen').removeClass('hidden'); if(result == 'affair') { $('#assignedTo').attr('multiple', 'multiple'); $('#assignedTo').chosen('destroy'); $('#assignedTo').chosen(defaultChosenOptions); + $('.affair').hide(); + $('.team-group').addClass('hidden'); $('#selectAllUser').removeClass('hidden'); } else if($('#assignedTo').attr('multiple') == 'multiple') @@ -32,6 +37,7 @@ function setOwners(result) $('#assignedTo').removeAttr('multiple'); $('#assignedTo').chosen('destroy'); $('#assignedTo').chosen(defaultChosenOptions); + $('.affair').show(); $('#selectAllUser').addClass('hidden'); } } @@ -219,8 +225,8 @@ $('[name^=multiple]').change(function() $('#estimate').attr('readonly', false); } }); - -$(".btn[data-toggle='modalTeam']").click(function() +$(".team-group[data-toggle='modalTeam']").css('cursor', 'pointer'); +$(".team-group[data-toggle='modalTeam']").click(function() { $('#modalTeam').modal('show'); adjustSortBtn(); diff --git a/module/task/view/batchcreate.html.php b/module/task/view/batchcreate.html.php index af9b4e682c..ab74f65672 100644 --- a/module/task/view/batchcreate.html.php +++ b/module/task/view/batchcreate.html.php @@ -34,12 +34,12 @@ $hiddenStory = ((isonlybody() and $storyID) || $this->config->global->flow == 'o if($hiddenStory and isset($visibleFields['story'])) $colspan -= 1; ?> -
action->objectType);?> idAB);?>action->objectName;?>action->actor);?>action->date);?>actions;?>score->id;?>score->account;?>score->model;?>score->method;?>score->type;?>score->score;?>score->time;?>
action->objectTypes, $action->objectType, '');?>objectID;?> - objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; - if($module == 'doclib') - { - echo $action->objectName; - } - else - { - echo html::a($this->createLink($module, $methodName, $params), $action->objectName); - } - ?> - actor];?>date;?> - id", $lang->action->undelete, 'hiddenwin'); - if($type == 'all') common::printLink('action', 'hideOne', "actionid=$action->id", $lang->action->hideOne, 'hiddenwin'); - ?> -
- -
- action->hideAll, inlink('hideAll'), 'hiddenwin');?> -
action->trashTips;?>
-
- +
show();?>
+
- + type != 'ops'):?> @@ -56,6 +56,7 @@ if($hiddenStory and isset($visibleFields['story'])) $colspan -= 1; $lang->task->typeList['ditto'] = $lang->task->ditto; $members['ditto'] = $lang->task->ditto; $modules['ditto'] = $lang->task->ditto; + if($project->type == 'ops') $colspan = $colspan-1; ?> task->batchCreate; $i++):?> - +
idAB;?> '>task->module?>'>task->story;?>'>task->story;?> task->name;?> typeAB;?> '>task->assignedTo;?> style='overflow:visible'>id, $i)'")?> style='overflow: visible'> + type != 'ops'):?> style='overflow: visible'>
@@ -83,7 +84,7 @@ if($hiddenStory and isset($visibleFields['story'])) $colspan -= 1;
-
task->colorTag}' data-update-text='#name\\[{$i}\\]'");?> @@ -131,6 +132,7 @@ if($hiddenStory and isset($visibleFields['story'])) $colspan -= 1;
+type);?> task->ditto);?> diff --git a/module/task/view/create.html.php b/module/task/view/create.html.php index d88374d6e0..bdd3991996 100644 --- a/module/task/view/create.html.php +++ b/module/task/view/create.html.php @@ -39,25 +39,17 @@
task->assignedTo;?> -
-
- assignedTo, "class='form-control chosen'");?> -
+
+ assignedTo, "class='form-control chosen'");?> type == 'ops') :?> -
- -
- -
- -
+ + +
- type != 'ops') :?> - - +
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='bugIDList[]'> +
' id='bugList' data-checkable='true' data-fixed-left-width='' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='bugIDList[]'> datatable->setFixedFieldWidth($setting); $columns = 0; ?> -
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='storyIDList[]'> +
' id='storyList' data-checkable='true' data-fixed-left-width='' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='storyIDList[]'> datatable->setFixedFieldWidth($setting); $columns = 0; ?> -
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='taskIDList[]'> +
' id='taskList' data-checkable='true' data-fixed-left-width='' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='taskIDList[]'> $value) diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index a2f5099c74..7a2d74378d 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -51,7 +51,7 @@ js::set('branch', $branch); $widths = $this->datatable->setFixedFieldWidth($setting); $columns = 0; ?> -
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> +
' id='caseList' data-checkable='true' data-fixed-left-width='' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> '; $widths = $this->datatable->setFixedFieldWidth($setting); $columns = 0; ?> -
' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> +
' id='caseList' data-checkable='' data-fixed-left-width='' data-fixed-right-width='' data-custom-menu='true' data-checkbox-name='caseIDList[]'> Date: Mon, 23 Oct 2017 17:32:20 +0800 Subject: [PATCH 25/40] * change children style --- module/project/css/task.css | 2 +- module/project/view/task.html.php | 2 +- module/task/model.php | 19 +++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/module/project/css/task.css b/module/project/css/task.css index 92018e1102..5fdb5c8091 100644 --- a/module/project/css/task.css +++ b/module/project/css/task.css @@ -27,4 +27,4 @@ tbody > tr > td > .btn-icon {margin-right: 2px} .table-children{border-left:2px solid #ccc;border-right:2px solid #ccc;} .table-children.table-child-top{border-top:2px solid #ccc;} .table-children.table-child-bottom{border-bottom:2px solid #ccc;} -.table-children td{background-color:#f0f8ff!important;} +.table-children td{background-color:#fff!important;} diff --git a/module/project/view/task.html.php b/module/project/view/task.html.php index 78c7a36c79..8b15438e12 100644 --- a/module/project/view/task.html.php +++ b/module/project/view/task.html.php @@ -78,7 +78,7 @@ js::set('browseType', $browseType); { ?> parent-id;?>' data-id='id?>'> - $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table');?> + $value) $this->task->printCell($value, $child, $users, $browseType, $branchGroups, $modulePairs, $useDatatable ? 'datatable' : 'table', true);?> diff --git a/module/task/model.php b/module/task/model.php index 369279eec0..4e9fce9f80 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1804,14 +1804,19 @@ class taskModel extends model /** * Print cell data. * - * @param object $col - * @param object $task - * @param array $users - * @param string $browseType + * @param $col + * @param $task + * @param $users + * @param $browseType + * @param $branchGroups + * @param array $modulePairs + * @param string $mode + * @param bool $child + * * @access public * @return void */ - public function printCell($col, $task, $users, $browseType, $branchGroups, $modulePairs = array(), $mode = 'datatable') + public function printCell($col, $task, $users, $browseType, $branchGroups, $modulePairs = array(), $mode = 'datatable', $child = false) { $canView = common::hasPriv('task', 'view'); $taskLink = helper::createLink('task', 'view', "taskID=$task->id"); @@ -1830,7 +1835,8 @@ class taskModel extends model switch($id) { case 'id': - if($mode == 'table') echo " "; + if($mode == 'table' && $child == false) echo " "; + if($child) echo '    '; echo $canView ? html::a($taskLink, sprintf('%03d', $task->id)) : sprintf('%03d', $task->id); break; case 'pri': @@ -1841,6 +1847,7 @@ class taskModel extends model case 'name': if(!empty($task->product) && isset($branchGroups[$task->product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . ' '; if($modulePairs and $task->module) echo "" . $modulePairs[$task->module] . ' '; + if($child) echo ''.$this->lang->task->childrenAB.' '; echo $canView ? html::a($taskLink, $task->name, null, "style='color: $task->color'") : "$task->name"; if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); if(!empty($task->children)) echo '    '; From c0ccf6ff4bc060dc055e5e00c21e80aea4c42f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Mon, 23 Oct 2017 17:48:26 +0800 Subject: [PATCH 26/40] * Task style --- module/task/lang/zh-cn.php | 1 - module/task/model.php | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 5d1087ef11..1c7ad02246 100644 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -103,7 +103,6 @@ $lang->task->transmitTo = '转交给'; $lang->task->children = '子任务'; $lang->task->childrenAB = '子'; $lang->task->parent = '父任务'; -$lang->task->unfinished = '未完成'; $lang->task->lblPri = 'P'; $lang->task->lblHour = '(h)'; diff --git a/module/task/model.php b/module/task/model.php index 4e9fce9f80..8cabec4709 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1848,6 +1848,7 @@ class taskModel extends model if(!empty($task->product) && isset($branchGroups[$task->product][$task->branch])) echo "" . $branchGroups[$task->product][$task->branch] . ' '; if($modulePairs and $task->module) echo "" . $modulePairs[$task->module] . ' '; if($child) echo ''.$this->lang->task->childrenAB.' '; + if(!empty($task->team)) echo ''.$this->lang->task->multipleAB.' '; echo $canView ? html::a($taskLink, $task->name, null, "style='color: $task->color'") : "$task->name"; if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '_blank', "class='bug'"); if(!empty($task->children)) echo '    '; From 4a7c7c74027d73e2b413293ec52fbe176b1ca465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Mon, 23 Oct 2017 21:13:35 +0800 Subject: [PATCH 27/40] * Task Status change --- module/task/control.php | 3 +- module/task/model.php | 62 ++++++++++++++++++++++++++++++++++ module/task/view/edit.html.php | 2 ++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/module/task/control.php b/module/task/control.php index 002e5c1db6..c3865d194d 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -233,7 +233,8 @@ class task extends control */ public function commonAction($taskID) { - $this->view->task = $this->loadModel('task')->getByID($taskID); + $task=$this->loadModel('task')->getByID($taskID); + $this->view->task = $task; $this->view->project = $this->project->getById($this->view->task->project); $this->view->members = $this->project->getTeamMemberPairs($this->view->project->id ,'nodeleted'); $this->view->actions = $this->loadModel('action')->getList('task', $taskID); diff --git a/module/task/model.php b/module/task/model.php index 8cabec4709..28da7f60cf 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -278,6 +278,31 @@ class taskModel extends model $this->dao->update(TABLE_TASK)->data($newTask)->autoCheck()->where('id')->eq($taskID)->exec(); } + /** + * Check that all child status + * + * @param $parentID + * @param $status + * + * @access private + * @return bool + */ + private function parentStatus($parentID,$status='done') + { + if(!$parentID) return true; + $data = new stdClass(); + $children = $this->dao->select('id,status')->from(TABLE_TASK)->where('parent')->eq($parentID)->fetchPairs('id', 'status'); + $values = array_values(array_unique($children)); + if(count($values) == 1 && $values[0] == $status) + { + $data->status = $status; + $this->dao->update(TABLE_TASK)->data($data) + ->autoCheck() + ->where('id')->eq($parentID)->exec(); + } + } + + /** * Update a task. * @@ -690,6 +715,23 @@ class taskModel extends model ->check('consumed,left', 'float') ->where('id')->eq((int)$taskID)->exec(); + if($oldTask->parent) + { + switch($task->status) + { + case 'doing': + $status = new stdClass(); + $status->status = 'doing'; + $this->dao->update(TABLE_TASK)->data($status)->autoCheck()->where('id')->eq((int)$oldTask->parent)->exec(); + break; + case 'done': + $this->parentStatus($oldTask->parent,'done'); + break; + } + } + + $this->countTime($oldTask->parent); + if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); } @@ -802,6 +844,7 @@ class taskModel extends model if(!empty($actionID)) $this->action->logHistory($actionID, $changes); if($task->story) $this->loadModel('story')->setStage($task->story); + if($task->status=='done') $this->parentStatus($task->parent,'done'); $this->countTime($task->parent); return $changes; @@ -849,6 +892,9 @@ class taskModel extends model ->check('consumed', 'notempty') ->where('id')->eq((int)$taskID)->exec(); + if($task->status=='done') $this->parentStatus($oldTask->parent,'done'); + $this->countTime($oldTask->parent); + if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); } @@ -898,6 +944,12 @@ class taskModel extends model ->remove('comment')->get(); $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec(); + $this->parentStatus($oldTask->parent,'closed'); + $this->countTime($oldTask->parent); + + $data = new stdClass(); + $data->status = 'closed'; + $this->dao->update(TABLE_TASK)->data($data)->autoCheck()->where('parent')->eq($taskID)->exec(); if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); @@ -954,6 +1006,13 @@ class taskModel extends model ->check('left', 'notempty') ->where('id')->eq((int)$taskID)->exec(); + $this->countTime($oldTask->parent); + + $data = new stdClass(); + $data->status = 'doing'; + $this->dao->update(TABLE_TASK)->data($data)->autoCheck()->where('parent')->eq($taskID)->exec(); + if($oldTask->parent) $this->dao->update(TABLE_TASK)->data($data)->autoCheck()->where('id')->eq((int)$oldTask->parent)->exec(); + if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); @@ -1756,6 +1815,9 @@ class taskModel extends model if($action == 'start' and !empty($task->children)) return false; if($action == 'recordestimate' and !empty($task->children)) return false; + if($action == 'finish' and !empty($task->children)) return false; + if($action == 'cancel' and !empty($task->children)) return false; + if($action == 'pause' and !empty($task->children)) return false; if($action == 'batchcreate' and !empty($task->team)) return false; if($action == 'batchcreate' and $task->parent) return false; diff --git a/module/task/view/edit.html.php b/module/task/view/edit.html.php index bf6af02834..015d52aa9b 100644 --- a/module/task/view/edit.html.php +++ b/module/task/view/edit.html.php @@ -97,10 +97,12 @@ + children)):?> + From 91eb91ecb1b9a41a3823e1cb0679cd2e39c59881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Mon, 23 Oct 2017 21:16:23 +0800 Subject: [PATCH 28/40] * format task model --- module/task/model.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/module/task/model.php b/module/task/model.php index 28da7f60cf..bc3c58ccc9 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -249,10 +249,10 @@ class taskModel extends model * * @param $taskID * - * @access public + * @access private * @return void */ - public function countTime($taskID) + private function countTime($taskID) { if(!$taskID) return true; @@ -287,7 +287,7 @@ class taskModel extends model * @access private * @return bool */ - private function parentStatus($parentID,$status='done') + private function parentStatus($parentID, $status = 'done') { if(!$parentID) return true; $data = new stdClass(); @@ -296,9 +296,7 @@ class taskModel extends model if(count($values) == 1 && $values[0] == $status) { $data->status = $status; - $this->dao->update(TABLE_TASK)->data($data) - ->autoCheck() - ->where('id')->eq($parentID)->exec(); + $this->dao->update(TABLE_TASK)->data($data)->autoCheck()->where('id')->eq($parentID)->exec(); } } From a8e3dd25b97012534e23676a620f5827e3f2395c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 08:46:41 +0800 Subject: [PATCH 29/40] * childTask status change --- module/task/model.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/module/task/model.php b/module/task/model.php index bc3c58ccc9..6232d3eb6c 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -293,7 +293,7 @@ class taskModel extends model $data = new stdClass(); $children = $this->dao->select('id,status')->from(TABLE_TASK)->where('parent')->eq($parentID)->fetchPairs('id', 'status'); $values = array_values(array_unique($children)); - if(count($values) == 1 && $values[0] == $status) + if((count($values) == 1 && $values[0] == $status) || (count($values) == 2 && in_array('closed', $values) && $status == 'done')) { $data->status = $status; $this->dao->update(TABLE_TASK)->data($data)->autoCheck()->where('id')->eq($parentID)->exec(); @@ -842,7 +842,7 @@ class taskModel extends model if(!empty($actionID)) $this->action->logHistory($actionID, $changes); if($task->story) $this->loadModel('story')->setStage($task->story); - if($task->status=='done') $this->parentStatus($task->parent,'done'); + if($task->status == 'done') $this->parentStatus($task->parent, 'done'); $this->countTime($task->parent); return $changes; @@ -890,7 +890,7 @@ class taskModel extends model ->check('consumed', 'notempty') ->where('id')->eq((int)$taskID)->exec(); - if($task->status=='done') $this->parentStatus($oldTask->parent,'done'); + if($task->status == 'done') $this->parentStatus($oldTask->parent, 'done'); $this->countTime($oldTask->parent); if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); From b267870db4d88e941dbb716680d1571c9af11a07 Mon Sep 17 00:00:00 2001 From: sgm0422 <976204163@qq.com> Date: Tue, 24 Oct 2017 09:03:43 +0800 Subject: [PATCH 30/40] * Fix bug #1245. --- module/bug/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/bug/model.php b/module/bug/model.php index 33e52442e6..48a870551d 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -2401,7 +2401,7 @@ class bugModel extends model echo substr($bug->openedDate, 5, 11); break; case 'openedBuild': - foreach(explode(',', $bug->openedBuild) as $build) echo zget($builds, $build) . '
'; + foreach(explode(',', $bug->openedBuild) as $build) echo zget($builds, $build) . ' '; break; case 'assignedTo': echo zget($users, $bug->assignedTo, $bug->assignedTo); From 7eefad2430248f8d53f1f090b080a09a0535111c Mon Sep 17 00:00:00 2001 From: sgm0422 <976204163@qq.com> Date: Tue, 24 Oct 2017 09:19:44 +0800 Subject: [PATCH 31/40] * Fix bug #1237. --- module/company/lang/en.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/company/lang/en.php b/module/company/lang/en.php index 1f65792ae8..2404d5a94e 100644 --- a/module/company/lang/en.php +++ b/module/company/lang/en.php @@ -31,5 +31,5 @@ $lang->company->product = $lang->productCommon; $lang->company->project = $lang->projectCommon; $lang->company->user = 'User'; -$lang->company->guestList[0] = 'On'; -$lang->company->guestList[1] = 'Off'; +$lang->company->guestList[0] = 'Off'; +$lang->company->guestList[1] = 'On'; From 6c6c6183376b3f951bbe0f527798d30028657bb3 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Tue, 24 Oct 2017 14:54:34 +0800 Subject: [PATCH 32/40] =?UTF-8?q?Finish=20task#3262=20=E5=90=84=E4=B8=AA?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E7=9A=84=E5=BF=85=E5=A1=AB=E9=A1=B9=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=8F=AF=E4=BB=A5=E8=87=AA=E5=AE=9A=E4=B9=89,cost:16?= =?UTF-8?q?=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/zentaopms.php | 2 + module/custom/config.php | 37 +++++++++++ module/custom/control.php | 61 ++++++++++++++++++ module/custom/lang/en.php | 1 + module/custom/lang/zh-cn.php | 1 + module/custom/model.php | 96 ++++++++++++++++++++++++++++ module/custom/view/flow.html.php | 25 ++------ module/custom/view/header.html.php | 20 ++++++ module/custom/view/required.html.php | 69 ++++++++++++++++++++ module/custom/view/set.html.php | 19 +----- module/custom/view/working.html.php | 25 ++------ 11 files changed, 302 insertions(+), 54 deletions(-) create mode 100644 module/custom/view/header.html.php create mode 100644 module/custom/view/required.html.php diff --git a/config/zentaopms.php b/config/zentaopms.php index 8933ef5f0f..7468f54b80 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -127,3 +127,5 @@ $config->objectTables['custom'] = TABLE_LANG; $config->objectTables['branch'] = TABLE_BRANCH; $config->objectTables['module'] = TABLE_MODULE; $config->objectTables['caselib'] = TABLE_TESTSUITE; +$config->objectTables['entry'] = TABLE_ENTRY; +$config->objectTables['webhook'] = TABLE_WEBHOOK; diff --git a/module/custom/config.php b/module/custom/config.php index 68704b8316..ac2db60060 100644 --- a/module/custom/config.php +++ b/module/custom/config.php @@ -8,3 +8,40 @@ $config->custom->canAdd['testtask'] = 'priList'; $config->custom->canAdd['todo'] = 'priList,typeList'; $config->custom->canAdd['user'] = 'roleList'; $config->custom->canAdd['block'] = ''; + +$config->custom->requiredModules[10] = 'todo'; + +$config->custom->requiredModules[15] = 'product'; +$config->custom->requiredModules[20] = 'story'; +$config->custom->requiredModules[25] = 'productplan'; +$config->custom->requiredModules[30] = 'release'; + +$config->custom->requiredModules[35] = 'project'; +$config->custom->requiredModules[40] = 'task'; +$config->custom->requiredModules[45] = 'build'; + +$config->custom->requiredModules[50] = 'bug'; +$config->custom->requiredModules[55] = 'testcase'; +$config->custom->requiredModules[60] = 'testsuite'; +$config->custom->requiredModules[65] = 'testreport'; +$config->custom->requiredModules[70] = 'testtask'; + +$config->custom->requiredModules[75] = 'doc'; + +$config->custom->requiredModules[80] = 'group'; +$config->custom->requiredModules[85] = 'user'; + +$config->custom->requiredModules[95] = 'entry'; +$config->custom->requiredModules[100] = 'webhook'; + +$config->custom->fieldList['group'] = 'name,desc'; + +$config->custom->excludeFieldList['product'] = 'order'; +$config->custom->excludeFieldList['story'] = 'version,duplicateStory,linkStories,childStories,toBug,fromBug'; +$config->custom->excludeFieldList['project'] = 'order'; +$config->custom->excludeFieldList['task'] = 'fromBug'; +$config->custom->excludeFieldList['bug'] = 'storyVersion,toTask,toStory,hardware,found,confirmed,activatedCount,activatedDate,linkBug,case,caseVersion,result,testtask,repo'; +$config->custom->excludeFieldList['testcase'] = 'version,lastRunResult,lastRunDate,lastRunner,linkCase,fromBug'; +$config->custom->excludeFieldList['testreport'] = 'objectType'; +$config->custom->excludeFieldList['doc'] = 'version'; +$config->custom->excludeFieldList['user'] = 'ranzhi,visits,ip,last'; diff --git a/module/custom/control.php b/module/custom/control.php index c57bd58301..f6d9afdd76 100644 --- a/module/custom/control.php +++ b/module/custom/control.php @@ -230,6 +230,51 @@ class custom extends control $this->display(); } + /** + * Set Required. + * + * @param string $moduleName + * @access public + * @return void + */ + public function required($moduleName = '') + { + if($_POST) + { + $this->custom->saveRequiredFields($moduleName); + die(js::reload('parent.parent')); + } + + if(empty($moduleName)) $moduleName = current($this->config->custom->requiredModules); + + foreach($this->config->custom->requiredModules as $requiredModule) $this->app->loadLang($requiredModule); + + /* Get this module requiredFields. */ + $this->loadModel($moduleName); + $requiredFields = $this->custom->getRequiredFields($this->config->$moduleName); + + if($moduleName == 'doc') + { + unset($requiredFields['createLib']); + unset($requiredFields['editLib']); + } + + $fields = $this->custom->getDBFields($moduleName); + if($moduleName == 'testsuite') + { + $this->app->loadLang('testcase'); + $this->view->caseFields = $this->custom->getDBFields('testcase'); + } + + $this->view->title = $this->lang->custom->required; + $this->view->position[] = $this->lang->custom->required; + + $this->view->requiredFields = $requiredFields; + $this->view->moduleName = $moduleName; + $this->view->fields = $fields; + $this->display(); + } + /** * Ajax save custom fields. * @@ -383,4 +428,20 @@ class custom extends control $this->loadModel('setting')->deleteItems("owner={$account}&module=common§ion=customMenu"); die(js::reload('parent.parent')); } + + /** + * Reset required. + * + * @param srting $module + * @param string $confirm + * @access public + * @return void + */ + public function resetRequired($module, $confirm = 'no') + { + if($confirm == 'no') die(js::confirm($this->lang->custom->confirmRestore, inlink('resetRequired', "module=$module&confirm=yes"))); + + $this->loadModel('setting')->deleteItems("owner=system&module={$module}&key=requiredFields"); + die(js::reload('parent.parent')); + } } diff --git a/module/custom/lang/en.php b/module/custom/lang/en.php index 5fe8a27fe1..e8e23962d1 100644 --- a/module/custom/lang/en.php +++ b/module/custom/lang/en.php @@ -14,6 +14,7 @@ $lang->custom->module = 'Module'; $lang->custom->section = 'Section'; $lang->custom->lang = 'Language'; $lang->custom->setPublic = 'Set Public'; +$lang->custom->required = 'Required'; $lang->custom->object['story'] = 'Story'; $lang->custom->object['task'] = 'Task'; diff --git a/module/custom/lang/zh-cn.php b/module/custom/lang/zh-cn.php index b443b578a0..fad9789299 100644 --- a/module/custom/lang/zh-cn.php +++ b/module/custom/lang/zh-cn.php @@ -14,6 +14,7 @@ $lang->custom->module = '模块'; $lang->custom->section = '附加部分'; $lang->custom->lang = '所属语言'; $lang->custom->setPublic = '设为公共'; +$lang->custom->required = '必填项'; $lang->custom->object['story'] = '需求'; $lang->custom->object['task'] = '任务'; diff --git a/module/custom/model.php b/module/custom/model.php index 1d10e5b9f8..56357abac1 100644 --- a/module/custom/model.php +++ b/module/custom/model.php @@ -375,4 +375,100 @@ class customModel extends model $this->loadModel('setting')->setItem($settingKey, $menu); } + + /** + * Get required fields by config. + * + * @param object $moduleConfig + * @access public + * @return array + */ + public function getRequiredFields($moduleConfig) + { + $requiredFields = array(); + foreach($moduleConfig as $method => $subConfig) + { + if(is_object($subConfig) and isset($subConfig->requiredFields)) $requiredFields[$method] = trim(str_replace(' ', '', $subConfig->requiredFields)); + } + + return $requiredFields; + } + + /** + * Get module fields. + * + * @param string $moduleName + * @param string $method + * @access public + * @return array + */ + public function getDBFields($moduleName, $method = '') + { + $fields = array(); + $moduleLang = $this->lang->$moduleName; + $customFields = $this->config->custom->fieldList; + if(isset($customFields[$moduleName])) + { + $fieldList = isset($customFields[$moduleName][$method]) ? $customFields[$moduleName][$method] : $customFields[$moduleName]; + foreach(explode(',', $fieldList) as $fieldName) + { + if(isset($moduleLang->$fieldName) and is_string($moduleLang->$fieldName)) $fields[$fieldName] = $moduleLang->$fieldName; + } + } + else + { + $table = zget($this->config->objectTables, $moduleName, ''); + if($table) + { + $excludeFieldList = zget($this->config->custom->excludeFieldList, $moduleName, ''); + foreach($this->dao->query("DESC $table")->fetchAll() as $field) + { + $fieldName = $field->Field; + if($fieldName == 'id' or $fieldName == 'deleted') continue; + if($fieldName == 'openedBy' or $fieldName == 'createdBy' or $fieldName == 'addedBy') continue; + if($fieldName == 'openedDate' or $fieldName == 'createdDate' or $fieldName == 'addedDate') continue; + if($fieldName == 'lastEditedBy' or $fieldName == 'editedBy') continue; + if($fieldName == 'lastEditedDate' or $fieldName == 'editedDate') continue; + if(isset($excludeFieldList) and strpos(",{$excludeFieldList},", ",$fieldName,") !== false) continue; + if(isset($moduleLang->$fieldName) and is_string($moduleLang->$fieldName)) $fields[$fieldName] = $moduleLang->$fieldName; + } + } + } + return $fields; + } + + /** + * Save required fields. + * + * @param int $moduleName + * @access public + * @return void + */ + public function saveRequiredFields($moduleName) + { + if(isset($this->config->system->$moduleName)) unset($this->config->system->$moduleName); + if(isset($this->config->personal->$moduleName)) unset($this->config->personal->$moduleName); + + $this->loadModel($moduleName); + $systemFields = $this->getRequiredFields($this->config->$moduleName); + + $data = fixer::input('post')->get(); + $requiredFields = array(); + foreach($data->requiredFields as $method => $fields) + { + $method = strtolower($method); + $systemField = $this->config->$moduleName->$method->requiredFields; + + $fields = join(',', $fields); + foreach(explode(',', $systemField) as $field) + { + $field = trim($field); + if(strpos(",$fields,", ",$field,") === false) $fields .= ",$field"; + } + + $requiredFields[$method]['requiredFields'] = $fields; + } + + $this->loadModel('setting')->setItems("system.{$moduleName}", $requiredFields); + } } diff --git a/module/custom/view/flow.html.php b/module/custom/view/flow.html.php index 136f901914..b906fd201d 100644 --- a/module/custom/view/flow.html.php +++ b/module/custom/view/flow.html.php @@ -10,24 +10,7 @@ * @link http://www.zentao.net */ ?> - -
- -
+
task->type;?> task->typeList, $task->type, 'class=form-control');?>
task->status;?> task->statusList, $task->status, 'class=form-control');?>
task->pri;?> task->priList, $task->pri, 'class=form-control');?>
@@ -49,4 +32,10 @@
+ diff --git a/module/custom/view/header.html.php b/module/custom/view/header.html.php new file mode 100644 index 0000000000..53e96d64b7 --- /dev/null +++ b/module/custom/view/header.html.php @@ -0,0 +1,20 @@ + +
+ +
diff --git a/module/custom/view/required.html.php b/module/custom/view/required.html.php new file mode 100644 index 0000000000..71161126a7 --- /dev/null +++ b/module/custom/view/required.html.php @@ -0,0 +1,69 @@ + + * @package custom + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+ custom->requiredModules as $requiredModule) + { + echo "
  • " . html::a(inlink('required', "module=$requiredModule"), $lang->$requiredModule->common) . "
  • "; + } + ?> +
    +
    +
    +
    +
    +
    + $moduleName->common?> +
    + + $requiredField):?> + + + + + + + + + + +
    + $moduleName->$method; + ?> +
    + custom->restore, 'hiddenwin', "class='btn'"); + ?> +
    +
    +
    +
    + + diff --git a/module/custom/view/set.html.php b/module/custom/view/set.html.php index baafa0006d..726474c997 100644 --- a/module/custom/view/set.html.php +++ b/module/custom/view/set.html.php @@ -10,7 +10,7 @@ * @link http://www.zentao.net */ ?> - + @@ -31,23 +31,6 @@ EOT; -
    - -
    - -
    - -
    +
    @@ -39,4 +22,10 @@
    + From 9b077f12c66c0490e4a82053c1f8b78b64a9623c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 15:36:28 +0800 Subject: [PATCH 33/40] * fix batchCreate --- module/task/model.php | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/module/task/model.php b/module/task/model.php index 6232d3eb6c..bae469801c 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -184,28 +184,21 @@ class taskModel extends model $module = 0; $type = ''; $assignedTo = ''; - for($i = 0; $i < $batchNum; $i++) - { - $story = $tasks->story[$i] == 'ditto' ? $story : $tasks->story[$i]; - $module = $tasks->module[$i] == 'ditto' ? $module : $tasks->module[$i]; - $type = $tasks->type[$i] == 'ditto' ? $type : $tasks->type[$i]; - $assignedTo = $tasks->assignedTo[$i] == 'ditto' ? $assignedTo: $tasks->assignedTo[$i]; - - $tasks->story[$i] = (int)$story; - $tasks->module[$i] = (int)$module; - $tasks->type[$i] = $type; - $tasks->assignedTo[$i] = $assignedTo; - } for($i = 0; $i < $batchNum; $i++) { + $story = !isset($tasks->story[$i]) || $tasks->story[$i] == 'ditto' ? $story : $tasks->story[$i]; + $module = !isset($tasks->module[$i]) || $tasks->module[$i] == 'ditto' ? $module : $tasks->module[$i]; + $type = !isset($tasks->type[$i]) || $tasks->type[$i] == 'ditto' ? $type : $tasks->type[$i]; + $assignedTo = !isset($tasks->assignedTo[$i]) || $tasks->assignedTo[$i] == 'ditto' ? $assignedTo: $tasks->assignedTo[$i]; + if(empty($tasks->name[$i])) continue; $data[$i] = new stdclass(); - $data[$i]->story = $tasks->story[$i]; - $data[$i]->type = $tasks->type[$i]; - $data[$i]->module = $tasks->module[$i]; - $data[$i]->assignedTo = $tasks->assignedTo[$i]; + $data[$i]->story = (int)$story; + $data[$i]->type = $type; + $data[$i]->module = (int)$module; + $data[$i]->assignedTo = $assignedTo; $data[$i]->color = $tasks->color[$i]; $data[$i]->name = $tasks->name[$i]; $data[$i]->desc = nl2br($tasks->desc[$i]); @@ -240,7 +233,7 @@ class taskModel extends model } $this->countTime($tasks->parent[0]); - +exit('wwewrewr'); return $mails; } From 9b5f00ea0d26580117954216ae6b327ae1b93bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 15:36:46 +0800 Subject: [PATCH 34/40] * fix batchCreate --- module/project/css/task.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/module/project/css/task.css b/module/project/css/task.css index 5fdb5c8091..6713024ae5 100644 --- a/module/project/css/task.css +++ b/module/project/css/task.css @@ -24,7 +24,11 @@ .pl-5px{padding-left:5px;} tbody > tr > td > .btn-icon {margin-right: 2px} -.table-children{border-left:2px solid #ccc;border-right:2px solid #ccc;} +.table-children > td:first-child{border-left:2px solid #ccc} +.table-children > td:last-child{;border-right:2px solid #ccc;} +.fixed-left .table-children > td{border-right:none;} +.flexarea .table-children > td{border-left:none;border-right:none;} +.fixed-right .table-children > td{border-left:none;} .table-children.table-child-top{border-top:2px solid #ccc;} .table-children.table-child-bottom{border-bottom:2px solid #ccc;} .table-children td{background-color:#fff!important;} From 0c04dcbaa390f109a9b0ca4fb2293bdaa5bf644c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 15:38:55 +0800 Subject: [PATCH 35/40] * fix task --- module/task/model.php | 1 - 1 file changed, 1 deletion(-) diff --git a/module/task/model.php b/module/task/model.php index bae469801c..d368a7947a 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -233,7 +233,6 @@ class taskModel extends model } $this->countTime($tasks->parent[0]); -exit('wwewrewr'); return $mails; } From 014e9585303b4b2d3d457156da9ee59b96c48636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 16:00:32 +0800 Subject: [PATCH 36/40] * fix dataTable style --- module/task/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/task/model.php b/module/task/model.php index d368a7947a..e90e040c69 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1888,7 +1888,7 @@ class taskModel extends model { case 'id': if($mode == 'table' && $child == false) echo " "; - if($child) echo '    '; + if($mode == 'table' && $child) echo '    '; echo $canView ? html::a($taskLink, sprintf('%03d', $task->id)) : sprintf('%03d', $task->id); break; case 'pri': From a85c0ceaef0fa131ce756722bbe4662b78361818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 16:00:50 +0800 Subject: [PATCH 37/40] * fix dataTable style --- module/project/js/task.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/project/js/task.js b/module/project/js/task.js index e464c5916e..7089e6548f 100644 --- a/module/project/js/task.js +++ b/module/project/js/task.js @@ -20,6 +20,9 @@ $(function() $option.toggleClass('hide', $option.text().toString().toLowerCase().indexOf(val) < 0 && $option.data('key').toString().toLowerCase().indexOf(val) < 0); }); }); + + $(".table-children > td.check-btn").html('') + }); function setQueryBar(queryID, title) From a9bc7fbd27e7dcaaea675e94d6090c7376702b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=94=E5=93=A5?= Date: Tue, 24 Oct 2017 16:07:41 +0800 Subject: [PATCH 38/40] * fix childTask --- module/task/model.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/module/task/model.php b/module/task/model.php index e90e040c69..9056c23929 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -1133,7 +1133,14 @@ class taskModel extends model $taskList = array_keys($tasks); if(!empty($taskList)) { - $children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->in($taskList)->orderBy('id_desc')->fetchGroup('parent'); + $children = $this->dao->select('t1.*, t2.id AS storyID, t2.title AS storyTitle, t2.product, t2.branch, t2.version AS latestStoryVersion, t2.status AS storyStatus, t3.realname AS assignedToRealName') + ->from(TABLE_TASK)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') + ->leftJoin(TABLE_USER)->alias('t3')->on('t1.assignedTo = t3.account') + ->where('t1.parent')->in($taskList) + ->andWhere('t1.deleted')->eq(0) + ->orderBy('id_desc') + ->fetchGroup('parent'); if(!empty($children)) foreach($children as $key => $child) $tasks[$key]->children = $child; $teams = $this->dao->select('*')->from(TABLE_TEAM)->where('task')->in($taskList)->fetchGroup('task'); if($teams) foreach($teams as $key => $team) if(!empty($team)) $tasks[$key]->team = $team; From 9b2bb638dd53148c923c7eff68f1b64f8e8ff5e0 Mon Sep 17 00:00:00 2001 From: sgm0422 <976204163@qq.com> Date: Tue, 24 Oct 2017 16:15:03 +0800 Subject: [PATCH 39/40] * Fix Bug #1235. --- module/project/view/build.html.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/project/view/build.html.php b/module/project/view/build.html.php index b49659195f..6ff45fae47 100644 --- a/module/project/view/build.html.php +++ b/module/project/view/build.html.php @@ -44,8 +44,8 @@ branchName) echo "{$build->branchName}"?> createLink('build', 'view', "build=$build->id"), $build->name);?>
    scmPath, 'http') === 0 ? printf(html::a($build->scmPath)) : printf($build->scmPath);?>filePath, 'http') === 0 ? printf(html::a($build->filePath)) : printf($build->filePath);?>scmPath, 'http') === 0 ? html::a($build->scmPath) : $build->scmPath;?>filePath, 'http') === 0 ? html::a($build->filePath):$build->filePath;?> date?> builder]?> From 87d230dd2a3faa98567f5b788e7c506b2ad62044 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Tue, 24 Oct 2017 17:21:39 +0800 Subject: [PATCH 40/40] * fix bug for report. --- module/tree/model.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/module/tree/model.php b/module/tree/model.php index 4de81d4574..669cf08aca 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -1135,7 +1135,11 @@ class treeModel extends model $branchs = $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($branchIDList)->andWhere('deleted')->eq(0)->fetchALL('id'); foreach($modules as $module) { - if(isset($modulePairs[$module->id])) $modulePairs[$module->id] = '/' . $branchs[$module->branch]->name . $modulePairs[$module->id]; + if(isset($modulePairs[$module->id])) + { + $branchName = isset($branchs[$module->branch]) ? '/' . $branchs[$module->branch]->name : ''; + $modulePairs[$module->id] = $branchName . $modulePairs[$module->id]; + } } return $modulePairs;