Bunch of progress. Tuesday 1PM
This commit is contained in:
parent
78dafcc631
commit
dba181d92e
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -17,7 +17,18 @@
|
|||
<div class="sql">
|
||||
{{textarea value=selectedItem.sql}}
|
||||
</div>
|
||||
<div class="left-buttons">
|
||||
{{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="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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -31,3 +31,6 @@ en:
|
|||
export: "Export"
|
||||
save: "Save Changes"
|
||||
run: "Run Query"
|
||||
undo: "Revert"
|
||||
delete: "Delete"
|
||||
recover: "Undelete Query"
|
||||
|
|
53
plugin.rb
53
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
|
||||
|
|
Loading…
Reference in New Issue