diff --git a/assets/javascripts/discourse/components/query-result.js.es6 b/assets/javascripts/discourse/components/query-result.js.es6 index ac748ee..3b05ab8 100644 --- a/assets/javascripts/discourse/components/query-result.js.es6 +++ b/assets/javascripts/discourse/components/query-result.js.es6 @@ -100,43 +100,50 @@ const QueryResultComponent = Ember.Component.extend({ }); }.property('content', 'columns.@each'), + downloadResult(format) { + // Create a frame to submit the form in (?) + // to avoid leaving an about:blank behind + let windowName = randomIdShort(); + const newWindowContents = "Click anywhere to close this window once the download finishes."; + + let newWindow = window.open('data:text/html;base64,' + btoa(newWindowContents), windowName); + + let form = document.createElement("form"); + form.setAttribute('id', 'query-download-result'); + form.setAttribute('method', 'post'); + form.setAttribute('action', Discourse.getURL('/admin/plugins/explorer/queries/' + this.get('query.id') + '/run.' + format + '?download=1')); + form.setAttribute('target', windowName); + form.setAttribute('style', 'display:none;'); + + function addInput(form, name, value) { + let field; + field = document.createElement('input'); + field.setAttribute('name', name); + field.setAttribute('value', value); + form.appendChild(field); + } + + addInput(form, 'params', JSON.stringify(this.get('params'))); + addInput(form, 'explain', this.get('hasExplain')); + addInput(form, 'limit', '1000000'); + + Discourse.ajax('/session/csrf.json').then(function(csrf) { + addInput(form, 'authenticity_token', csrf.csrf); + + document.body.appendChild(form); + form.submit(); + Em.run.next('afterRender', function() { + document.body.removeChild(form); + }) + }); + }, + actions: { - downloadResult() { - // Create a frame to submit the form in (?) - // to avoid leaving an about:blank behind - let windowName = randomIdShort(); - const newWindowContents = "Click anywhere to close this window once the download finishes."; - - let newWindow = window.open('data:text/html;base64,' + btoa(newWindowContents), windowName); - - let form = document.createElement("form"); - form.setAttribute('id', 'query-download-result'); - form.setAttribute('method', 'post'); - form.setAttribute('action', Discourse.getURL('/admin/plugins/explorer/queries/' + this.get('query.id') + '/run.json?download=1')); - form.setAttribute('target', windowName); - form.setAttribute('style', 'display:none;'); - - function addInput(form, name, value) { - let field; - field = document.createElement('input'); - field.setAttribute('name', name); - field.setAttribute('value', value); - form.appendChild(field); - } - - addInput(form, 'params', JSON.stringify(this.get('params'))); - addInput(form, 'explain', this.get('hasExplain')); - addInput(form, 'limit', '1000000'); - - Discourse.ajax('/session/csrf.json').then(function(csrf) { - addInput(form, 'authenticity_token', csrf.csrf); - - document.body.appendChild(form); - form.submit(); - Em.run.next('afterRender', function() { - document.body.removeChild(form); - }) - }); + downloadResultJson() { + this.downloadResult('json'); + }, + downloadResultCsv() { + this.downloadResult('csv'); } }, diff --git a/assets/javascripts/discourse/templates/explorer-query-result.hbs b/assets/javascripts/discourse/templates/explorer-query-result.hbs index 8f1978b..40a40f2 100644 --- a/assets/javascripts/discourse/templates/explorer-query-result.hbs +++ b/assets/javascripts/discourse/templates/explorer-query-result.hbs @@ -1,5 +1,7 @@
- {{d-button action="downloadResult" icon="download" label="explorer.download_json"}} + {{i18n "explorer.download"}} + {{d-button action="downloadResultJson" icon="download" label="explorer.download_json"}} + {{d-button action="downloadResultCsv" icon="download" label="explorer.download_csv"}}
{{duration}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index a913955..8fed453 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -46,13 +46,15 @@ en: edit: "Edit" delete: "Delete" recover: "Undelete Query" - download_json: "Download Results" + download: "Download Results" + download_json: "JSON" + download_csv: "CSV" others_dirty: "A query has unsaved changes that will be lost if you navigate away." run_time: "Query completed in {{value}} ms." column: "Column {{number}}" explain_label: "Include query plan?" save_params: "Set Defaults" reset_params: "Reset" - https_warning: "Use of the Data Explorer on sites not protected by HTTPS is discouraged. Please be careful to not retrieve sensitive information over insecure links." + https_warning: "This site is not protected by HTTPS. Please be careful to not retrieve sensitive information over insecure links." no_queries: "There are no queries. Why not " no_queries_hook: "create one?" diff --git a/plugin.rb b/plugin.rb index 3b2d4fa..be5ff46 100644 --- a/plugin.rb +++ b/plugin.rb @@ -917,8 +917,6 @@ SQL check_xhr unless params[:download] query = DataExplorer::Query.find(params[:id].to_i) if params[:download] - response.headers['Content-Disposition'] = - "attachment; filename=#{query.slug}@#{Slug.for(Discourse.current_hostname, 'discourse')}-#{Date.today}.dcqresult.json" response.sending_file = true end @@ -952,23 +950,45 @@ SQL else pg_result = result[:pg_result] cols = pg_result.fields - json = { - success: true, - errors: [], - duration: (result[:duration_secs].to_f * 1000).round(1), - params: query_params, - columns: cols, - } - json[:explain] = result[:explain] if opts[:explain] - # TODO - special serialization - # This is dead code in the client right now - # if cols.any? { |col_name| special_serialization? col_name } - # json[:relations] = DataExplorer.add_extra_data(pg_result) - # end + respond_to do |format| + format.json do + if params[:download] + response.headers['Content-Disposition'] = + "attachment; filename=#{query.slug}@#{Slug.for(Discourse.current_hostname, 'discourse')}-#{Date.today}.dcqresult.json" + end + json = { + success: true, + errors: [], + duration: (result[:duration_secs].to_f * 1000).round(1), + params: query_params, + columns: cols, + } + json[:explain] = result[:explain] if opts[:explain] + # TODO - special serialization + # This is dead code in the client right now + # if cols.any? { |col_name| special_serialization? col_name } + # json[:relations] = DataExplorer.add_extra_data(pg_result) + # end - json[:rows] = pg_result.values + json[:rows] = pg_result.values - render json: json + render json: json + end + format.csv do + response.headers['Content-Disposition'] = + "attachment; filename=#{query.slug}@#{Slug.for(Discourse.current_hostname, 'discourse')}-#{Date.today}.dcqresult.csv" + + require 'csv' + text = CSV.generate do |csv| + csv << cols + pg_result.values.each do |row| + csv << row + end + end + + render text: text + end + end end end end