<?php
 namespace TCMS;
 interface ViewData {
 public function addStyle(string $url, bool $isAddVersion = true);
 public function addScript(string $url, bool $isAddVersion = true);
 public function removeStyle(string $url);
 public function removeScript(string $url);
 public function setYieldValue(string $yieldKey, $value);
 public function getYieldValue(string $yieldKey);
 public function injectViewSource(callable $callble);
 }
 class TCmsParentView {
 public $__v__data = [];
 protected $__controller = null;
 public $__parentExtension = null;
 public $___isNeedRemoveJsOrCss = null;
 public $__langFileLoaded = null;
 private $__s_page_cache_t = false;
 private $__model_for_bind = null;
 public function __set($name, $value) {
 $this->__v__data[$name] = $value;
 }
 public function __get($key) {
 if (array_key_exists($key, $this->__v__data)) {
 return $this->__v__data[$key];
 }
 return false;
 }
 protected function callExtension($name = null, $value = null, $option = null) {
 $ex = load_extension($name);
 if ($ex && method_exists($ex, "render")) {
 $ex->render($value, $option);
 }
 }
 protected function includePartial($viewName, $includeOnce = false) {
 if ($this->__controller == null) {
 if ($this->__parentExtension != null) {
 if ($includeOnce) {
 include_once($this->__parentExtension->getExtPath() . 'views' . DIRECTORY_SEPARATOR . $viewName);
 }
 else {
 include($this->__parentExtension->getExtPath() . 'views' . DIRECTORY_SEPARATOR . $viewName);
 }
 }
 return;
 }
 $isLayout = false;
 if ($this->__controller->__layoutModule != null) {
 $isLayout = $this->__controller->isLayoutController;
 if ($isLayout === false) return;
 }
 $particalPath = str_replace('.', DIRECTORY_SEPARATOR, $viewName);
 if ($isLayout) {
 $particalPath = $this->__controller->__layoutModule->moduleAbsolutePath . 'views' . DIRECTORY_SEPARATOR . $particalPath;
 }
 else {
 $particalPath = $this->__controller->__module->moduleAbsolutePath . 'views' . DIRECTORY_SEPARATOR . $particalPath;
 }
 $isExist = file_exists($particalPath . '.php');
 if ($isExist) {
 if ($this->__tviewChildFilePath != $particalPath . '.php') {
 if ($includeOnce) include_once($particalPath . '.php');
 else include($particalPath . '.php');
 }
 }
 else {
 $isExist = file_exists($particalPath . TCMS_VIEW_ENGINE . '.php');
 if ($isExist) {
 if ($this->__tviewChildFilePath != $particalPath . TCMS_VIEW_ENGINE . '.php') {
 $particalPath = $this->parseTCmsViewEngine($particalPath . TCMS_VIEW_ENGINE . '.php', $this->__controller->__module->moduleName, false, false);
 if ($includeOnce) include_once($particalPath);
 else include($particalPath);
 }
 }
 }
 if(!$isExist && $isLayout && $this->__controller && $this->__controller->__module) {
 $particalPath = str_replace('.', DIRECTORY_SEPARATOR, $viewName);
 $particalPath = $this->__controller->__module->moduleAbsolutePath . 'views' . DIRECTORY_SEPARATOR . $particalPath;
 $isExist = file_exists($particalPath . TCMS_VIEW_ENGINE . '.php');
 if ($isExist) {
 if ($this->__tviewChildFilePath != $particalPath . TCMS_VIEW_ENGINE . '.php') {
 $particalPath = $this->parseTCmsViewEngine($particalPath . TCMS_VIEW_ENGINE . '.php', $this->__controller->__module->moduleName, false, false);
 if ($includeOnce) include_once($particalPath);
 else include($particalPath);
 }
 }
 }
 }
 protected function modelBinding($modelName, $isCreatePage = true) {
 $data = $modelName;
 $this->__model_for_bind = $modelName;
 echo '<input type="hidden" name="__tcms_model_binding" value="' . $data . '" />';
 }
 protected function generalModel($modelName, $isCreatePage = true, $formLayoutExtension = false, $backUrl = null) {
 $model = \Model::load($modelName);
 if (!$model) {
 throw new TException("Không tìm thấy model " . $modelName . ' khi dùng @model', 501);
 }
 if (!$formLayoutExtension) {
 $formLayoutExtension = 'form-layout';
 }
 $extension = load_extension($formLayoutExtension);
 if ($extension) {
 $fields = $model->getFields();
 foreach ($fields as &$field) {
 $fieldName = $field->name;
 if ($this->$fieldName != '') {
 $field->value = $this->$fieldName;
 }
 else if ($field->defaultValue != null && $field->value == null) $field->value = $field->defaultValue;
 $errorField = $field->name . '_err_field__tvt';
 if ($this->$errorField) {
 $field->error = $this->$errorField;
 }
 }
 if ($this->__parentExtension == null) {
 if (!$backUrl) {
 $backUrl = $this->route('index');
 }
 $extension->render($fields, [ 'isCreatePage' => $isCreatePage, 'model' => $modelName, 'backUrl' => $backUrl ]);
 }
 else {
 $extension->render($fields, [ 'isCreatePage' => $isCreatePage, 'model' => $modelName ]);
 }
 }
 else {
 throw new TException("Không tìm thấy extension " . $formLayoutExtension, 501);
 }
 }
 public function addStyle(string $url, bool $isAddVersion = true) {
 $this->addOrRemoveStyleScript($url, true, false, $isAddVersion);
 }
 public function addScript(string $url, bool $isAddVersion = true) {
 $this->addOrRemoveStyleScript($url, false, false, $isAddVersion);
 }
 public function removeStyle(string $url) {
 $this->addOrRemoveStyleScript($url, true, true, true);
 }
 public function removeScript(string $url) {
 $this->addOrRemoveStyleScript($url, false, true, true);
 }
 protected function addOrRemoveStyleScript($url, $isStyle, $isRemove, $isAddVersion = true) {
 if ($isRemove && $this->__parentExtension != null) return;
 $arr = explode('/', $url);
 $fileName = $arr[count($arr) - 1];
 $isExistFile = array_key_exists($fileName, \GlobalVars::$styleScriptAdded);
 if (!$isRemove && $isExistFile && $this->__controller != null && $this->__controller->isLayoutController) {
 \GlobalVars::$styleScriptAdded[$fileName]['isLayout'] = true;
 return;
 }
 if ($isRemove === false && $isExistFile === false) {
 if ($isAddVersion) {
 if (strpos($fileName, "?") === false) {
 $url .= '?v=' . APP_VERSION;
 }
 else {
 $url .= '&v=' . APP_VERSION;
 }
 }
 \GlobalVars::$styleScriptAdded[$fileName] = [ 'url' => $url, 'isStyle' => $isStyle, 'extension' => $this->__parentExtension ? $this->__parentExtension->getExtName() : false, 'isLayout' => $this->__controller != null && $this->__controller->isLayoutController ];
 if ($this->___isNeedRemoveJsOrCss != null) {
 unset($this->___isNeedRemoveJsOrCss[$fileName]);
 }
 }
 else if ($isRemove) {
 unset(\GlobalVars::$styleScriptAdded[$fileName]);
 if ( $this->__controller != null && $this->__controller->isLayoutController === false ) {
 if ($this->___isNeedRemoveJsOrCss == null) $this->___isNeedRemoveJsOrCss = [];
 $this->___isNeedRemoveJsOrCss[$fileName] = true;
 }
 }
 }
 protected function renderStyles($url = null) {
 if ($url) {
 if ($this->__parentExtension == null) $this->addStyle($url);
 else {
 $arr = explode('/', $url);
 $fileName = $arr[count($arr) - 1];
 if (!array_key_exists($fileName, \GlobalVars::$styleScriptAdded)) {
 if (strpos($fileName, "?") === false) {
 $url .= '?v=' . APP_VERSION;
 }
 else {
 $url .= '&v=' . APP_VERSION;
 }
 \GlobalVars::$styleScriptAdded[$fileName] = [ 'url' => $url, 'isStyle' => true, 'extension' => $this->__parentExtension->getExtName(), 'r' => true ];
 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $url . "\">";
 }
 }
 return;
 }
 if ($this->__parentExtension != null) return;
 $isChild = $this->__controller != null && $this->__controller->__module->layout && $this->__controller->isLayoutController === false && $this->__controller->layout;
 if ($isChild === false) {
 if ($this->__parentExtension == null) {
 $this->removeStyleOrCssFile();
 foreach (\GlobalVars::$styleScriptAdded as $k => $style) {
 if (!isset($style['r']) && $style['isStyle'] && !$style['extension'] && !empty($style['isLayout'])) {
 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $style['url'] . "\">";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 }
 foreach (\GlobalVars::$styleScriptAdded as $k => $style) {
 if (!isset($style['r']) && $style['isStyle'] && !$style['extension']) {
 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $style['url'] . "\">";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 }
 }
 else {
 $extName = $this->__parentExtension->getExtName();
 foreach (\GlobalVars::$styleScriptAdded as $k => $style) {
 if (!isset($style['r']) && $style['isStyle'] && $style['extension'] == $extName) {
 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $style['url'] . "\">";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 }
 }
 }
 }
 protected function renderScripts($url = null) {
 if ($url) {
 if ($this->__parentExtension == null) $this->addScript($url);
 else {
 $arr = explode('/', $url);
 $fileName = $arr[count($arr) - 1];
 if (!array_key_exists($fileName, \GlobalVars::$styleScriptAdded)) {
 if (strpos($fileName, "?") === false) {
 $url .= '?v=' . APP_VERSION;
 }
 else {
 $url .= '&v=' . APP_VERSION;
 }
 \GlobalVars::$styleScriptAdded[$fileName] = [ 'url' => $url, 'isStyle' => false, 'extension' => $this->__parentExtension->getExtName(), 'r' => true ];
 echo "<script type=\"text/javascript\" src=\"" . $url . "\"></script>";
 }
 }
 return;
 }
 if ($this->__parentExtension != null) return;
 $isChild = $this->__controller != null && $this->__controller->__module->layout && $this->__controller->isLayoutController === false && $this->__controller->layout;
 if ($isChild === false) {
 if ($this->__parentExtension == null) $this->removeStyleOrCssFile();
 foreach (\GlobalVars::$styleScriptAdded as $k => $script) {
 if (isset($script['r']) || empty($script['isLayout'])) continue;
 if ($script['isStyle'] === false) {
 echo "<script type=\"text/javascript\" src=\"" . $script['url'] . "\"></script>";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 }
 foreach (\GlobalVars::$styleScriptAdded as $k => $script) {
 if (isset($script['r'])) continue;
 if ($script['isStyle'] === false) {
 echo "<script type=\"text/javascript\" src=\"" . $script['url'] . "\"></script>";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 else if ($script['isStyle'] && !$script['extension']) {
 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $script['url'] . "\">";
 \GlobalVars::$styleScriptAdded[$k]['r'] = true;
 }
 }
 }
 }
 protected function removeStyleOrCssFile() {
 if ($this->___isNeedRemoveJsOrCss != null) {
 foreach ($this->___isNeedRemoveJsOrCss as $k => $v) {
 unset(\GlobalVars::$styleScriptAdded[$k]);
 }
 unset($this->___isNeedRemoveJsOrCss);
 $this->___isNeedRemoveJsOrCss = null;
 }
 }
 protected function beginForm($action, $method = 'POST', $encType = false) {
 if ($encType) {
 echo '<form method="' . $method . '" action="' . $action . '" enctype="' . $encType . '">';
 }
 else {
 echo '<form method="' . $method . '" action="' . $action . '">';
 }
 }
 protected function endForm() {
 $this->__model_for_bind = null;
 echo '</form>';
 }
 protected function createHtmlInput($fieldName, $option = null, $value = null) {
 if ($value === null) $value = $this->$fieldName;
 if ($option != null) {
 $type = 'text';
 $attr = '';
 $cls = '';
 $err = $fieldName . '_err_field__tvt';
 if ($this->$err) {
 $cls = 'error';
 }
 foreach ($option as $k => $v) {
 switch ($k) {
 case 'type': $type = strtolower($v);
 break;
 case 'default': if ($value == '') $value = $v;
 break;
 case 'class': if ($cls) $cls .= ' ' . $v;
 else $cls = $v;
 break;
 case 'error': if ($v && !$this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 break;
 default: if ($k != 'description') {
 if ($k == 'multiple' && $type == 'file') {
 $fieldName = $fieldName . '[]';
 }
 $attr .= ' ' . $k . '="' . $v . '" ';
 }
 break;
 }
 }
 if ($value && $value instanceof \DateTime) $value = $value->format('Y-m-d H:i:s');
 if ($cls != '') $cls = 'class="' . $cls . '"';
 if ($type == 'textarea') {
 echo '<textarea name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' ' . $attr . '>' . $value . '</textarea>' . (!empty($option['description']) ? ('<small class="desc">' . $option['description'] . '</small>') : '');
 }
 else {
 if ($type == 'checkbox') echo '<input type="hidden" name="' . $fieldName . '" value="0" /><label style="font-weight: normal"><input type="checkbox" name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' ' . $attr . ' ' . ($value ? 'checked="true"' : '') . ' value="1" /> ' . (!empty($option['description']) ? $option['description'] : '') . '</label>';
 else echo '<input type="' . $type . '" name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' ' . $attr . ' value="' . $value . '" />' . (!empty($option['description']) ? ('<small class="desc">' . $option['description'] . '</small>') : '');
 }
 }
 else {
 $cls = '';
 $err = $fieldName . '_err_field__tvt';
 if ($this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 if ($cls != '') $cls = 'class="' . $cls . '"';
 echo '<input type="text" name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' value="' . $value . '" />';
 }
 }
 protected function createHtmlInputFor($fieldName, $option = null, $extensionOption = null, $vvalue = null) {
 if ($this->__model_for_bind == null) return;
 if (is_string($this->__model_for_bind)) {
 $modelName = $this->__model_for_bind;
 $this->__model_for_bind = \Model::load($modelName);
 if (!$this->__model_for_bind) {
 throw new TException("Không tìm thấy model " . $modelName . ' khi dùng @modelBinding', 501);
 }
 $this->__model_for_bind = $this->__model_for_bind->getFields();
 }
 if (array_key_exists($fieldName, $this->__model_for_bind)) {
 $field = $this->__model_for_bind[$fieldName];
 if ($field->extension) {
 $ext = load_extension($field->extension);
 if ($ext && method_exists($ext, "render")) {
 if (!$field->extensionOption) {
 $field->extensionOption = [];
 }
 $field->extensionOption['name'] = $fieldName;
 $err = $fieldName . '_err_field__tvt';
 $field->extensionOption['error'] = $this->$err || !empty($option['error']);
 if ($extensionOption) {
 copy_option($field->extensionOption, $extensionOption);
 }
 if ($field->description && !isset($field->extensionOption['description'])) {
 $field->extensionOption['description'] = $field->description;
 }
 if ($field->type == \FIELD_DATA_TYPE::SELECT || ($field->dataSource && $field->displayMember)) {
 if ($field->valueMember) $field->extensionOption['value_member'] = $field->valueMember;
 if ($field->displayMember) $field->extensionOption['display_member'] = $field->displayMember;
 if ($field->dataSource) $field->extensionOption['data_source'] = $field->dataSource;
 if ($field->defaultText) $field->extensionOption['display_text'] = $field->defaultText;
 }
 if ($field->defaultValue) {
 $field->extensionOption['default_value'] = $field->defaultValue;
 }
 if ($field->format) {
 $field->extensionOption['format'] = $field->format;
 }
 $ext->render($vvalue ? $vvalue : $this->$fieldName, $field->extensionOption);
 return;
 }
 }
 if ($field->defaultValue) {
 if (!$option) {
 $option = [];
 }
 if (!array_key_exists('default', $option)) {
 $option['default'] = $field->defaultValue;
 }
 }
 if ($field->description) {
 if (!$option) {
 $option = [];
 }
 $option['description'] = $field->description;
 }
 if ( $field->dataSource && $field->displayMember && $field->type != \FIELD_DATA_TYPE::SELECT && $field->type != \FIELD_DATA_TYPE::CHECKBOX_LIST && $field->type != \FIELD_DATA_TYPE::RADIO_LIST ) {
 if ($field->defaultText) {
 if (!$option) {
 $option = [];
 }
 $option['default-text'] = $field->defaultText;
 }
 $this->createHtmlSelect($fieldName, $field->dataSource, $field->displayMember, $field->valueMember ?? 'id', $option, $vvalue);
 return;
 }
 switch ($field->type) {
 case \FIELD_DATA_TYPE::STRING: case \FIELD_DATA_TYPE::JSON: if ($field->validate && isset($field->validate['max'])) {
 if (is_array($field->validate['max']) && isset($field->validate['max']['value'])) {
 $max = $field->validate['max']['value'];
 }
 else {
 $max = $field->validate['max'];
 }
 if ($max > 200) {
 if (!$option) {
 $option = [];
 }
 $option['type'] = 'textarea';
 }
 }
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::PASSWORD: if (!$option) {
 $option = [];
 }
 $option['type'] = 'password';
 $this->$fieldName = '';
 $vvalue = '';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::TEXT: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'textarea';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::INTEGER: case \FIELD_DATA_TYPE::BIGINT: case \FIELD_DATA_TYPE::DOUBLE: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'number';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::IMAGE: case \FIELD_DATA_TYPE::FILE: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'file';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::IMAGE_LIST: case \FIELD_DATA_TYPE::FILE_LIST: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'file';
 if (empty($option['multiple'])) $option['multiple'] = 'true';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::DATETIME: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'datetime';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::DATE: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'date';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::TIME: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'time';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::PRICE: case \FIELD_DATA_TYPE::EMAIL: case \FIELD_DATA_TYPE::PHONE: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'text';
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 case \FIELD_DATA_TYPE::SELECT: if ($field->defaultText) {
 if (!$option) {
 $option = [];
 }
 $option['default-text'] = $field->defaultText;
 }
 if ($field->dataSource) {
 $this->createHtmlSelect($fieldName, $field->dataSource, $field->displayMember, $field->valueMember, $option, $vvalue);
 }
 else {
 $this->createHtmlSelect($fieldName, [], "name", "id", $option, $vvalue);
 }
 break;
 case \FIELD_DATA_TYPE::CHECKBOX_LIST: if ($field->dataSource) {
 $this->createHtmlOption($fieldName, $field->dataSource, 'checkbox', $field->displayMember, $field->valueMember, $option, $vvalue);
 }
 else {
 $this->createHtmlOption($fieldName, [], "name", 'checkbox', "id", $option, $vvalue);
 }
 break;
 case \FIELD_DATA_TYPE::RADIO_LIST: if ($field->dataSource) {
 $this->createHtmlOption($fieldName, $field->dataSource, 'radio', $field->displayMember, $field->valueMember, $option, $vvalue);
 }
 else {
 $this->createHtmlOption($fieldName, [], "name", 'radio', "id", $option, $vvalue);
 }
 break;
 case \FIELD_DATA_TYPE::BOOLEAN: if (!$option) {
 $option = [];
 }
 if (empty($option['type'])) $option['type'] = 'checkbox';
 if ($field->description) {
 $option['description'] = $field->description;
 }
 $this->createHtmlInput($fieldName, $option, $vvalue);
 break;
 }
 }
 }
 protected function createHtmlLabelFor($fieldName, $option = null, $extensionOption = null) {
 if ($this->__model_for_bind == null) return;
 if (is_string($this->__model_for_bind)) {
 $modelName = $this->__model_for_bind;
 $this->__model_for_bind = \Model::load($modelName);
 if (!$this->__model_for_bind) {
 throw new TException("Không tìm thấy model " . $modelName . ' khi dùng @modelBinding', 501);
 }
 $this->__model_for_bind = $this->__model_for_bind->getFields();
 }
 if (array_key_exists($fieldName, $this->__model_for_bind)) {
 $field = $this->__model_for_bind[$fieldName];
 $tag = 'label';
 $cls = '';
 $required = '';
 $for = true;
 if ($option) {
 if (isset($option['tag'])) {
 $tag = $option['tag'];
 }
 if (!empty($option['class'])) {
 $cls = ' class="' . $option['class'] . '"';
 }
 if (!empty($option['required'])) {
 $required = $option['required'];
 if ($required === true) {
 $required = ' (*)';
 }
 }
 if (isset($option['for'])) {
 $for = $option['for'];
 }
 }
 if ($tag) {
 echo '<' . $tag . (($for && $tag == 'label') ? ' for="' . $fieldName . '"' : '') . '>' . $cls . ($field->displayName ? $field->displayName : $fieldName) . $required . '</' . $tag . '>';
 }
 else {
 echo ($field->displayName ? $field->displayName : $fieldName) . $required;
 }
 }
 }
 protected function createHtmlSelect(string $fieldName, $data, string $displayMember, string $valueMember, $option = null, $value = null) {
 if ($value === null) $value = $this->$fieldName;
 $selectData = null;
 if (is_string($data)) {
 $model = \Model::load($data);
 if (!$model) {
 throw new TException("Model $data không tồn tại", 501);
 }
 $selectData = $model->select($valueMember, $displayMember)->toArray();
 }
 else if (is_array($data)) {
 $selectData = $data;
 }
 else {
 throw new TException("Dữ liệu của select phải là model hoặc array", 501);
 }
 if ($option != null) {
 $attr = '';
 $cls = '';
 $defaultText = false;
 $err = $fieldName . '_err_field__tvt';
 foreach ($option as $k => $v) {
 switch ($k) {
 case 'default': if ($value == '') $value = $v;
 break;
 case 'default-text': $defaultText = $v;
 break;
 case 'class': if ($cls) $cls .= ' ' . $v;
 else $cls = $v;
 break;
 case 'error': if ($v && !$this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 break;
 default: if ($k != 'description') {
 $attr .= ' ' . $k . '="' . $v . '" ';
 }
 break;
 }
 }
 if ($this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 if ($cls != '') $cls = 'class="' . $cls . '"';
 echo '<select name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' ' . $attr . ' value="' . $value . '">';
 if ($defaultText) {
 echo "<option value=''>" . $defaultText . "</option>\n";
 }
 foreach ($selectData as $k => $v) {
 if (is_object($v)) {
 if ($v->$valueMember == $value) echo "<option selected value='" . $v->$valueMember . "'>" . $v->$displayMember . "</option>\n";
 else echo "<option value='" . $v->$valueMember . "'>" . $v->$displayMember . "</option>\n";
 }
 else {
 if ($v[$valueMember] == $value) echo "<option selected value='" . $v[$valueMember] . "'>" . $v[$displayMember] . "</option>\n";
 else echo "<option value='" . $v[$valueMember] . "'>" . $v[$displayMember] . "</option>\n";
 }
 }
 echo '</select>' . (!empty($option['description']) ? ('<small class="desc">' . $option['description'] . '</small>') : '');
 }
 else {
 $cls = '';
 $err = $fieldName . '_err_field__tvt';
 if ($this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 if ($cls != '') $cls = 'class="' . $cls . '"';
 echo '<select name="' . $fieldName . '" id="' . $fieldName . '" ' . $cls . ' value="' . $value . '">';
 foreach ($selectData as $k => $v) {
 if (is_object($v)) {
 if ($value !== false && $v->$valueMember == $value) echo "<option selected value='" . $v->$valueMember . "'>" . $v->$displayMember . "</option>\n";
 else echo "<option value='" . $v->$valueMember . "'>" . $v->$displayMember . "</option>\n";
 }
 else {
 if ($value !== false && $v[$valueMember] == $value) echo "<option selected value='" . $v[$valueMember] . "'>" . $v[$displayMember] . "</option>\n";
 else echo "<option value='" . $v[$valueMember] . "'>" . $v[$displayMember] . "</option>\n";
 }
 }
 echo '</select>';
 }
 }
 protected function createHtmlOption(string $fieldName, $data, $type, string $displayMember, string $valueMember, $option = null, $value = null) {
 if ($value === null) $value = $this->$fieldName;
 $selectData = null;
 if (is_string($data)) {
 $model = \Model::load($data);
 $selectData = $model->select($valueMember, $displayMember)->toArray();
 }
 else if (is_array($data)) {
 $selectData = $data;
 }
 else {
 throw new TException("Dữ liệu của option phải là model hoặc array", 501);
 }
 echo '<div class="' . $type . '_list_' . $fieldName . '">';
 if ($option != null) {
 $attr = '';
 $cls = '';
 foreach ($option as $k => $v) {
 switch ($k) {
 case 'default': if ($value == '') $value = $v;
 break;
 case 'class': $cls = $v;
 break;
 default: if ($k != 'description') $attr .= ' ' . $k . '="' . $v . '" ';
 break;
 }
 }
 $err = $fieldName . '_err_field__tvt';
 if ($this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 if ($cls != '') $cls = 'class="' . $cls . '"';
 foreach ($selectData as $k => $v) {
 if (is_object($v)) {
 if ($v->$valueMember == $value) echo "<label><input type='$type' name='$fieldName' $cls checked value='" . $v->$valueMember . "' /> " . $v->$displayMember . "</label>\n";
 else echo "<label><input type='$type' name='$fieldName' $cls value='" . $v->$valueMember . "' /> " . $v->$displayMember . "</label>\n";
 }
 else {
 if ($v[$valueMember] == $value) echo "<label><input type='$type' name='$fieldName' $cls checked value='" . $v[$valueMember] . "' /> " . $v[$displayMember] . "</label>\n";
 else echo "<label><input type='$type' name='$fieldName' $cls value='" . $v[$valueMember] . "' /> " . $v[$displayMember] . "</label>\n";
 }
 }
 }
 else {
 $cls = '';
 $err = $fieldName . '_err_field__tvt';
 if ($this->$err) {
 if ($cls == '') $cls = 'error';
 else $cls .= ' error';
 }
 if ($cls != '') $cls = 'class="' . $cls . '"';
 foreach ($selectData as $k => $v) {
 if (is_object($v)) {
 if ($value !== false && $v->$valueMember == $value) echo "<label><input type='$type' name='$fieldName' $cls checked value='" . $v->$valueMember . "' /> " . $v->$displayMember . "</label>\n";
 else echo "<label><input type='$type' name='$fieldName' $cls value='" . $v->$valueMember . "' /> " . $v->$displayMember . "</label>\n";
 }
 else {
 if ($value !== false && $v[$valueMember] == $value) echo "<label><input type='$type' name='$fieldName' $cls checked value='" . $v[$valueMember] . "' /> " . $v[$displayMember] . "</label>\n";
 else echo "<label><input type='$type' name='$fieldName' $cls value='" . $v[$valueMember] . "' /> " . $v[$displayMember] . "</label>\n";
 }
 }
 }
 echo '</div>';
 }
 protected function showErrorMessage($name) {
 $name = ($name . '_err_field__tvt');
 if ($this->$name) {
 echo '<span class="error_msg">' . $this->$name . '</span>';
 }
 }
 protected function showError($isShowOnlyCustomError = false) {
 if (\Request::isPost()) {
 $errorList = [];
 foreach ($this->__v__data as $k => $v) {
 if (strpos($k, '_err_field__tvt') !== false) {
 if ($isShowOnlyCustomError) {
 $name = substr($k, 0, strlen($k) - 15);
 if (!array_key_exists($name, $_POST)) {
 $errorList[] = $v;
 }
 }
 else {
 $errorList[] = $v;
 }
 }
 }
 if (count($errorList) > 0) {
 echo '<ul class="validation-summary-errors">';
 foreach ($errorList as $e) {
 echo '<li>' . $e . '</li>';
 }
 echo '</ul>';
 }
 }
 }
 public function addErrorMessage($name, $message) {
 $name = ($name . '_err_field__tvt');
 $this->$name = $message;
 }
 public function addFlashMessage($message, $messageType = null) {
 $stdCls = new \stdClass();
 $stdCls->message = $message;
 $stdCls->type = $messageType == null ? 'success' : $messageType;
 \Session::flash('message', $stdCls);
 }
 public function cache(string $key, $expiresTime = 0) {
 $key = $key . '.page';
 $data = \Cache::get($key);
 if ($data) {
 echo $data;
 exit();
 }
 else {
 $this->__s_page_cache_t = [ 't' => $expiresTime, 'n' => $key ];
 }
 }
 public function removeCache(string $key) {
 \Cache::remove($key . '.page');
 }
 public function lang($langKey, array $args = null) {
 $ret = $this->showLang($langKey, $this->__parentExtension != null, $args);
 if (!$ret) return lang($langKey, $args);
 return $ret;
 }
 public function layoutLang($langKey, array $args = null) {
 $ret = $this->showLang($langKey, true, $args);
 if (!$ret) return lang($langKey, $args);
 return $ret;
 }
 protected function showLang($langKey, $isLayout, $args = null) {
 $langKeyArray = explode(".", $langKey);
 if (count($langKeyArray) == 2) {
 $langFile = null;
 if ($isLayout) {
 if ($this->__parentExtension != null) {
 $langFile = $this->__parentExtension->getExtPath() . 'langs' . DIRECTORY_SEPARATOR . $langKeyArray[0] . '.' . \GlobalVars::$requestInfo['cur_lang'] . '.php';
 }
 else if ($this->__controller != null && $this->__controller->__layoutModule != null) {
 $langFile = $this->__controller->__layoutModule->moduleAbsolutePath . 'langs' . DIRECTORY_SEPARATOR . $langKeyArray[0] . '.' . \GlobalVars::$requestInfo['cur_lang'] . '.php';
 }
 }
 else if ($this->__controller != null) {
 $langFile = $this->__controller->__module->moduleAbsolutePath . 'langs' . DIRECTORY_SEPARATOR . $langKeyArray[0] . '.' . \GlobalVars::$requestInfo['cur_lang'] . '.php';
 }
 if ($langFile != null) {
 if ($this->__langFileLoaded == null) {
 $this->__langFileLoaded = [];
 }
 $key = ($isLayout ? 'layout_' : '') . $langKeyArray[0];
 if (array_key_exists($key, $this->__langFileLoaded)) {
 if (array_key_exists($langKeyArray[1], $this->__langFileLoaded[$key])) {
 if ($args == null) return $this->__langFileLoaded[$key][$langKeyArray[1]];
 $text = $this->__langFileLoaded[$key][$langKeyArray[1]];
 foreach ($args as $k => $v) {
 $text = str_replace('{' . $k . '}', $v, $text);
 }
 return $text;
 }
 return false;
 }
 if (file_exists($langFile)) {
 $lData = include($langFile);
 if ($lData && is_array($lData)) {
 $this->__langFileLoaded[$key] = $lData;
 if (array_key_exists($langKeyArray[1], $this->__langFileLoaded[$key])) {
 if ($args == null) return $this->__langFileLoaded[$key][$langKeyArray[1]];
 $text = $this->__langFileLoaded[$key][$langKeyArray[1]];
 foreach ($args as $k => $v) {
 $text = str_replace('{' . $k . '}', $v, $text);
 }
 return $text;
 }
 }
 }
 }
 }
 return false;
 }
 public function setViewData($key, $value = null) {
 if (is_string($key)) {
 if (isset($value)) {
 $this->__v__data[$key] = $value;
 }
 else {
 $this->__v__data[$key] = '';
 }
 }
 else if (is_array($key)) {
 foreach ($key as $k => $v) {
 $this->__v__data[$k] = $v;
 }
 }
 else if ($key instanceof \stdClass) {
 foreach ($key as $k => $v) {
 $this->__v__data[$k] = $v;
 }
 }
 else if ($key instanceof \TCMS\Model) {
 $fields = $key->getFields();
 foreach ($fields as $k => $v) {
 if ($v->value != null) {
 $this->__v__data[$k] = $v->value;
 }
 if ($v->error) {
 $this->__v__data[$k . '_err_field__tvt'] = $v->error;
 }
 }
 }
 }
 public function renderTCmsViewEngine($viewFile, $useViewEngine) {
 $isRenderChildView = $this->__controller != null && $this->__controller->__module->layout && $this->__controller->isLayoutController === false && $this->__controller->layout;
 if ($isRenderChildView) {
 $this->__tviewChildFilePath = $viewFile;
 }
 if ($useViewEngine) {
 if ($this->__parentExtension == null) $viewFile = $this->parseTCmsViewEngine($viewFile, null, $this->__controller->isLayoutController);
 else $viewFile = $this->parseTCmsViewEngine($viewFile, $this->__parentExtension->getExtName(), true);
 }
 if ($isRenderChildView) {
 $this->__tviewFilePath = $viewFile;
 if ($useViewEngine) {
 $viewFile = substr($viewFile, 0, strlen($viewFile) - 4) . '.s.php';
 include($viewFile);
 }
 else {
 include($viewFile);
 }
 }
 else {
 $isObStart = false;
 if ($this->__parentExtension == null) {
 __exec_hook_exte('beforeViewLoad', $this);
 if (\Request::isGet()) {
 if ($this->__s_page_cache_t) {
 $isObStart = true;
 ob_start();
 }
 }
 }
 \GlobalVars::$mViewClass->__injectViewSources = null;
 include($viewFile);
 if ($isObStart) {
 $content = ob_get_clean();
 if (ENVIRONMENT == 'dev') {
 \Cache::set($this->__s_page_cache_t['n'], "<!--cache -->\n" . $content, $this->__s_page_cache_t['t']);
 }
 else {
 \Cache::set($this->__s_page_cache_t['n'], $content, $this->__s_page_cache_t['t']);
 }
 echo $content;
 }
 }
 }
 protected function parseTCmsViewEngine($viewFile, $moduleName, $isLayout, $isSaveSView = true) {
 $lastModifierTime = filemtime($viewFile);
 $tempPath = null;
 $tempFileName = null;
 $tempFile = $this->getTempFile($viewFile, $lastModifierTime, $moduleName, $tempPath, $tempFileName);
 if ($tempFile != null) return $tempFile;
 include_once("TCmsViewEngine.php");
 $viewEng = new TCmsViewEngine();
 $viewFile = $viewEng->convertTCmsToPhpView($viewFile, $lastModifierTime, $tempPath, $tempFileName, $isLayout, $moduleName, $isSaveSView);
 unset($viewEng);
 $viewEng = null;
 return $viewFile;
 }
 protected function getTempFile($viewFile, $lastModifierTime, $moduleName, &$_tempPath, &$_tempFileName) {
 $tempFileName = null;
 $endIndex = strripos($viewFile, DIRECTORY_SEPARATOR);
 if ($endIndex !== false) {
 $tempFileName = substr($viewFile, $endIndex + 1);
 }
 else {
 $tempFileName = str_replace(MODULE_PATH, '', $viewFile);
 $tempFileName = str_replace(DIRECTORY_SEPARATOR, '-', $tempFileName);
 }
 $dir = ROOT_PATH . 'data' . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR;
 if ($moduleName == null) {
 if ($this->__controller->isLayoutController && $this->__controller->__layoutModule != null) {
 $dir .= $this->__controller->__layoutModule->moduleName . APP_ID;
 }
 else {
 $dir .= \GlobalVars::$requestInfo['module'] . APP_ID;
 if (!file_exists($dir) && mkdir($dir) === false) {
 return null;
 }
 $dir .= DIRECTORY_SEPARATOR . \GlobalVars::$requestInfo['controller'];
 }
 }
 else {
 $dir .= $moduleName . APP_ID;
 }
 if (!file_exists($dir) && mkdir($dir) === false) {
 return null;
 }
 $_tempPath = $dir . DIRECTORY_SEPARATOR;
 $_tempFileName = $tempFileName;
 $fileName = $_tempPath . $tempFileName;
 if (file_exists($fileName) === false) return null;
 if (filemtime($fileName) >= $lastModifierTime) return $fileName;
 return null;
 }
 }
 class TCmsView extends TCmsParentView {
 public $__v__section = [];
 public $__v__yield = [];
 public $__tviewFilePath = null;
 public $__tviewChildFilePath = null;
 protected $__curSectionKey = null;
 public $__injectViewSources = null;
 public function __setController($controller) {
 $this->__controller = $controller;
 }
 protected function getLayoutMenu(string $layoutMenuId) {
 echo '<!--- Layout Menu ' . $layoutMenuId . ' --->';
 $parentMenuList = [];
 $childMenuList = [];
 $menuList = [];
 $authExtension = false;
 \GlobalVars::$requestInfo['mid_menu_count'] = 1;
 $index = 0;
 foreach (\LayoutData::$layoutMenuItem as $v) {
 if ($v->layoutMenuId == $layoutMenuId) {
 if ($v->middleware != null) {
 if ($authExtension === false) {
 $authExtension = load_auth();
 if (!$authExtension) $authExtension = null;
 }
 if ($authExtension) {
 if (is_callable($v->middleware)) $isPermission = call_user_func($v->middleware, $authExtension);
 else {
 $v->middleware = str_replace("&apos;", "'", $v->middleware);
 $mdFun = "_my_midd_" . (\GlobalVars::$requestInfo['mid_menu_count']++);
 eval("function " . $mdFun . $v->middleware . "};");
 if (function_exists($mdFun)) {
 $isPermission = $mdFun($authExtension);
 }
 else {
 $isPermission = false;
 }
 }
 if ($isPermission === false) continue;
 }
 else {
 continue;
 }
 }
 $v->isActive = false;
 $v->index = $index++;
 $v->subMenu = [];
 $menuList[] = $v;
 }
 }
 foreach ($menuList as $menu) {
 if ($menu->parentMenuId == null) {
 $parentMenuList[] = $menu;
 }
 else {
 $childMenuList[] = $menu;
 }
 }
 $indexAdded = [];
 foreach ($parentMenuList as &$parentMenu) {
 foreach ($childMenuList as $childMenu) {
 if ($parentMenu->id == $childMenu->parentMenuId) {
 $parentMenu->subMenu[] = $childMenu;
 $indexAdded[$childMenu->index] = true;
 }
 }
 }
 $retVal = [];
 $length = count($parentMenuList);
 for ($i = 0;
 $i < $length;
 $i++) {
 if ($parentMenuList[$i]->displayAfter == null) {
 $retVal[] = $parentMenuList[$i];
 $indexAdded[$parentMenuList[$i]->index] = true;
 for ($j = $i + 1;
 $j < $length;
 $j++) {
 if ($parentMenuList[$j]->displayAfter == $parentMenuList[$i]->id) {
 $retVal[] = $parentMenuList[$j];
 $indexAdded[$parentMenuList[$j]->index] = true;
 }
 }
 }
 }
 if (count($indexAdded) != $index) {
 for ($i = 0;
 $i < $index;
 $i++) {
 if (isset($indexAdded[$i]) == false) {
 $retVal[] = $menuList[$i];
 $indexAdded[$menuList[$i]->index] = true;
 }
 }
 }
 $fullUrl = full_url();
 if (strrpos($fullUrl, '/') === strlen($fullUrl) - 1) {
 $fullUrl = substr($fullUrl, 0, strlen($fullUrl) - 1);
 }
 foreach ($retVal as &$v) {
 if ($v->url == $fullUrl) {
 $v->isActive = true;
 break;
 }
 $isActive = false;
 if ($v->subMenu && count($v->subMenu) > 0) {
 foreach ($v->subMenu as &$subMenu) {
 if ($subMenu->url == $fullUrl) {
 $subMenu->isActive = true;
 $isActive = true;
 break;
 }
 }
 }
 if ($isActive) {
 $v->isActive = true;
 break;
 }
 }
 return $retVal;
 }
 protected function readerLayoutMenu($menuId, $template = null, $extName = 'menu') {
 $menuList = $this->getLayoutMenu($menuId);
 if ($menuList) {
 $ext = load_extension($extName);
 if ($ext) {
 if ($template) {
 $ext->render($menuList);
 }
 else {
 $ext->render($menuList, ['template' => $template]);
 }
 }
 }
 }
 protected function viewBody() {
 if ($this->__controller == null) return;
 if ($this->__controller->isLayoutController && $this->__tviewFilePath) {
 include($this->__tviewFilePath);
 }
 }
 protected function viewField($key, $value = null) {
 if ($value == null) {
 if (array_key_exists($key, $this->__v__yield)) {
 echo $this->__v__yield[$key];
 }
 }
 else {
 $this->__v__yield[$key] = $value;
 }
 }
 protected function viewSection($name) {
 if ($this->__parentExtension != null) return;
 if (isset($name)) {
 if (array_key_exists($name, $this->__v__section)) echo $this->__v__section[$name];
 }
 }
 protected function startViewSection($name) {
 if ($this->__parentExtension != null) return;
 $this->__curSectionKey = $name;
 ob_start();
 }
 protected function endViewSection() {
 if ($this->__parentExtension != null) return;
 if ($this->__curSectionKey != null) {
 $content = ob_get_clean();
 if ($content !== false) {
 if (array_key_exists($this->__curSectionKey, $this->__v__section) === false) {
 $this->__v__section[$this->__curSectionKey] = $content;
 }
 }
 $this->__curSectionKey = null;
 }
 }
 public function route(string $routeName, array $param = null) {
 if ($this->__controller == null) return '';
 if ($param == null) return $this->__controller->__module->findRouteByName($routeName);
 $url = $this->__controller->__module->findRouteByName($routeName);
 if ($url == '') return $url;
 foreach ($param as $k => $v) {
 $url = str_replace('{' . $k . '}', $v, $url);
 }
 return $url;
 }
 public function setYieldValue($yieldKey, $value) {
 $this->__v__yield[$yieldKey] = $value;
 }
 public function getYieldValue($yieldKey) {
 if (array_key_exists($yieldKey, $this->__v__yield)) {
 return $this->__v__yield[$yieldKey];
 }
 return false;
 }
 public function injectViewSource($callble) {
 if (!$this->__injectViewSources) $this->__injectViewSources = [];
 $this->__injectViewSources[] = $callble;
 }
 }
 