diff --git a/module/dataview/config.php b/module/dataview/config.php
new file mode 100644
index 0000000000..3a0b8e26ac
--- /dev/null
+++ b/module/dataview/config.php
@@ -0,0 +1,49 @@
+dataview = new stdclass();
+$config->dataview->create = new stdclass();
+$config->dataview->create->requiredFields = 'name,code,group';
+
+$config->dataview->edit = new stdclass();
+$config->dataview->edit->requiredFields = 'name,group';
+
+$config->dataview->groupChild['product'] = array('story' => 'story,storyestimate,storyreview,storyspec,storystage');
+$config->dataview->groupChild['project'] = array('measure' => 'measqueue,measrecords,meastemplate', 'projectContent' => 'programactivity,programoutput,programprocess,programreport,projectcase,projectproduct,projectspec,projectstory', 'review' => 'review,reviewissue,reviewlist,reviewresult');
+$config->dataview->groupChild['company'] = array('user' => 'user,usercontact,usergroup,userquery,usertpl,userview');
+
+$config->dataview->columnTypes = new stdclass();
+$config->dataview->columnTypes->TINY = 'number';
+$config->dataview->columnTypes->SHORT = 'number';
+$config->dataview->columnTypes->LONG = 'number';
+$config->dataview->columnTypes->FLOAT = 'number';
+$config->dataview->columnTypes->DOUBLE = 'number';
+$config->dataview->columnTypes->TIMESTAMP = 'string';
+$config->dataview->columnTypes->LONGLONG = 'string';
+$config->dataview->columnTypes->INT24 = 'number';
+$config->dataview->columnTypes->DATE = 'date';
+$config->dataview->columnTypes->TIME = 'string';
+$config->dataview->columnTypes->DATETIME = 'date';
+$config->dataview->columnTypes->YEAR = 'date';
+$config->dataview->columnTypes->ENUM = 'string';
+$config->dataview->columnTypes->SET = 'string';
+$config->dataview->columnTypes->TINYBLOB = 'string';
+$config->dataview->columnTypes->MEDIUMBLOB = 'string';
+$config->dataview->columnTypes->LONG_BLOB = 'string';
+$config->dataview->columnTypes->BLOB = 'string';
+$config->dataview->columnTypes->VAR_STRING = 'string';
+$config->dataview->columnTypes->STRING = 'string';
+$config->dataview->columnTypes->NULL = 'null';
+$config->dataview->columnTypes->NEWDATE = 'date';
+$config->dataview->columnTypes->INTERVAL = 'string';
+$config->dataview->columnTypes->GEOMETRY = 'string';
+$config->dataview->columnTypes->NEWDECIMAL = 'number';
+
+/* Dameng native_type. */
+$config->dataview->columnTypes->int = 'number';
+$config->dataview->columnTypes->varchar = 'string';
+$config->dataview->columnTypes->text = 'string';
+$config->dataview->columnTypes->timestamp = 'string';
+$config->dataview->columnTypes->date = 'date';
+$config->dataview->columnTypes->time = 'string';
+$config->dataview->columnTypes->double = 'number';
+$config->dataview->columnTypes->number = 'number';
+$config->dataview->columnTypes->bigint = 'number';
diff --git a/module/dataview/control.php b/module/dataview/control.php
new file mode 100644
index 0000000000..082ac15587
--- /dev/null
+++ b/module/dataview/control.php
@@ -0,0 +1,14 @@
+
+ * @package dept
+ * @version $Id: control.php 4157 2013-01-20 07:09:42Z wwccss $
+ * @link http://www.zentao.net
+ */
+class dataview extends control
+{
+}
diff --git a/module/dataview/css/browse.css b/module/dataview/css/browse.css
new file mode 100644
index 0000000000..865798eef3
--- /dev/null
+++ b/module/dataview/css/browse.css
@@ -0,0 +1,31 @@
+#sidebar .modulegroup {font-weight:bold; padding-top: 8px; border-top: 1px solid #ddd;}
+#sidebar .active {background-color: #e9f2fb; color: #006af1;}
+
+.side-col {width: 240px;}
+#sidebar> .cell {width: 220px;}
+
+#datas {overflow: scroll;}
+#data th, #data td, #schema td, #modules li > strong {overflow: hidden; text-overflow: unset; white-space: nowrap;}
+#dataContent {margin: 0px; padding: 0px;}
+#dataContent .detail {margin: 0px; padding: 0px;}
+#dataContent .detail .detail-title {padding: 16px; border-bottom: 1px solid #f4f5f7; line-height: 16px;}
+#dataContent .detail .detail-title .dataview-title {overflow: hidden; white-space: nowrap; max-width: calc(100% - 120px); display: inline-block;}
+#dataContent .detail .detail-title .create-action {display: inline-block;}
+#dataContent .detail .detail-content {margin-top: 0px;}
+#dataContent .detail .detail-content .tab-content {padding: 16px;}
+#dataContent .tabs .nav-tabs .actions {padding: 8px 15px;}
+#dataContent .tabs .nav-tabs .actions a {padding-left: 5px;}
+#dataContent .tabs table {margin-bottom: 0px;}
+#dataContent .tabs table .dataview-sql {white-space: unset;}
+
+.nav-tabs {padding:16px 16px 0px 16px;}
+.nav-tabs>li>a {padding: 5px 10px;}
+.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover {color: #2e7fff!important; background-color: #e6f0ff; padding: 5px 10px;}
+.nav-tabs>li.active>a:before {background: unset;}
+.nav-tabs {border: unset;}
+
+.cell> .panel> .panel-heading {padding: 5px 0px 10px;}
+.detail-title>.pull-right {position: unset;}
+.detail-title>.pull-right>a {margin: 0px 10px; font-weight: normal; font-size: 13px;}
+
+.table-code {margin-left: 12px;}
diff --git a/module/dataview/css/browse.zh-cn.css b/module/dataview/css/browse.zh-cn.css
new file mode 100644
index 0000000000..d3f7b11190
--- /dev/null
+++ b/module/dataview/css/browse.zh-cn.css
@@ -0,0 +1,2 @@
+.side-col {width: 200px;}
+#sidebar> .cell {width: 180px;}
diff --git a/module/dataview/css/common.css b/module/dataview/css/common.css
new file mode 100644
index 0000000000..470d28b7b3
--- /dev/null
+++ b/module/dataview/css/common.css
@@ -0,0 +1,6 @@
+.form-actions .btn {margin-right: 5px;}
+.td-row {display: flex;}
+.td-row>div {flex: 1;}
+.error td {color: red;}
+.datatable{width: auto;}
+.table-panel {overflow: auto;}
diff --git a/module/dataview/css/create.css b/module/dataview/css/create.css
new file mode 100644
index 0000000000..e59d229232
--- /dev/null
+++ b/module/dataview/css/create.css
@@ -0,0 +1,5 @@
+#main>.container {height: auto; min-height: 340px;}
+
+#dataform .table-form>tbody>tr>th {width: 70px;}
+#dataform .table-form td.form-actions.text-center {padding-top: 20px;}
+#dataform #group_chosen .chosen-drop .chosen-results {max-height: 180px;}
diff --git a/module/dataview/css/edit.css b/module/dataview/css/edit.css
new file mode 100644
index 0000000000..4eda8ef47c
--- /dev/null
+++ b/module/dataview/css/edit.css
@@ -0,0 +1,4 @@
+#main>.container {height: auto; min-height: 280px;}
+#dataform tr > th {width: 70px;}
+
+#dataform #group_chosen .chosen-drop .chosen-results {max-height: 140px;}
diff --git a/module/dataview/css/query.css b/module/dataview/css/query.css
new file mode 100644
index 0000000000..1d57043ff5
--- /dev/null
+++ b/module/dataview/css/query.css
@@ -0,0 +1 @@
+.main-content>.center-block {border: none;}
diff --git a/module/dataview/css/querybase.css b/module/dataview/css/querybase.css
new file mode 100644
index 0000000000..62487e527a
--- /dev/null
+++ b/module/dataview/css/querybase.css
@@ -0,0 +1,23 @@
+.form-actions .btn {margin-right: 5px;}
+.td-row {display: flex;}
+.td-row>div {flex: 1;}
+.error td {color: red;}
+.datatable{width: auto;}
+.table-panel {overflow: auto;}
+.ellipsis-multi {overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;}
+.query-padding {padding-left: 15px;}
+
+#mainContent {padding: 0px; background: none; box-shadow: none;}
+#mainContent .panel {padding: 0px 10px 10px 10px; border: 0px;}
+#mainContent .panel-heading {padding: 20px 20px 10px 20px;}
+#dataform {padding: 0px 10px;}
+#dataform textarea.form-control {padding: 20px 10px; box-shadow: inset 0 0px 0px rgb(0 0 0 / 10%), 0 0 15px rgb(0 0 0 / 10%); border: 0px;}
+#mainContent .panel.table-panel .datatable.table tr>th, #mainContent .table-panel .datatable.table tr>td {padding: 5px 20px;}
+
+#queryTable .titleResult {padding:20px 0px 10px 20px;}
+.fieldSettings {margin: -8px 10px; border: none;}
+.dataview-export {margin: -8px 0px; border: none;}
+#edit select[name^='relatedTable'] + div .picker-selections:first-child {border-right: none;}
+#edit select[name^='relatedTable'] + div.picker-focus .picker-selections {border-right: 1px solid #2e7fff;}
+
+#queryFilters .radio-inline input[type=radio] {margin-top: 0px;}
diff --git a/module/dataview/js/browse.js b/module/dataview/js/browse.js
new file mode 100644
index 0000000000..a16b1bb8bd
--- /dev/null
+++ b/module/dataview/js/browse.js
@@ -0,0 +1,179 @@
+$(function()
+{
+ if(table)
+ {
+ $('.dataview-' + table).addClass('active');
+
+ var $tree = $('#modules').tree({name:'tree-group'});
+ var tree = $tree.data('zui.tree');
+ tree.expand($('.dataview-' + table).parents('li'));
+ }
+
+ $(document).on('click','.query-view', function()
+ {
+ var url = createLink('dataview', 'query', 'id=' + table);
+ var result = true;
+
+ if(dataview.used) result = confirm(warningDesign);
+ if(result) window.location.href = url;
+ });
+
+ initPager(pageID, recPerPage, recTotal, fieldCount);
+
+ $('.first-page,.left-page,.last-page,.right-page').click(function()
+ {
+ pageID = $(this).data('page');
+ queryTableData();
+ });
+
+ $('.dropup li a').click(function()
+ {
+ pageID = 1;
+ recPerPage = $(this).data('size');
+ queryTableData();
+ })
+
+});
+
+/**
+ * Locate page.
+ *
+ * @param string module
+ * @param string method
+ * @param string params
+ * @access public
+ * @return void
+ */
+function locate(module, method, params)
+{
+ var link = createLink(module, method, params);
+ window.location.href = link;
+}
+
+/**
+ * Query table data.
+ *
+ * @return void
+ */
+function queryTableData()
+{
+ var params = {};
+ params.pageID = pageID;
+ params.recPerPage = recPerPage;
+ params.recTotal = recTotal;
+ params.table = table;
+ params.type = type;
+ $.post(createLink('dataview', 'ajaxGetTableData'), params,function(resp)
+ {
+ resp = JSON.parse(resp);
+ buildTable(resp);
+
+ recTotal = resp.recTotal;
+ fieldCount = resp.fieldCount;
+ initPager(pageID, recPerPage, recTotal, fieldCount);
+ });
+}
+
+/**
+ * Build dataview data table.
+ *
+ * @param object resp
+ * @return void
+ */
+function buildTable(resp)
+{
+ var dataview = resp.dataview;
+ var clientLang = resp.clientLang;
+ var minWidth = resp.fieldCount * 100 + 'px';
+
+ var html = "
";
+ html += "";
+ for(var field in resp.fields)
+ {
+ var fieldName = field;
+ if(typeof(dataview) != 'undefined' && typeof(dataview.fieldSettings) != 'undefined' && typeof(dataview.fieldSettings[field]) != 'undefined' && typeof(dataview.fieldSettings[field].name) != 'undefined') fieldName = dataview.fieldSettings[field].name ? dataview.fieldSettings[field].name : field;
+ if(typeof(dataview.langs) != 'undefined')
+ {
+ var langs = JSON.parse(dataview.langs);
+ if(langs && typeof(langs[field]) != 'undefined' && typeof(langs[field][clientLang]) != 'undefined') fieldName = langs[field][clientLang] ? langs[field][clientLang] : fieldName;
+ }
+
+ html += "| " + fieldName + " | ";
+ }
+ html += "
";
+ html += "";
+
+ for(var index in resp.datas)
+ {
+ var data = resp.datas[index];
+
+ html += '';
+ for(var field in resp.fields)
+ {
+ var tdValue = (typeof(data[field]) != 'undefined' && data[field]) ? data[field] : 'null';
+ html += "| " + tdValue + " | ";
+ }
+ html += '
';
+ }
+ html += '';
+ html += '
';
+
+ $('#datas').empty();
+ $('#datas').append(html);
+}
+
+/**
+ * Init pager.
+ *
+ * @param int pageID
+ * @param int recPerPage
+ * @param int recTotal
+ * @access public
+ * @return void
+ */
+function initPager(pageID, recPerPage, recTotal, fieldCount)
+{
+ var pageID = parseInt(pageID);
+ var recPerPage = parseInt(recPerPage);
+ var recTotal = parseInt(recTotal);
+ var pageTotal = parseInt(Math.ceil(recTotal / recPerPage));
+
+ $('.recTotal').html(recTotalTip.replace('%s', recTotal));
+ $('.recPerPage').html(recPerPageTip.replace('%s', recPerPage));
+
+ $('.dropup li').removeClass('active');
+ $('.dropup li a').each(function()
+ {
+ if($(this).data('size') == recPerPage)
+ {
+ $(this).parent().addClass('active');
+ return;
+ }
+ });
+
+ $('.page-number').html('' + pageID + '/' + pageTotal + '');
+
+ $('.first-page').data('page', 1);
+ $('.left-page').data('page', pageID - 1);
+ $('.right-page').data('page', pageID + 1);
+ $('.last-page').data('page', pageTotal);
+
+ $('.first-page,.left-page,.last-page,.right-page').removeClass('disabled');
+ if(pageID == 1) $('.first-page,.left-page').addClass('disabled');
+ if(pageID == pageTotal) $('.last-page,.right-page').addClass('disabled');
+
+ $('#queryResult').html(viewResult.replace('%s', recTotal).replace('%s', fieldCount));
+}
+
+/**
+ * Set export show or hidden.
+ *
+ * @param string stat show|hide
+ * @access public
+ * @return void
+ */
+function setExport(stat)
+{
+ if(stat == 'show') $('.dataview-export').removeClass('hidden');
+ else $('.dataview-export').addClass('hidden');
+}
diff --git a/module/dataview/js/common.js b/module/dataview/js/common.js
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/module/dataview/js/common.js
@@ -0,0 +1 @@
+
diff --git a/module/dataview/js/create.js b/module/dataview/js/create.js
new file mode 100644
index 0000000000..28f62d63a3
--- /dev/null
+++ b/module/dataview/js/create.js
@@ -0,0 +1,25 @@
+$(function()
+{
+ var dataviewSql = JSON.parse(sessionStorage.getItem('dataviewSql'));
+ var fieldSettings = sessionStorage.getItem('fieldSettings');
+ var langs = sessionStorage.getItem('langs');
+
+ $.each(dataviewSql, function(index, value)
+ {
+ if(value.name == 'sql')
+ {
+ dataviewSql = value.value;
+ return;
+ }
+ });
+
+ $('#sql').val(dataviewSql);
+ $('#fields').val(fieldSettings);
+ $('#langs').val(langs);
+});
+
+function locate(method, params)
+{
+ var link = createLink('dataview', method, params);
+ window.location.href = link;
+}
diff --git a/module/dataview/js/datastorage.js b/module/dataview/js/datastorage.js
new file mode 100644
index 0000000000..13f5507687
--- /dev/null
+++ b/module/dataview/js/datastorage.js
@@ -0,0 +1,73 @@
+function initStorage(obj, debug = false)
+{
+ var dataStorage =
+ {
+ clone: function(key)
+ {
+ if(typeof this['_' + key] != 'object') return this['_' + key]
+ return JSON.parse(JSON.stringify(this['_' + key]));
+ },
+ addProperty: function(key, value)
+ {
+ this[key] =
+ {
+ get: function()
+ {
+ if(debug)
+ {
+ console.groupCollapsed('Get %c' + key, 'color: #3785ff');
+ console.log(this['_' + key]);
+ console.trace();
+ console.groupEnd();
+ }
+ return this['_' + key];
+ },
+ set: function(value)
+ {
+ if(debug)
+ {
+ console.groupCollapsed('Set %c' + key, 'color: #ff6970');
+ console.log('Before', this['_' + key]);
+ console.log('Value', value);
+ console.trace();
+ console.groupEnd();
+ }
+ this['_' + key] = value;
+ }
+ }
+ }
+ };
+ for(let key in obj)
+ {
+ dataStorage['_' + key] = obj[key];
+
+ Object.defineProperty(dataStorage, key,
+ {
+ get: function()
+ {
+ if(debug)
+ {
+ console.groupCollapsed('Get %c' + key, 'color: #3785ff');
+ console.log(this.clone(key));
+ console.trace();
+ console.groupEnd();
+ }
+ return this.clone(key);
+ },
+ set: function(value)
+ {
+ if(debug)
+ {
+ console.groupCollapsed('Set %c' + key, 'color: #ff6970');
+ console.log('Before', this.clone(key));
+ console.log('Value', value);
+ console.trace();
+ console.groupEnd();
+ }
+ this['_' + key] = value;
+ }
+ });
+ }
+
+ return dataStorage;
+}
diff --git a/module/dataview/js/edit.js b/module/dataview/js/edit.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/module/dataview/js/query.js b/module/dataview/js/query.js
new file mode 100644
index 0000000000..bbc3ea9079
--- /dev/null
+++ b/module/dataview/js/query.js
@@ -0,0 +1,59 @@
+$(function()
+{
+ var fileName = dataview != '' ? dataview.name : '';
+ var exportModal = new $.zui.ModalTrigger({url: createLink('dataview', 'export', 'type=query&table=&fileName=' + fileName, '', true), type: 'iframe', width: 900});
+ if(document.getElementById('exportDataview'))
+ {
+ document.getElementById('exportDataview').addEventListener('click', function(e)
+ {
+ tryQuery(e);
+ showModel(exportModal);
+ });
+ }
+
+ if(rawMethod !== 'create') return;
+ var createModal = new $.zui.ModalTrigger({url: createLink('dataview', 'create', 'step=create', '', true), type: 'iframe', width: 480});
+ document.getElementById('saveButton').addEventListener('click', function(e)
+ {
+ tryQuery(e);
+ showModel(createModal);
+ });
+});
+
+function tryQuery(e)
+{
+ $.ajaxSettings.async = false;
+ query();
+ $.ajaxSettings.async = true;
+ e.preventDefault();
+}
+
+function showModel(objModel)
+{
+ if($('#dataform .error').length == $('#dataform .error.hidden').length)
+ {
+ var params = $('#dataform').serializeArray();
+
+ getFieldSettings();
+
+ /* Fix bug #26716. */
+ var fieldSettings = DataStorage.clone('fieldSettings');
+ for(let index in fieldSettings)
+ {
+ if(!Object.keys(DataStorage.fields).includes(index)) delete fieldSettings[index];
+ }
+ DataStorage.fieldSettings = fieldSettings;
+
+ sessionStorage.setItem('dataviewSql', JSON.stringify(params));
+ sessionStorage.setItem('fieldSettings', JSON.stringify(fieldSettings));
+ sessionStorage.setItem('langs', JSON.stringify(DataStorage.langs));
+
+ objModel.show();
+ }
+}
+
+function locate(method, params)
+{
+ var link = createLink('dataview', method, params);
+ window.location.href = link;
+}
diff --git a/module/dataview/js/querybase.js b/module/dataview/js/querybase.js
new file mode 100644
index 0000000000..1cfb0752ef
--- /dev/null
+++ b/module/dataview/js/querybase.js
@@ -0,0 +1,486 @@
+var modal;
+
+/**
+ * Query sql.
+ *
+ * @param function callback
+ * @access public
+ * @return void
+ */
+function query(callback) {
+ DataStorage.isInsert = false;
+ var pivot = DataStorage.pivot;
+ var filters = '';
+ if(pivot)
+ {
+ if(!setVarFrom()) return false;
+
+ filters = DataStorage.pivot.filters;
+
+ filters.forEach(function(filter, index)
+ {
+ if(filter.from == 'query')
+ {
+ var find = $('#queryFilters').find('.filter-item-' + index + ' .form-' + filter.type);
+ if(find.length != 0) filter.default = find.val();
+ }
+ });
+ }
+
+ DataStorage.fields = {};
+ DataStorage.columns = {};
+ DataStorage.rows = [];
+
+ $('.query').addClass('disabled');
+ $('#querying').removeClass('hidden');
+ $.post(createLink('dataview', 'ajaxQuery'), {sql: $('#sql').val(), filters: filters, recPerPage: DataStorage.recPerPage, pageID: DataStorage.pageID}, function(resp)
+ {
+ resp = JSON.parse(resp);
+ $('.query').removeClass('disabled');
+ $('#querying').addClass('hidden');
+ if(resp.result !== 'success')
+ {
+ $('#exportDataview').addClass('hidden');
+ var message = resp.message.errorInfo ? resp.message.errorInfo[2] : resp.message;
+ $('.error').removeClass('hidden');
+ $('.error td').html(message);
+ drawTable([], []);
+ }
+ else
+ {
+ $('#exportDataview').removeClass('hidden');
+ DataStorage.fields = resp.fields;
+ DataStorage.columns = resp.columns;
+ DataStorage.rows = resp.rows;
+ vars = resp.vars;
+ DataStorage.relatedObject = resp.relatedObject;
+
+ if(resp.filters.length)
+ {
+ pivot.filters = resp.filters;
+ DataStorage.pivot = pivot;
+
+ resp.filters.forEach(function(filter, index)
+ {
+ if(filter.from != 'query') return;
+ if(filter.type != 'date' && filter.type != 'datetime') return;
+
+ var find = $('#queryFilters').find('.filter-item-' + index + ' .form-' + filter.type);
+ find.val(filter.default);
+ });
+ }
+
+ drawTable(resp.fields, resp.rows);
+ getFieldSettings();
+ addResult(resp);
+ initPager(resp);
+ if(typeof callback == 'function') callback();
+ }
+ });
+}
+
+/**
+ * Add query result.
+ *
+ * @access public
+ */
+function addResult(resp)
+{
+ $('#queryResult').remove();
+
+ var lineCount = resp.lineCount;
+ var columnCount = resp.columnCount;
+ var queryMsg = queryResult;
+
+ queryMsg = queryMsg.replace('%s', lineCount);
+ queryMsg = queryMsg.replace('%s', columnCount);
+
+ $('#queryTable .table-footer').append("" + queryMsg + "");
+}
+
+function initPager(resp)
+{
+ var pageID = parseInt(resp.pageID);
+ var recPerPage = parseInt(resp.recPerPage);
+ var recTotal = parseInt(resp.lineCount);
+ var pageTotal = parseInt(Math.ceil(recTotal / recPerPage));
+
+ $('.recTotal').html(recTotalTip.replace('%s', recTotal));
+ $('.recPerPage').html(recPerPageTip.replace('%s', recPerPage));
+
+ $('.dropup li').removeClass('active');
+ $('.dropup li a').each(function()
+ {
+ if($(this).data('size') == resp.recPerPage)
+ {
+ $(this).parent().addClass('active');
+ return;
+ }
+ });
+
+ $('.page-number').html('' + pageID + '/' + pageTotal + '');
+
+ $('.left-page').data('page', pageID - 1);
+ $('.right-page').data('page', pageID + 1);
+ $('.last-page').data('page', pageTotal);
+
+ $('.first-page,.left-page,.last-page,.right-page').removeClass('disabled');
+ if(pageID == 1) $('.first-page,.left-page').addClass('disabled');
+ if(pageID == pageTotal) $('.last-page,.right-page').addClass('disabled');
+ $('.table-footer').toggle(!!recTotal);
+}
+
+/**
+ * Save field settings.
+ *
+ * @access public
+ * @return bool
+ */
+function saveSettings()
+{
+ var fieldSettings = DataStorage.clone('fieldSettings');
+ var relatedObject = DataStorage.relatedObject;
+ var objectFields = DataStorage.objectFields;
+ for(let index in DataStorage.fields)
+ {
+ var relatedTable = $('#relatedTable' + index).val();
+ var relatedField = $('#relatedField' + index).val();
+ fieldSettings[index].object = relatedTable;
+ fieldSettings[index].field = relatedField;
+
+ if(relatedTable != relatedObject[index])
+ {
+ fieldSettings[index].type = 'object';
+ if(typeof(objectFields) != 'undefined' && typeof(objectFields[relatedTable]) != 'undefined' && typeof(objectFields[relatedTable][index]) != 'undefined') fieldSettings[index].type = objectFields[relatedTable][index].type == 'object' ? 'string' : objectFields[relatedTable][index].type;
+ }
+ if(relatedTable == relatedObject[index] && fieldSettings[index].type == 'object') fieldSettings[index].type = 'string';
+ }
+
+ DataStorage.fieldSettings = fieldSettings;
+
+ var langs = DataStorage.clone('langs');
+ $("input[name^='langs']").each(function()
+ {
+ var field = $(this).data('field');
+ var lang = $(this).data('lang');
+ var value = $(this).val();
+
+ if(typeof(langs[field]) == 'undefined') langs[field] = {};
+ langs[field][lang] = value;
+ })
+ DataStorage.langs = langs;
+
+ $('#addModal').modal('hide');
+ this.drawTable(DataStorage.fields, DataStorage.rows);
+ return false;
+}
+
+/**
+ * Draw table content for sql result.
+ *
+ * @param object fields
+ * @param array rows
+ * @access public
+ * @return void
+ */
+function drawTable(fields, rows)
+{
+ $.post(createLink('dataview', 'ajaxGetFieldName'), {fields: fields, fieldSettings: DataStorage.fieldSettings}, function(resp)
+ {
+ resp = JSON.parse(resp);
+ if(resp.result == 'success') fields = resp.fields ? resp.fields : fields;
+
+ $('.table-empty-tip').addClass('hidden');
+ if(rows.length == 0)
+ {
+ $('.table-empty-tip').removeClass('hidden');
+ $('table.result').empty();
+ return;
+ }
+ var head = '';
+ var langs = DataStorage.langs;
+ for(let field in fields)
+ {
+ var fieldName = fields[field];
+ if(!$.isEmptyObject(langs) && typeof(langs[field]) !== 'undefined') fieldName = langs[field][clientLang] ? langs[field][clientLang] : fieldName;
+ head += '| ' + fieldName + ' | ';
+ }
+ head += '
';
+
+ var html = '' + head + '';
+
+ var body = '';
+ for(let row of rows)
+ {
+ body += '';
+ for(let index in row) body += '' + row[index] + ' | ';
+ body += '
';
+ }
+
+ html += '' + body + '';
+
+ $('table.result').html(html);
+ });
+}
+
+/**
+ * Get text width.
+ *
+ * @param string text
+ * @access public
+ * @return float
+ */
+function getTextWidth(text)
+{
+ var font = "bold 12pt arial"
+ var canvas = document.createElement("canvas");
+ var context = canvas.getContext("2d");
+ context.font = font;
+ var measure = context.measureText(text);
+
+ return measure.width;
+}
+
+/**
+ * Get field settings.
+ *
+ * @access public
+ * @return float
+ */
+function getFieldSettings()
+{
+ var fieldSettings = DataStorage.clone('fieldSettings');
+ var fields = DataStorage.fields;
+ var columns = DataStorage.columns;
+ var relatedObject = DataStorage.relatedObject;
+ var objectFields = DataStorage.objectFields;
+
+ var fieldSettingsNew = {};
+
+ for(let index in fields)
+ {
+ var field = fields[index];
+ var defaultType = columns[index];
+ var defaultObject = relatedObject[index];
+
+ if(typeof(objectFields) != 'undefined' && typeof(objectFields[defaultObject]) != 'undefined' && typeof(objectFields[defaultObject][index]) != 'undefined') defaultType = objectFields[defaultObject][index].type == 'object' ? 'string' : objectFields[defaultObject][index].type;
+
+ if(!fieldSettings[index])
+ {
+ fieldSettingsNew[index] = {
+ name: field,
+ object: defaultObject,
+ field: index,
+ type: defaultType
+ };
+ }
+ else
+ {
+ var object = fieldSettings[index].object;
+ if(!object || object.length == 0) object = defaultObject;
+ fieldSettings[index].object = object;
+
+ var field = fieldSettings[index].field;
+ if(!field || field.length == 0)
+ {
+ fieldSettings[index].field = index;
+ fieldSettings[index].object = defaultObject;
+ fieldSettings[index].type = 'string';
+ }
+
+ if(!fieldSettings[index].name) fieldSettings[index].name = fields[index];
+
+ var type = fieldSettings[index].type;
+ if(object == defaultObject && type != defaultType) fieldSettings[index].type = defaultType;
+
+ fieldSettingsNew[index] = fieldSettings[index];
+ }
+ }
+
+ DataStorage.fieldSettings = fieldSettingsNew;
+}
+
+function submitForm()
+{
+ if($('#fields').length == 0) $('#dataform table').before($('').attr({name: 'fields', id: 'fields'}).addClass('hidden'));
+ if($('#langs').length == 0) $('#dataform table').before($('').attr({name: 'langs', id: 'langs'}).addClass('hidden'));
+
+ getFieldSettings();
+
+ /* Fix bug #26716. */
+ var fieldSettings = DataStorage.clone('fieldSettings');
+ var fields = DataStorage.fields;
+ for(let index in fieldSettings)
+ {
+ if(!Object.keys(fields).includes(index)) delete fieldSettings[index];
+ }
+ DataStorage.fieldSettings = fieldSettings;
+
+ $('#fields').val(JSON.stringify(DataStorage.fieldSettings));
+ $('#langs').val(JSON.stringify(DataStorage.langs));
+ $('#dataform').submit();
+}
+
+/**
+ * Set related field.
+ *
+ * @param obj $obj
+ * @access public
+ * @return void
+ */
+function setRelatedField(obj)
+{
+ var selectedTable = $(obj).val();
+ var relatedFields = [];
+ var objectFields = DataStorage.objectFields;
+ for(let index in objectFields[selectedTable]) relatedFields[index] = objectFields[selectedTable][index].name;
+ var relatedFieldOptions = Object.keys(relatedFields).map(function(key)
+ {
+ return {text: relatedFields[key], value: key};
+ });
+
+ var $relatedField = $(obj).closest('tr').find("select[name^='relatedField']");
+ var picker = $relatedField.data('zui.picker');
+
+ if(picker) picker.destroy();
+ $relatedField.picker({list: relatedFieldOptions, autoselectfirst: true});
+}
+
+/**
+ * Active field.
+ *
+ * @param obj $obj
+ * @access public
+ * @return void
+ */
+function activeField(obj)
+{
+ if(currentTheme == 'default')
+ {
+ $(obj).closest('tr').find('#' + $(obj).data('field')).css('color', '#2e7fff');
+ }
+ else
+ {
+ $(obj).closest('tr').find('#' + $(obj).data('field')).addClass('text-primary');
+ }
+}
+
+/**
+ * Remove field active.
+ *
+ * @param obj $obj
+ * @access public
+ * @return void
+ */
+function removeActive(obj)
+{
+ if(currentTheme == 'default')
+ {
+ $(obj).closest('tr').find('#' + $(obj).data('field')).css('color', '#0b0f18');
+ }
+ else
+ {
+ $(obj).closest('tr').find('#' + $(obj).data('field')).removeClass('text-primary');
+ }
+}
+
+$(document).ready(function()
+{
+ $('#sql').on('input', function()
+ {
+ $('.error').addClass('hidden');
+ // fieldSettings = {};
+ })
+
+ $('#sql').on('change', function()
+ {
+ DataStorage.fieldSettings = {};
+ DataStorage.langs = {};
+ })
+
+ if($('#sql').val()) setTimeout(query, 500);
+ $('.query').click(function()
+ {
+ DataStorage.pageID = 1;
+ query();
+ });
+
+ $('.fieldSettings').click(function() {
+ query(function() {
+
+ var formTpl = $('#fieldSettingTpl').html();
+ var formHtml = $(formTpl);
+
+ var tbodyTpl = $('#tbodyTpl').html();
+
+ var fieldTypeOptions = Object.keys(lang.objects).map(function(key)
+ {
+ return {text: lang.objects[key], value: key};
+ });
+
+ var fieldSettings = DataStorage.fieldSettings;
+ var objectFields = DataStorage.objectFields;
+ var langs = DataStorage.langs;
+ var tbodyHtmls = Object.keys(fieldSettings).map(function(field)
+ {
+ var relatedFields = [];
+ var relatedObjectFields = objectFields[fieldSettings[field].object];
+ for(let index in relatedObjectFields) relatedFields[index] = relatedObjectFields[index].name;
+ var relatedFieldOptions = Object.keys(relatedFields).map(function(key)
+ {
+ return {text: relatedFields[key], value: key};
+ });
+
+ var html = $($.zui.formatString(tbodyTpl, {field: field, name: fieldSettings[field].name}));
+ html.find('select#relatedTable' + field).picker({list: fieldTypeOptions, defaultValue: fieldSettings[field].object});
+ html.find('select#relatedField' + field).picker({list: relatedFieldOptions, defaultValue: fieldSettings[field].field});
+ html.find("input[name^='langs']").each(function()
+ {
+ var field = $(this).data('field');
+ var lang = $(this).data('lang');
+
+ var fieldName = '';
+ if(lang == clientLang)
+ {
+ var relatedFieldName = $(this).closest('tr').find('select#relatedField' + field + '~ div.picker').find('span.picker-selection-text').text();
+ fieldName = relatedFieldName ? relatedFieldName : DataStorage.fields[field];
+ }
+ fieldName = !$.isEmptyObject(langs) && typeof(langs[field]) != 'undefined' && typeof(langs[field][lang]) != 'undefined' && langs[field][lang] ? langs[field][lang] : fieldName;
+ $(this).val(fieldName);
+ });
+ return html;
+ });
+
+ formHtml.find('tbody').append(tbodyHtmls);
+
+ var modal = $('#addModal');
+ modal.find('.modal-body').html(formHtml);
+ modal.modal({width: 500});
+ })
+ });
+
+ $('#save').click(function()
+ {
+ if(typeof dataview !== 'undefined' && dataview.used)
+ {
+ bootbox.confirm(warningDesign, function(res) { if(res) query(submitForm); });
+ }
+ else
+ {
+ query(submitForm);
+ }
+ });
+
+ $('.first-page,.left-page,.last-page,.right-page').click(function()
+ {
+ DataStorage.pageID = $(this).data('page');
+ query();
+ });
+
+ $('.dropup li a').click(function()
+ {
+ DataStorage.pageID = 1;
+ DataStorage.recPerPage = $(this).data('size');
+ query();
+ })
+});
diff --git a/module/dataview/lang/de.php b/module/dataview/lang/de.php
index e5dce9a094..38e9f04927 100644
--- a/module/dataview/lang/de.php
+++ b/module/dataview/lang/de.php
@@ -1,3 +1,170 @@
dataview->id = 'ID';
-$lang->dataview->name = 'Name';
+$lang->dataview->id = 'ID';
+$lang->dataview->name = 'Name';
+$lang->dataview->export = 'Export';
+
+$lang->dataview->common = 'Data Table';
+$lang->dataview->id = 'ID';
+$lang->dataview->type = 'Type';
+$lang->dataview->name = 'Name';
+$lang->dataview->code = 'Code';
+$lang->dataview->group = 'Group';
+$lang->dataview->view = 'View Name';
+$lang->dataview->desc = 'Description';
+$lang->dataview->data = 'Data';
+$lang->dataview->schema = 'Schema';
+$lang->dataview->details = 'Details';
+$lang->dataview->fieldName = 'Field Name';
+$lang->dataview->fieldType = 'Field Type';
+$lang->dataview->create = 'Create';
+$lang->dataview->browse = 'Browse';
+$lang->dataview->edit = 'Edit';
+$lang->dataview->design = 'Design';
+$lang->dataview->delete = 'Delete';
+$lang->dataview->createPriv = 'Create Custom Table';
+$lang->dataview->browsePriv = 'Browse Data Table';
+$lang->dataview->editPriv = 'Edit Custom Table';
+$lang->dataview->designPriv = 'Design Custom Table';
+$lang->dataview->deletePriv = 'Delete Custom Table';
+$lang->dataview->viewAction = 'View Custom Table';
+$lang->dataview->sql = 'SQL Query';
+$lang->dataview->sqlPlaceholder = 'Please enter a query statement. Only select query is supported.';
+$lang->dataview->query = 'Query';
+$lang->dataview->add = 'Add ';
+$lang->dataview->sqlQuery = 'SQL Query';
+$lang->dataview->onlyOne = 'Only allow one query';
+$lang->dataview->empty = 'Please enter a query statement';
+$lang->dataview->allowSelect = 'Only SELECT is allowed';
+$lang->dataview->noStar = "For performance, 'SELECT *' is not allowed";
+$lang->dataview->fieldSettings = "Field Settings";
+$lang->dataview->queryFilters = "Query Filters";
+$lang->dataview->varSettings = "Var Settings";
+$lang->dataview->result = "Query Result";
+$lang->dataview->chart = "Chart";
+$lang->dataview->table = "Table";
+$lang->dataview->save = "Save";
+$lang->dataview->field = "Field";
+$lang->dataview->varError = "Format of var is error";
+$lang->dataview->time = "Time";
+$lang->dataview->confirmDelete = 'Do you want to delete this custom table?';
+$lang->dataview->manageGroup = 'Manage group';
+$lang->dataview->noModule = 'You have no groups.
Manage Now
';
+$lang->dataview->notSelect = 'Please select a data table to dislay.';
+$lang->dataview->existView = "View exist.";
+$lang->dataview->noQueryData = "No Query Data";
+$lang->dataview->builtIn = 'Built-in Data Group';
+$lang->dataview->default = 'Default Group';
+$lang->dataview->relatedTable = 'Table';
+$lang->dataview->relatedField = 'Field';
+$lang->dataview->multilingual = 'After switching the system language, the corresponding name will be displayed';
+$lang->dataview->duplicateField = 'Duplicate field names exist: %s. You are advised to: (1) Modify the * query to a specific field. (2) Use as to alias the field.';
+$lang->dataview->errorField = 'The current field %s is an invalid character. Please [as] it in English or English underscore format.';
+
+$lang->dataview->varFilter = new stdclass();
+$lang->dataview->varFilter->varCode = 'Var Code';
+$lang->dataview->varFilter->varLabel = 'Var Label';
+$lang->dataview->varFilter->default = 'Default';
+$lang->dataview->varFilter->requestType = 'Request Type';
+
+$lang->dataview->varFilter->noticeVarName = 'Var name cannot be empty';
+$lang->dataview->varFilter->noticeRequestType = 'Request type cannot be empty';
+$lang->dataview->varFilter->noticeShowName = 'Show name cannot be empty';
+
+$lang->dataview->varFilter->requestTypeList['input'] = 'Input';
+$lang->dataview->varFilter->requestTypeList['date'] = 'Date';
+$lang->dataview->varFilter->requestTypeList['datetime'] = 'Datetime';
+$lang->dataview->varFilter->requestTypeList['select'] = 'Select';
+
+$lang->dataview->varFilter->selectList['user'] = 'User List';
+$lang->dataview->varFilter->selectList['product'] = $lang->productCommon . ' List';
+$lang->dataview->varFilter->selectList['project'] = 'Project';
+$lang->dataview->varFilter->selectList['execution'] = $lang->executionCommon . ' List';
+$lang->dataview->varFilter->selectList['dept'] = 'Dept List';
+$lang->dataview->varFilter->selectList['project.status'] = 'Project List';
+
+$lang->dataview->objects = array();
+$lang->dataview->objects['product'] = $lang->product->common;
+$lang->dataview->objects['story'] = $lang->story->common;
+$lang->dataview->objects['build'] = $lang->build->common;
+$lang->dataview->objects['productplan'] = 'plan';
+$lang->dataview->objects['release'] = $lang->release->common;
+$lang->dataview->objects['bug'] = $lang->bug->common;
+$lang->dataview->objects['project'] = $lang->project->common;
+$lang->dataview->objects['task'] = $lang->task->common;
+$lang->dataview->objects['team'] = 'team';
+$lang->dataview->objects['execution'] = $lang->execution->common;
+$lang->dataview->objects['testtask'] = 'testtask';
+$lang->dataview->objects['testrun'] = 'testrun';
+$lang->dataview->objects['testcase'] = 'testcase';
+$lang->dataview->objects['testresult'] = 'result';
+$lang->dataview->objects['casemodule'] = 'case module';
+$lang->dataview->objects['action'] = 'Action';
+$lang->dataview->objects['effort'] = 'Effort';
+
+$lang->dataview->tables = array();
+$lang->dataview->tables['build'] = array('name' => 'Build', 'desc' => '');
+$lang->dataview->tables['product'] = array('name' => 'Product', 'desc' => '');
+$lang->dataview->tables['productplan'] = array('name' => 'Product Plan', 'desc' => '');
+$lang->dataview->tables['release'] = array('name' => 'Release', 'desc' => '');
+$lang->dataview->tables['project'] = array('name' => 'Project', 'desc' => '');
+$lang->dataview->tables['execution'] = array('name' => 'Execution', 'desc' => '');
+$lang->dataview->tables['task'] = array('name' => 'Task', 'desc' => '');
+//$lang->dataview->tables['team'] = array('name' => 'Team', 'desc' => '');
+$lang->dataview->tables['bug'] = array('name' => 'Bug', 'desc' => 'pri, severity, project, product...');
+$lang->dataview->tables['bugbuild'] = array('name' => 'Build Bug', 'desc' => 'pri, severity, project, build...');
+$lang->dataview->tables['story'] = array('name' => 'Story', 'desc' => '');
+$lang->dataview->tables['testcase'] = array('name' => 'Testcase', 'desc' => '');
+$lang->dataview->tables['casestep'] = array('name' => 'Testcase step', 'desc' => '');
+$lang->dataview->tables['testtask'] = array('name' => 'Testtask', 'desc' => '');
+$lang->dataview->tables['testrun'] = array('name' => 'Test run', 'desc' => '');
+$lang->dataview->tables['testresult'] = array('name' => 'Test result', 'desc' => '');
+
+$lang->dataview->typeList = array();
+$lang->dataview->typeList['view'] = 'Custom Table';
+$lang->dataview->typeList['table'] = 'Origin Table';
+
+$lang->dataview->fieldTypeList = array();
+$lang->dataview->fieldTypeList['string'] = 'String';
+$lang->dataview->fieldTypeList['option'] = 'Option';
+$lang->dataview->fieldTypeList['date'] = 'Date';
+
+$lang->dataview->groupList['my'] = 'Dashboard';
+$lang->dataview->groupList['program'] = 'Program';
+$lang->dataview->groupList['product'] = $lang->productCommon;
+$lang->dataview->groupList['project'] = 'project';
+$lang->dataview->groupList['execution'] = $lang->execution->common;
+$lang->dataview->groupList['kanban'] = 'Kanban';
+$lang->dataview->groupList['qa'] = 'Test';
+$lang->dataview->groupList['doc'] = 'Doc';
+$lang->dataview->groupList['assetlib'] = 'Assetlib';
+$lang->dataview->groupList['report'] = 'Report';
+$lang->dataview->groupList['company'] = 'Company';
+$lang->dataview->groupList['repo'] = 'CI';
+$lang->dataview->groupList['api'] = 'API';
+$lang->dataview->groupList['message'] = 'Message';
+$lang->dataview->groupList['search'] = 'Search';
+$lang->dataview->groupList['admin'] = 'Admin';
+$lang->dataview->groupList['system'] = 'System';
+$lang->dataview->groupList['other'] = 'Others';
+
+$lang->dataview->secondGroup['story'] = 'Story';
+$lang->dataview->secondGroup['review'] = 'Review';
+$lang->dataview->secondGroup['projectContent'] = 'Project content';
+$lang->dataview->secondGroup['measure'] = 'Measure';
+$lang->dataview->secondGroup['user'] = 'User';
+
+$lang->dataview->error = new stdclass();
+$lang->dataview->error->canNotDesign = 'This table is already in use and cannot be redesigned.';
+$lang->dataview->error->canNotDelete = 'This table is already in use and cannot be deleted.';
+$lang->dataview->error->warningDesign = 'The custom table has been referenced. Design will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+$lang->dataview->error->warningDelete = 'The custom table has been referenced. Delete will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+
+$lang->dataview->querying = 'Querying, please wait...';
+$lang->dataview->queryResult = 'Query data in row %s x %s column';
+$lang->dataview->viewResult = '%s x %s rows of data';
+$lang->dataview->recTotalTip = ' %s items in total';
+$lang->dataview->recPerPageTip = " %s items per page ";
+
+$lang->dataview->projectStatusList['done'] = 'Done';
+$lang->dataview->projectStatusList['cancel'] = 'Cancel';
+$lang->dataview->projectStatusList['pause'] = 'Pause';
diff --git a/module/dataview/lang/en.php b/module/dataview/lang/en.php
index e5dce9a094..38e9f04927 100644
--- a/module/dataview/lang/en.php
+++ b/module/dataview/lang/en.php
@@ -1,3 +1,170 @@
dataview->id = 'ID';
-$lang->dataview->name = 'Name';
+$lang->dataview->id = 'ID';
+$lang->dataview->name = 'Name';
+$lang->dataview->export = 'Export';
+
+$lang->dataview->common = 'Data Table';
+$lang->dataview->id = 'ID';
+$lang->dataview->type = 'Type';
+$lang->dataview->name = 'Name';
+$lang->dataview->code = 'Code';
+$lang->dataview->group = 'Group';
+$lang->dataview->view = 'View Name';
+$lang->dataview->desc = 'Description';
+$lang->dataview->data = 'Data';
+$lang->dataview->schema = 'Schema';
+$lang->dataview->details = 'Details';
+$lang->dataview->fieldName = 'Field Name';
+$lang->dataview->fieldType = 'Field Type';
+$lang->dataview->create = 'Create';
+$lang->dataview->browse = 'Browse';
+$lang->dataview->edit = 'Edit';
+$lang->dataview->design = 'Design';
+$lang->dataview->delete = 'Delete';
+$lang->dataview->createPriv = 'Create Custom Table';
+$lang->dataview->browsePriv = 'Browse Data Table';
+$lang->dataview->editPriv = 'Edit Custom Table';
+$lang->dataview->designPriv = 'Design Custom Table';
+$lang->dataview->deletePriv = 'Delete Custom Table';
+$lang->dataview->viewAction = 'View Custom Table';
+$lang->dataview->sql = 'SQL Query';
+$lang->dataview->sqlPlaceholder = 'Please enter a query statement. Only select query is supported.';
+$lang->dataview->query = 'Query';
+$lang->dataview->add = 'Add ';
+$lang->dataview->sqlQuery = 'SQL Query';
+$lang->dataview->onlyOne = 'Only allow one query';
+$lang->dataview->empty = 'Please enter a query statement';
+$lang->dataview->allowSelect = 'Only SELECT is allowed';
+$lang->dataview->noStar = "For performance, 'SELECT *' is not allowed";
+$lang->dataview->fieldSettings = "Field Settings";
+$lang->dataview->queryFilters = "Query Filters";
+$lang->dataview->varSettings = "Var Settings";
+$lang->dataview->result = "Query Result";
+$lang->dataview->chart = "Chart";
+$lang->dataview->table = "Table";
+$lang->dataview->save = "Save";
+$lang->dataview->field = "Field";
+$lang->dataview->varError = "Format of var is error";
+$lang->dataview->time = "Time";
+$lang->dataview->confirmDelete = 'Do you want to delete this custom table?';
+$lang->dataview->manageGroup = 'Manage group';
+$lang->dataview->noModule = 'You have no groups.
Manage Now
';
+$lang->dataview->notSelect = 'Please select a data table to dislay.';
+$lang->dataview->existView = "View exist.";
+$lang->dataview->noQueryData = "No Query Data";
+$lang->dataview->builtIn = 'Built-in Data Group';
+$lang->dataview->default = 'Default Group';
+$lang->dataview->relatedTable = 'Table';
+$lang->dataview->relatedField = 'Field';
+$lang->dataview->multilingual = 'After switching the system language, the corresponding name will be displayed';
+$lang->dataview->duplicateField = 'Duplicate field names exist: %s. You are advised to: (1) Modify the * query to a specific field. (2) Use as to alias the field.';
+$lang->dataview->errorField = 'The current field %s is an invalid character. Please [as] it in English or English underscore format.';
+
+$lang->dataview->varFilter = new stdclass();
+$lang->dataview->varFilter->varCode = 'Var Code';
+$lang->dataview->varFilter->varLabel = 'Var Label';
+$lang->dataview->varFilter->default = 'Default';
+$lang->dataview->varFilter->requestType = 'Request Type';
+
+$lang->dataview->varFilter->noticeVarName = 'Var name cannot be empty';
+$lang->dataview->varFilter->noticeRequestType = 'Request type cannot be empty';
+$lang->dataview->varFilter->noticeShowName = 'Show name cannot be empty';
+
+$lang->dataview->varFilter->requestTypeList['input'] = 'Input';
+$lang->dataview->varFilter->requestTypeList['date'] = 'Date';
+$lang->dataview->varFilter->requestTypeList['datetime'] = 'Datetime';
+$lang->dataview->varFilter->requestTypeList['select'] = 'Select';
+
+$lang->dataview->varFilter->selectList['user'] = 'User List';
+$lang->dataview->varFilter->selectList['product'] = $lang->productCommon . ' List';
+$lang->dataview->varFilter->selectList['project'] = 'Project';
+$lang->dataview->varFilter->selectList['execution'] = $lang->executionCommon . ' List';
+$lang->dataview->varFilter->selectList['dept'] = 'Dept List';
+$lang->dataview->varFilter->selectList['project.status'] = 'Project List';
+
+$lang->dataview->objects = array();
+$lang->dataview->objects['product'] = $lang->product->common;
+$lang->dataview->objects['story'] = $lang->story->common;
+$lang->dataview->objects['build'] = $lang->build->common;
+$lang->dataview->objects['productplan'] = 'plan';
+$lang->dataview->objects['release'] = $lang->release->common;
+$lang->dataview->objects['bug'] = $lang->bug->common;
+$lang->dataview->objects['project'] = $lang->project->common;
+$lang->dataview->objects['task'] = $lang->task->common;
+$lang->dataview->objects['team'] = 'team';
+$lang->dataview->objects['execution'] = $lang->execution->common;
+$lang->dataview->objects['testtask'] = 'testtask';
+$lang->dataview->objects['testrun'] = 'testrun';
+$lang->dataview->objects['testcase'] = 'testcase';
+$lang->dataview->objects['testresult'] = 'result';
+$lang->dataview->objects['casemodule'] = 'case module';
+$lang->dataview->objects['action'] = 'Action';
+$lang->dataview->objects['effort'] = 'Effort';
+
+$lang->dataview->tables = array();
+$lang->dataview->tables['build'] = array('name' => 'Build', 'desc' => '');
+$lang->dataview->tables['product'] = array('name' => 'Product', 'desc' => '');
+$lang->dataview->tables['productplan'] = array('name' => 'Product Plan', 'desc' => '');
+$lang->dataview->tables['release'] = array('name' => 'Release', 'desc' => '');
+$lang->dataview->tables['project'] = array('name' => 'Project', 'desc' => '');
+$lang->dataview->tables['execution'] = array('name' => 'Execution', 'desc' => '');
+$lang->dataview->tables['task'] = array('name' => 'Task', 'desc' => '');
+//$lang->dataview->tables['team'] = array('name' => 'Team', 'desc' => '');
+$lang->dataview->tables['bug'] = array('name' => 'Bug', 'desc' => 'pri, severity, project, product...');
+$lang->dataview->tables['bugbuild'] = array('name' => 'Build Bug', 'desc' => 'pri, severity, project, build...');
+$lang->dataview->tables['story'] = array('name' => 'Story', 'desc' => '');
+$lang->dataview->tables['testcase'] = array('name' => 'Testcase', 'desc' => '');
+$lang->dataview->tables['casestep'] = array('name' => 'Testcase step', 'desc' => '');
+$lang->dataview->tables['testtask'] = array('name' => 'Testtask', 'desc' => '');
+$lang->dataview->tables['testrun'] = array('name' => 'Test run', 'desc' => '');
+$lang->dataview->tables['testresult'] = array('name' => 'Test result', 'desc' => '');
+
+$lang->dataview->typeList = array();
+$lang->dataview->typeList['view'] = 'Custom Table';
+$lang->dataview->typeList['table'] = 'Origin Table';
+
+$lang->dataview->fieldTypeList = array();
+$lang->dataview->fieldTypeList['string'] = 'String';
+$lang->dataview->fieldTypeList['option'] = 'Option';
+$lang->dataview->fieldTypeList['date'] = 'Date';
+
+$lang->dataview->groupList['my'] = 'Dashboard';
+$lang->dataview->groupList['program'] = 'Program';
+$lang->dataview->groupList['product'] = $lang->productCommon;
+$lang->dataview->groupList['project'] = 'project';
+$lang->dataview->groupList['execution'] = $lang->execution->common;
+$lang->dataview->groupList['kanban'] = 'Kanban';
+$lang->dataview->groupList['qa'] = 'Test';
+$lang->dataview->groupList['doc'] = 'Doc';
+$lang->dataview->groupList['assetlib'] = 'Assetlib';
+$lang->dataview->groupList['report'] = 'Report';
+$lang->dataview->groupList['company'] = 'Company';
+$lang->dataview->groupList['repo'] = 'CI';
+$lang->dataview->groupList['api'] = 'API';
+$lang->dataview->groupList['message'] = 'Message';
+$lang->dataview->groupList['search'] = 'Search';
+$lang->dataview->groupList['admin'] = 'Admin';
+$lang->dataview->groupList['system'] = 'System';
+$lang->dataview->groupList['other'] = 'Others';
+
+$lang->dataview->secondGroup['story'] = 'Story';
+$lang->dataview->secondGroup['review'] = 'Review';
+$lang->dataview->secondGroup['projectContent'] = 'Project content';
+$lang->dataview->secondGroup['measure'] = 'Measure';
+$lang->dataview->secondGroup['user'] = 'User';
+
+$lang->dataview->error = new stdclass();
+$lang->dataview->error->canNotDesign = 'This table is already in use and cannot be redesigned.';
+$lang->dataview->error->canNotDelete = 'This table is already in use and cannot be deleted.';
+$lang->dataview->error->warningDesign = 'The custom table has been referenced. Design will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+$lang->dataview->error->warningDelete = 'The custom table has been referenced. Delete will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+
+$lang->dataview->querying = 'Querying, please wait...';
+$lang->dataview->queryResult = 'Query data in row %s x %s column';
+$lang->dataview->viewResult = '%s x %s rows of data';
+$lang->dataview->recTotalTip = ' %s items in total';
+$lang->dataview->recPerPageTip = " %s items per page ";
+
+$lang->dataview->projectStatusList['done'] = 'Done';
+$lang->dataview->projectStatusList['cancel'] = 'Cancel';
+$lang->dataview->projectStatusList['pause'] = 'Pause';
diff --git a/module/dataview/lang/fr.php b/module/dataview/lang/fr.php
index e5dce9a094..38e9f04927 100644
--- a/module/dataview/lang/fr.php
+++ b/module/dataview/lang/fr.php
@@ -1,3 +1,170 @@
dataview->id = 'ID';
-$lang->dataview->name = 'Name';
+$lang->dataview->id = 'ID';
+$lang->dataview->name = 'Name';
+$lang->dataview->export = 'Export';
+
+$lang->dataview->common = 'Data Table';
+$lang->dataview->id = 'ID';
+$lang->dataview->type = 'Type';
+$lang->dataview->name = 'Name';
+$lang->dataview->code = 'Code';
+$lang->dataview->group = 'Group';
+$lang->dataview->view = 'View Name';
+$lang->dataview->desc = 'Description';
+$lang->dataview->data = 'Data';
+$lang->dataview->schema = 'Schema';
+$lang->dataview->details = 'Details';
+$lang->dataview->fieldName = 'Field Name';
+$lang->dataview->fieldType = 'Field Type';
+$lang->dataview->create = 'Create';
+$lang->dataview->browse = 'Browse';
+$lang->dataview->edit = 'Edit';
+$lang->dataview->design = 'Design';
+$lang->dataview->delete = 'Delete';
+$lang->dataview->createPriv = 'Create Custom Table';
+$lang->dataview->browsePriv = 'Browse Data Table';
+$lang->dataview->editPriv = 'Edit Custom Table';
+$lang->dataview->designPriv = 'Design Custom Table';
+$lang->dataview->deletePriv = 'Delete Custom Table';
+$lang->dataview->viewAction = 'View Custom Table';
+$lang->dataview->sql = 'SQL Query';
+$lang->dataview->sqlPlaceholder = 'Please enter a query statement. Only select query is supported.';
+$lang->dataview->query = 'Query';
+$lang->dataview->add = 'Add ';
+$lang->dataview->sqlQuery = 'SQL Query';
+$lang->dataview->onlyOne = 'Only allow one query';
+$lang->dataview->empty = 'Please enter a query statement';
+$lang->dataview->allowSelect = 'Only SELECT is allowed';
+$lang->dataview->noStar = "For performance, 'SELECT *' is not allowed";
+$lang->dataview->fieldSettings = "Field Settings";
+$lang->dataview->queryFilters = "Query Filters";
+$lang->dataview->varSettings = "Var Settings";
+$lang->dataview->result = "Query Result";
+$lang->dataview->chart = "Chart";
+$lang->dataview->table = "Table";
+$lang->dataview->save = "Save";
+$lang->dataview->field = "Field";
+$lang->dataview->varError = "Format of var is error";
+$lang->dataview->time = "Time";
+$lang->dataview->confirmDelete = 'Do you want to delete this custom table?';
+$lang->dataview->manageGroup = 'Manage group';
+$lang->dataview->noModule = 'You have no groups.
Manage Now
';
+$lang->dataview->notSelect = 'Please select a data table to dislay.';
+$lang->dataview->existView = "View exist.";
+$lang->dataview->noQueryData = "No Query Data";
+$lang->dataview->builtIn = 'Built-in Data Group';
+$lang->dataview->default = 'Default Group';
+$lang->dataview->relatedTable = 'Table';
+$lang->dataview->relatedField = 'Field';
+$lang->dataview->multilingual = 'After switching the system language, the corresponding name will be displayed';
+$lang->dataview->duplicateField = 'Duplicate field names exist: %s. You are advised to: (1) Modify the * query to a specific field. (2) Use as to alias the field.';
+$lang->dataview->errorField = 'The current field %s is an invalid character. Please [as] it in English or English underscore format.';
+
+$lang->dataview->varFilter = new stdclass();
+$lang->dataview->varFilter->varCode = 'Var Code';
+$lang->dataview->varFilter->varLabel = 'Var Label';
+$lang->dataview->varFilter->default = 'Default';
+$lang->dataview->varFilter->requestType = 'Request Type';
+
+$lang->dataview->varFilter->noticeVarName = 'Var name cannot be empty';
+$lang->dataview->varFilter->noticeRequestType = 'Request type cannot be empty';
+$lang->dataview->varFilter->noticeShowName = 'Show name cannot be empty';
+
+$lang->dataview->varFilter->requestTypeList['input'] = 'Input';
+$lang->dataview->varFilter->requestTypeList['date'] = 'Date';
+$lang->dataview->varFilter->requestTypeList['datetime'] = 'Datetime';
+$lang->dataview->varFilter->requestTypeList['select'] = 'Select';
+
+$lang->dataview->varFilter->selectList['user'] = 'User List';
+$lang->dataview->varFilter->selectList['product'] = $lang->productCommon . ' List';
+$lang->dataview->varFilter->selectList['project'] = 'Project';
+$lang->dataview->varFilter->selectList['execution'] = $lang->executionCommon . ' List';
+$lang->dataview->varFilter->selectList['dept'] = 'Dept List';
+$lang->dataview->varFilter->selectList['project.status'] = 'Project List';
+
+$lang->dataview->objects = array();
+$lang->dataview->objects['product'] = $lang->product->common;
+$lang->dataview->objects['story'] = $lang->story->common;
+$lang->dataview->objects['build'] = $lang->build->common;
+$lang->dataview->objects['productplan'] = 'plan';
+$lang->dataview->objects['release'] = $lang->release->common;
+$lang->dataview->objects['bug'] = $lang->bug->common;
+$lang->dataview->objects['project'] = $lang->project->common;
+$lang->dataview->objects['task'] = $lang->task->common;
+$lang->dataview->objects['team'] = 'team';
+$lang->dataview->objects['execution'] = $lang->execution->common;
+$lang->dataview->objects['testtask'] = 'testtask';
+$lang->dataview->objects['testrun'] = 'testrun';
+$lang->dataview->objects['testcase'] = 'testcase';
+$lang->dataview->objects['testresult'] = 'result';
+$lang->dataview->objects['casemodule'] = 'case module';
+$lang->dataview->objects['action'] = 'Action';
+$lang->dataview->objects['effort'] = 'Effort';
+
+$lang->dataview->tables = array();
+$lang->dataview->tables['build'] = array('name' => 'Build', 'desc' => '');
+$lang->dataview->tables['product'] = array('name' => 'Product', 'desc' => '');
+$lang->dataview->tables['productplan'] = array('name' => 'Product Plan', 'desc' => '');
+$lang->dataview->tables['release'] = array('name' => 'Release', 'desc' => '');
+$lang->dataview->tables['project'] = array('name' => 'Project', 'desc' => '');
+$lang->dataview->tables['execution'] = array('name' => 'Execution', 'desc' => '');
+$lang->dataview->tables['task'] = array('name' => 'Task', 'desc' => '');
+//$lang->dataview->tables['team'] = array('name' => 'Team', 'desc' => '');
+$lang->dataview->tables['bug'] = array('name' => 'Bug', 'desc' => 'pri, severity, project, product...');
+$lang->dataview->tables['bugbuild'] = array('name' => 'Build Bug', 'desc' => 'pri, severity, project, build...');
+$lang->dataview->tables['story'] = array('name' => 'Story', 'desc' => '');
+$lang->dataview->tables['testcase'] = array('name' => 'Testcase', 'desc' => '');
+$lang->dataview->tables['casestep'] = array('name' => 'Testcase step', 'desc' => '');
+$lang->dataview->tables['testtask'] = array('name' => 'Testtask', 'desc' => '');
+$lang->dataview->tables['testrun'] = array('name' => 'Test run', 'desc' => '');
+$lang->dataview->tables['testresult'] = array('name' => 'Test result', 'desc' => '');
+
+$lang->dataview->typeList = array();
+$lang->dataview->typeList['view'] = 'Custom Table';
+$lang->dataview->typeList['table'] = 'Origin Table';
+
+$lang->dataview->fieldTypeList = array();
+$lang->dataview->fieldTypeList['string'] = 'String';
+$lang->dataview->fieldTypeList['option'] = 'Option';
+$lang->dataview->fieldTypeList['date'] = 'Date';
+
+$lang->dataview->groupList['my'] = 'Dashboard';
+$lang->dataview->groupList['program'] = 'Program';
+$lang->dataview->groupList['product'] = $lang->productCommon;
+$lang->dataview->groupList['project'] = 'project';
+$lang->dataview->groupList['execution'] = $lang->execution->common;
+$lang->dataview->groupList['kanban'] = 'Kanban';
+$lang->dataview->groupList['qa'] = 'Test';
+$lang->dataview->groupList['doc'] = 'Doc';
+$lang->dataview->groupList['assetlib'] = 'Assetlib';
+$lang->dataview->groupList['report'] = 'Report';
+$lang->dataview->groupList['company'] = 'Company';
+$lang->dataview->groupList['repo'] = 'CI';
+$lang->dataview->groupList['api'] = 'API';
+$lang->dataview->groupList['message'] = 'Message';
+$lang->dataview->groupList['search'] = 'Search';
+$lang->dataview->groupList['admin'] = 'Admin';
+$lang->dataview->groupList['system'] = 'System';
+$lang->dataview->groupList['other'] = 'Others';
+
+$lang->dataview->secondGroup['story'] = 'Story';
+$lang->dataview->secondGroup['review'] = 'Review';
+$lang->dataview->secondGroup['projectContent'] = 'Project content';
+$lang->dataview->secondGroup['measure'] = 'Measure';
+$lang->dataview->secondGroup['user'] = 'User';
+
+$lang->dataview->error = new stdclass();
+$lang->dataview->error->canNotDesign = 'This table is already in use and cannot be redesigned.';
+$lang->dataview->error->canNotDelete = 'This table is already in use and cannot be deleted.';
+$lang->dataview->error->warningDesign = 'The custom table has been referenced. Design will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+$lang->dataview->error->warningDelete = 'The custom table has been referenced. Delete will cause the chart, pivot, and screen to fail to be displayed. Do you want to continue?';
+
+$lang->dataview->querying = 'Querying, please wait...';
+$lang->dataview->queryResult = 'Query data in row %s x %s column';
+$lang->dataview->viewResult = '%s x %s rows of data';
+$lang->dataview->recTotalTip = ' %s items in total';
+$lang->dataview->recPerPageTip = " %s items per page ";
+
+$lang->dataview->projectStatusList['done'] = 'Done';
+$lang->dataview->projectStatusList['cancel'] = 'Cancel';
+$lang->dataview->projectStatusList['pause'] = 'Pause';
diff --git a/module/dataview/lang/zh-cn.php b/module/dataview/lang/zh-cn.php
index f4e3a0a39f..a113cd487c 100644
--- a/module/dataview/lang/zh-cn.php
+++ b/module/dataview/lang/zh-cn.php
@@ -1,3 +1,170 @@
dataview->id = '编号';
-$lang->dataview->name = '名称';
+$lang->dataview->id = '编号';
+$lang->dataview->name = '名称';
+$lang->dataview->export = '导出';
+
+$lang->dataview->common = '数据表';
+$lang->dataview->id = '编号';
+$lang->dataview->type = '类型';
+$lang->dataview->name = '名称';
+$lang->dataview->code = '代号';
+$lang->dataview->group = '所属分组';
+$lang->dataview->view = '视图名称';
+$lang->dataview->desc = '描述';
+$lang->dataview->data = '数据';
+$lang->dataview->schema = '字段';
+$lang->dataview->details = '详情';
+$lang->dataview->fieldName = '字段名称';
+$lang->dataview->fieldType = '字段类型';
+$lang->dataview->create = '创建';
+$lang->dataview->browse = '查看';
+$lang->dataview->edit = '编辑';
+$lang->dataview->design = '设计';
+$lang->dataview->delete = '删除';
+$lang->dataview->createPriv = '创建中间表';
+$lang->dataview->browsePriv = '查看数据表';
+$lang->dataview->editPriv = '编辑中间表';
+$lang->dataview->designPriv = '设计中间表';
+$lang->dataview->deletePriv = '删除中间表';
+$lang->dataview->viewAction = '查看数据集';
+$lang->dataview->sql = '查询语句';
+$lang->dataview->sqlPlaceholder = '请输入查询语句,只支持select查询';
+$lang->dataview->query = '查询';
+$lang->dataview->add = '添加';
+$lang->dataview->sqlQuery = 'SQL语句查询';
+$lang->dataview->onlyOne = '只能输入一条SQL语句';
+$lang->dataview->empty = '请输入一条正确的SQL语句';
+$lang->dataview->allowSelect = '只允许SELECT查询';
+$lang->dataview->noStar = "考虑性能,不允许使用 'SELECT *' 查询";
+$lang->dataview->fieldSettings = "设置字段名";
+$lang->dataview->queryFilters = "查询筛选器";
+$lang->dataview->varSettings = "变量设置";
+$lang->dataview->result = "查询结果";
+$lang->dataview->chart = "图表";
+$lang->dataview->table = "透视表";
+$lang->dataview->save = "保存";
+$lang->dataview->field = "字段";
+$lang->dataview->varError = "变量格式错误";
+$lang->dataview->time = "时间";
+$lang->dataview->confirmDelete = '您确认要删除吗?';
+$lang->dataview->manageGroup = '维护分组';
+$lang->dataview->noModule = "您现在还没有分组信息
请维护分组
";
+$lang->dataview->notSelect = "请选择数据表进行展示";
+$lang->dataview->existView = "视图已存在";
+$lang->dataview->noQueryData = "暂无查询结果";
+$lang->dataview->builtIn = '内置数据分组';
+$lang->dataview->default = '默认分组';
+$lang->dataview->relatedTable = '所属表';
+$lang->dataview->relatedField = '对应字段';
+$lang->dataview->multilingual = '切换系统语言项后,将显示对应名称';
+$lang->dataview->duplicateField = '存在重复的字段名: %s。建议您:(1)修改 * 查询为具体的字段。(2)使用 as 为字段设置别名。';
+$lang->dataview->errorField = '当前字段%s为非法字符,请[as]成英文或英文下划线的格式。';
+
+$lang->dataview->varFilter = new stdclass();
+$lang->dataview->varFilter->varCode = '变量代号';
+$lang->dataview->varFilter->varLabel = '变量标签';
+$lang->dataview->varFilter->default = '默认值';
+$lang->dataview->varFilter->requestType = '筛选器类型';
+
+$lang->dataview->varFilter->noticeVarName = '变量名称不能为空';
+$lang->dataview->varFilter->noticeRequestType = '筛选器类型不能为空';
+$lang->dataview->varFilter->noticeShowName = '显示名称不能为空';
+
+$lang->dataview->varFilter->requestTypeList['input'] = '文本框';
+$lang->dataview->varFilter->requestTypeList['date'] = '日期选择';
+$lang->dataview->varFilter->requestTypeList['datetime'] = '时间选择';
+$lang->dataview->varFilter->requestTypeList['select'] = '下拉选择';
+
+$lang->dataview->varFilter->selectList['user'] = '用户列表';
+$lang->dataview->varFilter->selectList['product'] = $lang->productCommon . '列表';
+$lang->dataview->varFilter->selectList['project'] = '项目列表';
+$lang->dataview->varFilter->selectList['execution'] = $lang->executionCommon . '列表';
+$lang->dataview->varFilter->selectList['dept'] = '部门列表';
+$lang->dataview->varFilter->selectList['project.status'] = '项目状态列表';
+
+$lang->dataview->objects = array();
+$lang->dataview->objects['product'] = $lang->product->common;
+$lang->dataview->objects['story'] = $lang->story->common;
+$lang->dataview->objects['build'] = $lang->build->common;
+$lang->dataview->objects['productplan'] = '产品计划';
+$lang->dataview->objects['release'] = $lang->release->common;
+$lang->dataview->objects['bug'] = $lang->bug->common;
+$lang->dataview->objects['project'] = $lang->project->common;
+$lang->dataview->objects['task'] = $lang->task->common;
+$lang->dataview->objects['team'] = '团队';
+$lang->dataview->objects['execution'] = '阶段';
+$lang->dataview->objects['testtask'] = '测试单';
+$lang->dataview->objects['testrun'] = '测试执行';
+$lang->dataview->objects['testcase'] = '测试用例';
+$lang->dataview->objects['testresult'] = '执行结果';
+$lang->dataview->objects['casemodule'] = '用例模块';
+$lang->dataview->objects['action'] = '动态';
+$lang->dataview->objects['effort'] = '日志';
+
+$lang->dataview->tables = array();
+$lang->dataview->tables['build'] = array('name' => '版本数据', 'desc' => '包括版本名称、所属产品、所属项目、所属执行、创建人等');
+$lang->dataview->tables['product'] = array('name' => '产品数据', 'desc' => '包括产品名称、所属产品线、所属项目集、产品代号、创建人等');
+$lang->dataview->tables['productplan'] = array('name' => '产品计划数据', 'desc' => '包括计划名称、所属产品、父计划、开始时间、结束时间等');
+$lang->dataview->tables['release'] = array('name' => '产品发布数据', 'desc' => '包括发布名称、所属产品、所属项目、相关版本、创建人等');
+$lang->dataview->tables['project'] = array('name' => '项目数据', 'desc' => '包括项目名称、项目代号、项目模型、开始时间、结束时间等');
+$lang->dataview->tables['execution'] = array('name' => '执行数据', 'desc' => '包括执行名称、执行代号、所属项目、开始时间、结束时间等');
+$lang->dataview->tables['task'] = array('name' => '任务数据', 'desc' => '包括任务名称、优先级、指派人、所属执行、创建人等');
+//$lang->dataview->tables['team'] = array('name' => '团队数据', 'desc' => '包括所属项目、所属执行、团队成员等');
+$lang->dataview->tables['bug'] = array('name' => 'Bug数据', 'desc' => '包括Bug严重程度、优先级、解决人、所属项目、所属产品等');
+$lang->dataview->tables['bugbuild'] = array('name' => '版本Bug数据', 'desc' => '版本的Bug严重程度、优先级、解决人、所属项目、所属产品等');
+$lang->dataview->tables['story'] = array('name' => '需求数据', 'desc' => '包括需求优先级、创建人、所属产品、状态、阶段等');
+$lang->dataview->tables['testcase'] = array('name' => '用例数据', 'desc' => '包括用例优先级、创建人、所属产品、状态、看护人等');
+$lang->dataview->tables['casestep'] = array('name' => '用例步骤数据', 'desc' => '包括所属用例、用例步骤、期望结果等');
+$lang->dataview->tables['testtask'] = array('name' => '测试单列表', 'desc' => '包括用例优先级、创建人、所属产品、状态等');
+$lang->dataview->tables['testrun'] = array('name' => '测试单用例执行情况', 'desc' => '包括所属测试单、所属用例、用例编号、用例版本、指派人、最后执行人等');
+$lang->dataview->tables['testresult'] = array('name' => '测试单用例每次执行结果', 'desc' => '包括用例编号、用例版本、执行人、执行结果等');
+
+$lang->dataview->typeList = array();
+$lang->dataview->typeList['view'] = '中间表';
+$lang->dataview->typeList['table'] = '原始表';
+
+$lang->dataview->fieldTypeList = array();
+$lang->dataview->fieldTypeList['string'] = '文本';
+$lang->dataview->fieldTypeList['option'] = '选项';
+$lang->dataview->fieldTypeList['date'] = '日期';
+
+$lang->dataview->groupList['my'] = '我的地盘';
+$lang->dataview->groupList['program'] = '项目集';
+$lang->dataview->groupList['product'] = $lang->productCommon;
+$lang->dataview->groupList['project'] = '项目';
+$lang->dataview->groupList['execution'] = $lang->execution->common;
+$lang->dataview->groupList['kanban'] = '看板';
+$lang->dataview->groupList['qa'] = '测试';
+$lang->dataview->groupList['doc'] = '文档';
+$lang->dataview->groupList['assetlib'] = '资产库';
+$lang->dataview->groupList['report'] = '统计';
+$lang->dataview->groupList['company'] = '组织';
+$lang->dataview->groupList['repo'] = '持续集成';
+$lang->dataview->groupList['api'] = 'API';
+$lang->dataview->groupList['message'] = '消息';
+$lang->dataview->groupList['search'] = '搜索';
+$lang->dataview->groupList['admin'] = '后台';
+$lang->dataview->groupList['system'] = '系统';
+$lang->dataview->groupList['other'] = '其他';
+
+$lang->dataview->secondGroup['story'] = '需求';
+$lang->dataview->secondGroup['review'] = '评审';
+$lang->dataview->secondGroup['projectContent'] = '项目内容';
+$lang->dataview->secondGroup['measure'] = '度量';
+$lang->dataview->secondGroup['user'] = '用户';
+
+$lang->dataview->error = new stdclass();
+$lang->dataview->error->canNotDesign = '该中间表已被使用,不能重新设计。';
+$lang->dataview->error->canNotDelete = '该中间表已被使用,不能删除。';
+$lang->dataview->error->warningDesign = '中间表已被引用,设计会导致图表、透视表、大屏无法展示,是否继续?';
+$lang->dataview->error->warningDelete = '中间表已被引用,删除会导致图表、透视表、大屏无法展示,是否继续?';
+
+$lang->dataview->querying = '正在查询,请稍后...';
+$lang->dataview->queryResult = '查询到 %s行 x %s列数据';
+$lang->dataview->viewResult = '共 %s行 x %s列数据';
+$lang->dataview->recTotalTip = '共 %s 项';
+$lang->dataview->recPerPageTip = "每页 %s 项 ";
+
+$lang->dataview->projectStatusList['done'] = '已完成';
+$lang->dataview->projectStatusList['cancel'] = '已取消';
+$lang->dataview->projectStatusList['pause'] = '已暂停';
diff --git a/module/dataview/model.php b/module/dataview/model.php
new file mode 100644
index 0000000000..53520c21b1
--- /dev/null
+++ b/module/dataview/model.php
@@ -0,0 +1,369 @@
+
+ * @package company
+ * @version $Id: model.php 5086 2013-07-10 02:25:22Z wyd621@gmail.com $
+ * @link http://www.zentao.net
+ */
+?>
+loadBIDAO();
+ }
+
+ /**
+ * Get module names.
+ *
+ * @param string $tables
+ * @access public
+ * @return array
+ */
+ public function getModuleNames($tables)
+ {
+ $moduleNames = array();
+ foreach($tables as $table)
+ {
+ if(strpos($table, $this->config->db->prefix) === false) continue;
+
+ $module = str_replace($this->config->db->prefix, '', $table);
+ if(!preg_match("/^[a-zA-Z]+$/", $module)) continue;
+
+ if($module == 'case') $module = 'testcase';
+ if($module == 'module') $module = 'tree';
+
+ /* Code for workflow.*/
+ if(strpos($module, 'flow_') !== false)
+ {
+ $moduleName = substr($module, 5);
+
+ $flowFields = $this->loadModel('workflowfield')->getFieldPairs($moduleName);
+ $this->lang->$moduleName = new stdclass();
+ foreach($flowFields as $flowField => $fieldName)
+ {
+ if(!$flowField) continue;
+ $this->lang->$moduleName->$flowField = $fieldName;
+ }
+
+ $moduleNames[$table] = $module;
+ }
+ else
+ {
+ if($this->app->loadLang($module))
+ {
+ if($module == 'project') $this->lang->project->statusList += $this->lang->dataview->projectStatusList;
+ $moduleNames[$table] = $module;
+ }
+ }
+ }
+
+ return $moduleNames;
+ }
+
+ /**
+ * Get alias names.
+ *
+ * @param object $statement
+ * @param array $moduleNames
+ * @access public
+ * @return array
+ */
+ public function getAliasNames($statement, $moduleNames)
+ {
+ $aliasNames = array();
+ if(isset($statement->from))
+ {
+ foreach($statement->from as $from)
+ {
+ if(isset($moduleNames[$from->table]))
+ {
+ $aliasNames[$from->alias] = $from->table;
+ }
+ }
+ }
+ if(isset($statement->join))
+ {
+ foreach($statement->join as $join)
+ {
+ if(isset($moduleNames[$join->expr->table]))
+ {
+ $aliasNames[$join->expr->alias] = $join->expr->table;
+ }
+ }
+ }
+
+ return $aliasNames;
+ }
+
+ /**
+ * Merge fields.
+ *
+ * @param array $dataFields
+ * @param array $sqlFields
+ * @param array $moduleNames
+ * @param array $aliasNames
+ * @access public
+ * @return void
+ */
+ public function mergeFields($dataFields, $sqlFields, $moduleNames, $aliasNames = array())
+ {
+ $mergeFields = array();
+ $relatedObject = array();
+ foreach($dataFields as $field)
+ {
+ $mergeFields[$field] = $field;
+ $relatedObject[$field] = current($moduleNames);
+
+ /* Such as $sqlFields['id'] = zt_task.id. */
+ if(isset($sqlFields[$field]) and strrpos($sqlFields[$field], '.') !== false)
+ {
+ $sqlField = $sqlFields[$field];
+ $table = substr($sqlField, 0, strrpos($sqlField, '.'));
+ $fieldName = substr($sqlField, strrpos($sqlField, '.') + 1);
+
+ if(isset($moduleNames[$table]))
+ {
+ $moduleName = $moduleNames[$table];
+ if(strpos($moduleName, 'flow_') !== false) $moduleName = substr($moduleName, 5);
+ $mergeFields[$field] = isset($this->lang->$moduleName->$fieldName) ? $this->lang->$moduleName->$fieldName : $field;
+ $relatedObject[$field] = $moduleName;
+ continue;
+ }
+ elseif(isset($aliasNames[$table]))
+ {
+ $moduleName = $aliasNames[$table];
+ if(strpos($moduleName, 'flow_') !== false) $moduleName = substr($moduleName, 5);
+ $mergeFields[$field] = isset($this->lang->$moduleName->$fieldName) ? $this->lang->$moduleName->$fieldName : $field;
+ $relatedObject[$field] = $moduleName;
+ continue;
+ }
+ }
+
+ if(strpos(join(',', $sqlFields), '.*') !== false)
+ {
+ /* Such as $sqlFields['zt_task.*'] = zt_task.*. */
+ $existField = false;
+ foreach($sqlFields as $sqlField)
+ {
+ if(strrpos($sqlField, '.*') !== false)
+ {
+ $table = substr($sqlField, 0, strrpos($sqlField, '.'));
+ if(isset($moduleNames[$table]))
+ {
+ $moduleName = $moduleNames[$table];
+ if(strpos($moduleName, 'flow_') !== false) $moduleName = substr($moduleName, 5);
+ $mergeFields[$field] = isset($this->lang->$moduleName->$field) ? $this->lang->$moduleName->$field : $field;
+ $relatedObject[$field] = $moduleName;
+ $existField = true;
+ break;
+ }
+ }
+ }
+ if($existField) continue;
+ }
+
+ foreach($moduleNames as $table => $moduleName)
+ {
+ if(strpos($moduleName, 'flow_') !== false) $moduleName = substr($moduleName, 5);
+
+ if(isset($this->lang->$moduleName) and isset($this->lang->$moduleName->$field))
+ {
+ $mergeFields[$field] = $this->lang->$moduleName->$field;
+ $relatedObject[$field] = $moduleName;
+ break;
+ }
+ $mergeFields[$field] = $field;
+ $relatedObject[$field] = $moduleName;
+ }
+ }
+
+ foreach($mergeFields as $fieldName => $fieldValue)
+ {
+ if(empty($fieldValue)) $mergeFields[$fieldName] = $fieldName;
+ }
+
+ foreach($mergeFields as $field => $name) $mergeFields[$field] = $this->replace4Workflow($name);
+ return array($mergeFields, $relatedObject);
+ }
+
+ /**
+ * Get table data.
+ *
+ * @param string $sql
+ * @access public
+ * @return array
+ */
+ public function getColumns($sql)
+ {
+ $columns = $this->dao->getColumns($sql);
+
+ $columnTypes = new stdclass();
+ foreach($columns as $column)
+ {
+ $field = $column['name'];
+ $nativeType = $column['native_type'];
+ $type = $this->config->dataview->columnTypes->$nativeType;
+
+ if(isset($columnTypes->$field)) $field = $column['table'] . $field;
+ $columnTypes->$field = $type;
+ }
+
+ return $columnTypes;
+ }
+
+ /**
+ * Check that the column of an sql query is unique.
+ *
+ * @param string $sql
+ * @param bool $repeat
+ * @access public
+ * @return bool
+ */
+ public function checkUniColumn($sql, $repeat = false)
+ {
+ $columns = $this->dao->getColumns($sql);
+
+ $isUnique = true;
+ $columnList = array();
+ $repeatColumn = array();
+ foreach($columns as $column)
+ {
+ $field = $column['name'];
+ if(isset($columnTypes[$field]))
+ {
+ $isUnique = false;
+ $repeatColumn[$field] = $field;
+ }
+
+ $columnTypes[$field] = $field;
+ }
+
+ if($repeat) return array($isUnique, $repeatColumn);
+ return $isUnique;
+ }
+
+ /**
+ * Get type options
+ *
+ * @param string $objectName
+ * @access public
+ * @return array
+ */
+ public function getTypeOptions($objectName)
+ {
+ $schema = $this->includeTable($objectName);
+ if(empty($schema)) return array();
+
+ $options = array();
+ foreach($schema->fields as $key => $field)
+ {
+ //if($field['type'] == 'object') continue;
+ $options[$key] = $field;
+ }
+ return $options;
+ }
+
+ /**
+ * Replace title for workflow.
+ *
+ * @param string $title
+ * @access public
+ * @return string
+ */
+ public function replace4Workflow($title)
+ {
+ $clientLang = $this->app->getClientLang();
+
+ $productCommonList = isset($this->config->productCommonList[$clientLang]) ? $this->config->productCommonList[$clientLang] : $this->config->productCommonList['en'];
+ $projectCommonList = isset($this->config->projectCommonList[$clientLang]) ? $this->config->projectCommonList[$clientLang] : $this->config->projectCommonList['en'];
+
+ $productCommon = $productCommonList[0];
+ $projectCommon = $projectCommonList[0];
+
+ if(strpos($title, strtolower($productCommon)) !== false) $title = str_replace(strtolower($productCommon), strtolower($this->lang->productCommon), $title);
+ if(strpos($title, $productCommon) !== false) $title = str_replace($productCommon, $this->lang->productCommon, $title);
+
+ return $title;
+ }
+
+ /**
+ * Include table
+ *
+ * @param string $table
+ * @access public
+ * @return void
+ */
+ public function includeTable($table)
+ {
+ $path = __DIR__ . DS . 'table' . DS . "$table.php";
+ if(file_exists($path))
+ {
+ include $path;
+ return $schema;
+ }
+
+ $path = $this->app->getExtensionRoot() . 'custom' . DS . 'dataview' . DS . 'table' . DS . "$table.php";
+ if(file_exists($path))
+ {
+ include $path;
+ return $schema;
+ }
+ }
+
+ /**
+ * Gen tree options.
+ *
+ * @param object $tree
+ * @param array $values
+ * @param array $paths
+ * @access public
+ * @return void
+ */
+ public function genTreeOptions(&$moduleTree, $values, $paths)
+ {
+ $path = $paths[0];
+ if(!isset($moduleTree->children))$moduleTree->children = array();
+
+ foreach($moduleTree->children as $child)
+ {
+ if($child->value == $path)
+ {
+ if(count($paths) > 1) return $this->genTreeOptions($child, $values, array_slice($paths, 1));
+ return;
+ }
+ }
+
+ $child = new stdclass();
+ $child->title = $values[$path];
+ $child->value = $path;
+ $moduleTree->children[] = $child;
+ if(count($paths) > 1) return $this->genTreeOptions($child, $values, array_slice($paths, 1));
+ }
+
+ /**
+ * Adjust the action is clickable.
+ *
+ * @param object $dataview
+ * @param string $action
+ * @static
+ * @access public
+ * @return bool
+ */
+ public static function isClickable($dataview, $action)
+ {
+ return true;
+ }
+}
diff --git a/module/dataview/table/action.php b/module/dataview/table/action.php
new file mode 100644
index 0000000000..40aaea58da
--- /dev/null
+++ b/module/dataview/table/action.php
@@ -0,0 +1,14 @@
+app->loadLang('action');
+
+$schema = new stdclass();
+
+$schema->primaryTable = 'action';
+
+$schema->tables = array();
+$schema->tables['action'] = 'zt_action';
+
+$schema->fields = array();
+$schema->fields['id'] = array('type' => 'number', 'name' => $this->lang->action->id);
+$schema->fields['actor'] = array('type' => 'user', 'name' => $this->lang->action->actor);
+
diff --git a/module/dataview/table/effort.php b/module/dataview/table/effort.php
new file mode 100644
index 0000000000..ae789aa3e0
--- /dev/null
+++ b/module/dataview/table/effort.php
@@ -0,0 +1,14 @@
+app->loadLang('account');
+
+$schema = new stdclass();
+
+$schema->primaryTable = 'effort';
+
+$schema->tables = array();
+$schema->tables['effort'] = 'zt_effort';
+
+$schema->fields = array();
+$schema->fields['id'] = array('type' => 'number', 'name' => $this->lang->account->id);
+$schema->fields['account'] = array('type' => 'user', 'name' => $this->lang->account->name);
+
diff --git a/module/dataview/view/browse.html.php b/module/dataview/view/browse.html.php
new file mode 100644
index 0000000000..0942c62fce
--- /dev/null
+++ b/module/dataview/view/browse.html.php
@@ -0,0 +1,239 @@
+
+ * @package dataview
+ * @version $Id: browse.html.php 4129 2022-11-14 14:42:12 $
+ * @link http://www.zentao.net
+ */
+?>
+getModuleRoot() . 'common/view/header.html.php';?>
+
+
+
+
+dataview->error->warningDesign);?>
+dataview->viewResult);?>
+
+
+
+
+dataview->recTotalTip);?>
+dataview->recPerPageTip);?>
+
+
+
+
+
+ createLink('dataview', 'export', "type=$type&table=$selectedTable", '', true), " {$lang->dataview->export}", '', "class='iframe btn btn-link dataview-export'");?>
+
+ createLink('dataview', 'create'), "" . $lang->dataview->create . "", '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
+
+
+
+
$table";?>
+
+
+ dataview->design;
+ $editTitle = $lang->dataview->edit;
+ $deleteTitle = $lang->dataview->delete;
+
+ if(common::hasPriv('dataview', 'query') and $type == 'view') echo html::a('#', " {$lang->dataview->design}", '', "class='query-view' title='{$designTitle}'");
+ if(common::hasPriv('dataview', 'edit') and $type == 'view') echo html::a($this->createLink('dataview', 'edit', "id=$selectedTable", '', true), " {$lang->dataview->edit}", '', "class='iframe' title='{$editTitle}' data-width='480'");
+ if(common::hasPriv('dataview', 'delete') and $type == 'view') echo html::a($this->createLink('dataview', 'delete', "id=$selectedTable"), " {$lang->dataview->delete}", 'hiddenwin', "class='query-delete' title='{$deleteTitle}'");
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $field):?>
+ fieldSettings->$key->name) ? $dataview->fieldSettings->$key->name : $key;
+ if(!empty($dataview->langs))
+ {
+ $langs = json_decode($dataview->langs, true);
+ if(!empty($langs)) $fieldName = $langs[$key][$clientLang] ? $langs[$key][$clientLang] : $fieldName;
+ }
+ ?>
+ |
+
+
+
+
+
+ ';?>
+ $field):?>
+ $key) ? $value->$key : 'null';?>
+ |
+
+ ';?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | dev->fields['id']?> |
+ dev->fields['name']?> |
+ config->db->prefix) !== false) echo "{$lang->dev->fields['desc']} | "?>
+ dev->fields['type']?> |
+ dev->fields['length']?> |
+ dev->fields['null']?> |
+
+
+
+
+ $field):?>
+
+ |
+ |
+ config->db->prefix) !== false) echo "{$field['name']} | "?>
+ |
+ '> |
+ '> |
+
+
+
+
+
+
+
+
+
+
+
+ | dataview->name;?> |
+ name;?> |
+
+
+ | dataview->code;?> |
+ code;?> |
+
+
+ | dataview->view;?> |
+ sql) ? '' : $dataview->view;?> |
+
+
+ | dataview->group;?> |
+ group);?> |
+
+
+ | dataview->sql;?> |
+ sql;?> |
+
+
+
+
+
+
+
+
+
+
+
+
dataview->notSelect;?>
+
+
+
+
+
+
+app->getModuleRoot() . 'common/view/footer.html.php';?>
diff --git a/module/dataview/view/create.html.php b/module/dataview/view/create.html.php
new file mode 100644
index 0000000000..5653584120
--- /dev/null
+++ b/module/dataview/view/create.html.php
@@ -0,0 +1,37 @@
+getModuleRoot() . 'common/view/header.html.php';?>
+
+
+getModuleRoot() . 'common/view/footer.html.php';?>
diff --git a/module/dataview/view/edit.html.php b/module/dataview/view/edit.html.php
new file mode 100644
index 0000000000..dba9cbb2fb
--- /dev/null
+++ b/module/dataview/view/edit.html.php
@@ -0,0 +1,39 @@
+getModuleRoot() . 'common/view/header.html.php';?>
+dataview);?>
+
+
+
+getModuleRoot() . 'common/view/footer.html.php';?>
diff --git a/module/dataview/view/query.html.php b/module/dataview/view/query.html.php
new file mode 100644
index 0000000000..6c8d805f5e
--- /dev/null
+++ b/module/dataview/view/query.html.php
@@ -0,0 +1,45 @@
+getModuleRoot() . 'common/view/header.html.php';?>
+rawMethod);?>
+
+dataview->error->warningDesign);?>
+langs) ? $data->langs : '')?>
+
+
+
+
+
+
+
+
+ rawMethod == 'create') echo html::a($saveLink, ' ' . $lang->save, '', "class='btn btn-primary' id='saveButton'");?>
+ rawMethod != 'create') echo '';?>
+
+
+
+getModuleRoot() . 'common/view/footer.html.php';?>
diff --git a/module/dataview/view/querybase.html.php b/module/dataview/view/querybase.html.php
new file mode 100644
index 0000000000..fb94cc79f0
--- /dev/null
+++ b/module/dataview/view/querybase.html.php
@@ -0,0 +1,174 @@
+dataview);?>
+dataview->queryResult);?>
+app->getClientLang());?>
+app->cookie->theme);?>
+dataview->recTotalTip);?>
+dataview->recPerPageTip);?>
+
+
+
dataview->sqlQuery;?>
+
+
+
+
+
+
+ dataview->result;?>
+
+ app->rawModule == 'dataview' and common::hasPriv('dataview', 'export')):?>
+
+
+
+
+
+
dataview->noQueryData;?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ app->getModuleName() !== 'dataview'):?>
+ |
+ |
+
+
+
+ langs as $key => $name):?>
+
+
+
+
+ |
+
+
+