<?php
 namespace TCMS;
 include_once(LIB_PATH . 'modules' . DIRECTORY_SEPARATOR . 'TCmsRoute.php');
 abstract class Module {
 private $routes = null;
 public $moduleName = 'module';
 public $moduleNamespace = '';
 public $moduleAbsolutePath = '';
 public $isLayout = false;
 public $layout = true;
 public $authExtension = null;
 private $mp = null;
 public function __construct() {
 $this->routes = new Route();
 if(\GlobalVars::$mRouteClass) \GlobalVars::$mRouteClass = null;
 \GlobalVars::$mRouteClass = $this->routes;
 $this->initialize();
 if(!$this->isLayout) {
 $this->registerRoutes();
 }
 if(!\GlobalVars::$isPublish) {
 $this->boot();
 }
 }
 abstract protected function initialize();
 abstract protected function registerRoutes();
 abstract public function registerComponent();
 abstract public function boot();
 abstract public function middleware(string $routeName, $auth);
 private function auth() {
 if($this->authExtension) {
 $auth = load_auth($this->authExtension);
 if(!$auth) {
 throw new TException('Không tìm thấy extension ' . $this->authExtension, 501);
 }
 $user = $auth->auth();
 if($user == null) {
 releaseApp();
 header("Location: " . $auth->loginUrl . '?redirect_url='.\GlobalVars::$requestInfo['area_url'].\GlobalVars::$requestInfo['url']);
 exit();
 }
 else {
 if($this->middleware(\GlobalVars::$requestInfo['route_name'], $auth) === false) {
 throw new TException('Bạn không có quyền truy cập trang này', 401);
 }
 }
 }
 }
 public function loadModule($layout) {
 $this->auth();
 if(\GlobalVars::$isPublish) {
 $extModels = require_once(BIN_PATH . \GlobalVars::$_areaKey . '-ext-model-data.php');
 foreach($extModels as $e => $m) {
 if(is_string($m)) {
 \Model::overrideModel($m, $e);
 }
 else {
 \Model::add($e, $m['n'], ROOT_PATH . $m['p']);
 }
 }
 }
 $this->mp = new ModuleProc();
 $this->mp->loadBootstrapFile();
 \GlobalVars::$curModulePath = $this->moduleAbsolutePath;
 include_once(LIB_PATH . 'views' . DIRECTORY_SEPARATOR . 'TCmsView.php');
 include_once(LIB_PATH . 'controllers' . DIRECTORY_SEPARATOR . 'TCmsController.php');
 if($layout != null) {
 if($this->layout) {
 $this->loadLayout($layout);
 return;
 }
 $layout->destroy();
 unset($layout);
 $layout = null;
 }
 $this->loadController(false);
 }
 private function loadLayout($layout) {
 $ctr = $this->loadController($layout != null);
 if(!$ctr->layout) return;
 if(is_string($ctr->layout)) {
 if($layout->moduleName != $ctr->layout) {
 $layout->destroy();
 unset($layout);
 $layout = null;
 $layout = $this->mp->findLayoutModuleByKey($ctr->layout);
 }
 }
 else if(is_string($this->layout)) {
 if($layout->moduleName != $this->layout) {
 $layout->destroy();
 unset($layout);
 $layout = null;
 $layout = $this->mp->findLayoutModuleByKey($this->layout);
 }
 }
 if(\GlobalVars::$isPublish) {
 \AppPublish::$layoutModules = null;
 }
 else {
 \Config::$moduleConfig = null;
 }
 $controllerPath = $layout->moduleAbsolutePath . 'controllers' . DIRECTORY_SEPARATOR . 'LayoutController.php';
 if(file_exists($controllerPath) === false) {
 throw new TException('Không tìm thấy file layout controller ' . $controllerPath, 501);
 }
 include_once($controllerPath);
 $controller = '\\' . $layout->moduleNamespace . '\\LayoutController';
 $controllerCls = new $controller();
 $action = 'index';
 if(method_exists($controllerCls, $action) === false) throw new TException('Không tìm thấy action ' . $action . ' trong ' . $layout->moduleName . '\\' . 'LayoutController.php', 404);
 $layout->registerComponent();
 $controllerCls->initialize(false);
 $controllerCls->view = $ctr->view;
 $controllerCls->__setAppModule($this, $layout);
 $controllerCls->isLayoutController = true;
 $controllerCls->$action();
 if(\GlobalVars::$returnJson) {
 $controllerCls->json(\GlobalVars::$_mviewDataArg);
 \GlobalVars::$returnJson = false;
 }
 else if(\GlobalVars::$useViewHelper) {
 $controllerCls->view(\GlobalVars::$_mviewDataArg, \GlobalVars::$_mviewDataArg2);
 \GlobalVars::$_mviewDataArg = null;
 \GlobalVars::$_mviewDataArg2 = null;
 \GlobalVars::$useViewHelper = false;
 }
 }
 public function loadController($hasLayout = true) {
 $controller = \GlobalVars::$requestInfo['controller'];
 $controllerPath = $this->moduleAbsolutePath . 'controllers' . DIRECTORY_SEPARATOR . $controller . '.php';
 if(!file_exists($controllerPath)) {
 throw new TException('Không tìm thấy file controller ' . $controllerPath, 501);
 }
 $this->mp->verifyCSRFToken();
 if(!\GlobalVars::$mobileDevice && \Config::getAppConfig('detect_mobi_device') && isset($_SERVER["HTTP_USER_AGENT"])) {
 \GlobalVars::$mobileDevice = preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]);
 }
 \GlobalVars::$minifyView = \Config::getAppConfig('minify_view');
 include_once($controllerPath);
 $controller = '\\' . $this->moduleNamespace . '\\' . $controller;
 $controllerCls = new $controller();
 $action = \GlobalVars::$requestInfo['action'];
 if(!method_exists($controllerCls, $action)) throw new TException('Không tìm thấy action ' . $action . ' trong ' . \GlobalVars::$requestInfo['module'] . '\\' . \GlobalVars::$requestInfo['controller'] . '.php', 404);
 $this->registerComponent();
 $controllerCls->initialize(true);
 if(!$hasLayout && $controllerCls->layout) $controllerCls->layout = false;
 $controllerCls->__setAppModule($this, null);
 __exec_hook_exte('beforeControllerLoad', $controllerCls);
 $model = $this->mp->getModelBindingData();
 $this->mp->callFormExtensionOnSubmit($model);
 if(\GlobalVars::$requestInfo['params'] || $model) {
 $params = array_values(\GlobalVars::$requestInfo['params']);
 if($model) {
 array_unshift($params, $model);
 }
 \GlobalVars::$requestInfo['params'] = [];
 switch(count($params)) {
 case 0: $controllerCls->$action();
 break;
 case 1: $controllerCls->$action($params[0]);
 break;
 case 2: $controllerCls->$action($params[0], $params[1]);
 break;
 case 3: $controllerCls->$action($params[0], $params[1], $params[2]);
 break;
 case 4: $controllerCls->$action($params[0], $params[1], $params[2], $params[3]);
 break;
 case 5: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4]);
 break;
 case 6: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4], $params[5]);
 break;
 case 7: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6]);
 break;
 case 8: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7]);
 break;
 case 9: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8]);
 break;
 case 10: $controllerCls->$action($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8], $params[9]);
 break;
 }
 }
 else {
 $controllerCls->$action();
 }
 if(\GlobalVars::$returnJson) {
 $controllerCls->json(\GlobalVars::$_mviewDataArg);
 \GlobalVars::$returnJson = false;
 }
 else if(\GlobalVars::$useViewHelper) {
 $controllerCls->view(\GlobalVars::$_mviewDataArg, \GlobalVars::$_mviewDataArg2);
 \GlobalVars::$_mviewDataArg = null;
 \GlobalVars::$_mviewDataArg2 = null;
 \GlobalVars::$useViewHelper = false;
 }
 return $controllerCls;
 }
 public function getRoutes() {
 return $this->routes->getAllRoutes();
 }
 public function findRouteByName($routeName) {
 return $this->routes->findRouteByName($routeName);
 }
 public function destroy() {
 unset($this->routes);
 }
 }
 class ModuleProc {
 public function loadBootstrapFile() {
 include_once APP_PATH . 'bootstrap.php';
 $appBootstrap = new \AppBootstrap();
 $appBootstrap->boot();
 }
 public function getModelBindingData() {
 if(\Request::isPost() && isset($_POST['__tcms_model_binding'])) {
 $model = \Model::load(trim($_POST['__tcms_model_binding']));
 $fields = $model->getFields();
 foreach($_POST as $k => $v) {
 if(array_key_exists($k, $fields)) {
 $model->$k = $v;
 }
 }
 return $model;
 }
 else if(\Request::isAjax() && \Request::isPost()) {
 $h = getallheaders();
 if(isset($h['__tcms_model_binding'])) {
 $model = \Model::load(trim($h['__tcms_model_binding']));
 $fields = $model->getFields();
 if(count($_POST)) {
 foreach($_POST as $k => $v) {
 if(array_key_exists($k, $fields)) {
 $model->$k = $v;
 }
 }
 }
 else {
 $dd = json_decode(file_get_contents('php://input'), true);
 if($dd) {
 foreach($dd as $k => $v) {
 if(array_key_exists($k, $fields)) {
 $model->$k = $v;
 }
 }
 }
 }
 return $model;
 }
 }
 return null;
 }
 public function findLayoutModuleByKey($layoutModuleName) {
 if(\GlobalVars::$isPublish) {
 foreach(\AppPublish::$layoutModules as $module) {
 if($module['n'] == $layoutModuleName) {
 $moduleConfig = $module;
 break;
 }
 }
 }
 else {
 $moduless = \Config::$moduleConfig;
 foreach($moduless as $k => $m) {
 if($layoutModuleName == $k) {
 $moduleConfig = $m;
 if(isset($moduleConfig['private'])) $moduleConfig['private'] = $moduleConfig['private'];
 $moduleConfig['namespace'] = $moduleConfig['namespace'];
 break;
 }
 }
 }
 if(isset($moduleConfig)) {
 if(empty($moduleConfig['private'])) {
 if(\GlobalVars::$isPublish) $moduleFilePath = MODULE_PATH . $layoutModuleName . DIRECTORY_SEPARATOR . $layoutModuleName . '.php';
 else $moduleFilePath = MODULE_PATH . area_name() . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR . $layoutModuleName . '.php';
 }
 else {
 if(\GlobalVars::$isPublish) $moduleFilePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR . $layoutModuleName . '.php';
 else $moduleFilePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . area_name() . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR . $layoutModuleName . '.php';
 }
 if(!file_exists($moduleFilePath)) {
 throw new TException('Không tìm thấy layout module ' . $layoutModuleName);
 }
 include_once($moduleFilePath);
 $moduleCls = "\\" . $moduleConfig['namespace'];
 $moduleCls = new $moduleCls();
 if(!$moduleCls->isLayout) {
 throw new TException('module ' . $layoutModuleName . ' không phải là layout');
 }
 $moduleCls->moduleName = $layoutModuleName;
 $moduleCls->moduleNamespace = $moduleConfig['namespace'];
 if(empty($moduleConfig['private'])) {
 if(\GlobalVars::$isPublish) $moduleCls->moduleAbsolutePath = MODULE_PATH . $layoutModuleName . DIRECTORY_SEPARATOR;
 else $moduleCls->moduleAbsolutePath = MODULE_PATH . area_name() . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR;
 }
 else {
 $moduleCls->isPrivate = true;
 if(\GlobalVars::$isPublish) $moduleCls->moduleAbsolutePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR;
 else $moduleCls->moduleAbsolutePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . area_name() . DIRECTORY_SEPARATOR . $layoutModuleName . DIRECTORY_SEPARATOR;
 }
 return $moduleCls;
 }
 throw new TException('Không tìm thấy layout module ' . $layoutModuleName);
 }
 public function verifyCSRFToken() {
 if(empty(\Config::$appConfig['use_csrf_token'])) {
 return true;
 }
 if(\Request::isPost()) {
 if(!\Request::has('__csrf_token')) {
 throw new TException("CSRF Token không hợp lệ!", 501);
 }
 if (!isset($_COOKIE['csrf_token'])) {
 throw new TException("CSRF Token không hợp lệ!", 501);
 }
 $csrfToken = $_COOKIE['csrf_token'];
 if(!$csrfToken) {
 throw new TException("CSRF Token không hợp lệ!", 501);
 }
 \GlobalVars::$csrfToken = $csrfToken;
 return $csrfToken == \Request::get('__csrf_token');
 }
 else {
 if(!\GlobalVars::$csrfToken && !\Request::isAjax() && !\Request::isAPI()) {
 \GlobalVars::$csrfToken = bin2hex(random_bytes(16));
 setcookie("csrf_token", \GlobalVars::$csrfToken, time() + 86400, "/");
 }
 }
 return true;
 }
 public function callFormExtensionOnSubmit($model) {
 if(isset($_POST['__tcms_call_ext_model'])) {
 $exts = $_POST['__tcms_call_ext_model'];
 foreach($exts as $extensionKey) {
 $extensionCls = load_sub_extension($extensionKey);
 if($extensionCls && $extensionCls instanceof FormControlExtension) {
 $extensionCls->onSubmit($model);
 }
 }
 }
 }
 }
 ?>