Bunch of progress. Tuesday 1PM

This commit is contained in:
Kane York 2015-06-30 12:52:17 -07:00
parent 78dafcc631
commit dba181d92e
8 changed files with 136 additions and 55 deletions

View File

@ -1,5 +1,6 @@
import showModal from 'discourse/lib/show-modal'; import showModal from 'discourse/lib/show-modal';
import Query from 'discourse/plugins/discourse-data-explorer/discourse/models/query'; import Query from 'discourse/plugins/discourse-data-explorer/discourse/models/query';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.ArrayController.extend({ export default Ember.ArrayController.extend({
selectedQueryId: null, selectedQueryId: null,
@ -20,24 +21,16 @@ export default Ember.ArrayController.extend({
}); });
}.property('selectedQueryId'), }.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: { actions: {
dummy() {}, 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() { importQuery() {
showModal('import-query'); showModal('import-query');
this.set('showCreate', false); this.set('showCreate', false);
@ -51,6 +44,22 @@ export default Ember.ArrayController.extend({
this.set('editName', true); 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() { save() {
const self = this; const self = this;
this.set('loading', true); this.set('loading', true);
@ -58,7 +67,7 @@ export default Ember.ArrayController.extend({
const query = self.get('selectedItem'); const query = self.get('selectedItem');
query.markNotDirty(); query.markNotDirty();
self.set('editName', false); self.set('editName', false);
}).finally(function() { }).catch(popupAjaxError).finally(function() {
self.set('loading', false); self.set('loading', false);
}); });
}, },
@ -72,7 +81,29 @@ export default Ember.ArrayController.extend({
query.markNotDirty(); query.markNotDirty();
self.set('editName', false); self.set('editName', false);
self.set('results', null); 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); self.set('loading', false);
}); });
}, },
@ -93,7 +124,7 @@ export default Ember.ArrayController.extend({
}).then(function(result) { }).then(function(result) {
console.log(result); console.log(result);
self.set('results', result); self.set('results', result);
}).finally(function() { }).catch(popupAjaxError).finally(function() {
self.set('loading', false); self.set('loading', false);
}); });
} }

View File

@ -1,4 +1,5 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
notReady: Em.computed.not('ready'), notReady: Em.computed.not('ready'),
@ -30,16 +31,8 @@ export default Ember.Controller.extend(ModalFunctionality, {
self.set('loading', false); self.set('loading', false);
const parentController = self.get('controllers.admin-plugins-explorer'); const parentController = self.get('controllers.admin-plugins-explorer');
parentController.pushObject(query); parentController.addCreatedRecord(query.target);
parentController.set('selectedItem', query); }).catch(popupAjaxError);
}).catch(function(xhr) {
self.set('loading', false);
if (xhr.responseJSON) {
bootbox.alert(xhr.responseJSON.errors.join("<br>"));
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
} }
} }

View File

