diff --git a/assets/javascripts/discourse/components/param-input.js.es6 b/assets/javascripts/discourse/components/param-input.js.es6 new file mode 100644 index 0000000..4a86a1e --- /dev/null +++ b/assets/javascripts/discourse/components/param-input.js.es6 @@ -0,0 +1,92 @@ +const layoutMap = { + int: 'int', + bigint: 'int', + boolean: 'boolean', + string: 'generic', + time: 'generic', + date: 'generic', + datetime: 'generic', + double: 'string', + inet: 'generic', + user_id: 'user_id', + post_id: 'string', + topic_id: 'int', + category_id: 'int', + group_id: 'int', + badge_id: 'int', + int_list: 'generic', + string_list: 'generic' +}; + +function allowsInputTypeTime() { + try { + const inp = document.createElement('input'); + inp.attributes.type = 'time'; + inp.attributes.type = 'date'; + return true; + } catch (e) { + return false; + } +} + +export default Ember.Component.extend({ + + classNameBindings: ['valid:valid:invalid', ':param'], + + boolTypes: [ {name: I18n.t('explorer.types.bool.true'), id: 'Y'}, {name: I18n.t('explorer.types.bool.false'), id: 'N'}, {name: I18n.t('explorer.types.bool.null_'), id: '#null'} ], + + value: function(key, value, previousValue) { + if (arguments.length > 1) { + this.get('params')[this.get('info.identifier')] = value.toString(); + } + return this.get('params')[this.get('info.identifier')]; + }.property('params', 'pname'), + + valid: function() { + const type = this.get('info.type'), + value = this.get('value'); + + if (Em.isEmpty(this.get('value'))) { + return this.get('info.nullable'); + } + + function matches(regex) { + return regex.test(value); + } + + const intVal = parseInt(value, 10); + const intValid = !isNaN(intVal) && intVal < 2147483648 && intVal > -2147483649; + switch (type) { + case 'int': + return /^-?\d+$/.test(value) && intValid; + case 'bigint': + return /^-?\d+$/.test(value) && !isNaN(intVal); + case 'boolean': + return /^Y|N|#null|true|false/.test(value); + case 'double': + return !isNaN(parseFloat(value)); + case 'int_list': + return value.split(',').every(function(i) { + return /^(-?\d+|null)$/.test(i.trim()); + }); + case 'post_id': + return /^\d+$/.test(value) || /\d+\/\d+(\?u=.*)?$/.test(value); + } + return true; + }.property('value', 'info.type', 'info.nullable'), + + layoutType: function() { + const type = this.get('info.type'); + if ((type === "time" || type === "date") && !allowsInputTypeTime()) { + return "string"; + } + if (layoutMap[type]) { + return layoutMap[type]; + } + return type; + }.property('info.type'), + + layoutName: function() { + return "admin/components/q-params/" + this.get('layoutType'); + }.property('layoutType') +}); diff --git a/assets/javascripts/discourse/models/query.js.es6 b/assets/javascripts/discourse/models/query.js.es6 index a9ced7f..a12f85d 100644 --- a/assets/javascripts/discourse/models/query.js.es6 +++ b/assets/javascripts/discourse/models/query.js.es6 @@ -7,37 +7,35 @@ const Query = RestModel.extend({ _init: function() { this._super(); - if (!this.get('options')) { - this.set('options', {defaults:{}}); - } this.set('dirty', false); }.on('init'), _initParams: function() { this.resetParams(); - }.on('init').observes('param_names'), - - // the server uses 'qopts' and the client uses 'options' due to ActiveRecord - // freaking out if a serialized value is named 'options' - options: Em.computed.alias('qopts'), + }.on('init').observes('param_info'), markDirty: function() { this.set('dirty', true); - }.observes('name', 'description', 'sql', 'options', 'options.defaults'), + }.observes('name', 'description', 'sql'), markNotDirty() { this.set('dirty', false); }, + hasParams: function() { + return this.get('param_info.length') > 0; + }.property('param_info'), + resetParams() { const newParams = {}; const oldParams = this.get('params'); - const defaults = this.get('options.defaults') || {}; - (this.get('param_names') || []).forEach(function(name) { - if (defaults[name]) { - newParams[name] = defaults[name]; - } else if (oldParams[name]) { + const paramInfo = this.get('param_info') || []; + paramInfo.forEach(function(pinfo) { + const name = pinfo.identifier; + if (oldParams[pinfo.identifier]) { newParams[name] = oldParams[name]; + } else if (pinfo['default'] !== null) { + newParams[name] = pinfo['default']; } else { newParams[name] = ''; } @@ -45,19 +43,6 @@ const Query = RestModel.extend({ this.set('params', newParams); }, - saveDefaults() { - const currentParams = this.get('params'); - let defaults = {}; - (this.get('param_names') || []).forEach(function(name) { - if (currentParams[name]) { - defaults[name] = currentParams[name]; - } else { - delete defaults[name]; - } - }); - this.set('options.defaults', defaults); - }, - 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"); @@ -88,15 +73,11 @@ const Query = RestModel.extend({ props.id = this.get('id'); } return props; - }, - - run() { - console.log("Called query#run"); } }); Query.reopenClass({ - updatePropertyNames: ["name", "description", "sql", "qopts"] + updatePropertyNames: ["name", "description", "sql"] }); export default Query; diff --git a/assets/javascripts/discourse/templates/admin/components/q-params/boolean.hbs b/assets/javascripts/discourse/templates/admin/components/q-params/boolean.hbs new file mode 100644 index 0000000..fe27cf4 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/components/q-params/boolean.hbs @@ -0,0 +1,6 @@ +{{#if info.nullable}} + {{combo-box valueAttribute="id" value=value nameProperty="name" content=boolTypes}} +{{else}} + {{input type="checkbox" checked=value}} +{{/if}} +{{info.identifier}} diff --git a/assets/javascripts/discourse/templates/admin/components/q-params/generic.hbs b/assets/javascripts/discourse/templates/admin/components/q-params/generic.hbs new file mode 100644 index 0000000..2b0fd48 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/components/q-params/generic.hbs @@ -0,0 +1,2 @@ +{{text-field value=value}} +{{info.identifier}} diff --git a/assets/javascripts/discourse/templates/admin/components/q-params/int.hbs b/assets/javascripts/discourse/templates/admin/components/q-params/int.hbs new file mode 100644 index 0000000..efb887d --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/components/q-params/int.hbs @@ -0,0 +1,2 @@ +{{input type="number" value=value}} +{{info.identifier}} diff --git a/assets/javascripts/discourse/templates/admin/components/q-params/string.hbs b/assets/javascripts/discourse/templates/admin/components/q-params/string.hbs new file mode 100644 index 0000000..2b0fd48 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/components/q-params/string.hbs @@ -0,0 +1,2 @@ +{{text-field value=value}} +{{info.identifier}} diff --git a/assets/javascripts/discourse/templates/admin/components/q-params/user_id.hbs b/assets/javascripts/discourse/templates/admin/components/q-params/user_id.hbs new file mode 100644 index 0000000..d6bcde8 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/components/q-params/user_id.hbs @@ -0,0 +1,2 @@ +{{user-selector usernames=value single="true"}} +{{info.identifier}} diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs index c613299..3bb34e1 100644 --- a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs +++ b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs @@ -75,14 +75,20 @@