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 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);
});
}

View File

@ -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("<br>"));
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
parentController.addCreatedRecord(query.target);
}).catch(popupAjaxError);
}
}

View File

@ -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() {

View File

@ -17,7 +17,18 @@
<div class="sql">
{{textarea value=selectedItem.sql}}
</div>
{{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}}
<div class="left-buttons">
{{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"}}
</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}}

View File

@ -1,13 +1,13 @@
<h3>Queries</h3>
<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="importQuery" label="explorer.import.label" icon="upload"}}
</div>
{{#if showCreate}}
<div class="query-create">
{{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>
{{/if}}
<hr>

View File

@ -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 {

View File

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

View File

@ -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