@ -12,19 +12,36 @@ Query = RestModel.extend({
this.set('dirty', false); 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() { listName: function() {
let name = this.get('name');
if (this.get('dirty')) { if (this.get('dirty')) {
return this.get('name') + " (*)"; name += " (*)";
} }
return this.get('name'); if (this.get('destroyed')) {
}.property('name', 'dirty'), name += " (deleted)";
}
return name;
}.property('name', 'dirty', 'destroyed'),
createProperties() { createProperties() {
if (this.get('sql')) {
// Importing
return this.updateProperties();
}
return this.getProperties("name"); return this.getProperties("name");
}, },
updateProperties() { updateProperties() {
return this.getProperties(Query.updatePropertyNames); let props = this.getProperties(Query.updatePropertyNames);
if (this.get('destroyed')) {
props.id = this.get('id');
}
return props;
}, },
run() { run() {

View File

@ -17,7 +17,18 @@
<div class="sql"> <div class="sql">
{{textarea value=selectedItem.sql}} {{textarea value=selectedItem.sql}}
</div> </div>
{{d-button action="save" label="explorer.save" disabled=saveDisabled}} <div class="left-buttons">
{{d-button action="discard" class="no-text btn-danger" icon="undo" disabled=saveDisabled}} {{d-button action="save" label="explorer.save" disabled=saveDisabled}}
{{d-button action="run" label="explorer.run" disabled=runDisabled}} {{d-button action="run" label="explorer.run" disabled=runDisabled}}
{{d-button action="download" label="explorer.export" disabled=runDisabled icon="download"}}
</div>
<div class="right-buttons">
{{#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}}
</div>
<div class="clear"></div>
{{/if}} {{/if}}

View File

@ -1,13 +1,13 @@
<h3>Queries</h3> <h3>Queries</h3>
<div class="query-list"> <div class="query-list">
{{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="showCreate" icon="plus" class="no-text"}}
{{d-button action="importQuery" label="explorer.import.label" icon="upload"}} {{d-button action="importQuery" label="explorer.import.label" icon="upload"}}
</div> </div>
{{#if showCreate}} {{#if showCreate}}
<div class="query-create"> <div class="query-create">
{{text-field value=newQueryName placeholderKey="explorer.create_placeholder"}} {{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"}}
</div> </div>
{{/if}} {{/if}}
<hr> <hr>

View File

@ -14,11 +14,18 @@
width: 200px; width: 200px;
} }
.sql textarea { .sql textarea {
width: 800px; width: calc(100% - 8px);
height: 100px; height: 100px;
font-family: monospace; font-family: monospace;
border-color: $tertiary; border-color: $tertiary;
} }
.left-buttons {
float: left;
}
.right-buttons {
float: right;
}
.clear { clear: both; }
} }
.query-list, .query-edit, .query-results { .query-list, .query-edit, .query-results {

View File

@ -31,3 +31,6 @@ en:
export: "Export" export: "Export"
save: "Save Changes" save: "Save Changes"
run: "Run Query" run: "Run Query"
undo: "Revert"
delete: "Delete"
recover: "Undelete Query"

View File

@ -125,6 +125,13 @@ SQL
class DataExplorer::Query class DataExplorer::Query
attr_accessor :id, :name, :description, :sql, :defaults 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 def param_names
param_info = DataExplorer.extract_params sql param_info = DataExplorer.extract_params sql
param_info[:names] param_info[:names]
@ -171,16 +178,19 @@ SQL
def to_hash def to_hash
{ {
id: @id, id: @id,
name: @name || 'Query', name: @name,
description: @description || '', description: @description,
sql: @sql || 'SELECT 1', sql: @sql,
defaults: @defaults || {}, defaults: @defaults,
} }
end end
def self.find(id) def self.find(id, opts={})
hash = DataExplorer.pstore_get("q:#{id}") 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 from_hash hash
end end
@ -212,7 +222,6 @@ SQL
require_dependency 'application_controller' require_dependency 'application_controller'
class DataExplorer::QueryController < ::ApplicationController class DataExplorer::QueryController < ::ApplicationController
requires_plugin DataExplorer.plugin_name requires_plugin DataExplorer.plugin_name
skip_before_filter :check_xhr, only: [:show]
def index def index
# guardian.ensure_can_use_data_explorer! # guardian.ensure_can_use_data_explorer!
@ -220,14 +229,15 @@ SQL
render_serialized queries, DataExplorer::QuerySerializer, root: 'queries' render_serialized queries, DataExplorer::QuerySerializer, root: 'queries'
end end
skip_before_filter :check_xhr, only: [:show]
def show def show
check_xhr unless params[:export]
query = DataExplorer::Query.find(params[:id].to_i) query = DataExplorer::Query.find(params[:id].to_i)
if params[:export] 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 response.sending_file = true
else
check_xhr
end end
# guardian.ensure_can_see! query # guardian.ensure_can_see! query
@ -243,18 +253,26 @@ SQL
# guardian.ensure_can_create_explorer_query! # guardian.ensure_can_create_explorer_query!
query = DataExplorer::Query.from_hash params.require(:query) query = DataExplorer::Query.from_hash params.require(:query)
# Set the ID _only_ if undeleting binding.pry
if params[:recover] query.id = nil # json import will assign an id, which is wrong
query.id = params[:id].to_i
end
query.save query.save
render_serialized query, DataExplorer::QuerySerializer, root: 'queries' render_serialized query, DataExplorer::QuerySerializer, root: 'query'
end end
def update 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) 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| [:name, :sql, :defaults, :description].each do |sym|
query.send("#{sym}=", hash[sym]) if hash[sym] query.send("#{sym}=", hash[sym]) if hash[sym]
end end
@ -266,7 +284,8 @@ SQL
def destroy def destroy
query = DataExplorer::Query.find(params[:id].to_i) query = DataExplorer::Query.find(params[:id].to_i)
query.destroy query.destroy
render nothing: true
render json: {success: true, errors: []}
end end
def run def run