mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-25 18:20:20 -06:00
Allow configuration of CSV and clipboard formatting of query results. Fixes #2781
This commit is contained in:
parent
2579458091
commit
0c566f132e
BIN
docs/en_US/images/preferences_sql_csv_output.png
Normal file
BIN
docs/en_US/images/preferences_sql_csv_output.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
BIN
docs/en_US/images/preferences_sql_result_grid.png
Normal file
BIN
docs/en_US/images/preferences_sql_result_grid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
@ -48,6 +48,14 @@ Use the *Help* dialog to customize links to support documentation.
|
|||||||
|
|
||||||
Expand the **SQL Editor** node to specify your preferences for the SQL Editor tool.
|
Expand the **SQL Editor** node to specify your preferences for the SQL Editor tool.
|
||||||
|
|
||||||
|
.. image:: images/preferences_sql_csv_output.png
|
||||||
|
|
||||||
|
Use the options in the *CSV Output* dialog to control the CSV output.
|
||||||
|
|
||||||
|
* Use the *CSV field separator* option to specify the field separator for the CSV output.
|
||||||
|
* Use the *CSV quote character* option to specify the quote character for the CSV output.
|
||||||
|
* Use the *CSV quoting* option to specify which type of fields need quoting.
|
||||||
|
|
||||||
.. image:: images/preferences_sql_display.png
|
.. image:: images/preferences_sql_display.png
|
||||||
|
|
||||||
Use the *Display* dialog to specify your preferences for the SQL Editor display.
|
Use the *Display* dialog to specify your preferences for the SQL Editor display.
|
||||||
@ -76,6 +84,14 @@ Use the options in the *Options* dialog to manage modifications to a SQL stateme
|
|||||||
* Move the *Tab size* field to specify the number of spaces per tab in the editor.
|
* Move the *Tab size* field to specify the number of spaces per tab in the editor.
|
||||||
* Move the *Use spaces* switch to the *True* to insert spaces instead of tabs.
|
* Move the *Use spaces* switch to the *True* to insert spaces instead of tabs.
|
||||||
|
|
||||||
|
.. image:: images/preferences_sql_result_grid.png
|
||||||
|
|
||||||
|
Use the options in the *Results grid* dialog to control the copied data from the result grid.
|
||||||
|
|
||||||
|
* Use the *Result copy field separator* option to specify the field separator for copied data.
|
||||||
|
* Use the *Result copy quote character* option to specify the quote character for the copied data.
|
||||||
|
* Use the *Result copy quoting* option to specify which type of fields need quoting.
|
||||||
|
|
||||||
Expand the **Storage** node to specify a maximum file size for uploads.
|
Expand the **Storage** node to specify a maximum file size for uploads.
|
||||||
|
|
||||||
.. image:: images/preferences_storage_options.png
|
.. image:: images/preferences_storage_options.png
|
||||||
|
@ -46,7 +46,8 @@ Hover over an icon to display a tooltip that describes the icon's functionality:
|
|||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
| *Copy* | Click the *Copy* icon to copy the currently selected row. |
|
| *Copy* | Click the *Copy* icon to copy the currently selected row. |
|
||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
| *Paste* | Click the *Paste* icon to paste the content that is currently on the clipboard. |
|
| *Paste* | Click the *Paste* icon to paste the content that is currently on the clipboard. You can control |
|
||||||
|
| | the copied data settings through *Preferences -> SQL Editor -> Results grid* dialogue. |
|
||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
| *Add New Row* | Use the *Add New Row* icon to add a new row in the output panel. |
|
| *Add New Row* | Use the *Add New Row* icon to add a new row in the output panel. |
|
||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
@ -102,7 +103,8 @@ Hover over an icon to display a tooltip that describes the icon's functionality:
|
|||||||
| | the SQL editor panel or the *History* tab. |
|
| | the SQL editor panel or the *History* tab. |
|
||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
| *Download as CSV* | Click the *Download as CSV* icon to download the result set of the current query to a |
|
| *Download as CSV* | Click the *Download as CSV* icon to download the result set of the current query to a |
|
||||||
| | comma-separated list. |
|
| | comma-separated list. You can control the CSV settings through |
|
||||||
|
| | *Preferences -> SQL Editor -> CSV output* dialogue. |
|
||||||
+----------------------+---------------------------------------------------------------------------------------------------+
|
+----------------------+---------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
|
|
||||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
self.assertEqual("'Some-Name','6','some info'",
|
self.assertEqual('"Some-Name"\t"6"\t"some info"',
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _copies_columns(self):
|
def _copies_columns(self):
|
||||||
@ -75,9 +75,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"""'Some-Name'
|
"""\"Some-Name"
|
||||||
'Some-Other-Name'
|
"Some-Other-Name"
|
||||||
'Yet-Another-Name'""",
|
"Yet-Another-Name\"""",
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _copies_row_using_keyboard_shortcut(self):
|
def _copies_row_using_keyboard_shortcut(self):
|
||||||
@ -86,7 +86,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
|
|
||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual("'Some-Name','6','some info'",
|
self.assertEqual('"Some-Name"\t"6"\t"some info"',
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _copies_column_using_keyboard_shortcut(self):
|
def _copies_column_using_keyboard_shortcut(self):
|
||||||
@ -96,9 +96,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"""'Some-Name'
|
"""\"Some-Name"
|
||||||
'Some-Other-Name'
|
"Some-Other-Name"
|
||||||
'Yet-Another-Name'""",
|
"Yet-Another-Name\"""",
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _copies_rectangular_selection(self):
|
def _copies_rectangular_selection(self):
|
||||||
@ -112,8 +112,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
.release(bottom_right_cell).perform()
|
.release(bottom_right_cell).perform()
|
||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual("""'Some-Other-Name','22'
|
self.assertEqual("""\"Some-Other-Name"\t"22"
|
||||||
'Yet-Another-Name','14'""", pyperclip.paste())
|
"Yet-Another-Name"\t"14\"""", pyperclip.paste())
|
||||||
|
|
||||||
def _shift_resizes_rectangular_selection(self):
|
def _shift_resizes_rectangular_selection(self):
|
||||||
pyperclip.copy("old clipboard contents")
|
pyperclip.copy("old clipboard contents")
|
||||||
@ -128,8 +128,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
ActionChains(self.page.driver).key_down(Keys.SHIFT).send_keys(Keys.ARROW_RIGHT).key_up(Keys.SHIFT).perform()
|
ActionChains(self.page.driver).key_down(Keys.SHIFT).send_keys(Keys.ARROW_RIGHT).key_up(Keys.SHIFT).perform()
|
||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual("""'Some-Other-Name','22','some other info'
|
self.assertEqual("""\"Some-Other-Name"\t"22"\t"some other info"
|
||||||
'Yet-Another-Name','14','cool info'""", pyperclip.paste())
|
"Yet-Another-Name"\t"14"\t"cool info\"""", pyperclip.paste())
|
||||||
|
|
||||||
def _shift_resizes_column_selection(self):
|
def _shift_resizes_column_selection(self):
|
||||||
pyperclip.copy("old clipboard contents")
|
pyperclip.copy("old clipboard contents")
|
||||||
@ -141,9 +141,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"""'Some-Name','6'
|
"""\"Some-Name"\t"6"
|
||||||
'Some-Other-Name','22'
|
"Some-Other-Name"\t"22"
|
||||||
'Yet-Another-Name','14'""",
|
"Yet-Another-Name"\t"14\"""",
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _mouseup_outside_grid_still_makes_a_selection(self):
|
def _mouseup_outside_grid_still_makes_a_selection(self):
|
||||||
@ -160,7 +160,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
|||||||
|
|
||||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||||
|
|
||||||
self.assertEqual("'cool info'", pyperclip.paste())
|
self.assertEqual('"cool info"', pyperclip.paste())
|
||||||
|
|
||||||
def after(self):
|
def after(self):
|
||||||
self.page.close_query_tool()
|
self.page.close_query_tool()
|
||||||
|
@ -54,7 +54,7 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||||||
self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
|
self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
self.assertEqual("'Some-Name','6','some info'",
|
self.assertEqual('"Some-Name"\t"6"\t"some info"',
|
||||||
pyperclip.paste())
|
pyperclip.paste())
|
||||||
|
|
||||||
def _test_copies_columns(self):
|
def _test_copies_columns(self):
|
||||||
@ -65,9 +65,9 @@ class QueryToolJourneyTest(BaseFeatureTest):
|
|||||||
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
|
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
|
||||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
self.assertTrue("'Some-Name'" in pyperclip.paste())
|
self.assertTrue('"Some-Name"' in pyperclip.paste())
|
||||||
self.assertTrue("'Some-Other-Name'" in pyperclip.paste())
|
self.assertTrue('"Some-Other-Name"' in pyperclip.paste())
|
||||||
self.assertTrue("'Yet-Another-Name'" in pyperclip.paste())
|
self.assertTrue('"Yet-Another-Name"' in pyperclip.paste())
|
||||||
|
|
||||||
def _test_history_tab(self):
|
def _test_history_tab(self):
|
||||||
self.__clear_query_tool()
|
self.__clear_query_tool()
|
||||||
|
@ -54,10 +54,12 @@ define('pgadmin.preferences', [
|
|||||||
model: PreferenceModel,
|
model: PreferenceModel,
|
||||||
url: url_for('preferences.index'),
|
url: url_for('preferences.index'),
|
||||||
updateAll: function() {
|
updateAll: function() {
|
||||||
|
var cnt = 0;
|
||||||
// We will send only the modified data to the server.
|
// We will send only the modified data to the server.
|
||||||
for (var key in changed) {
|
for (var key in changed) {
|
||||||
this.get(key).save();
|
this.get(key).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}))(null);
|
}))(null);
|
||||||
@ -215,15 +217,23 @@ define('pgadmin.preferences', [
|
|||||||
case 'datetime':
|
case 'datetime':
|
||||||
return 'datetimepicker';
|
return 'datetimepicker';
|
||||||
case 'options':
|
case 'options':
|
||||||
var opts = [];
|
var opts = [],
|
||||||
|
has_value = false;
|
||||||
// Convert the array to SelectControl understandable options.
|
// Convert the array to SelectControl understandable options.
|
||||||
_.each(p.options, function(o) {
|
_.each(p.options, function(o) {
|
||||||
if('label' in o && 'value' in o){
|
if('label' in o && 'value' in o){
|
||||||
opts.push({'label': o.label, 'value': o.value});
|
opts.push({'label': o.label, 'value': o.value});
|
||||||
|
if (o.value == p.value)
|
||||||
|
has_value = true;
|
||||||
} else {
|
} else {
|
||||||
opts.push({'label': o, 'value': o});
|
opts.push({'label': o, 'value': o});
|
||||||
|
if (o == p.value)
|
||||||
|
has_value = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (p.select2 && p.select2.tags == true && p.value && has_value == false){
|
||||||
|
opts.push({'label': p.value, 'value': p.value});
|
||||||
|
}
|
||||||
p.options = opts;
|
p.options = opts;
|
||||||
return 'select2';
|
return 'select2';
|
||||||
case 'multiline':
|
case 'multiline':
|
||||||
@ -389,9 +399,9 @@ define('pgadmin.preferences', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e.button.text == gettext('OK')){
|
if (e.button.text == gettext('OK')){
|
||||||
preferences.updateAll();
|
preferences.updateAll();
|
||||||
// Refresh preferences cache
|
// Refresh preferences cache
|
||||||
pgBrowser.cache_preferences();
|
setTimeout(pgBrowser.cache_preferences(), 2000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
build: function() {
|
build: function() {
|
||||||
|
@ -1723,7 +1723,7 @@
|
|||||||
' <% var option = options[i]; %>',
|
' <% var option = options[i]; %>',
|
||||||
' <option ',
|
' <option ',
|
||||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||||
' value=<%= formatter.fromRaw(option.value) %>',
|
' value=<%- formatter.fromRaw(option.value) %>',
|
||||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||||
|
@ -14,6 +14,7 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
|
|||||||
var selectedRanges = grid.getSelectionModel().getSelectedRanges();
|
var selectedRanges = grid.getSelectionModel().getSelectedRanges();
|
||||||
var dataView = grid.getData();
|
var dataView = grid.getData();
|
||||||
var rows = grid.getSelectedRows();
|
var rows = grid.getSelectedRows();
|
||||||
|
var CSVOptions = grid.CSVOptions;
|
||||||
|
|
||||||
if (RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges)) {
|
if (RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges)) {
|
||||||
self.copied_rows = rows.map(function (rowIndex) {
|
self.copied_rows = rows.map(function (rowIndex) {
|
||||||
@ -24,7 +25,7 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
|
|||||||
self.copied_rows = [];
|
self.copied_rows = [];
|
||||||
setPasteRowButtonEnablement(self.can_edit, false);
|
setPasteRowButtonEnablement(self.can_edit, false);
|
||||||
}
|
}
|
||||||
var csvText = rangeBoundaryNavigator.rangesToCsv(dataView.getItems(), columnDefinitions, selectedRanges);
|
var csvText = rangeBoundaryNavigator.rangesToCsv(dataView.getItems(), columnDefinitions, selectedRanges, CSVOptions);
|
||||||
if (csvText) {
|
if (csvText) {
|
||||||
clipboard.copyTextToClipboard(csvText);
|
clipboard.copyTextToClipboard(csvText);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ function (RangeSelectionHelper) {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
rangesToCsv: function (data, columnDefinitions, selectedRanges) {
|
rangesToCsv: function (data, columnDefinitions, selectedRanges, CSVOptions) {
|
||||||
|
|
||||||
var rowRangeBounds = selectedRanges.map(function (range) {
|
var rowRangeBounds = selectedRanges.map(function (range) {
|
||||||
return [range.fromRow, range.toRow];
|
return [range.fromRow, range.toRow];
|
||||||
@ -70,8 +70,9 @@ function (RangeSelectionHelper) {
|
|||||||
colRangeBounds = this.removeFirstColumn(colRangeBounds);
|
colRangeBounds = this.removeFirstColumn(colRangeBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
var csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions), function (rowData) {
|
var csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions, CSVOptions), function (rowData) {
|
||||||
return rowData.join(',');
|
var field_separator = CSVOptions.field_separator || '\t';
|
||||||
|
return rowData.join(field_separator);
|
||||||
});
|
});
|
||||||
|
|
||||||
return csvRows.join('\n');
|
return csvRows.join('\n');
|
||||||
@ -102,17 +103,30 @@ function (RangeSelectionHelper) {
|
|||||||
return unionedColRanges;
|
return unionedColRanges;
|
||||||
},
|
},
|
||||||
|
|
||||||
csvCell: function (data, columnDefinitions, rowId, colId) {
|
csvCell: function (data, columnDefinitions, CSVOptions, rowId, colId) {
|
||||||
var val = data[rowId][columnDefinitions[colId].field];
|
var val = data[rowId][columnDefinitions[colId].field],
|
||||||
|
quoting = CSVOptions.quoting || 'strings',
|
||||||
|
quote_char = CSVOptions.quote_char || '"';
|
||||||
|
|
||||||
if (val && _.isObject(val)) {
|
if (quoting == 'all') {
|
||||||
val = '\'' + JSON.stringify(val) + '\'';
|
if (val && _.isObject(val)) {
|
||||||
} else if (val && typeof val != 'number' && typeof val != 'boolean') {
|
val = quote_char + JSON.stringify(val) + quote_char;
|
||||||
val = '\'' + val.toString() + '\'';
|
} else if (val) {
|
||||||
} else if (_.isNull(val) || _.isUndefined(val)) {
|
val = quote_char + val.toString() + quote_char;
|
||||||
val = '';
|
} else if (_.isNull(val) || _.isUndefined(val)) {
|
||||||
|
val = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(quoting == 'strings') {
|
||||||
|
if (val && _.isObject(val)) {
|
||||||
|
val = quote_char + JSON.stringify(val) + quote_char;
|
||||||
|
} else if (val && typeof val != 'number' && typeof val != 'boolean') {
|
||||||
|
val = quote_char + val.toString() + quote_char;
|
||||||
|
} else if (_.isNull(val) || _.isUndefined(val)) {
|
||||||
|
val = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ function (copyData, RangeSelectionHelper) {
|
|||||||
var isModifierDown = event.ctrlKey || event.metaKey;
|
var isModifierDown = event.ctrlKey || event.metaKey;
|
||||||
var self = this || window;
|
var self = this || window;
|
||||||
self.slickgrid = args.grid;
|
self.slickgrid = args.grid;
|
||||||
|
self.csvOptions
|
||||||
|
|
||||||
if (isModifierDown && modifiedKey == KEY_C) {
|
if (isModifierDown && modifiedKey == KEY_C) {
|
||||||
copyData.apply(self);
|
copyData.apply(self);
|
||||||
@ -19,4 +20,4 @@ function (copyData, RangeSelectionHelper) {
|
|||||||
RangeSelectionHelper.selectAll(self.slickgrid);
|
RangeSelectionHelper.selectAll(self.slickgrid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -235,6 +235,83 @@ class SqlEditorModule(PgAdminModule):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.csv_quoting = self.preference.register(
|
||||||
|
'CSV_output', 'csv_quoting',
|
||||||
|
gettext("CSV quoting"), 'options', 'strings',
|
||||||
|
category_label=gettext('CSV Output'),
|
||||||
|
options=[{'label': 'None', 'value': 'none'},
|
||||||
|
{'label': 'All', 'value': 'all'},
|
||||||
|
{'label': 'Strings', 'value': 'strings'}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.csv_quote_char = self.preference.register(
|
||||||
|
'CSV_output', 'csv_quote_char',
|
||||||
|
gettext("CSV quote character"), 'options', '"',
|
||||||
|
category_label=gettext('CSV Output'),
|
||||||
|
options=[{'label': '"', 'value': '"'},
|
||||||
|
{'label': '\'', 'value': '\''}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': True
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.csv_field_separator = self.preference.register(
|
||||||
|
'CSV_output', 'csv_field_separator',
|
||||||
|
gettext("CSV field separator"), 'options', ',',
|
||||||
|
category_label=gettext('CSV output'),
|
||||||
|
options=[{'label': ';', 'value': ';'},
|
||||||
|
{'label': ',', 'value': ','},
|
||||||
|
{'label': '|', 'value': '|'},
|
||||||
|
{'label': 'Tab', 'value': '\t'}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': True
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.results_grid_quoting = self.preference.register(
|
||||||
|
'Results_grid', 'results_grid_quoting',
|
||||||
|
gettext("Result copy quoting"), 'options', 'strings',
|
||||||
|
category_label=gettext('Results grid'),
|
||||||
|
options=[{'label': 'None', 'value': 'none'},
|
||||||
|
{'label': 'All', 'value': 'all'},
|
||||||
|
{'label': 'Strings', 'value': 'strings'}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.results_grid_quote_char = self.preference.register(
|
||||||
|
'Results_grid', 'results_grid_quote_char',
|
||||||
|
gettext("Result copy quote character"), 'options', '"',
|
||||||
|
category_label=gettext('Results grid'),
|
||||||
|
options=[{'label': '"', 'value': '"'},
|
||||||
|
{'label': '\'', 'value': '\''}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': True
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.results_grid_field_separator = self.preference.register(
|
||||||
|
'Results_grid', 'results_grid_field_separator',
|
||||||
|
gettext("Result copy field separator"), 'options', '\t',
|
||||||
|
category_label=gettext('Results grid'),
|
||||||
|
options=[{'label': ';', 'value': ';'},
|
||||||
|
{'label': ',', 'value': ','},
|
||||||
|
{'label': '|', 'value': '|'},
|
||||||
|
{'label': 'Tab', 'value': '\t'}],
|
||||||
|
select2={
|
||||||
|
'allowClear': False,
|
||||||
|
'tags': True
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
blueprint = SqlEditorModule(MODULE_NAME, __name__, static_url_path='/static')
|
blueprint = SqlEditorModule(MODULE_NAME, __name__, static_url_path='/static')
|
||||||
|
|
||||||
@ -1634,7 +1711,9 @@ def start_query_download_tool(trans_id):
|
|||||||
r.call_on_close(cleanup)
|
r.call_on_close(cleanup)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
r = Response(gen(), mimetype='text/csv')
|
r = Response(gen(quote=blueprint.csv_quoting.get(),
|
||||||
|
quote_char=blueprint.csv_quote_char.get(),
|
||||||
|
field_separator=blueprint.csv_field_separator.get()), mimetype='text/csv')
|
||||||
|
|
||||||
if 'filename' in data and data['filename'] != "":
|
if 'filename' in data and data['filename'] != "":
|
||||||
filename = data['filename']
|
filename = data['filename']
|
||||||
|
@ -792,6 +792,20 @@ define('tools.querytool', [
|
|||||||
handleQueryOutputKeyboardEvent(event, args);
|
handleQueryOutputKeyboardEvent(event, args);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
var pref_cache = undefined;
|
||||||
|
args.grid.CSVOptions = {};
|
||||||
|
|
||||||
|
if (self.handler.is_new_browser_tab) {
|
||||||
|
pref_cache = window.opener.pgAdmin.Browser.preferences_cache;
|
||||||
|
} else {
|
||||||
|
pref_cache = window.top.pgAdmin.Browser.preferences_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CSV options from preferences cache
|
||||||
|
args.grid.CSVOptions.quoting = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_quoting'}).value;
|
||||||
|
args.grid.CSVOptions.quote_char = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_quote_char'}).value;
|
||||||
|
args.grid.CSVOptions.field_separator = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_field_separator'}).value;
|
||||||
|
|
||||||
handleQueryOutputKeyboardEvent(event, args);
|
handleQueryOutputKeyboardEvent(event, args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1236,7 +1250,20 @@ define('tools.querytool', [
|
|||||||
|
|
||||||
// Callback function for copy button click.
|
// Callback function for copy button click.
|
||||||
on_copy_row: function () {
|
on_copy_row: function () {
|
||||||
var self = this;
|
var self = this,
|
||||||
|
pref_cache = undefined;
|
||||||
|
self.grid.CSVOptions = {};
|
||||||
|
|
||||||
|
if (self.handler.is_new_browser_tab) {
|
||||||
|
pref_cache = window.opener.pgAdmin.Browser.preferences_cache;
|
||||||
|
} else {
|
||||||
|
pref_cache = window.top.pgAdmin.Browser.preferences_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CSV options from preferences cache
|
||||||
|
self.grid.CSVOptions.quoting = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_quoting'}).value;
|
||||||
|
self.grid.CSVOptions.quote_char = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_quote_char'}).value;
|
||||||
|
self.grid.CSVOptions.field_separator = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'results_grid_field_separator'}).value;
|
||||||
|
|
||||||
// Trigger the copy signal to the SqlEditorController class
|
// Trigger the copy signal to the SqlEditorController class
|
||||||
self.handler.trigger(
|
self.handler.trigger(
|
||||||
@ -1244,6 +1271,7 @@ define('tools.querytool', [
|
|||||||
self,
|
self,
|
||||||
self.handler
|
self.handler
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Callback function for paste button click.
|
// Callback function for paste button click.
|
||||||
@ -1496,7 +1524,7 @@ define('tools.querytool', [
|
|||||||
|
|
||||||
keyAction: function (event) {
|
keyAction: function (event) {
|
||||||
keyboardShortcuts.processEvent(this.handler, queryToolActions, event);
|
keyboardShortcuts.processEvent(this.handler, queryToolActions, event);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Defining controller class for data grid, which actually
|
/* Defining controller class for data grid, which actually
|
||||||
@ -3649,7 +3677,6 @@ define('tools.querytool', [
|
|||||||
explain_timing = res.data.explain_timing;
|
explain_timing = res.data.explain_timing;
|
||||||
auto_commit = res.data.auto_commit;
|
auto_commit = res.data.auto_commit;
|
||||||
auto_rollback = res.data.auto_rollback;
|
auto_rollback = res.data.auto_rollback;
|
||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -697,7 +697,6 @@ WHERE
|
|||||||
params: Additional parameters
|
params: Additional parameters
|
||||||
formatted_exception_msg: For exception
|
formatted_exception_msg: For exception
|
||||||
records: Number of initial records
|
records: Number of initial records
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Generator response
|
Generator response
|
||||||
"""
|
"""
|
||||||
@ -787,7 +786,7 @@ WHERE
|
|||||||
)
|
)
|
||||||
return new_results
|
return new_results
|
||||||
|
|
||||||
def gen():
|
def gen(quote='strings', quote_char="'", field_separator=','):
|
||||||
|
|
||||||
results = cur.fetchmany(records)
|
results = cur.fetchmany(records)
|
||||||
if not results:
|
if not results:
|
||||||
@ -816,9 +815,17 @@ WHERE
|
|||||||
|
|
||||||
res_io = StringIO()
|
res_io = StringIO()
|
||||||
|
|
||||||
|
if quote == 'strings':
|
||||||
|
quote = csv.QUOTE_NONNUMERIC
|
||||||
|
elif quote == 'all':
|
||||||
|
quote = csv.QUOTE_ALL
|
||||||
|
else:
|
||||||
|
quote = csv.QUOTE_NONE
|
||||||
|
|
||||||
csv_writer = csv.DictWriter(
|
csv_writer = csv.DictWriter(
|
||||||
res_io, fieldnames=header, delimiter=u',',
|
res_io, fieldnames=header, delimiter=field_separator,
|
||||||
quoting=csv.QUOTE_NONNUMERIC
|
quoting=quote,
|
||||||
|
quotechar=quote_char
|
||||||
)
|
)
|
||||||
|
|
||||||
csv_writer.writeheader()
|
csv_writer.writeheader()
|
||||||
@ -837,8 +844,9 @@ WHERE
|
|||||||
res_io = StringIO()
|
res_io = StringIO()
|
||||||
|
|
||||||
csv_writer = csv.DictWriter(
|
csv_writer = csv.DictWriter(
|
||||||
res_io, fieldnames=header, delimiter=u',',
|
res_io, fieldnames=header, delimiter=field_separator,
|
||||||
quoting=csv.QUOTE_NONNUMERIC
|
quoting=quote,
|
||||||
|
quotechar=quote_char
|
||||||
)
|
)
|
||||||
|
|
||||||
if IS_PY2:
|
if IS_PY2:
|
||||||
|
@ -31,7 +31,7 @@ class _Preference(object):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, cid, name, label, _type, default, help_str=None, min_val=None,
|
self, cid, name, label, _type, default, help_str=None, min_val=None,
|
||||||
max_val=None, options=None
|
max_val=None, options=None, select2=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
__init__
|
__init__
|
||||||
@ -53,6 +53,7 @@ class _Preference(object):
|
|||||||
:param min_val: minimum value
|
:param min_val: minimum value
|
||||||
:param max_val: maximum value
|
:param max_val: maximum value
|
||||||
:param options: options (Array of list objects)
|
:param options: options (Array of list objects)
|
||||||
|
:param select2: select2 options (object)
|
||||||
|
|
||||||
:returns: nothing
|
:returns: nothing
|
||||||
"""
|
"""
|
||||||
@ -65,6 +66,7 @@ class _Preference(object):
|
|||||||
self.min_val = min_val
|
self.min_val = min_val
|
||||||
self.max_val = max_val
|
self.max_val = max_val
|
||||||
self.options = options
|
self.options = options
|
||||||
|
self.select2 = select2
|
||||||
|
|
||||||
# Look into the configuration table to find out the id of the specific
|
# Look into the configuration table to find out the id of the specific
|
||||||
# preference.
|
# preference.
|
||||||
@ -129,6 +131,8 @@ class _Preference(object):
|
|||||||
for opt in self.options:
|
for opt in self.options:
|
||||||
if 'value' in opt and opt['value'] == res.value:
|
if 'value' in opt and opt['value'] == res.value:
|
||||||
return res.value
|
return res.value
|
||||||
|
if self.select2 and self.select2['tags']:
|
||||||
|
return res.value
|
||||||
return self.default
|
return self.default
|
||||||
if self._type == 'text':
|
if self._type == 'text':
|
||||||
if res.value == '':
|
if res.value == '':
|
||||||
@ -190,7 +194,7 @@ class _Preference(object):
|
|||||||
if 'value' in opt and opt['value'] == value:
|
if 'value' in opt and opt['value'] == value:
|
||||||
has_value = True
|
has_value = True
|
||||||
|
|
||||||
if not has_value:
|
if not has_value and self.select2 and not self.select2['tags']:
|
||||||
return False, gettext("Invalid value for an options option.")
|
return False, gettext("Invalid value for an options option.")
|
||||||
|
|
||||||
pref = UserPrefTable.query.filter_by(
|
pref = UserPrefTable.query.filter_by(
|
||||||
@ -226,6 +230,7 @@ class _Preference(object):
|
|||||||
'min_val': self.min_val,
|
'min_val': self.min_val,
|
||||||
'max_val': self.max_val,
|
'max_val': self.max_val,
|
||||||
'options': self.options,
|
'options': self.options,
|
||||||
|
'select2': self.select2,
|
||||||
'value': self.get()
|
'value': self.get()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
@ -365,7 +370,8 @@ class Preferences(object):
|
|||||||
|
|
||||||
def register(
|
def register(
|
||||||
self, category, name, label, _type, default, min_val=None,
|
self, category, name, label, _type, default, min_val=None,
|
||||||
max_val=None, options=None, help_str=None, category_label=None
|
max_val=None, options=None, help_str=None, category_label=None,
|
||||||
|
select2=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
register
|
register
|
||||||
@ -386,6 +392,7 @@ class Preferences(object):
|
|||||||
:param options:
|
:param options:
|
||||||
:param help_str:
|
:param help_str:
|
||||||
:param category_label:
|
:param category_label:
|
||||||
|
:param select2: select2 control extra options
|
||||||
"""
|
"""
|
||||||
cat = self.__category(category, category_label)
|
cat = self.__category(category, category_label)
|
||||||
if name in cat['preferences']:
|
if name in cat['preferences']:
|
||||||
@ -400,7 +407,7 @@ class Preferences(object):
|
|||||||
|
|
||||||
(cat['preferences'])[name] = res = _Preference(
|
(cat['preferences'])[name] = res = _Preference(
|
||||||
cat['id'], name, label, _type, default, help_str, min_val,
|
cat['id'], name, label, _type, default, help_str, min_val,
|
||||||
max_val, options
|
max_val, options, select2
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
@ -73,6 +73,7 @@ class PgadminPage:
|
|||||||
self.find_by_partial_link_text("Query Tool").click()
|
self.find_by_partial_link_text("Query Tool").click()
|
||||||
self.click_tab('Query -')
|
self.click_tab('Query -')
|
||||||
|
|
||||||
|
|
||||||
def enable_menu_item(self, menu_item, wait_time):
|
def enable_menu_item(self, menu_item, wait_time):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
# wait until menu becomes enabled.
|
# wait until menu becomes enabled.
|
||||||
|
@ -22,11 +22,12 @@ describe('copyData', function () {
|
|||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
SlickGrid = Slick.Grid;
|
SlickGrid = Slick.Grid;
|
||||||
var data = [{'id': 1, 'brand':'leopord', 'size':'12', '__temp_PK': '123'},
|
var data = [{'id': 1, 'brand':'leopord', 'size':12, '__temp_PK': '123'},
|
||||||
{'id': 2, 'brand':'lion', 'size':'13', '__temp_PK': '456'},
|
{'id': 2, 'brand':'lion', 'size':13, '__temp_PK': '456'},
|
||||||
{'id': 3, 'brand':'puma', 'size':'9', '__temp_PK': '789'}],
|
{'id': 3, 'brand':'puma', 'size':9, '__temp_PK': '789'}],
|
||||||
dataView = new Slick.Data.DataView();
|
dataView = new Slick.Data.DataView();
|
||||||
|
|
||||||
|
var CSVOptions = {'quoting': 'strings', 'quote_char': '"', 'field_separator': ','};
|
||||||
var columns = [
|
var columns = [
|
||||||
{
|
{
|
||||||
id: 'row-header-column',
|
id: 'row-header-column',
|
||||||
@ -66,6 +67,7 @@ describe('copyData', function () {
|
|||||||
buttonPasteRow = $('<button id="btn-paste-row" disabled></button>');
|
buttonPasteRow = $('<button id="btn-paste-row" disabled></button>');
|
||||||
$('body').append(buttonPasteRow);
|
$('body').append(buttonPasteRow);
|
||||||
grid = new SlickGrid('#grid', dataView, columns, {});
|
grid = new SlickGrid('#grid', dataView, columns, {});
|
||||||
|
grid.CSVOptions = CSVOptions;
|
||||||
dataView.setItems(data, '__temp_PK');
|
dataView.setItems(data, '__temp_PK');
|
||||||
grid.setSelectionModel(new XCellSelectionModel());
|
grid.setSelectionModel(new XCellSelectionModel());
|
||||||
sqlEditor = {slickgrid: grid};
|
sqlEditor = {slickgrid: grid};
|
||||||
@ -93,8 +95,8 @@ describe('copyData', function () {
|
|||||||
expect(sqlEditor.copied_rows.length).toBe(2);
|
expect(sqlEditor.copied_rows.length).toBe(2);
|
||||||
|
|
||||||
expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
|
expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
|
||||||
expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,\'leopord\',\'12\'');
|
expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,"leopord",12');
|
||||||
expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,\'puma\',\'9\'');
|
expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,"puma",9');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the user can edit the grid', function () {
|
describe('when the user can edit the grid', function () {
|
||||||
|
@ -132,7 +132,7 @@ describe('RangeBoundaryNavigator', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#rangesToCsv', function () {
|
describe('#rangesToCsv', function () {
|
||||||
var data, columnDefinitions, ranges;
|
var data, columnDefinitions, ranges, CSVOptions;
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
data = [{'id':1, 'animal':'leopard', 'size':'12'},
|
data = [{'id':1, 'animal':'leopard', 'size':'12'},
|
||||||
{'id':2, 'animal':'lion', 'size':'13'},
|
{'id':2, 'animal':'lion', 'size':'13'},
|
||||||
@ -143,16 +143,21 @@ describe('RangeBoundaryNavigator', function () {
|
|||||||
{name: 'animal', field: 'animal', pos: 1},
|
{name: 'animal', field: 'animal', pos: 1},
|
||||||
{name: 'size', field: 'size', pos: 2}];
|
{name: 'size', field: 'size', pos: 2}];
|
||||||
ranges = [new Slick.Range(0, 0, 0, 2), new Slick.Range(3, 0, 3, 2)];
|
ranges = [new Slick.Range(0, 0, 0, 2), new Slick.Range(3, 0, 3, 2)];
|
||||||
|
|
||||||
|
CSVOptions = [{'quoting': 'all', 'quote_char': '"', 'field_separator': ','},
|
||||||
|
{'quoting': 'strings', 'quote_char': '"', 'field_separator': ';'},
|
||||||
|
{'quoting': 'strings', 'quote_char': '\'', 'field_separator': '|'},
|
||||||
|
{'quoting': 'none', 'quote_char': '"', 'field_separator': '\t'}];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns csv for the provided ranges', function () {
|
it('returns csv for the provided ranges for CSV options quoting All with char " with field separator ,', function () {
|
||||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
|
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges, CSVOptions[0]);
|
||||||
expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
|
expect(csvResult).toEqual('"1","leopard","12"\n"4","tiger","10"');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when no cells are selected', function () {
|
describe('when no cells are selected for CSV options quoting Strings with char " with field separator ;', function () {
|
||||||
it('should return an empty string', function () {
|
it('should return an empty string', function () {
|
||||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
|
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, [], CSVOptions[1]);
|
||||||
|
|
||||||
expect(csvResult).toEqual('');
|
expect(csvResult).toEqual('');
|
||||||
});
|
});
|
||||||
@ -167,14 +172,14 @@ describe('RangeBoundaryNavigator', function () {
|
|||||||
ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)];
|
ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns csv for the columns with data', function () {
|
it('returns csv for the columns with data for CSV options quoting Strings with char \' with field separator |', function () {
|
||||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
|
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges, CSVOptions[2]);
|
||||||
|
|
||||||
expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
|
expect(csvResult).toEqual('1|\'leopard\'|\'12\'\n4|\'tiger\'|\'10\'');
|
||||||
});
|
});
|
||||||
describe('when no cells are selected', function () {
|
describe('when no cells are selected for CSV options quoting none with field separator tab', function () {
|
||||||
it('should return an empty string', function () {
|
it('should return an empty string', function () {
|
||||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
|
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, [], CSVOptions[3]);
|
||||||
|
|
||||||
expect(csvResult).toEqual('');
|
expect(csvResult).toEqual('');
|
||||||
});
|
});
|
||||||
|
@ -36,10 +36,12 @@ describe('#handleQueryOutputKeyboardEvent', function () {
|
|||||||
columnDefinitions = [{name: 'checkboxColumn'},
|
columnDefinitions = [{name: 'checkboxColumn'},
|
||||||
{pos: 1, name: 'firstColumn', field: 'firstColumn'},
|
{pos: 1, name: 'firstColumn', field: 'firstColumn'},
|
||||||
{ pos: 2, name: 'secondColumn', field: 'secondColumn'}],
|
{ pos: 2, name: 'secondColumn', field: 'secondColumn'}],
|
||||||
dataView = new Slick.Data.DataView();
|
dataView = new Slick.Data.DataView(),
|
||||||
|
CSVOptions = {'quoting': 'all', 'quote_char': '\'', 'field_separator': ','};
|
||||||
|
|
||||||
grid = new Slick.Grid($('<div></div>'), dataView, columnDefinitions);
|
grid = new Slick.Grid($('<div></div>'), dataView, columnDefinitions);
|
||||||
grid.setSelectionModel(new XCellSelectionModel());
|
grid.setSelectionModel(new XCellSelectionModel());
|
||||||
|
grid.CSVOptions = CSVOptions;
|
||||||
dataView.setItems(data, '__temp_PK');
|
dataView.setItems(data, '__temp_PK');
|
||||||
slickEvent = {
|
slickEvent = {
|
||||||
grid: grid,
|
grid: grid,
|
||||||
|
Loading…
Reference in New Issue
Block a user