From dba181d92ebea159002540d69571c34efba6373f Mon Sep 17 00:00:00 2001 From: Kane York Date: Tue, 30 Jun 2015 12:52:17 -0700 Subject: [PATCH] Bunch of progress. Tuesday 1PM --- .../controllers/admin-plugins-explorer.js.es6 | 67 ++++++++++++++----- .../discourse/controllers/import-query.js.es6 | 13 +--- .../javascripts/discourse/models/query.js.es6 | 25 +++++-- .../templates/admin/plugins-explorer-show.hbs | 17 ++++- .../templates/admin/plugins-explorer.hbs | 4 +- assets/stylesheets/explorer.scss | 9 ++- config/locales/client.en.yml | 3 + plugin.rb | 53 ++++++++++----- 8 files changed, 136 insertions(+), 55 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 index 2e1d9a1..4118918 100644 --- a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 @@ -1,5 +1,6 @@ import showModal from 'discourse/lib/show-modal'; import Query from 'discourse/plugins/discourse-data-explorer/discourse/models/query'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; export default Ember.ArrayController.extend({ selectedQueryId: null, @@ -20,24 +21,16 @@ export default Ember.ArrayController.extend({ }); }.property('selectedQueryId'), + addCreatedRecord(record) { + this.pushObject(record); + this.set('selectedQueryId', Ember.get(record, 'id')); + this.get('selectedItem').set('dirty', false); + this.set('results', null); + }, + actions: { dummy() {}, - create() { - const self = this; - this.set('loading', true); - this.set('showCreate', false); - var newQuery = this.store.createRecord('query', {name: this.get('newQueryName')}); - newQuery.save().then(function(result) { - self.pushObject(result.target); - self.set('selectedQueryId', result.target.id); - self.set('selectedItem.dirty', false); - self.set('results', null); - }).finally(function() { - self.set('loading', false); - }); - }, - importQuery() { showModal('import-query'); this.set('showCreate', false); @@ -51,6 +44,22 @@ export default Ember.ArrayController.extend({ this.set('editName', true); }, + download() { + window.open(this.get('selectedItem.downloadUrl'), "_blank"); + }, + + create() { + const self = this; + this.set('loading', true); + this.set('showCreate', false); + var newQuery = this.store.createRecord('query', {name: this.get('newQueryName')}); + newQuery.save().then(function(result) { + self.addCreatedRecord(result.target); + }).catch(popupAjaxError).finally(function() { + self.set('loading', false); + }); + }, + save() { const self = this; this.set('loading', true); @@ -58,7 +67,7 @@ export default Ember.ArrayController.extend({ const query = self.get('selectedItem'); query.markNotDirty(); self.set('editName', false); - }).finally(function() { + }).catch(popupAjaxError).finally(function() { self.set('loading', false); }); }, @@ -72,7 +81,29 @@ export default Ember.ArrayController.extend({ query.markNotDirty(); self.set('editName', false); self.set('results', null); - }).finally(function() { + }).catch(popupAjaxError).finally(function() { + self.set('loading', false); + }); + }, + + destroy() { + const self = this; + const query = this.get('selectedItem'); + this.set('loading', true); + this.store.destroyRecord('query', query).then(function() { + query.set('destroyed', true); + }).catch(popupAjaxError).finally(function() { + self.set('loading', false); + }); + }, + + recover() { + const self = this; + const query = this.get('selectedItem'); + this.set('loading', true); + query.save().then(function() { + query.set('destroyed', false); + }).catch(popupAjaxError).finally(function() { self.set('loading', false); }); }, @@ -93,7 +124,7 @@ export default Ember.ArrayController.extend({ }).then(function(result) { console.log(result); self.set('results', result); - }).finally(function() { + }).catch(popupAjaxError).finally(function() { self.set('loading', false); }); } diff --git a/assets/javascripts/discourse/controllers/import-query.js.es6 b/assets/javascripts/discourse/controllers/import-query.js.es6 index f409bed..e072413 100644 --- a/assets/javascripts/discourse/controllers/import-query.js.es6 +++ b/assets/javascripts/discourse/controllers/import-query.js.es6 @@ -1,4 +1,5 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; export default Ember.Controller.extend(ModalFunctionality, { notReady: Em.computed.not('ready'), @@ -30,16 +31,8 @@ export default Ember.Controller.extend(ModalFunctionality, { self.set('loading', false); const parentController = self.get('controllers.admin-plugins-explorer'); - parentController.pushObject(query); - parentController.set('selectedItem', query); - }).catch(function(xhr) { - self.set('loading', false); - if (xhr.responseJSON) { - bootbox.alert(xhr.responseJSON.errors.join("
")); - } else { - bootbox.alert(I18n.t('generic_error')); - } - }); + parentController.addCreatedRecord(query.target); + }).catch(popupAjaxError); } } diff --git a/assets/javascripts/discourse/models/query.js.es6 b/assets/javascripts/discourse/models/query.js.es6 index 63d95c2..ddb878c 100644 --- a/assets/javascripts/discourse/models/query.js.es6 +++ b/assets/javascripts/discourse/models/query.js.es6 @@ -12,19 +12,36 @@ Query = RestModel.extend({ this.set('dirty', false); }, + downloadUrl: function() { + // TODO - can we change this to use the store/adapter? + return Discourse.getURL("/admin/plugins/explorer/queries/" + this.get('id') + ".json?export=1"); + }.property('id'), + listName: function() { + let name = this.get('name'); if (this.get('dirty')) { - return this.get('name') + " (*)"; + name += " (*)"; } - return this.get('name'); - }.property('name', 'dirty'), + if (this.get('destroyed')) { + name += " (deleted)"; + } + return name; + }.property('name', 'dirty', 'destroyed'), createProperties() { + if (this.get('sql')) { + // Importing + return this.updateProperties(); + } return this.getProperties("name"); }, updateProperties() { - return this.getProperties(Query.updatePropertyNames); + let props = this.getProperties(Query.updatePropertyNames); + if (this.get('destroyed')) { + props.id = this.get('id'); + } + return props; }, run() { diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer-show.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer-show.hbs index 317d31a..535941c 100644 --- a/assets/javascripts/discourse/templates/admin/plugins-explorer-show.hbs +++ b/assets/javascripts/discourse/templates/admin/plugins-explorer-show.hbs @@ -17,7 +17,18 @@
{{textarea value=selectedItem.sql}}
- {{d-button action="save" label="explorer.save" disabled=saveDisabled}} - {{d-button action="discard" class="no-text btn-danger" icon="undo" disabled=saveDisabled}} - {{d-button action="run" label="explorer.run" disabled=runDisabled}} +
+ {{d-button action="save" label="explorer.save" disabled=saveDisabled}} + {{d-button action="run" label="explorer.run" disabled=runDisabled}} + {{d-button action="download" label="explorer.export" disabled=runDisabled icon="download"}} +
+
+ {{#if selectedItem.destroyed}} + {{d-button action="recover" class="" icon="undo" label="explorer.recover"}} + {{else}} + {{d-button action="discard" class="btn-danger" icon="undo" label="explorer.undo" disabled=saveDisabled}} + {{d-button action="destroy" class="btn-danger" icon="trash" label="explorer.delete"}} + {{/if}} +
+
{{/if}} diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs index de28987..519ea19 100644 --- a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs +++ b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs @@ -1,13 +1,13 @@

Queries

- {{combo-box valueAttribute="id" value=selectedQueryId nameProperty="name" content=content castInteger="true"}} + {{combo-box valueAttribute="id" value=selectedQueryId nameProperty="listName" content=content castInteger="true"}} {{d-button action="showCreate" icon="plus" class="no-text"}} {{d-button action="importQuery" label="explorer.import.label" icon="upload"}}
{{#if showCreate}}
{{text-field value=newQueryName placeholderKey="explorer.create_placeholder"}} - {{d-button action="create" label="explorer.create" icon="plus"}} + {{d-button action="create" label="explorer.create" icon="plus" class="btn-primary"}}
{{/if}}
diff --git a/assets/stylesheets/explorer.scss b/assets/stylesheets/explorer.scss index e1ef59a..804826c 100644 --- a/assets/stylesheets/explorer.scss +++ b/assets/stylesheets/explorer.scss @@ -14,11 +14,18 @@ width: 200px; } .sql textarea { - width: 800px; + width: calc(100% - 8px); height: 100px; font-family: monospace; border-color: $tertiary; } + .left-buttons { + float: left; + } + .right-buttons { + float: right; + } + .clear { clear: both; } } .query-list, .query-edit, .query-results { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 6812782..750fc87 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -31,3 +31,6 @@ en: export: "Export" save: "Save Changes" run: "Run Query" + undo: "Revert" + delete: "Delete" + recover: "Undelete Query" diff --git a/plugin.rb b/plugin.rb index e75f56d..61f29de 100644 --- a/plugin.rb +++ b/plugin.rb @@ -125,6 +125,13 @@ SQL class DataExplorer::Query attr_accessor :id, :name, :description, :sql, :defaults + def initialize + @name = 'Unnamed Query' + @description = 'Enter a description here' + @sql = 'SELECT 1' + @defaults = {} + end + def param_names param_info = DataExplorer.extract_params sql param_info[:names] @@ -171,16 +178,19 @@ SQL def to_hash { id: @id, - name: @name || 'Query', - description: @description || '', - sql: @sql || 'SELECT 1', - defaults: @defaults || {}, + name: @name, + description: @description, + sql: @sql, + defaults: @defaults, } end - def self.find(id) + def self.find(id, opts={}) hash = DataExplorer.pstore_get("q:#{id}") - raise Discourse::NotFound unless hash + unless hash + return DataExplorer::Query.new if opts[:ignore_deleted] + raise Discourse::NotFound + end from_hash hash end @@ -212,7 +222,6 @@ SQL require_dependency 'application_controller' class DataExplorer::QueryController < ::ApplicationController requires_plugin DataExplorer.plugin_name - skip_before_filter :check_xhr, only: [:show] def index # guardian.ensure_can_use_data_explorer! @@ -220,14 +229,15 @@ SQL render_serialized queries, DataExplorer::QuerySerializer, root: 'queries' end + skip_before_filter :check_xhr, only: [:show] def show + check_xhr unless params[:export] + query = DataExplorer::Query.find(params[:id].to_i) if params[:export] - response.headers['Content-Disposition'] = "attachment; filename=#{query.slug}.json" + response.headers['Content-Disposition'] = "attachment; filename=#{query.slug}.dcquery.json" response.sending_file = true - else - check_xhr end # guardian.ensure_can_see! query @@ -243,18 +253,26 @@ SQL # guardian.ensure_can_create_explorer_query! query = DataExplorer::Query.from_hash params.require(:query) - # Set the ID _only_ if undeleting - if params[:recover] - query.id = params[:id].to_i - end + binding.pry + query.id = nil # json import will assign an id, which is wrong query.save - render_serialized query, DataExplorer::QuerySerializer, root: 'queries' + render_serialized query, DataExplorer::QuerySerializer, root: 'query' end def update - query = DataExplorer::Query.find(params[:id].to_i) + query = DataExplorer::Query.find(params[:id].to_i, ignore_deleted: true) hash = params.require(:query) + + # Undeleting + unless query.id + if hash[:id] + query.id = hash[:id].to_i + else + raise Discourse::NotFound + end + end + [:name, :sql, :defaults, :description].each do |sym| query.send("#{sym}=", hash[sym]) if hash[sym] end @@ -266,7 +284,8 @@ SQL def destroy query = DataExplorer::Query.find(params[:id].to_i) query.destroy - render nothing: true + + render json: {success: true, errors: []} end def run