From 3cc45fa7144829902b1cc14543a81f7348f3b977 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 26 Jan 2021 13:35:10 +0530 Subject: [PATCH 01/38] frontend validator apis --- .../wizard/components/field-validators.js.es6 | 9 +++++++++ .../wizard/components/validator.js.es6 | 16 ++++++++++++++++ .../templates/components/field-validators.hbs | 13 +++++++++++++ .../wizard/templates/components/validator.hbs | 5 +++++ .../wizard/templates/components/wizard-field.hbs | 12 +++++++----- 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 assets/javascripts/wizard/components/field-validators.js.es6 create mode 100644 assets/javascripts/wizard/components/validator.js.es6 create mode 100644 assets/javascripts/wizard/templates/components/field-validators.hbs create mode 100644 assets/javascripts/wizard/templates/components/validator.hbs diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 new file mode 100644 index 00000000..8607379c --- /dev/null +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; +import { observes } from "discourse-common/utils/decorators"; +export default Component.extend({ + actions:{ + perform() { + this.toggleProperty('performValidation'); + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 new file mode 100644 index 00000000..9eb3ad6c --- /dev/null +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -0,0 +1,16 @@ +import Component from "@ember/component"; +import { not } from "@ember/object/computed"; +import { observes } from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNameBindings: ['isValid', 'isInvalid'], + validMessageKey: null, + invalidMessageKey: null, + isValid: null, + isInvalid: not('isValid'), + layoutName: 'components/validator', // useful for sharing the template with extending components + @observes('perform') + performValidation() { + this.validate(); + }, +}); \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs new file mode 100644 index 00000000..197fd112 --- /dev/null +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -0,0 +1,13 @@ +{{#if field.validations}} + {{#each field.validations.top as |validation|}} + {{validation.component field=field perform=perform}} + {{/each}} + + {{yield (hash perform=(action 'perform')) }} + + {{#each field.validations.bottom as |validation|}} + {{validation.component field=field perform=perform}} + {{/each}} +{{else}} + {{yield}} +{{/if}} diff --git a/assets/javascripts/wizard/templates/components/validator.hbs b/assets/javascripts/wizard/templates/components/validator.hbs new file mode 100644 index 00000000..09a4c262 --- /dev/null +++ b/assets/javascripts/wizard/templates/components/validator.hbs @@ -0,0 +1,5 @@ +{{#if isValid}} + {{i18n validMessageKey}} +{{else}} + {{i18n invalidMessageKey}} +{{/if}} diff --git a/assets/javascripts/wizard/templates/components/wizard-field.hbs b/assets/javascripts/wizard/templates/components/wizard-field.hbs index 5f524e92..8f342e54 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field.hbs @@ -10,11 +10,13 @@
{{cookedDescription}}
{{/if}} -{{#if inputComponentName}} -
- {{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard}} -
-{{/if}} +{{#field-validators field=field as |validators|}} + {{#if inputComponentName}} +
+ {{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard focusOut=validators.perform}} +
+ {{/if}} +{{/field-validators}} {{#if field.char_counter}} {{#if textType}} From 5d749fe42651c1d2abd3e5e8eab15fb01bb87839 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Wed, 27 Jan 2021 10:38:26 +0530 Subject: [PATCH 02/38] some changes --- .../realtime-validation-settings.js.es6 | 5 +++++ .../components/wizard-custom-field.js.es6 | 13 +++++++++++++ .../discourse/lib/wizard-schema.js.es6 | 4 ++++ .../discourse/routes/admin-wizards-wizard.js.es6 | 5 +++-- .../components/realtime-validation-settings.hbs | 15 +++++++++++++++ .../templates/components/wizard-custom-field.hbs | 4 ++++ .../wizard/components/alpha-validator.js.es6 | 13 +++++++++++++ config/locales/client.en.yml | 3 +++ controllers/custom_wizard/admin/wizard.rb | 1 + lib/custom_wizard/realtime_validation.rb | 7 +++++++ plugin.rb | 1 + 11 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 assets/javascripts/discourse/components/realtime-validation-settings.js.es6 create mode 100644 assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs create mode 100644 assets/javascripts/wizard/components/alpha-validator.js.es6 create mode 100644 lib/custom_wizard/realtime_validation.rb diff --git a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 new file mode 100644 index 00000000..c9862978 --- /dev/null +++ b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +Component.extend({ + +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index 97f003df..87b27a89 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -4,6 +4,7 @@ import { computed } from "@ember/object"; import { selectKitContent } from '../lib/wizard'; import UndoChanges from '../mixins/undo-changes'; import Component from "@ember/component"; +import wizardSchema from '../lib/wizard-schema'; export default Component.extend(UndoChanges, { componentType: 'field', @@ -26,6 +27,18 @@ export default Component.extend(UndoChanges, { showAdvanced: alias('field.type'), messageUrl: 'https://thepavilion.io/t/2809', + @discourseComputed('field.type') + validations(type) { + const applicableToField = []; + for(let validation in wizardSchema.field.validations) { + if (wizardSchema.field.validations[validation].includes(type)) { + applicableToField.push(validation); + } + } + + return applicableToField; + }, + @discourseComputed('field.type') isDateTime(type) { return ['date_time', 'date', 'time'].indexOf(type) > -1; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 7d34a79d..36e1bb70 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -244,6 +244,10 @@ export function buildFieldTypes(types) { wizardSchema.field.types = types; } +export function buildFieldValidations(validations) { + wizardSchema.field.validations = validations; +} + if (Discourse.SiteSettings.wizard_apis_enabled) { wizardSchema.action.types.send_to_api = { api: null, diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 index 588936f8..e92e9d60 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 @@ -1,5 +1,5 @@ import DiscourseRoute from "discourse/routes/discourse"; -import { buildFieldTypes } from '../lib/wizard-schema'; +import { buildFieldTypes, buildFieldValidations } from '../lib/wizard-schema'; import EmberObject, { set } from "@ember/object"; import { A } from "@ember/array"; import { all } from "rsvp"; @@ -12,7 +12,8 @@ export default DiscourseRoute.extend({ afterModel(model) { buildFieldTypes(model.field_types); - + buildFieldValidations(model.realtime_validations); + return all([ this._getThemes(model), this._getApis(model), diff --git a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs new file mode 100644 index 00000000..2e6ee32e --- /dev/null +++ b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs @@ -0,0 +1,15 @@ +
+ +
+ +{{#each validations as |validation|}} + + {{i18n (concat 'admin.wizard.field.validations.' validation)}} {{input type="checkbox" }} + +
+ {{input type="radio"}} :before +
+
+ {{input type="radio"}} :after +
+{{/each}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 563ab716..20b3b602 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -221,4 +221,8 @@ {{/if}} + + {{#if validations}} + {{realtime-validation-settings field=field validations=validations}} + {{/if}} {{/if}} diff --git a/assets/javascripts/wizard/components/alpha-validator.js.es6 b/assets/javascripts/wizard/components/alpha-validator.js.es6 new file mode 100644 index 00000000..dfa16406 --- /dev/null +++ b/assets/javascripts/wizard/components/alpha-validator.js.es6 @@ -0,0 +1,13 @@ +import WizardFieldValidator from "../../wizard/components/validator"; + +export default WizardFieldValidator.extend({ + validMessageKey: 'hello', + invalidMessageKey: 'world', + validate() { + if(this.field.value) { + this.field.value.length > 0 ? this.set('isValid', true) : this.set('isValid', false); + } else { + this.set('isValid', false); + } + } +}); \ No newline at end of file diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 28304120..9e91654e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -176,6 +176,9 @@ en: date_time_format: label: "Format" instructions: "Moment.js format" + validations: + header: "Realtime Validation Settings" + suggested_topics: "Suggested Topics" type: text: "Text" diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index 9859f115..d27ae036 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -8,6 +8,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController each_serializer: CustomWizard::BasicWizardSerializer ), field_types: CustomWizard::Field.types, + realtime_validations: CustomWizard::RealtimeValidation.types, custom_fields: custom_field_list ) end diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb new file mode 100644 index 00000000..062045bb --- /dev/null +++ b/lib/custom_wizard/realtime_validation.rb @@ -0,0 +1,7 @@ +class CustomWizard::RealtimeValidation + cattr_accessor :types + @@types ||= { + suggested_topics: [:text] + } + end + \ No newline at end of file diff --git a/plugin.rb b/plugin.rb index 52b8348c..0521cc10 100644 --- a/plugin.rb +++ b/plugin.rb @@ -58,6 +58,7 @@ after_initialize do ../lib/custom_wizard/cache.rb ../lib/custom_wizard/custom_field.rb ../lib/custom_wizard/field.rb + ../lib/custom_wizard/realtime_validation.rb ../lib/custom_wizard/mapper.rb ../lib/custom_wizard/log.rb ../lib/custom_wizard/step_updater.rb From 79e2368b57b6e18ae7a56ddecf79f18ff02876d7 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Sat, 30 Jan 2021 23:16:04 +0530 Subject: [PATCH 03/38] validation framework code working --- .../realtime-validation-settings.js.es6 | 18 +++++++++++--- .../components/wizard-custom-field.js.es6 | 4 ++-- .../realtime-validation-settings.hbs | 24 +++++++++---------- .../wizard/components/field-validators.js.es6 | 4 ++-- .../wizard/components/validator.js.es6 | 9 ++++--- .../templates/components/field-validators.hbs | 14 +++++------ config/locales/client.en.yml | 2 ++ controllers/custom_wizard/admin/wizard.rb | 5 ++-- extensions/wizard_field.rb | 2 ++ lib/custom_wizard/builder.rb | 1 + lib/custom_wizard/field.rb | 3 ++- lib/custom_wizard/realtime_validation.rb | 2 +- .../custom_wizard/wizard_field_serializer.rb | 12 ++++++++++ 13 files changed, 67 insertions(+), 33 deletions(-) diff --git a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 index c9862978..33796cf3 100644 --- a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 +++ b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 @@ -1,5 +1,17 @@ import Component from "@ember/component"; -Component.extend({ - -}); \ No newline at end of file +export default Component.extend({ + init(){ + this._super(...arguments); + if (!this.validations) return; + + if (!this.field.validations) { + const validations = {}; + this.validations.forEach((validation) => { + validations[validation] = {}; + }); + + this.set('field.validations', EmberObject.create(validations)); + } + } +}); diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index 87b27a89..0da2cb31 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -31,8 +31,8 @@ export default Component.extend(UndoChanges, { validations(type) { const applicableToField = []; for(let validation in wizardSchema.field.validations) { - if (wizardSchema.field.validations[validation].includes(type)) { - applicableToField.push(validation); + if (wizardSchema.field.validations[validation]["type"] === type) { + applicableToField.push(validation) } } diff --git a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs index 2e6ee32e..7ca7b502 100644 --- a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs +++ b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs @@ -1,15 +1,15 @@
- -{{#each validations as |validation|}} - - {{i18n (concat 'admin.wizard.field.validations.' validation)}} {{input type="checkbox" }} - -
- {{input type="radio"}} :before -
-
- {{input type="radio"}} :after -
-{{/each}} \ No newline at end of file +{{#each-in field.validations as |name props|}} + + {{i18n (concat 'admin.wizard.field.validations.' name)}} + {{input type="checkbox" checked=props.status }} + +
+ {{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} +
+
+ {{radio-button name=(concat name field.id) value="below" selection=props.position}} {{i18n 'admin.wizard.field.validations.below'}} +
+{{/each-in}} diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index 8607379c..51a5b126 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -3,7 +3,7 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ actions:{ perform() { - this.toggleProperty('performValidation'); + this.appEvents.trigger('custom-wizard:validate'); } - } + }, }); \ No newline at end of file diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index 9eb3ad6c..68b6fb35 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -9,8 +9,11 @@ export default Component.extend({ isValid: null, isInvalid: not('isValid'), layoutName: 'components/validator', // useful for sharing the template with extending components - @observes('perform') - performValidation() { - this.validate(); + didInsertElement() { + this.appEvents.on('custom-wizard:validate', this, this.validate); }, + + willDestroyElement() { + this.appEvents.off('custom-wizard:validate', this, this.validate); + } }); \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs index 197fd112..fd5872b7 100644 --- a/assets/javascripts/wizard/templates/components/field-validators.hbs +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -1,13 +1,13 @@ {{#if field.validations}} - {{#each field.validations.top as |validation|}} - {{validation.component field=field perform=perform}} - {{/each}} + {{#each-in field.validations.above as |name validation|}} + {{component validation.component field=field}} + {{/each-in}} - {{yield (hash perform=(action 'perform')) }} + {{yield (hash perform=(action 'perform'))}} - {{#each field.validations.bottom as |validation|}} - {{validation.component field=field perform=perform}} - {{/each}} + {{#each-in field.validations.below as |name validation|}} + {{component validation.component field=field}} + {{/each-in}} {{else}} {{yield}} {{/if}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 9e91654e..bb1d03c0 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -179,6 +179,8 @@ en: validations: header: "Realtime Validation Settings" suggested_topics: "Suggested Topics" + above: "Above" + below: "Below" type: text: "Text" diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index d27ae036..1bc4ece8 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -64,7 +64,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController output: [], ] end - + def save_wizard_params params.require(:wizard).permit( :id, @@ -105,7 +105,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController :limit, :property, prefill: mapped_params, - content: mapped_params + content: mapped_params, + validations: {}, ] ], actions: [ diff --git a/extensions/wizard_field.rb b/extensions/wizard_field.rb index 5b73c6a3..e62f5d9a 100644 --- a/extensions/wizard_field.rb +++ b/extensions/wizard_field.rb @@ -4,6 +4,7 @@ module CustomWizardFieldExtension :description, :image, :key, + :validations, :min_length, :max_length, :char_counter, @@ -20,6 +21,7 @@ module CustomWizardFieldExtension @description = attrs[:description] @image = attrs[:image] @key = attrs[:key] + @validations = attrs[:validations] @min_length = attrs[:min_length] @max_length = attrs[:max_length] @char_counter = attrs[:char_counter] diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 996b0737..70078b30 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -158,6 +158,7 @@ class CustomWizard::Builder params[:description] = field_template['description'] if field_template['description'] params[:image] = field_template['image'] if field_template['image'] params[:key] = field_template['key'] if field_template['key'] + params[:validations] = field_template['validations'] if field_template['validations'] params[:min_length] = field_template['min_length'] if field_template['min_length'] params[:max_length] = field_template['max_length'] if field_template['max_length'] params[:char_counter] = field_template['char_counter'] if field_template['char_counter'] diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb index 0c19b321..f52e2d5a 100644 --- a/lib/custom_wizard/field.rb +++ b/lib/custom_wizard/field.rb @@ -5,7 +5,8 @@ class CustomWizard::Field min_length: nil, max_length: nil, prefill: nil, - char_counter: nil + char_counter: nil, + validations: nil }, textarea: { min_length: nil, diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index 062045bb..422211d3 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -1,7 +1,7 @@ class CustomWizard::RealtimeValidation cattr_accessor :types @@types ||= { - suggested_topics: [:text] + suggested_topics: {type: :text, component: "alpha-validator"} } end \ No newline at end of file diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index 805b0a88..f28a86d8 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -8,6 +8,7 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer :limit, :property, :content, + :validations, :max_length, :char_counter, :number @@ -58,6 +59,17 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer object.choices.present? end + def validations + validations = {} + object.validations&.each do |name, props| + next unless props["status"] + validations[props["position"]] ||= {} + validations[props["position"]][name] = props.merge CustomWizard::RealtimeValidation.types[name.to_sym] + end + + validations + end + def max_length object.max_length end From ffd2101a30ef079a83580a057df0805d6352a826 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 1 Feb 2021 19:28:37 +0530 Subject: [PATCH 04/38] added backend validation mechanism and refined code --- .../components/wizard-custom-field.js.es6 | 2 +- .../realtime-validation-settings.hbs | 2 +- .../wizard/components/alpha-validator.js.es6 | 13 -------- .../components/suggested-validator.js.es6 | 20 ++++++++++++ .../wizard/components/validator.js.es6 | 31 +++++++++++++++++-- .../templates/components/field-validators.hbs | 4 +-- .../components/suggested-validator.hbs | 0 config/routes.rb | 1 + .../custom_wizard/realtime_validation.rb | 8 +++++ lib/custom_wizard/realtime_validation.rb | 30 ++++++++++++++++-- plugin.rb | 1 + 11 files changed, 89 insertions(+), 23 deletions(-) delete mode 100644 assets/javascripts/wizard/components/alpha-validator.js.es6 create mode 100644 assets/javascripts/wizard/components/suggested-validator.js.es6 create mode 100644 assets/javascripts/wizard/templates/components/suggested-validator.hbs create mode 100644 controllers/custom_wizard/realtime_validation.rb diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index 0da2cb31..cc9d56eb 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -31,7 +31,7 @@ export default Component.extend(UndoChanges, { validations(type) { const applicableToField = []; for(let validation in wizardSchema.field.validations) { - if (wizardSchema.field.validations[validation]["type"] === type) { + if ((wizardSchema.field.validations[validation]["types"]).includes(type)) { applicableToField.push(validation) } } diff --git a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs index 7ca7b502..487c4de6 100644 --- a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs +++ b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs @@ -3,8 +3,8 @@ {{#each-in field.validations as |name props|}} - {{i18n (concat 'admin.wizard.field.validations.' name)}} {{input type="checkbox" checked=props.status }} + {{i18n (concat 'admin.wizard.field.validations.' name)}}
{{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} diff --git a/assets/javascripts/wizard/components/alpha-validator.js.es6 b/assets/javascripts/wizard/components/alpha-validator.js.es6 deleted file mode 100644 index dfa16406..00000000 --- a/assets/javascripts/wizard/components/alpha-validator.js.es6 +++ /dev/null @@ -1,13 +0,0 @@ -import WizardFieldValidator from "../../wizard/components/validator"; - -export default WizardFieldValidator.extend({ - validMessageKey: 'hello', - invalidMessageKey: 'world', - validate() { - if(this.field.value) { - this.field.value.length > 0 ? this.set('isValid', true) : this.set('isValid', false); - } else { - this.set('isValid', false); - } - } -}); \ No newline at end of file diff --git a/assets/javascripts/wizard/components/suggested-validator.js.es6 b/assets/javascripts/wizard/components/suggested-validator.js.es6 new file mode 100644 index 00000000..953acfa1 --- /dev/null +++ b/assets/javascripts/wizard/components/suggested-validator.js.es6 @@ -0,0 +1,20 @@ +import WizardFieldValidator from "../../wizard/components/validator"; +import { ajax } from "discourse/lib/ajax"; +import { getToken } from "wizard/lib/ajax"; +import { getOwner } from "discourse-common/lib/get-owner"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default WizardFieldValidator.extend({ + validMessageKey: 'hello', + invalidMessageKey: 'world', + validate() { + this.backendValidate({title: this.get("field.value")}).then(response => { + console.log(response) + }) + }, + + init() { + this._super(...arguments); + + } +}); \ No newline at end of file diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index 68b6fb35..f46b8319 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -1,6 +1,7 @@ import Component from "@ember/component"; import { not } from "@ember/object/computed"; -import { observes } from "discourse-common/utils/decorators"; +import { ajax } from "discourse/lib/ajax"; +import { getToken } from "wizard/lib/ajax"; export default Component.extend({ classNameBindings: ['isValid', 'isInvalid'], @@ -9,11 +10,35 @@ export default Component.extend({ isValid: null, isInvalid: not('isValid'), layoutName: 'components/validator', // useful for sharing the template with extending components + init() { + this._super(...arguments); + + if (this.get('validation.backend')) { + // set a function that can be called as often as it need to + // from the derived component + this.backendValidate = (params) => { + return ajax('/w/realtime_validation', { + type: 'put', + data: { + validation: this.get('name'), + authenticity_token: getToken(), + ...params + } + }); + } + + } + }, + didInsertElement() { - this.appEvents.on('custom-wizard:validate', this, this.validate); + this.appEvents.on('custom-wizard:validate', this, this.checkIsValid); }, willDestroyElement() { - this.appEvents.off('custom-wizard:validate', this, this.validate); + this.appEvents.off('custom-wizard:validate', this, this.checkIsValid); + }, + + checkIsValid() { + this.set('isValid', this.validate()); } }); \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs index fd5872b7..a732c3e2 100644 --- a/assets/javascripts/wizard/templates/components/field-validators.hbs +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -1,12 +1,12 @@ {{#if field.validations}} {{#each-in field.validations.above as |name validation|}} - {{component validation.component field=field}} + {{component validation.component field=field name=name validation=validation}} {{/each-in}} {{yield (hash perform=(action 'perform'))}} {{#each-in field.validations.below as |name validation|}} - {{component validation.component field=field}} + {{component validation.component field=field name=name validation=validation}} {{/each-in}} {{else}} {{yield}} diff --git a/assets/javascripts/wizard/templates/components/suggested-validator.hbs b/assets/javascripts/wizard/templates/components/suggested-validator.hbs new file mode 100644 index 00000000..e69de29b diff --git a/config/routes.rb b/config/routes.rb index 764c0fdd..7f30e311 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,6 +4,7 @@ CustomWizard::Engine.routes.draw do get ':wizard_id/steps' => 'wizard#index' get ':wizard_id/steps/:step_id' => 'wizard#index' put ':wizard_id/steps/:step_id' => 'steps#update' + put 'realtime_validation' => 'realtime_validation#validate' end Discourse::Application.routes.append do diff --git a/controllers/custom_wizard/realtime_validation.rb b/controllers/custom_wizard/realtime_validation.rb new file mode 100644 index 00000000..a2e0dac1 --- /dev/null +++ b/controllers/custom_wizard/realtime_validation.rb @@ -0,0 +1,8 @@ +class CustomWizard::RealtimeValidationController < ::ApplicationController + def validate + params.require(:validation) + params.require(::CustomWizard::RealtimeValidation.types[params[:validation].to_sym][:required_params]) + + render_json_dump(::CustomWizard::RealtimeValidation.send(params[:validation], params, current_user)) + end +end diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index 422211d3..f015d3fb 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -1,7 +1,31 @@ class CustomWizard::RealtimeValidation cattr_accessor :types @@types ||= { - suggested_topics: {type: :text, component: "alpha-validator"} + suggested_topics: { types: [:text], component: "suggested-validator", backend: true, required_params: [] } } - end - \ No newline at end of file + + class SimilarTopic + def initialize(topic) + @topic = topic + end + + attr_reader :topic + + def blurb + Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb)) + end + end + + def self.suggested_topics(params, current_user) + title = params[:title] + raw = params[:raw] + + if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? + return [] + end + + topics = Topic.similar_to(title, raw, current_user).to_a + topics.map! { |t| SimilarTopic.new(t) } + ::ActiveModel::ArraySerializer.new(topics, each_serializer: SimilarTopicSerializer, root: :similar_topics, rest_serializer: true, scope: ::Guardian.new(current_user)) + end +end diff --git a/plugin.rb b/plugin.rb index 0521cc10..83699751 100644 --- a/plugin.rb +++ b/plugin.rb @@ -47,6 +47,7 @@ after_initialize do ../controllers/custom_wizard/admin/custom_fields.rb ../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/steps.rb + ../controllers/custom_wizard/realtime_validation.rb ../jobs/clear_after_time_wizard.rb ../jobs/refresh_api_access_token.rb ../jobs/set_after_time_wizard.rb From 77ca946745959f44f907d5b312b55344ca396042 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Fri, 5 Feb 2021 18:29:30 +0530 Subject: [PATCH 05/38] completed categories and date after filters --- .../realtime-validation-settings.js.es6 | 18 ++ .../realtime-validation-settings.hbs | 19 +- assets/javascripts/wizard-custom.js | 26 ++ .../components/suggested-validator.js.es6 | 47 ++- .../wizard/components/validator.js.es6 | 3 +- .../initializers/register-widgets.js.es6 | 276 ++++++++++++++++++ .../components/suggested-validator.hbs | 10 + .../templates/components/wizard-field.hbs | 2 +- config/locales/client.en.yml | 4 +- config/routes.rb | 2 +- ..._validation.rb => realtime_validations.rb} | 2 +- lib/custom_wizard/realtime_validation.rb | 8 +- plugin.rb | 2 +- 13 files changed, 402 insertions(+), 17 deletions(-) create mode 100644 assets/javascripts/wizard/initializers/register-widgets.js.es6 rename controllers/custom_wizard/{realtime_validation.rb => realtime_validations.rb} (78%) diff --git a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 index 33796cf3..2fd306e6 100644 --- a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 +++ b/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 @@ -1,4 +1,7 @@ import Component from "@ember/component"; +import EmberObject from "@ember/object"; +import { cloneJSON } from "discourse-common/lib/object"; +import Category from "discourse/models/category"; export default Component.extend({ init(){ @@ -13,5 +16,20 @@ export default Component.extend({ this.set('field.validations', EmberObject.create(validations)); } + + const validationBuffer = cloneJSON(this.get('field.validations')); + let bufferCategories; + if( validationBuffer.similar_topics && (bufferCategories = validationBuffer.similar_topics.categories)) { + const categories = Category.findByIds(bufferCategories); + validationBuffer.similar_topics.categories = categories; + } + this.set('validationBuffer', validationBuffer); + }, + + actions: { + updateValidationCategories(name, validation, categories) { + this.set(`validationBuffer.${name}.categories`, categories); + this.set(`field.validations.${name}.categories`, categories.map(category => category.id)); + } } }); diff --git a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs index 487c4de6..c8d35aa4 100644 --- a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs +++ b/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs @@ -4,8 +4,25 @@ {{#each-in field.validations as |name props|}} {{input type="checkbox" checked=props.status }} - {{i18n (concat 'admin.wizard.field.validations.' name)}} + {{i18n (concat 'admin.wizard.field.validations.' name) }} +
+ {{i18n 'admin.wizard.field.validations.categories'}} +
+ {{category-selector + categories=(get this (concat 'validationBuffer.' name '.categories')) + onChange=(action 'updateValidationCategories' name props) }} +
+
+
+ {{i18n 'admin.wizard.field.validations.date_after'}} +
+ {{date-picker-past + value=(readonly props.date_after) + containerId="date-container" + onSelect=(action (mut props.date_after))}} +
+
{{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}}
diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 5d18328f..dd6bbf0c 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -144,3 +144,29 @@ //= require_tree ./wizard/models //= require_tree ./wizard/routes //= require_tree ./wizard/templates + +//= require discourse/app/components/mount-widget +//= require discourse/app/widgets/widget +//= require discourse/app/widgets/hooks +//= require discourse/app/widgets/decorator-helper +//= require discourse/app/widgets/connector +//= require discourse/app/widgets/post-cooked +//= require discourse/app/lib/highlight-html +//= require discourse/app/lib/highlight-search +//= require discourse/app/lib/constants +//= require discourse/app/lib/click-track +//= require discourse/app/helpers/loading-spinner +//= require discourse/app/widgets/raw-html +//= require discourse/app/lib/dirty-keys + +//= require discourse/app/widgets/search-menu +//= require discourse/app/widgets/search-menu-results +//= require discourse/app/widgets/post +//= require discourse/app/helpers/node +//= require discourse/app/widgets/post-stream + +//= require discourse/app/lib/posts-with-placeholders +//= require discourse/app/lib/transform-post +//= require discourse/app/helpers/category-link +//= require discourse/app/lib/render-tags +//= require discourse/app/helpers/topic-status-icons \ No newline at end of file diff --git a/assets/javascripts/wizard/components/suggested-validator.js.es6 b/assets/javascripts/wizard/components/suggested-validator.js.es6 index 953acfa1..599723e3 100644 --- a/assets/javascripts/wizard/components/suggested-validator.js.es6 +++ b/assets/javascripts/wizard/components/suggested-validator.js.es6 @@ -1,20 +1,53 @@ import WizardFieldValidator from "../../wizard/components/validator"; -import { ajax } from "discourse/lib/ajax"; -import { getToken } from "wizard/lib/ajax"; -import { getOwner } from "discourse-common/lib/get-owner"; -import discourseComputed from "discourse-common/utils/decorators"; +import { deepMerge } from "discourse-common/lib/object"; +import { observes } from "discourse-common/utils/decorators"; +import { cancel, later } from "@ember/runloop"; +import { A } from '@ember/array'; +import EmberObject from "@ember/object"; export default WizardFieldValidator.extend({ validMessageKey: 'hello', invalidMessageKey: 'world', + similarTopics: [], + validate() { - this.backendValidate({title: this.get("field.value")}).then(response => { - console.log(response) - }) + }, + @observes("field.value") + customValidate(){ + const lastKeyUp = new Date(); + this._lastKeyUp = lastKeyUp; + + // One second from now, check to see if the last key was hit when + // we recorded it. If it was, the user paused typing. + cancel(this._lastKeyTimeout); + this._lastKeyTimeout = later(() => { + if (lastKeyUp !== this._lastKeyUp) { + return; + } + + this.updateSimilarTopics(); + }, 1000); }, + updateSimilarTopics(){ + this.backendValidate({ + title: this.get("field.value"), + categories: this.get('validation.categories'), + date_after: this.get('validation.date_after') + }).then((result) => { + const similarTopics = A(deepMerge(result['topics'], result['similar_topics'])); + similarTopics.forEach(function(topic, index) { + similarTopics[index] = EmberObject.create(topic); + }); + + this.set('similarTopics', similarTopics); + }); + }, init() { this._super(...arguments); + }, + actions: { + closeMessage(){} } }); \ No newline at end of file diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index f46b8319..2e5c47e7 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -17,8 +17,7 @@ export default Component.extend({ // set a function that can be called as often as it need to // from the derived component this.backendValidate = (params) => { - return ajax('/w/realtime_validation', { - type: 'put', + return ajax('/realtime_validations', { data: { validation: this.get('name'), authenticity_token: getToken(), diff --git a/assets/javascripts/wizard/initializers/register-widgets.js.es6 b/assets/javascripts/wizard/initializers/register-widgets.js.es6 new file mode 100644 index 00000000..7040d150 --- /dev/null +++ b/assets/javascripts/wizard/initializers/register-widgets.js.es6 @@ -0,0 +1,276 @@ +import { escapeExpression, formatUsername } from "discourse/lib/utilities"; +import I18n from "I18n"; +import RawHtml from "discourse/widgets/raw-html"; +import { avatarImg } from "discourse/widgets/post"; +import { createWidget } from "discourse/widgets/widget"; +import { dateNode } from "discourse/helpers/node"; +import { emojiUnescape } from "discourse/lib/text"; +import { h } from "virtual-dom"; +import highlightSearch from "discourse/lib/highlight-search"; +import { iconNode } from "discourse-common/lib/icon-library"; +import renderTag from "discourse/lib/render-tag"; +import DiscourseURL from "discourse/lib/url"; +import getURL from "discourse-common/lib/get-url"; +import { wantsNewWindow } from "discourse/lib/intercept-click"; +import { htmlSafe } from "@ember/template"; +import { registerUnbound } from "discourse-common/lib/helpers"; +import renderTags from "discourse/lib/render-tags"; +import TopicStatusIcons from "discourse/helpers/topic-status-icons"; + +class Highlighted extends RawHtml { + constructor(html, term) { + super({ html: `${html}` }); + this.term = term; + } + + decorate($html) { + highlightSearch($html[0], this.term); + } +} + + + +export default { + name: "wizard-register-widgets", + after: "custom-routes", + initialize(app) { + if (window.location.pathname.indexOf("/w/") < 0) return; + + createWidget("link", { + tagName: "a", + + href(attrs) { + const route = attrs.route; + if (route) { + const router = this.register.lookup("router:main"); + if (router && router._routerMicrolib) { + const params = [route]; + if (attrs.model) { + params.push(attrs.model); + } + return getURL( + router._routerMicrolib.generate.apply(router._routerMicrolib, params) + ); + } + } else { + return getURL(attrs.href); + } + }, + + buildClasses(attrs) { + const result = []; + result.push("widget-link"); + if (attrs.className) { + result.push(attrs.className); + } + return result; + }, + + buildAttributes(attrs) { + const ret = { + href: this.href(attrs), + title: attrs.title + ? I18n.t(attrs.title, attrs.titleOptions) + : this.label(attrs), + }; + if (attrs.attributes) { + Object.keys(attrs.attributes).forEach( + (k) => (ret[k] = attrs.attributes[k]) + ); + } + return ret; + }, + + label(attrs) { + if (attrs.labelCount && attrs.count) { + return I18n.t(attrs.labelCount, { count: attrs.count }); + } + return attrs.rawLabel || (attrs.label ? I18n.t(attrs.label) : ""); + }, + + html(attrs) { + if (attrs.contents) { + return attrs.contents(); + } + + const result = []; + if (attrs.icon) { + if (attrs.alt) { + let icon = iconNode(attrs.icon); + icon.properties.attributes["alt"] = I18n.t(attrs.alt); + icon.properties.attributes["aria-hidden"] = false; + result.push(icon); + } else { + result.push(iconNode(attrs.icon)); + } + result.push(" "); + } + + if (!attrs.hideLabel) { + let label = this.label(attrs); + + if (attrs.omitSpan) { + result.push(label); + } else { + result.push(h("span.d-label", label)); + } + } + + const currentUser = this.currentUser; + if (currentUser && attrs.badgeCount) { + const val = parseInt(currentUser.get(attrs.badgeCount), 10); + if (val > 0) { + const title = attrs.badgeTitle ? I18n.t(attrs.badgeTitle) : ""; + result.push(" "); + result.push( + h( + "span.badge-notification", + { + className: attrs.badgeClass, + attributes: { title }, + }, + val + ) + ); + } + } + return result; + }, + + click(e) { + if (this.attrs.attributes && this.attrs.attributes.target === "_blank") { + return; + } + + if (wantsNewWindow(e)) { + return; + } + + e.preventDefault(); + + if (this.attrs.action) { + e.preventDefault(); + return this.sendWidgetAction(this.attrs.action, this.attrs.actionParam); + } else { + this.sendWidgetEvent("linkClicked", this.attrs); + } + + return DiscourseURL.routeToTag($(e.target).closest("a")[0]); + }, + }); + createWidget("topic-status", { + tagName: "div.topic-statuses", + + html(attrs) { + const topic = attrs.topic; + const canAct = this.currentUser && !attrs.disableActions; + + const result = []; + TopicStatusIcons.render(topic, function (name, key) { + const iconArgs = key === "unpinned" ? { class: "unpinned" } : null; + const icon = iconNode(name, iconArgs); + + const attributes = { + title: escapeExpression(I18n.t(`topic_statuses.${key}.help`)), + }; + result.push(h(`${canAct ? "a" : "span"}.topic-status`, attributes, icon)); + }); + + return result; + }, + }); + + createSearchResult({ + type: "topic", + linkField: "url", + builder(result, term) { + const topic = result; + + const firstLine = [ + this.attach("topic-status", { topic, disableActions: true }), + h( + "span.topic-title", + { attributes: { "data-topic-id": topic.id } }, + this.siteSettings.use_pg_headlines_for_excerpt && + result.topic_title_headline + ? new RawHtml({ + html: `${emojiUnescape( + result.topic_title_headline + )}`, + }) + : new Highlighted(topic.fancy_title, term) + ), + ]; + + const secondLine = [ + // this.attach("category-link", { + // category: topic.category, + // link: false, + // }), + ]; + // if (this.siteSettings.tagging_enabled) { + // secondLine.push( + // this.attach("discourse-tags", { topic, tagName: "span" }) + // ); + // } + + const link = h("span.topic", [ + h("div.first-line", firstLine), + h("div.second-line", secondLine), + ]); + + return postResult.call(this, result, link, term); + }, + }); + + } +} + +function createSearchResult({ type, linkField, builder }) { + return createWidget(`search-result-${type}`, { + tagName: "ul.list", + + html(attrs) { + return attrs.results.map((r) => { + let searchResultId; + + if (type === "topic") { + searchResultId = r.topic_id; + } else { + searchResultId = r.id; + } + + return h( + "li.item", + this.attach("link", { + href: r[linkField], + contents: () => builder.call(this, r, attrs.term), + className: "search-link", + searchResultId, + searchResultType: type, + searchContextEnabled: attrs.searchContextEnabled, + searchLogId: attrs.searchLogId, + }) + ); + }); + }, + }); +} + +function postResult(result, link, term) { + const html = [link]; + + if (!this.site.mobileView) { + html.push( + h("span.blurb", [ + dateNode(result.created_at), + h("span", " - "), + this.siteSettings.use_pg_headlines_for_excerpt + ? new RawHtml({ html: `${result.blurb}` }) + : new Highlighted(result.blurb, term), + ]) + ); + } + + return html; +} \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/suggested-validator.hbs b/assets/javascripts/wizard/templates/components/suggested-validator.hbs index e69de29b..87ddd03f 100644 --- a/assets/javascripts/wizard/templates/components/suggested-validator.hbs +++ b/assets/javascripts/wizard/templates/components/suggested-validator.hbs @@ -0,0 +1,10 @@ +{{#if similarTopics}} + {{d-icon "times"}} +

{{i18n "composer.similar_topics"}}

+ + +
    + {{mount-widget widget="search-result-topic" args=(hash results=similarTopics)}} +
+{{/if}} + diff --git a/assets/javascripts/wizard/templates/components/wizard-field.hbs b/assets/javascripts/wizard/templates/components/wizard-field.hbs index 8f342e54..b0563384 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field.hbs @@ -13,7 +13,7 @@ {{#field-validators field=field as |validators|}} {{#if inputComponentName}}
- {{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard focusOut=validators.perform}} + {{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard }}
{{/if}} {{/field-validators}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index bb1d03c0..555f8a94 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -178,9 +178,11 @@ en: instructions: "Moment.js format" validations: header: "Realtime Validation Settings" - suggested_topics: "Suggested Topics" + similar_topics: "Similar Topics" above: "Above" below: "Below" + categories: "Categories" + date_after: "Date After" type: text: "Text" diff --git a/config/routes.rb b/config/routes.rb index 7f30e311..3421d55a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,12 +4,12 @@ CustomWizard::Engine.routes.draw do get ':wizard_id/steps' => 'wizard#index' get ':wizard_id/steps/:step_id' => 'wizard#index' put ':wizard_id/steps/:step_id' => 'steps#update' - put 'realtime_validation' => 'realtime_validation#validate' end Discourse::Application.routes.append do mount ::CustomWizard::Engine, at: 'w' post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" + get 'realtime_validations' => 'custom_wizard/realtime_validations#validate' scope module: 'custom_wizard', constraints: AdminConstraint.new do get 'admin/wizards' => 'admin#index' diff --git a/controllers/custom_wizard/realtime_validation.rb b/controllers/custom_wizard/realtime_validations.rb similarity index 78% rename from controllers/custom_wizard/realtime_validation.rb rename to controllers/custom_wizard/realtime_validations.rb index a2e0dac1..38ae1a0d 100644 --- a/controllers/custom_wizard/realtime_validation.rb +++ b/controllers/custom_wizard/realtime_validations.rb @@ -1,4 +1,4 @@ -class CustomWizard::RealtimeValidationController < ::ApplicationController +class CustomWizard::RealtimeValidationsController < ::ApplicationController def validate params.require(:validation) params.require(::CustomWizard::RealtimeValidation.types[params[:validation].to_sym][:required_params]) diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index f015d3fb..b5df6a9e 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -1,7 +1,7 @@ class CustomWizard::RealtimeValidation cattr_accessor :types @@types ||= { - suggested_topics: { types: [:text], component: "suggested-validator", backend: true, required_params: [] } + similar_topics: { types: [:text], component: "suggested-validator", backend: true, required_params: [] } } class SimilarTopic @@ -16,15 +16,19 @@ class CustomWizard::RealtimeValidation end end - def self.suggested_topics(params, current_user) + def self.similar_topics(params, current_user) title = params[:title] raw = params[:raw] + categories = params[:categories] + date_after = params[:date_after] if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? return [] end topics = Topic.similar_to(title, raw, current_user).to_a + topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? + topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? topics.map! { |t| SimilarTopic.new(t) } ::ActiveModel::ArraySerializer.new(topics, each_serializer: SimilarTopicSerializer, root: :similar_topics, rest_serializer: true, scope: ::Guardian.new(current_user)) end diff --git a/plugin.rb b/plugin.rb index 83699751..46cd4e78 100644 --- a/plugin.rb +++ b/plugin.rb @@ -47,7 +47,7 @@ after_initialize do ../controllers/custom_wizard/admin/custom_fields.rb ../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/steps.rb - ../controllers/custom_wizard/realtime_validation.rb + ../controllers/custom_wizard/realtime_validations.rb ../jobs/clear_after_time_wizard.rb ../jobs/refresh_api_access_token.rb ../jobs/set_after_time_wizard.rb From 800795047ac45e4b2b410c12a3859355528c7377 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 12:45:54 +0530 Subject: [PATCH 06/38] added wizard- prefix to component name --- ...ttings.js.es6 => wizard-realtime-validation-settings.js.es6} | 0 .../discourse/templates/components/wizard-custom-field.hbs | 2 +- ...ion-settings.hbs => wizard-realtime-validation-settings.hbs} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename assets/javascripts/discourse/components/{realtime-validation-settings.js.es6 => wizard-realtime-validation-settings.js.es6} (100%) rename assets/javascripts/discourse/templates/components/{realtime-validation-settings.hbs => wizard-realtime-validation-settings.hbs} (100%) diff --git a/assets/javascripts/discourse/components/realtime-validation-settings.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validation-settings.js.es6 similarity index 100% rename from assets/javascripts/discourse/components/realtime-validation-settings.js.es6 rename to assets/javascripts/discourse/components/wizard-realtime-validation-settings.js.es6 diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 20b3b602..26d2cfa7 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -223,6 +223,6 @@ {{/if}} {{#if validations}} - {{realtime-validation-settings field=field validations=validations}} + {{wizard-realtime-validation-settings field=field validations=validations}} {{/if}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs similarity index 100% rename from assets/javascripts/discourse/templates/components/realtime-validation-settings.hbs rename to assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs From 176cd7489a42db8d2c3c95b5f6b80919bfca3911 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 19:22:09 +0530 Subject: [PATCH 07/38] improved admin side settings UI --- .../components/wizard-custom-field.hbs | 7 ++-- .../wizard-realtime-validation-settings.hbs | 36 ++++++++++--------- assets/stylesheets/common/wizard-admin.scss | 13 ++++++- config/locales/client.en.yml | 1 + 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 26d2cfa7..0502d3bc 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -219,10 +219,9 @@
+ {{#if validations}} + {{wizard-realtime-validation-settings field=field validations=validations}} + {{/if}} {{/if}} - - {{#if validations}} - {{wizard-realtime-validation-settings field=field validations=validations}} - {{/if}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs index c8d35aa4..a012cdee 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs @@ -6,27 +6,31 @@ {{input type="checkbox" checked=props.status }} {{i18n (concat 'admin.wizard.field.validations.' name) }} -
+
+
{{i18n 'admin.wizard.field.validations.categories'}}
{{category-selector categories=(get this (concat 'validationBuffer.' name '.categories')) - onChange=(action 'updateValidationCategories' name props) }} + onChange=(action 'updateValidationCategories' name props) + class="wizard" }}
-
-
- {{i18n 'admin.wizard.field.validations.date_after'}} -
- {{date-picker-past - value=(readonly props.date_after) - containerId="date-container" - onSelect=(action (mut props.date_after))}} -
-
- {{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} -
-
- {{radio-button name=(concat name field.id) value="below" selection=props.position}} {{i18n 'admin.wizard.field.validations.below'}} +
+ {{i18n 'admin.wizard.field.validations.date_after'}} +
+ {{date-picker-past + value=(readonly props.date_after) + containerId="date-container" + onSelect=(action (mut props.date_after))}} +
+
+
+ {{i18n 'admin.wizard.field.validations.position'}} +
+ {{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} + {{radio-button name=(concat name field.id) value="below" selection=props.position}} {{i18n 'admin.wizard.field.validations.below'}} +
+
{{/each-in}} diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 8816039b..8daf904d 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -642,4 +642,15 @@ } } } -} \ No newline at end of file +} + +.validation-container { + display: flex; + .validation-section { + width: 250px; + } +} + +.wizard.category-selector { + width: 200px !important; +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 555f8a94..899d0481 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -179,6 +179,7 @@ en: validations: header: "Realtime Validation Settings" similar_topics: "Similar Topics" + position: "Position" above: "Above" below: "Below" categories: "Categories" From cfb65245ea378f7600cb03df238e4ed15906366d Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 19:24:32 +0530 Subject: [PATCH 08/38] renamed endpoint to realtime-validations --- assets/javascripts/wizard/components/validator.js.es6 | 2 +- config/routes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index 2e5c47e7..e272fe52 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -17,7 +17,7 @@ export default Component.extend({ // set a function that can be called as often as it need to // from the derived component this.backendValidate = (params) => { - return ajax('/realtime_validations', { + return ajax('/realtime-validations', { data: { validation: this.get('name'), authenticity_token: getToken(), diff --git a/config/routes.rb b/config/routes.rb index 3421d55a..110d54c5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,7 +9,7 @@ end Discourse::Application.routes.append do mount ::CustomWizard::Engine, at: 'w' post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" - get 'realtime_validations' => 'custom_wizard/realtime_validations#validate' + get 'realtime-validations' => 'custom_wizard/realtime_validations#validate' scope module: 'custom_wizard', constraints: AdminConstraint.new do get 'admin/wizards' => 'admin#index' From 194389c9be55772ea2a85df28c5243226c57077c Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 19:55:23 +0530 Subject: [PATCH 09/38] settings are now prefixed with 'Enable' --- .../components/wizard-realtime-validation-settings.hbs | 2 +- config/locales/client.en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs index a012cdee..e993b543 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs @@ -4,7 +4,7 @@ {{#each-in field.validations as |name props|}} {{input type="checkbox" checked=props.status }} - {{i18n (concat 'admin.wizard.field.validations.' name) }} + {{i18n 'admin.wizard.field.validations.enable' validation_type=(i18n (concat 'admin.wizard.field.validations.' name))}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 899d0481..0ae2abc2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -178,6 +178,7 @@ en: instructions: "Moment.js format" validations: header: "Realtime Validation Settings" + enable: "Enable %{validation_type}" similar_topics: "Similar Topics" position: "Position" above: "Above" From d47770de88413a73268d6b8c92dcab117c034087 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 22:23:13 +0530 Subject: [PATCH 10/38] implemented similar topics UI via ember components --- .../components/suggested-validator.js.es6 | 4 +++- .../wizard/helpers/date-node.js.es6 | 22 +++++++++++++++++++ .../components/suggested-validator.hbs | 13 +++++------ .../components/wizard-similar-topic.hbs | 4 ++++ assets/stylesheets/wizard/custom/field.scss | 14 ++++++++++++ config/locales/client.en.yml | 2 ++ 6 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 assets/javascripts/wizard/helpers/date-node.js.es6 create mode 100644 assets/javascripts/wizard/templates/components/wizard-similar-topic.hbs diff --git a/assets/javascripts/wizard/components/suggested-validator.js.es6 b/assets/javascripts/wizard/components/suggested-validator.js.es6 index 599723e3..b5847c16 100644 --- a/assets/javascripts/wizard/components/suggested-validator.js.es6 +++ b/assets/javascripts/wizard/components/suggested-validator.js.es6 @@ -48,6 +48,8 @@ export default WizardFieldValidator.extend({ }, actions: { - closeMessage(){} + closeMessage(){ + this.set('showMessage', false); + } } }); \ No newline at end of file diff --git a/assets/javascripts/wizard/helpers/date-node.js.es6 b/assets/javascripts/wizard/helpers/date-node.js.es6 new file mode 100644 index 00000000..99fa01f3 --- /dev/null +++ b/assets/javascripts/wizard/helpers/date-node.js.es6 @@ -0,0 +1,22 @@ +import { registerUnbound } from "discourse-common/lib/helpers"; +import { longDate, number, relativeAge } from "discourse/lib/formatter"; + +export default registerUnbound("date-node", function (dt) { + if (typeof dt === "string") { + dt = new Date(dt); + } + if (dt) { + const attributes = { + title: longDate(dt), + "data-time": dt.getTime(), + "data-format": "tiny", + }; + + const finalString = `${relativeAge(dt)}`; + return new Handlebars.SafeString(finalString); + } +}); diff --git a/assets/javascripts/wizard/templates/components/suggested-validator.hbs b/assets/javascripts/wizard/templates/components/suggested-validator.hbs index 87ddd03f..651ad25e 100644 --- a/assets/javascripts/wizard/templates/components/suggested-validator.hbs +++ b/assets/javascripts/wizard/templates/components/suggested-validator.hbs @@ -1,10 +1,9 @@ {{#if similarTopics}} - {{d-icon "times"}} -

{{i18n "composer.similar_topics"}}

- - -
    - {{mount-widget widget="search-result-topic" args=(hash results=similarTopics)}} -
+

{{i18n 'realtime_validations.similar_topics_heading'}}

+
+ {{#each similarTopics as |similarTopic|}} + {{wizard-similar-topic topic=similarTopic}} + {{/each}} +
{{/if}} diff --git a/assets/javascripts/wizard/templates/components/wizard-similar-topic.hbs b/assets/javascripts/wizard/templates/components/wizard-similar-topic.hbs new file mode 100644 index 00000000..eeaaa751 --- /dev/null +++ b/assets/javascripts/wizard/templates/components/wizard-similar-topic.hbs @@ -0,0 +1,4 @@ + + {{html-safe topic.fancy_title}} +
{{date-node topic.created_at}} - {{html-safe topic.blurb}}
+
diff --git a/assets/stylesheets/wizard/custom/field.scss b/assets/stylesheets/wizard/custom/field.scss index c791d41a..8e36ff76 100644 --- a/assets/stylesheets/wizard/custom/field.scss +++ b/assets/stylesheets/wizard/custom/field.scss @@ -155,4 +155,18 @@ .select-kit.combo-box.group-dropdown { min-width: 220px; } +} + +.wizard-similar-topics { + background-color: var(--tertiary-low); + padding: 5px; + .title { + color: var(--primary); + } + + .blurb { + margin-left: 0.5em; + color: var(--primary-high); + font-size: $font-down-1; + } } \ No newline at end of file diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0ae2abc2..903c9fce 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -520,3 +520,5 @@ en: yourself_confirm: title: "Did you forget to add recipients?" body: "Right now this message is only being sent to yourself!" + realtime_validations: + similar_topics_heading: "Your topic is similar to..." From 4883d85d56be2b83080073a6ab3e53d9212a3d1b Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 22:35:04 +0530 Subject: [PATCH 11/38] renamed the validator --- ...ggested-validator.js.es6 => similar-topics-validator.js.es6} | 0 .../{suggested-validator.hbs => similar-topics-validator.hbs} | 0 lib/custom_wizard/realtime_validation.rb | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename assets/javascripts/wizard/components/{suggested-validator.js.es6 => similar-topics-validator.js.es6} (100%) rename assets/javascripts/wizard/templates/components/{suggested-validator.hbs => similar-topics-validator.hbs} (100%) diff --git a/assets/javascripts/wizard/components/suggested-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 similarity index 100% rename from assets/javascripts/wizard/components/suggested-validator.js.es6 rename to assets/javascripts/wizard/components/similar-topics-validator.js.es6 diff --git a/assets/javascripts/wizard/templates/components/suggested-validator.hbs b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs similarity index 100% rename from assets/javascripts/wizard/templates/components/suggested-validator.hbs rename to assets/javascripts/wizard/templates/components/similar-topics-validator.hbs diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index b5df6a9e..72aa5065 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -1,7 +1,7 @@ class CustomWizard::RealtimeValidation cattr_accessor :types @@types ||= { - similar_topics: { types: [:text], component: "suggested-validator", backend: true, required_params: [] } + similar_topics: { types: [:text], component: "similar-topics-validator", backend: true, required_params: [] } } class SimilarTopic From 17ba3f9d771123dce22d7bf3b739e0862b2241cb Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Thu, 11 Feb 2021 22:38:22 +0530 Subject: [PATCH 12/38] removed the widgets framework --- assets/javascripts/wizard-custom.js | 26 -- .../initializers/register-widgets.js.es6 | 276 ------------------ 2 files changed, 302 deletions(-) delete mode 100644 assets/javascripts/wizard/initializers/register-widgets.js.es6 diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index dd6bbf0c..5d18328f 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -144,29 +144,3 @@ //= require_tree ./wizard/models //= require_tree ./wizard/routes //= require_tree ./wizard/templates - -//= require discourse/app/components/mount-widget -//= require discourse/app/widgets/widget -//= require discourse/app/widgets/hooks -//= require discourse/app/widgets/decorator-helper -//= require discourse/app/widgets/connector -//= require discourse/app/widgets/post-cooked -//= require discourse/app/lib/highlight-html -//= require discourse/app/lib/highlight-search -//= require discourse/app/lib/constants -//= require discourse/app/lib/click-track -//= require discourse/app/helpers/loading-spinner -//= require discourse/app/widgets/raw-html -//= require discourse/app/lib/dirty-keys - -//= require discourse/app/widgets/search-menu -//= require discourse/app/widgets/search-menu-results -//= require discourse/app/widgets/post -//= require discourse/app/helpers/node -//= require discourse/app/widgets/post-stream - -//= require discourse/app/lib/posts-with-placeholders -//= require discourse/app/lib/transform-post -//= require discourse/app/helpers/category-link -//= require discourse/app/lib/render-tags -//= require discourse/app/helpers/topic-status-icons \ No newline at end of file diff --git a/assets/javascripts/wizard/initializers/register-widgets.js.es6 b/assets/javascripts/wizard/initializers/register-widgets.js.es6 deleted file mode 100644 index 7040d150..00000000 --- a/assets/javascripts/wizard/initializers/register-widgets.js.es6 +++ /dev/null @@ -1,276 +0,0 @@ -import { escapeExpression, formatUsername } from "discourse/lib/utilities"; -import I18n from "I18n"; -import RawHtml from "discourse/widgets/raw-html"; -import { avatarImg } from "discourse/widgets/post"; -import { createWidget } from "discourse/widgets/widget"; -import { dateNode } from "discourse/helpers/node"; -import { emojiUnescape } from "discourse/lib/text"; -import { h } from "virtual-dom"; -import highlightSearch from "discourse/lib/highlight-search"; -import { iconNode } from "discourse-common/lib/icon-library"; -import renderTag from "discourse/lib/render-tag"; -import DiscourseURL from "discourse/lib/url"; -import getURL from "discourse-common/lib/get-url"; -import { wantsNewWindow } from "discourse/lib/intercept-click"; -import { htmlSafe } from "@ember/template"; -import { registerUnbound } from "discourse-common/lib/helpers"; -import renderTags from "discourse/lib/render-tags"; -import TopicStatusIcons from "discourse/helpers/topic-status-icons"; - -class Highlighted extends RawHtml { - constructor(html, term) { - super({ html: `${html}` }); - this.term = term; - } - - decorate($html) { - highlightSearch($html[0], this.term); - } -} - - - -export default { - name: "wizard-register-widgets", - after: "custom-routes", - initialize(app) { - if (window.location.pathname.indexOf("/w/") < 0) return; - - createWidget("link", { - tagName: "a", - - href(attrs) { - const route = attrs.route; - if (route) { - const router = this.register.lookup("router:main"); - if (router && router._routerMicrolib) { - const params = [route]; - if (attrs.model) { - params.push(attrs.model); - } - return getURL( - router._routerMicrolib.generate.apply(router._routerMicrolib, params) - ); - } - } else { - return getURL(attrs.href); - } - }, - - buildClasses(attrs) { - const result = []; - result.push("widget-link"); - if (attrs.className) { - result.push(attrs.className); - } - return result; - }, - - buildAttributes(attrs) { - const ret = { - href: this.href(attrs), - title: attrs.title - ? I18n.t(attrs.title, attrs.titleOptions) - : this.label(attrs), - }; - if (attrs.attributes) { - Object.keys(attrs.attributes).forEach( - (k) => (ret[k] = attrs.attributes[k]) - ); - } - return ret; - }, - - label(attrs) { - if (attrs.labelCount && attrs.count) { - return I18n.t(attrs.labelCount, { count: attrs.count }); - } - return attrs.rawLabel || (attrs.label ? I18n.t(attrs.label) : ""); - }, - - html(attrs) { - if (attrs.contents) { - return attrs.contents(); - } - - const result = []; - if (attrs.icon) { - if (attrs.alt) { - let icon = iconNode(attrs.icon); - icon.properties.attributes["alt"] = I18n.t(attrs.alt); - icon.properties.attributes["aria-hidden"] = false; - result.push(icon); - } else { - result.push(iconNode(attrs.icon)); - } - result.push(" "); - } - - if (!attrs.hideLabel) { - let label = this.label(attrs); - - if (attrs.omitSpan) { - result.push(label); - } else { - result.push(h("span.d-label", label)); - } - } - - const currentUser = this.currentUser; - if (currentUser && attrs.badgeCount) { - const val = parseInt(currentUser.get(attrs.badgeCount), 10); - if (val > 0) { - const title = attrs.badgeTitle ? I18n.t(attrs.badgeTitle) : ""; - result.push(" "); - result.push( - h( - "span.badge-notification", - { - className: attrs.badgeClass, - attributes: { title }, - }, - val - ) - ); - } - } - return result; - }, - - click(e) { - if (this.attrs.attributes && this.attrs.attributes.target === "_blank") { - return; - } - - if (wantsNewWindow(e)) { - return; - } - - e.preventDefault(); - - if (this.attrs.action) { - e.preventDefault(); - return this.sendWidgetAction(this.attrs.action, this.attrs.actionParam); - } else { - this.sendWidgetEvent("linkClicked", this.attrs); - } - - return DiscourseURL.routeToTag($(e.target).closest("a")[0]); - }, - }); - createWidget("topic-status", { - tagName: "div.topic-statuses", - - html(attrs) { - const topic = attrs.topic; - const canAct = this.currentUser && !attrs.disableActions; - - const result = []; - TopicStatusIcons.render(topic, function (name, key) { - const iconArgs = key === "unpinned" ? { class: "unpinned" } : null; - const icon = iconNode(name, iconArgs); - - const attributes = { - title: escapeExpression(I18n.t(`topic_statuses.${key}.help`)), - }; - result.push(h(`${canAct ? "a" : "span"}.topic-status`, attributes, icon)); - }); - - return result; - }, - }); - - createSearchResult({ - type: "topic", - linkField: "url", - builder(result, term) { - const topic = result; - - const firstLine = [ - this.attach("topic-status", { topic, disableActions: true }), - h( - "span.topic-title", - { attributes: { "data-topic-id": topic.id } }, - this.siteSettings.use_pg_headlines_for_excerpt && - result.topic_title_headline - ? new RawHtml({ - html: `${emojiUnescape( - result.topic_title_headline - )}`, - }) - : new Highlighted(topic.fancy_title, term) - ), - ]; - - const secondLine = [ - // this.attach("category-link", { - // category: topic.category, - // link: false, - // }), - ]; - // if (this.siteSettings.tagging_enabled) { - // secondLine.push( - // this.attach("discourse-tags", { topic, tagName: "span" }) - // ); - // } - - const link = h("span.topic", [ - h("div.first-line", firstLine), - h("div.second-line", secondLine), - ]); - - return postResult.call(this, result, link, term); - }, - }); - - } -} - -function createSearchResult({ type, linkField, builder }) { - return createWidget(`search-result-${type}`, { - tagName: "ul.list", - - html(attrs) { - return attrs.results.map((r) => { - let searchResultId; - - if (type === "topic") { - searchResultId = r.topic_id; - } else { - searchResultId = r.id; - } - - return h( - "li.item", - this.attach("link", { - href: r[linkField], - contents: () => builder.call(this, r, attrs.term), - className: "search-link", - searchResultId, - searchResultType: type, - searchContextEnabled: attrs.searchContextEnabled, - searchLogId: attrs.searchLogId, - }) - ); - }); - }, - }); -} - -function postResult(result, link, term) { - const html = [link]; - - if (!this.site.mobileView) { - html.push( - h("span.blurb", [ - dateNode(result.created_at), - h("span", " - "), - this.siteSettings.use_pg_headlines_for_excerpt - ? new RawHtml({ html: `${result.blurb}` }) - : new Highlighted(result.blurb, term), - ]) - ); - } - - return html; -} \ No newline at end of file From b8671cef74c34a44c5b17d999d73b51a63ef82c1 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Sat, 13 Feb 2021 11:58:38 +1100 Subject: [PATCH 13/38] Remove redundant use of "settings" --- ...ation-settings.js.es6 => wizard-realtime-validations.js.es6} | 0 .../discourse/templates/components/wizard-custom-field.hbs | 2 +- ...-validation-settings.hbs => wizard-realtime-validations.hbs} | 0 config/locales/client.en.yml | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename assets/javascripts/discourse/components/{wizard-realtime-validation-settings.js.es6 => wizard-realtime-validations.js.es6} (100%) rename assets/javascripts/discourse/templates/components/{wizard-realtime-validation-settings.hbs => wizard-realtime-validations.hbs} (100%) diff --git a/assets/javascripts/discourse/components/wizard-realtime-validation-settings.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 similarity index 100% rename from assets/javascripts/discourse/components/wizard-realtime-validation-settings.js.es6 rename to assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 0502d3bc..62c5ea1e 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -220,7 +220,7 @@
{{#if validations}} - {{wizard-realtime-validation-settings field=field validations=validations}} + {{wizard-realtime-validations field=field validations=validations}} {{/if}}
{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs similarity index 100% rename from assets/javascripts/discourse/templates/components/wizard-realtime-validation-settings.hbs rename to assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 903c9fce..df0c8801 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -177,7 +177,7 @@ en: label: "Format" instructions: "Moment.js format" validations: - header: "Realtime Validation Settings" + header: "Realtime Validations" enable: "Enable %{validation_type}" similar_topics: "Similar Topics" position: "Position" From 4ca82b9f4da0ea28e9f6d72f094bd10d0c7e97eb Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Sat, 13 Feb 2021 12:51:37 +1100 Subject: [PATCH 14/38] Style updates (WIP) See further comments on thepavilion.io --- .../wizard-realtime-validations.js.es6 | 2 + .../wizard-realtime-validations.hbs | 82 +++++++++++-------- assets/stylesheets/common/wizard-admin.scss | 7 ++ 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 index 2fd306e6..a75fac64 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 @@ -4,6 +4,8 @@ import { cloneJSON } from "discourse-common/lib/object"; import Category from "discourse/models/category"; export default Component.extend({ + classNames: ['realtime-validations'], + init(){ this._super(...arguments); if (!this.validations) return; diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index e993b543..57cf6162 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -1,36 +1,48 @@ -
- -
-{{#each-in field.validations as |name props|}} - - {{input type="checkbox" checked=props.status }} - {{i18n 'admin.wizard.field.validations.enable' validation_type=(i18n (concat 'admin.wizard.field.validations.' name))}} - -
-
- {{i18n 'admin.wizard.field.validations.categories'}} -
- {{category-selector - categories=(get this (concat 'validationBuffer.' name '.categories')) - onChange=(action 'updateValidationCategories' name props) - class="wizard" }} -
-
-
- {{i18n 'admin.wizard.field.validations.date_after'}} -
- {{date-picker-past - value=(readonly props.date_after) - containerId="date-container" - onSelect=(action (mut props.date_after))}} +

{{i18n 'admin.wizard.field.validations.header'}}

+ +
    + {{#each-in field.validations as |name props|}} +
  • + + {{input type="checkbox" checked=props.status }} + {{i18n 'admin.wizard.field.validations.enable' + validation_type=(i18n (concat 'admin.wizard.field.validations.' name))}} + +
    +
    +
    + +
    +
    + {{category-selector + categories=(get this (concat 'validationBuffer.' name '.categories')) + onChange=(action 'updateValidationCategories' name props) + class="wizard"}} +
    +
    +
    +
    + +
    +
    + {{date-picker-past + value=(readonly props.date_after) + containerId="date-container" + onSelect=(action (mut props.date_after))}} +
    +
    +
    +
    + +
    +
    + {{radio-button name=(concat name field.id) value="above" selection=props.position}} + {{i18n 'admin.wizard.field.validations.above'}} + {{radio-button name=(concat name field.id) value="below" selection=props.position}} + {{i18n 'admin.wizard.field.validations.below'}} +
    +
    -
-
- {{i18n 'admin.wizard.field.validations.position'}} -
- {{radio-button name=(concat name field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} - {{radio-button name=(concat name field.id) value="below" selection=props.position}} {{i18n 'admin.wizard.field.validations.below'}} -
-
-
-{{/each-in}} + + {{/each-in}} + diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 8daf904d..6fead6da 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -644,8 +644,15 @@ } } +.realtime-validations ul { + list-style: none; + margin: 0; +} + .validation-container { display: flex; + padding: 1em 0; + .validation-section { width: 250px; } From b74526ae4fc06e9cb6d587009a9d5104d5227c07 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:39:37 +0530 Subject: [PATCH 15/38] fixed formatting --- .../wizard-realtime-validations.js.es6 | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 index a75fac64..6e7daa9e 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 @@ -4,34 +4,40 @@ import { cloneJSON } from "discourse-common/lib/object"; import Category from "discourse/models/category"; export default Component.extend({ - classNames: ['realtime-validations'], - - init(){ - this._super(...arguments); - if (!this.validations) return; + classNames: ["realtime-validations"], - if (!this.field.validations) { - const validations = {}; - this.validations.forEach((validation) => { - validations[validation] = {}; - }); + init() { + this._super(...arguments); + if (!this.validations) return; - this.set('field.validations', EmberObject.create(validations)); - } + if (!this.field.validations) { + const validations = {}; + this.validations.forEach((validation) => { + validations[validation] = {}; + }); - const validationBuffer = cloneJSON(this.get('field.validations')); - let bufferCategories; - if( validationBuffer.similar_topics && (bufferCategories = validationBuffer.similar_topics.categories)) { - const categories = Category.findByIds(bufferCategories); - validationBuffer.similar_topics.categories = categories; - } - this.set('validationBuffer', validationBuffer); - }, - - actions: { - updateValidationCategories(name, validation, categories) { - this.set(`validationBuffer.${name}.categories`, categories); - this.set(`field.validations.${name}.categories`, categories.map(category => category.id)); - } + this.set("field.validations", EmberObject.create(validations)); } + + const validationBuffer = cloneJSON(this.get("field.validations")); + let bufferCategories; + if ( + validationBuffer.similar_topics && + (bufferCategories = validationBuffer.similar_topics.categories) + ) { + const categories = Category.findByIds(bufferCategories); + validationBuffer.similar_topics.categories = categories; + } + this.set("validationBuffer", validationBuffer); + }, + + actions: { + updateValidationCategories(name, validation, categories) { + this.set(`validationBuffer.${name}.categories`, categories); + this.set( + `field.validations.${name}.categories`, + categories.map((category) => category.id) + ); + }, + }, }); From 3de3ea44d083903d3adc217b915b64c921391cd8 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:41:58 +0530 Subject: [PATCH 16/38] fixed formatting for field-validators component --- .../wizard/components/field-validators.js.es6 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index 51a5b126..85811076 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -1,9 +1,9 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - actions:{ - perform() { - this.appEvents.trigger('custom-wizard:validate'); - } + actions: { + perform() { + this.appEvents.trigger("custom-wizard:validate"); }, -}); \ No newline at end of file + }, +}); From 70cc20a919162f982f5e46589ed9e1324311e13e Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:44:46 +0530 Subject: [PATCH 17/38] remove unused keys --- .../wizard/components/similar-topics-validator.js.es6 | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index b5847c16..bc5f1e91 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -6,8 +6,6 @@ import { A } from '@ember/array'; import EmberObject from "@ember/object"; export default WizardFieldValidator.extend({ - validMessageKey: 'hello', - invalidMessageKey: 'world', similarTopics: [], validate() { From afbc4e1f57a7d80c0dce6822eddd950e1ffc4c61 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:46:19 +0530 Subject: [PATCH 18/38] fixed similar topics validator formatting --- .../similar-topics-validator.js.es6 | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index bc5f1e91..5c90ae7f 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -2,52 +2,52 @@ import WizardFieldValidator from "../../wizard/components/validator"; import { deepMerge } from "discourse-common/lib/object"; import { observes } from "discourse-common/utils/decorators"; import { cancel, later } from "@ember/runloop"; -import { A } from '@ember/array'; +import { A } from "@ember/array"; import EmberObject from "@ember/object"; export default WizardFieldValidator.extend({ - similarTopics: [], + similarTopics: [], - validate() { + validate() {}, + @observes("field.value") + customValidate() { + const lastKeyUp = new Date(); + this._lastKeyUp = lastKeyUp; + + // One second from now, check to see if the last key was hit when + // we recorded it. If it was, the user paused typing. + cancel(this._lastKeyTimeout); + this._lastKeyTimeout = later(() => { + if (lastKeyUp !== this._lastKeyUp) { + return; + } + + this.updateSimilarTopics(); + }, 1000); + }, + + updateSimilarTopics() { + this.backendValidate({ + title: this.get("field.value"), + categories: this.get("validation.categories"), + date_after: this.get("validation.date_after"), + }).then((result) => { + const similarTopics = A( + deepMerge(result["topics"], result["similar_topics"]) + ); + similarTopics.forEach(function (topic, index) { + similarTopics[index] = EmberObject.create(topic); + }); + + this.set("similarTopics", similarTopics); + }); + }, + init() { + this._super(...arguments); + }, + actions: { + closeMessage() { + this.set("showMessage", false); }, - @observes("field.value") - customValidate(){ - const lastKeyUp = new Date(); - this._lastKeyUp = lastKeyUp; - - // One second from now, check to see if the last key was hit when - // we recorded it. If it was, the user paused typing. - cancel(this._lastKeyTimeout); - this._lastKeyTimeout = later(() => { - if (lastKeyUp !== this._lastKeyUp) { - return; - } - - this.updateSimilarTopics(); - }, 1000); - }, - - updateSimilarTopics(){ - this.backendValidate({ - title: this.get("field.value"), - categories: this.get('validation.categories'), - date_after: this.get('validation.date_after') - }).then((result) => { - const similarTopics = A(deepMerge(result['topics'], result['similar_topics'])); - similarTopics.forEach(function(topic, index) { - similarTopics[index] = EmberObject.create(topic); - }); - - this.set('similarTopics', similarTopics); - }); - }, - init() { - this._super(...arguments); - - }, - actions: { - closeMessage(){ - this.set('showMessage', false); - } - } -}); \ No newline at end of file + }, +}); From 4e12e872be1c0576d03a35024412b49596e2f5df Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:47:37 +0530 Subject: [PATCH 19/38] removed unused code --- .../wizard/components/similar-topics-validator.js.es6 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index 5c90ae7f..814e3ade 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -9,6 +9,7 @@ export default WizardFieldValidator.extend({ similarTopics: [], validate() {}, + @observes("field.value") customValidate() { const lastKeyUp = new Date(); @@ -42,9 +43,7 @@ export default WizardFieldValidator.extend({ this.set("similarTopics", similarTopics); }); }, - init() { - this._super(...arguments); - }, + actions: { closeMessage() { this.set("showMessage", false); From eb29193d8b6ff97374d6fc333051ac7fa33bc08c Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:49:14 +0530 Subject: [PATCH 20/38] fixed formatting for main validator component --- .../wizard/components/validator.js.es6 | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index e272fe52..e2a656e0 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -4,40 +4,39 @@ import { ajax } from "discourse/lib/ajax"; import { getToken } from "wizard/lib/ajax"; export default Component.extend({ - classNameBindings: ['isValid', 'isInvalid'], - validMessageKey: null, - invalidMessageKey: null, - isValid: null, - isInvalid: not('isValid'), - layoutName: 'components/validator', // useful for sharing the template with extending components - init() { - this._super(...arguments); + classNameBindings: ["isValid", "isInvalid"], + validMessageKey: null, + invalidMessageKey: null, + isValid: null, + isInvalid: not("isValid"), + layoutName: "components/validator", // useful for sharing the template with extending components + init() { + this._super(...arguments); - if (this.get('validation.backend')) { - // set a function that can be called as often as it need to - // from the derived component - this.backendValidate = (params) => { - return ajax('/realtime-validations', { - data: { - validation: this.get('name'), - authenticity_token: getToken(), - ...params - } - }); - } - - } - }, - - didInsertElement() { - this.appEvents.on('custom-wizard:validate', this, this.checkIsValid); - }, - - willDestroyElement() { - this.appEvents.off('custom-wizard:validate', this, this.checkIsValid); - }, - - checkIsValid() { - this.set('isValid', this.validate()); + if (this.get("validation.backend")) { + // set a function that can be called as often as it need to + // from the derived component + this.backendValidate = (params) => { + return ajax("/realtime-validations", { + data: { + validation: this.get("name"), + authenticity_token: getToken(), + ...params, + }, + }); + }; } -}); \ No newline at end of file + }, + + didInsertElement() { + this.appEvents.on("custom-wizard:validate", this, this.checkIsValid); + }, + + willDestroyElement() { + this.appEvents.off("custom-wizard:validate", this, this.checkIsValid); + }, + + checkIsValid() { + this.set("isValid", this.validate()); + }, +}); From 3f4b8afb5553bd2c3209d37cb429c738464f168d Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 13:58:27 +0530 Subject: [PATCH 21/38] fixed code formatting --- .../templates/components/field-validators.hbs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs index a732c3e2..1fdff10f 100644 --- a/assets/javascripts/wizard/templates/components/field-validators.hbs +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -1,13 +1,13 @@ {{#if field.validations}} - {{#each-in field.validations.above as |name validation|}} - {{component validation.component field=field name=name validation=validation}} - {{/each-in}} + {{#each-in field.validations.above as |name validation|}} + {{component validation.component field=field name=name validation=validation}} + {{/each-in}} - {{yield (hash perform=(action 'perform'))}} +{{yield (hash perform=(action 'perform'))}} - {{#each-in field.validations.below as |name validation|}} - {{component validation.component field=field name=name validation=validation}} - {{/each-in}} +{{#each-in field.validations.below as |name validation|}} + {{component validation.component field=field name=name validation=validation}} +{{/each-in}} {{else}} - {{yield}} + {{yield}} {{/if}} From aeadef9aa54e7116b865ddc55ddd9e11f19caa5e Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 17:21:59 +0530 Subject: [PATCH 22/38] improved wizard settings UX --- .../templates/components/wizard-realtime-validations.hbs | 6 +++--- assets/stylesheets/common/wizard-admin.scss | 6 ++++++ config/locales/client.en.yml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index 57cf6162..8a381c9f 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -4,9 +4,9 @@ {{#each-in field.validations as |name props|}}
  • - {{input type="checkbox" checked=props.status }} - {{i18n 'admin.wizard.field.validations.enable' - validation_type=(i18n (concat 'admin.wizard.field.validations.' name))}} +

    {{i18n (concat 'admin.wizard.field.validations.' name)}}

    + {{input type="checkbox" checked=props.status}} + {{i18n 'admin.wizard.field.validations.enabled'}}
    diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 6fead6da..195594e9 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -647,6 +647,12 @@ .realtime-validations ul { list-style: none; margin: 0; + + li { + background-color: var(--tertiary-low); + padding: 0 1em; + margin: 0 0 1em 0; + } } .validation-container { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index df0c8801..381ca874 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -178,7 +178,7 @@ en: instructions: "Moment.js format" validations: header: "Realtime Validations" - enable: "Enable %{validation_type}" + enabled: "Enable this validation" similar_topics: "Similar Topics" position: "Position" above: "Above" From 20c8d8493e9a243250295153fa8050bf964e115c Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 17:57:15 +0530 Subject: [PATCH 23/38] fixed code formatting for realtime_validation class --- lib/custom_wizard/realtime_validation.rb | 64 ++++++++++++------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index 72aa5065..2dcaa50b 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -1,35 +1,37 @@ +# frozen_string_literal: true + class CustomWizard::RealtimeValidation - cattr_accessor :types - @@types ||= { - similar_topics: { types: [:text], component: "similar-topics-validator", backend: true, required_params: [] } - } + cattr_accessor :types + @@types ||= { + similar_topics: { types: [:text], component: "similar-topics-validator", backend: true, required_params: [] } + } - class SimilarTopic - def initialize(topic) - @topic = topic - end - - attr_reader :topic - - def blurb - Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb)) - end - end - - def self.similar_topics(params, current_user) - title = params[:title] - raw = params[:raw] - categories = params[:categories] - date_after = params[:date_after] - - if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? - return [] - end - - topics = Topic.similar_to(title, raw, current_user).to_a - topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? - topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? - topics.map! { |t| SimilarTopic.new(t) } - ::ActiveModel::ArraySerializer.new(topics, each_serializer: SimilarTopicSerializer, root: :similar_topics, rest_serializer: true, scope: ::Guardian.new(current_user)) + class SimilarTopic + def initialize(topic) + @topic = topic end + + attr_reader :topic + + def blurb + Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb)) + end + end + + def self.similar_topics(params, current_user) + title = params[:title] + raw = params[:raw] + categories = params[:categories] + date_after = params[:date_after] + + if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? + return [] + end + + topics = Topic.similar_to(title, raw, current_user).to_a + topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? + topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? + topics.map! { |t| SimilarTopic.new(t) } + ::ActiveModel::ArraySerializer.new(topics, each_serializer: SimilarTopicSerializer, root: :similar_topics, rest_serializer: true, scope: ::Guardian.new(current_user)) + end end From 69ce09bdf7d741ddc53a481f2efab22bb57d54d6 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 18:12:32 +0530 Subject: [PATCH 24/38] moved serialization logic to the controller --- controllers/custom_wizard/realtime_validations.rb | 5 ++++- lib/custom_wizard/realtime_validation.rb | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/controllers/custom_wizard/realtime_validations.rb b/controllers/custom_wizard/realtime_validations.rb index 38ae1a0d..00b8c686 100644 --- a/controllers/custom_wizard/realtime_validations.rb +++ b/controllers/custom_wizard/realtime_validations.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + class CustomWizard::RealtimeValidationsController < ::ApplicationController def validate params.require(:validation) params.require(::CustomWizard::RealtimeValidation.types[params[:validation].to_sym][:required_params]) - render_json_dump(::CustomWizard::RealtimeValidation.send(params[:validation], params, current_user)) + result = ::CustomWizard::RealtimeValidation.send(params[:validation], params, current_user) + render_serialized(result[:items], result[:serializer], result[:opts]) end end diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index 2dcaa50b..055965f6 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -32,6 +32,6 @@ class CustomWizard::RealtimeValidation topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? topics.map! { |t| SimilarTopic.new(t) } - ::ActiveModel::ArraySerializer.new(topics, each_serializer: SimilarTopicSerializer, root: :similar_topics, rest_serializer: true, scope: ::Guardian.new(current_user)) + { items: topics, serializer: SimilarTopicSerializer, opts: { root: :similar_topics } } end end From 7ef7b4b8e0837be565600078b582fd2bdfee542e Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 19:02:11 +0530 Subject: [PATCH 25/38] fixed formatting --- .../wizard/templates/components/field-validators.hbs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs index 1fdff10f..4b4769a2 100644 --- a/assets/javascripts/wizard/templates/components/field-validators.hbs +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -3,11 +3,11 @@ {{component validation.component field=field name=name validation=validation}} {{/each-in}} -{{yield (hash perform=(action 'perform'))}} + {{yield (hash perform=(action 'perform'))}} -{{#each-in field.validations.below as |name validation|}} - {{component validation.component field=field name=name validation=validation}} -{{/each-in}} + {{#each-in field.validations.below as |name validation|}} + {{component validation.component field=field name=name validation=validation}} + {{/each-in}} {{else}} {{yield}} {{/if}} From 8a6b240b4648500376af047b74e899b36b24ba63 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 15 Feb 2021 19:03:01 +0530 Subject: [PATCH 26/38] removed extra newline --- .../wizard/templates/components/similar-topics-validator.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs index 651ad25e..fc65aaa6 100644 --- a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs +++ b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs @@ -6,4 +6,3 @@ {{/each}}
    {{/if}} - From c45e51fcb61e30f882491374294638939428f135 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 16 Feb 2021 11:43:00 +1100 Subject: [PATCH 27/38] Various fixes * Code formatting * Both "type" and "name" are used to refer to the validation type. Changed all to "type". * Added proper abstraction of realtime validation classes on server * UI improvements in admin and wizard --- .../components/wizard-custom-field.js.es6 | 1 + .../wizard-realtime-validations.js.es6 | 7 ++-- .../wizard-realtime-validations.hbs | 12 +++--- .../similar-topics-validator.js.es6 | 29 +++++++++++-- .../wizard/components/validator.js.es6 | 8 ++-- .../templates/components/field-validators.hbs | 8 ++-- .../components/similar-topics-validator.hbs | 22 ++++++---- assets/stylesheets/common/wizard-admin.scss | 12 ++++-- assets/stylesheets/wizard/custom/field.scss | 14 +------ .../stylesheets/wizard/custom/validators.scss | 30 +++++++++++++ assets/stylesheets/wizard/wizard_custom.scss | 1 + config/locales/client.en.yml | 8 +++- .../custom_wizard/realtime_validations.rb | 17 +++++--- lib/custom_wizard/realtime_validation.rb | 37 ++++------------ .../realtime_validations/result.rb | 11 +++++ .../realtime_validations/similar_topics.rb | 42 +++++++++++++++++++ plugin.rb | 3 ++ .../similar_topics_serializer.rb | 2 + .../custom_wizard/wizard_field_serializer.rb | 4 +- 19 files changed, 186 insertions(+), 82 deletions(-) create mode 100644 assets/stylesheets/wizard/custom/validators.scss create mode 100644 lib/custom_wizard/realtime_validations/result.rb create mode 100644 lib/custom_wizard/realtime_validations/similar_topics.rb create mode 100644 serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index cc9d56eb..9f9470ac 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -30,6 +30,7 @@ export default Component.extend(UndoChanges, { @discourseComputed('field.type') validations(type) { const applicableToField = []; + for(let validation in wizardSchema.field.validations) { if ((wizardSchema.field.validations[validation]["types"]).includes(type)) { applicableToField.push(validation) diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 index 6e7daa9e..5bafaac3 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 @@ -12,6 +12,7 @@ export default Component.extend({ if (!this.field.validations) { const validations = {}; + this.validations.forEach((validation) => { validations[validation] = {}; }); @@ -32,10 +33,10 @@ export default Component.extend({ }, actions: { - updateValidationCategories(name, validation, categories) { - this.set(`validationBuffer.${name}.categories`, categories); + updateValidationCategories(type, validation, categories) { + this.set(`validationBuffer.${type}.categories`, categories); this.set( - `field.validations.${name}.categories`, + `field.validations.${type}.categories`, categories.map((category) => category.id) ); }, diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index 8a381c9f..fe053bc3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -1,10 +1,10 @@

    {{i18n 'admin.wizard.field.validations.header'}}

      - {{#each-in field.validations as |name props|}} + {{#each-in field.validations as |type props|}}
    • -

      {{i18n (concat 'admin.wizard.field.validations.' name)}}

      +

      {{i18n (concat 'admin.wizard.field.validations.' type)}}

      {{input type="checkbox" checked=props.status}} {{i18n 'admin.wizard.field.validations.enabled'}}
      @@ -15,8 +15,8 @@
    {{category-selector - categories=(get this (concat 'validationBuffer.' name '.categories')) - onChange=(action 'updateValidationCategories' name props) + categories=(get this (concat 'validationBuffer.' type '.categories')) + onChange=(action 'updateValidationCategories' type props) class="wizard"}}
  • @@ -36,9 +36,9 @@
    - {{radio-button name=(concat name field.id) value="above" selection=props.position}} + {{radio-button name=(concat type field.id) value="above" selection=props.position}} {{i18n 'admin.wizard.field.validations.above'}} - {{radio-button name=(concat name field.id) value="below" selection=props.position}} + {{radio-button name=(concat type field.id) value="below" selection=props.position}} {{i18n 'admin.wizard.field.validations.below'}}
    diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index 814e3ade..2375f434 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -3,15 +3,34 @@ import { deepMerge } from "discourse-common/lib/object"; import { observes } from "discourse-common/utils/decorators"; import { cancel, later } from "@ember/runloop"; import { A } from "@ember/array"; -import EmberObject from "@ember/object"; +import EmberObject, { computed } from "@ember/object"; +import { notEmpty, and, equal, empty } from "@ember/object/computed"; export default WizardFieldValidator.extend({ - similarTopics: [], + classNames: ['similar-topics-validator'], + similarTopics: null, + hasInput: notEmpty('field.value'), + hasSimilarTopics: notEmpty('similarTopics'), + hasNotSearched: equal('similarTopics', null), + noSimilarTopics: computed('similarTopics', function() { + return this.similarTopics !== null && this.similarTopics.length == 0; + }), + showDefault: and('hasNotSearched', 'hasInput'), validate() {}, @observes("field.value") customValidate() { + const field = this.field; + + if (!field.value) return; + const value = field.value; + + if (value && value.length < 5) { + this.set('similarTopics', null); + return; + } + const lastKeyUp = new Date(); this._lastKeyUp = lastKeyUp; @@ -22,12 +41,14 @@ export default WizardFieldValidator.extend({ if (lastKeyUp !== this._lastKeyUp) { return; } - + this.updateSimilarTopics(); }, 1000); }, updateSimilarTopics() { + this.set('updating', true); + this.backendValidate({ title: this.get("field.value"), categories: this.get("validation.categories"), @@ -41,7 +62,7 @@ export default WizardFieldValidator.extend({ }); this.set("similarTopics", similarTopics); - }); + }).finally(() => this.set('updating', false)); }, actions: { diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index e2a656e0..3bd13df4 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -1,15 +1,17 @@ import Component from "@ember/component"; -import { not } from "@ember/object/computed"; +import { equal } from "@ember/object/computed"; import { ajax } from "discourse/lib/ajax"; import { getToken } from "wizard/lib/ajax"; export default Component.extend({ + classNames: ['validator'], classNameBindings: ["isValid", "isInvalid"], validMessageKey: null, invalidMessageKey: null, isValid: null, - isInvalid: not("isValid"), + isInvalid: equal("isValid", false), layoutName: "components/validator", // useful for sharing the template with extending components + init() { this._super(...arguments); @@ -19,7 +21,7 @@ export default Component.extend({ this.backendValidate = (params) => { return ajax("/realtime-validations", { data: { - validation: this.get("name"), + type: this.get("type"), authenticity_token: getToken(), ...params, }, diff --git a/assets/javascripts/wizard/templates/components/field-validators.hbs b/assets/javascripts/wizard/templates/components/field-validators.hbs index 4b4769a2..d765f7ae 100644 --- a/assets/javascripts/wizard/templates/components/field-validators.hbs +++ b/assets/javascripts/wizard/templates/components/field-validators.hbs @@ -1,12 +1,12 @@ {{#if field.validations}} - {{#each-in field.validations.above as |name validation|}} - {{component validation.component field=field name=name validation=validation}} + {{#each-in field.validations.above as |type validation|}} + {{component validation.component field=field type=type validation=validation}} {{/each-in}} {{yield (hash perform=(action 'perform'))}} - {{#each-in field.validations.below as |name validation|}} - {{component validation.component field=field name=name validation=validation}} + {{#each-in field.validations.below as |type validation|}} + {{component validation.component field=field type=type validation=validation}} {{/each-in}} {{else}} {{yield}} diff --git a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs index fc65aaa6..5fda22b8 100644 --- a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs +++ b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs @@ -1,8 +1,16 @@ -{{#if similarTopics}} -

    {{i18n 'realtime_validations.similar_topics_heading'}}

    -
    - {{#each similarTopics as |similarTopic|}} - {{wizard-similar-topic topic=similarTopic}} - {{/each}} -
    +{{#if loading}} + +{{else if hasSimilarTopics}} + +
      + {{#each similarTopics as |similarTopic|}} +
    • {{wizard-similar-topic topic=similarTopic}}
    • + {{/each}} +
    +{{else if noSimilarTopics}} + +{{else if showDefault}} + +{{else}} + {{/if}} diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 195594e9..b974497c 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -644,14 +644,18 @@ } } -.realtime-validations ul { +.realtime-validations > ul { list-style: none; margin: 0; - li { - background-color: var(--tertiary-low); - padding: 0 1em; + > li { + background-color: var(--primary-low); + padding: 1em; margin: 0 0 1em 0; + + input { + margin-bottom: 0; + } } } diff --git a/assets/stylesheets/wizard/custom/field.scss b/assets/stylesheets/wizard/custom/field.scss index 8e36ff76..6ad99046 100644 --- a/assets/stylesheets/wizard/custom/field.scss +++ b/assets/stylesheets/wizard/custom/field.scss @@ -155,18 +155,8 @@ .select-kit.combo-box.group-dropdown { min-width: 220px; } -} - -.wizard-similar-topics { - background-color: var(--tertiary-low); - padding: 5px; - .title { - color: var(--primary); - } - .blurb { - margin-left: 0.5em; - color: var(--primary-high); - font-size: $font-down-1; + .text-field input { + margin-bottom: 0; } } \ No newline at end of file diff --git a/assets/stylesheets/wizard/custom/validators.scss b/assets/stylesheets/wizard/custom/validators.scss new file mode 100644 index 00000000..53746802 --- /dev/null +++ b/assets/stylesheets/wizard/custom/validators.scss @@ -0,0 +1,30 @@ +.similar-topics-validator { + position: relative; + + label { + min-height: 20px; + display: inline-block; + } +} + +.wizard-similar-topics { + background-color: var(--tertiary-low); + padding: 5px; + margin: 0; + list-style: none; + position: absolute; + top: 25px; + box-shadow: shadow("dropdown"); + width: 100%; + box-sizing: border-box; + + .title { + color: var(--primary); + } + + .blurb { + margin-left: 0.5em; + color: var(--primary-high); + font-size: $font-down-1; + } +} \ No newline at end of file diff --git a/assets/stylesheets/wizard/wizard_custom.scss b/assets/stylesheets/wizard/wizard_custom.scss index 103a4d2d..bedf4e0a 100644 --- a/assets/stylesheets/wizard/wizard_custom.scss +++ b/assets/stylesheets/wizard/wizard_custom.scss @@ -10,6 +10,7 @@ @import "custom/wizard"; @import "custom/step"; @import "custom/field"; +@import "custom/validators"; @import "custom/mobile"; @import "custom/autocomplete"; @import "custom/composer"; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 381ca874..3a2756e0 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -178,7 +178,7 @@ en: instructions: "Moment.js format" validations: header: "Realtime Validations" - enabled: "Enable this validation" + enabled: "Enabled" similar_topics: "Similar Topics" position: "Position" above: "Above" @@ -521,4 +521,8 @@ en: title: "Did you forget to add recipients?" body: "Right now this message is only being sent to yourself!" realtime_validations: - similar_topics_heading: "Your topic is similar to..." + similar_topics: + default: "When you stop typing we'll look for similar topics." + results: "Your topic is similar to..." + no_results: "No similar topics." + loading: "Looking for similar topics..." diff --git a/controllers/custom_wizard/realtime_validations.rb b/controllers/custom_wizard/realtime_validations.rb index 00b8c686..183e7ccd 100644 --- a/controllers/custom_wizard/realtime_validations.rb +++ b/controllers/custom_wizard/realtime_validations.rb @@ -2,10 +2,17 @@ class CustomWizard::RealtimeValidationsController < ::ApplicationController def validate - params.require(:validation) - params.require(::CustomWizard::RealtimeValidation.types[params[:validation].to_sym][:required_params]) - - result = ::CustomWizard::RealtimeValidation.send(params[:validation], params, current_user) - render_serialized(result[:items], result[:serializer], result[:opts]) + klass_str = "CustomWizard::RealtimeValidation::#{validation_params[:type].camelize}" + result = klass_str.constantize.new(current_user).perform(validation_params) + render_serialized(result.items, "#{klass_str}Serializer".constantize, result.serializer_opts) + end + + private + + def validation_params + params.require(:type) + settings = ::CustomWizard::RealtimeValidation.types[params[:type].to_sym] + params.require(settings[:required_params]) if settings[:required_params].present? + params end end diff --git a/lib/custom_wizard/realtime_validation.rb b/lib/custom_wizard/realtime_validation.rb index 055965f6..69341ebf 100644 --- a/lib/custom_wizard/realtime_validation.rb +++ b/lib/custom_wizard/realtime_validation.rb @@ -2,36 +2,13 @@ class CustomWizard::RealtimeValidation cattr_accessor :types + @@types ||= { - similar_topics: { types: [:text], component: "similar-topics-validator", backend: true, required_params: [] } + similar_topics: { + types: [:text], + component: "similar-topics-validator", + backend: true, + required_params: [] + } } - - class SimilarTopic - def initialize(topic) - @topic = topic - end - - attr_reader :topic - - def blurb - Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb)) - end - end - - def self.similar_topics(params, current_user) - title = params[:title] - raw = params[:raw] - categories = params[:categories] - date_after = params[:date_after] - - if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? - return [] - end - - topics = Topic.similar_to(title, raw, current_user).to_a - topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? - topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? - topics.map! { |t| SimilarTopic.new(t) } - { items: topics, serializer: SimilarTopicSerializer, opts: { root: :similar_topics } } - end end diff --git a/lib/custom_wizard/realtime_validations/result.rb b/lib/custom_wizard/realtime_validations/result.rb new file mode 100644 index 00000000..988bea73 --- /dev/null +++ b/lib/custom_wizard/realtime_validations/result.rb @@ -0,0 +1,11 @@ +class CustomWizard::RealtimeValidation::Result + attr_accessor :type, + :items, + :serializer_opts + + def initialize(type) + @type = type + @items = [] + @serializer_opts = {} + end +end \ No newline at end of file diff --git a/lib/custom_wizard/realtime_validations/similar_topics.rb b/lib/custom_wizard/realtime_validations/similar_topics.rb new file mode 100644 index 00000000..29425f5b --- /dev/null +++ b/lib/custom_wizard/realtime_validations/similar_topics.rb @@ -0,0 +1,42 @@ +class CustomWizard::RealtimeValidation::SimilarTopics + attr_accessor :user + + def initialize(user) + @user = user + end + + class SimilarTopic + def initialize(topic) + @topic = topic + end + + attr_reader :topic + + def blurb + Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb)) + end + end + + def perform(params) + title = params[:title] + raw = params[:raw] + categories = params[:categories] + date_after = params[:date_after] + + result = CustomWizard::RealtimeValidation::Result.new(:similar_topic) + + if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum? + return result + end + + topics = Topic.similar_to(title, raw, user).to_a + topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present? + topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present? + topics.map! { |t| SimilarTopic.new(t) } + + result.items = topics + result.serializer_opts = { root: :similar_topics } + + result + end +end \ No newline at end of file diff --git a/plugin.rb b/plugin.rb index 46cd4e78..95740607 100644 --- a/plugin.rb +++ b/plugin.rb @@ -60,6 +60,8 @@ after_initialize do ../lib/custom_wizard/custom_field.rb ../lib/custom_wizard/field.rb ../lib/custom_wizard/realtime_validation.rb + ../lib/custom_wizard/realtime_validations/result.rb + ../lib/custom_wizard/realtime_validations/similar_topics.rb ../lib/custom_wizard/mapper.rb ../lib/custom_wizard/log.rb ../lib/custom_wizard/step_updater.rb @@ -81,6 +83,7 @@ after_initialize do ../serializers/custom_wizard/wizard_step_serializer.rb ../serializers/custom_wizard/wizard_serializer.rb ../serializers/custom_wizard/log_serializer.rb + ../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb ../extensions/extra_locales_controller.rb ../extensions/invites_controller.rb ../extensions/users_controller.rb diff --git a/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb b/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb new file mode 100644 index 00000000..976eef1b --- /dev/null +++ b/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb @@ -0,0 +1,2 @@ +class ::CustomWizard::RealtimeValidation::SimilarTopicsSerializer < ::SimilarTopicSerializer +end \ No newline at end of file diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index f28a86d8..e89d5f0c 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -61,10 +61,10 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer def validations validations = {} - object.validations&.each do |name, props| + object.validations&.each do |type, props| next unless props["status"] validations[props["position"]] ||= {} - validations[props["position"]][name] = props.merge CustomWizard::RealtimeValidation.types[name.to_sym] + validations[props["position"]][type] = props.merge CustomWizard::RealtimeValidation.types[type.to_sym] end validations From c196c4fee787bcdc6119360d1efe67f2fccd9fc0 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 16 Feb 2021 19:58:43 +0530 Subject: [PATCH 28/38] added tests for CustomWizard::RealtimeValidation class --- .../custom_wizard/realtime_validation_spec.rb | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 spec/components/custom_wizard/realtime_validation_spec.rb diff --git a/spec/components/custom_wizard/realtime_validation_spec.rb b/spec/components/custom_wizard/realtime_validation_spec.rb new file mode 100644 index 00000000..d3a92452 --- /dev/null +++ b/spec/components/custom_wizard/realtime_validation_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::RealtimeValidation do + context 'classes for every validation type exist' do + validation_names = CustomWizard::RealtimeValidation.types.keys + + validation_names.each do |name| + klass_str = "CustomWizard::RealtimeValidation::#{name.to_s.camelize}" + + it "ensure class for validation: #{name} exists" do + expect(klass_str.safe_constantize).not_to be_nil + end + + it "#{klass_str} has a perform() method" do + expect(klass_str.safe_constantize.instance_methods).to include(:perform) + end + end + + end +end From c5ad11c0a4a941a958b9fb3876a9e28abc9226a0 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 16 Feb 2021 19:59:23 +0530 Subject: [PATCH 29/38] added tests for CustomWizard::RealtimeValidation::SimilarTopics class --- .../similar_topics_spec.rb | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb diff --git a/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb new file mode 100644 index 00000000..47676d98 --- /dev/null +++ b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +describe ::CustomWizard::RealtimeValidation::SimilarTopics do + let(:post) { create_post(title: "matching similar topic") } + let(:topic) { post.topic } + let(:user) { post.user } + + let(:category) { Fabricate(:category) } + let(:cat_post) { create_post(title: "matching similar topic slightly different", category: category) } + let(:cat_topic) { cat_post.topic } + let(:user) { cat_post.user } + + before do + SiteSetting.min_title_similar_length = 5 + Topic.stubs(:count_exceeds_minimum?).returns(true) + SearchIndexer.enable + end + + it "fetches similar topics" do + validation = ::CustomWizard::RealtimeValidation::SimilarTopics.new(user) + result = validation.perform({ title: topic.title.chars.take(10).join }) + expect(result.items.length).to eq(2) + end + + it "filters topics based on category" do + validation = ::CustomWizard::RealtimeValidation::SimilarTopics.new(user) + result = validation.perform({ title: "matching similar", categories: [category.id.to_s] }) + expect(result.items.length).to eq(1) + end + + it "filters topics based on created date" do + topic.update!(created_at: 1.day.ago) + cat_topic.update!(created_at: 2.days.ago) + + validation = ::CustomWizard::RealtimeValidation::SimilarTopics.new(user) + result = validation.perform({ title: "matching similar", date_after: 1.day.ago.to_date.to_s }) + expect(result.items.length).to eq(1) + end +end From 47019664b1e11ddf65316fe55f3e96fecf6746c9 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 16 Feb 2021 20:06:05 +0530 Subject: [PATCH 30/38] removed context block --- .../custom_wizard/realtime_validation_spec.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/spec/components/custom_wizard/realtime_validation_spec.rb b/spec/components/custom_wizard/realtime_validation_spec.rb index d3a92452..819ac2ae 100644 --- a/spec/components/custom_wizard/realtime_validation_spec.rb +++ b/spec/components/custom_wizard/realtime_validation_spec.rb @@ -3,20 +3,17 @@ require_relative '../../plugin_helper' describe CustomWizard::RealtimeValidation do - context 'classes for every validation type exist' do - validation_names = CustomWizard::RealtimeValidation.types.keys + validation_names = CustomWizard::RealtimeValidation.types.keys - validation_names.each do |name| - klass_str = "CustomWizard::RealtimeValidation::#{name.to_s.camelize}" + validation_names.each do |name| + klass_str = "CustomWizard::RealtimeValidation::#{name.to_s.camelize}" - it "ensure class for validation: #{name} exists" do - expect(klass_str.safe_constantize).not_to be_nil - end - - it "#{klass_str} has a perform() method" do - expect(klass_str.safe_constantize.instance_methods).to include(:perform) - end + it "ensure class for validation: #{name} exists" do + expect(klass_str.safe_constantize).not_to be_nil end + it "#{klass_str} has a perform() method" do + expect(klass_str.safe_constantize.instance_methods).to include(:perform) + end end end From 40a2380775646918bf345a9d9b90984baa5cec4f Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 16 Feb 2021 21:54:42 +0530 Subject: [PATCH 31/38] added specs for CustomWizard::RealtimeValidationsController --- .../realtime_validations_spec.rb | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 spec/requests/custom_wizard/realtime_validations_spec.rb diff --git a/spec/requests/custom_wizard/realtime_validations_spec.rb b/spec/requests/custom_wizard/realtime_validations_spec.rb new file mode 100644 index 00000000..b9b21e0a --- /dev/null +++ b/spec/requests/custom_wizard/realtime_validations_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +describe CustomWizard::RealtimeValidationsController do + + fab!(:validation_type) { "test_stub" } + fab!(:validation_type_stub) { + { + types: [:text], + component: "similar-topics-validator", + backend: true, + required_params: [] + } + } + + before(:all) do + sign_in(Fabricate(:user)) + CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub } + + class CustomWizard::RealtimeValidation::TestStub + attr_accessor :user + + def initialize(user) + @user = user + end + + def perform(params) + result = CustomWizard::RealtimeValidation::Result.new(:test_stub) + result.items = ["hello", "world"] + result + end + end + + class ::CustomWizard::RealtimeValidation::TestStubSerializer < ApplicationSerializer + attributes :item + + def item + object + end + end + end + + it "gives the correct response for a given type" do + get '/realtime-validations.json', params: { type: validation_type } + + expect(response.status).to eq(200) + expected_response = [ + { "item" => "hello" }, + { "item" => "world" } + ] + expect(JSON.parse(response.body)).to eq(expected_response) + end + + it "gives 400 error when no type is passed" do + get '/realtime-validations.json' + + expect(response.status).to eq(400) + end + + it "gives 500 response code when a non existant type is passed" do + get '/realtime-validations.json', params: { type: "random_type" } + + expect(response.status).to eq(500) + end +end From c1b04eaaecd40eaed65b5b3c21b0dee35461a12c Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Tue, 16 Feb 2021 23:43:16 +0530 Subject: [PATCH 32/38] added tests for required_params --- .../custom_wizard/realtime_validations_spec.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/requests/custom_wizard/realtime_validations_spec.rb b/spec/requests/custom_wizard/realtime_validations_spec.rb index b9b21e0a..a57333b8 100644 --- a/spec/requests/custom_wizard/realtime_validations_spec.rb +++ b/spec/requests/custom_wizard/realtime_validations_spec.rb @@ -41,7 +41,6 @@ describe CustomWizard::RealtimeValidationsController do it "gives the correct response for a given type" do get '/realtime-validations.json', params: { type: validation_type } - expect(response.status).to eq(200) expected_response = [ { "item" => "hello" }, @@ -52,13 +51,19 @@ describe CustomWizard::RealtimeValidationsController do it "gives 400 error when no type is passed" do get '/realtime-validations.json' - expect(response.status).to eq(400) end + it "gives 400 error when a required additional param is missing" do + CustomWizard::RealtimeValidation.types[:test_stub][:required_params] = [:test1] + get '/realtime-validations.json', params: { type: validation_type } + expect(response.status).to eq(400) + # the addition is only relevant to this test, so getting rid of it + CustomWizard::RealtimeValidation.types[:test_stub][:required_params] = [] + end + it "gives 500 response code when a non existant type is passed" do get '/realtime-validations.json', params: { type: "random_type" } - expect(response.status).to eq(500) end end From d68967c63f9cd4261891ff3c28c2d5ed938f5072 Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Wed, 17 Feb 2021 00:15:48 +0530 Subject: [PATCH 33/38] added plugin helper --- .../custom_wizard/realtime_validations/similar_topics_spec.rb | 2 ++ spec/requests/custom_wizard/realtime_validations_spec.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb index 47676d98..85a9fcf4 100644 --- a/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb +++ b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative '../../plugin_helper' + describe ::CustomWizard::RealtimeValidation::SimilarTopics do let(:post) { create_post(title: "matching similar topic") } let(:topic) { post.topic } diff --git a/spec/requests/custom_wizard/realtime_validations_spec.rb b/spec/requests/custom_wizard/realtime_validations_spec.rb index a57333b8..0d59e885 100644 --- a/spec/requests/custom_wizard/realtime_validations_spec.rb +++ b/spec/requests/custom_wizard/realtime_validations_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative '../../plugin_helper' + describe CustomWizard::RealtimeValidationsController do fab!(:validation_type) { "test_stub" } From 4036313e75fb811711fd84bc6925a10da9725e77 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Feb 2021 14:36:54 +1100 Subject: [PATCH 34/38] Fix relative path --- .../custom_wizard/realtime_validations/similar_topics_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb index 85a9fcf4..e76b7fdb 100644 --- a/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb +++ b/spec/components/custom_wizard/realtime_validations/similar_topics_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative '../../plugin_helper' +require_relative '../../../plugin_helper' describe ::CustomWizard::RealtimeValidation::SimilarTopics do let(:post) { create_post(title: "matching similar topic") } From 8d72aee4fc339111317ae12e79c546a59ccc98b3 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Feb 2021 14:40:04 +1100 Subject: [PATCH 35/38] Fix failing test (not RV related) --- spec/serializers/custom_wizard/wizard_field_serializer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb index 3398a9dc..73de1f6b 100644 --- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb @@ -33,6 +33,6 @@ describe CustomWizard::FieldSerializer do ).as_json expect(json_array[0][:format]).to eq("YYYY-MM-DD") expect(json_array[5][:file_types]).to eq(".jpg,.png") - expect(json_array[4][:number]).to eq("5") + expect(json_array[4][:number]).to eq(5) end end \ No newline at end of file From 9587dfd4886a09424f883bff5f55cbd7f4ac8e76 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Feb 2021 14:42:02 +1100 Subject: [PATCH 36/38] Update .last_run.json --- coverage/.last_run.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/.last_run.json b/coverage/.last_run.json index 6e843e2c..c135c6ad 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "covered_percent": 89.45 + "line": 89.57 } } From db17a7d1caa3a8f4848b825f5857e850410c5cff Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Feb 2021 14:57:42 +1100 Subject: [PATCH 37/38] Fix z-index issue --- assets/stylesheets/wizard/custom/validators.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/stylesheets/wizard/custom/validators.scss b/assets/stylesheets/wizard/custom/validators.scss index 53746802..be1f3b3f 100644 --- a/assets/stylesheets/wizard/custom/validators.scss +++ b/assets/stylesheets/wizard/custom/validators.scss @@ -17,6 +17,7 @@ box-shadow: shadow("dropdown"); width: 100%; box-sizing: border-box; + z-index: 100; .title { color: var(--primary); From 41be645c176339f2fe84978d397f151cc1eeeb38 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Feb 2021 15:49:20 +1100 Subject: [PATCH 38/38] Improve show/hide of similar topics results --- .../similar-topics-validator.js.es6 | 15 ++++++- .../components/wizard-similar-topics.js.es6 | 36 ++++++++++++++++ .../components/similar-topics-validator.hbs | 10 ++--- .../components/wizard-similar-topics.hbs | 11 +++++ .../stylesheets/wizard/custom/validators.scss | 43 +++++++++++-------- config/locales/client.en.yml | 2 + 6 files changed, 91 insertions(+), 26 deletions(-) create mode 100644 assets/javascripts/wizard/components/wizard-similar-topics.js.es6 create mode 100644 assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index 2375f434..aa490169 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -15,8 +15,16 @@ export default WizardFieldValidator.extend({ noSimilarTopics: computed('similarTopics', function() { return this.similarTopics !== null && this.similarTopics.length == 0; }), - showDefault: and('hasNotSearched', 'hasInput'), - + showDefault: computed('hasNotSearched', 'hasInput', 'typing', function() { + return this.hasInput && (this.hasNotSearched || this.typing); + }), + showSimilarTopics: computed('typing', 'hasSimilarTopics', function() { + return this.hasSimilarTopics && !this.typing; + }), + showNoSimilarTopics: computed('typing', 'noSimilarTopics', function() { + return this.noSimilarTopics && !this.typing; + }), + validate() {}, @observes("field.value") @@ -26,6 +34,8 @@ export default WizardFieldValidator.extend({ if (!field.value) return; const value = field.value; + this.set("typing", true); + if (value && value.length < 5) { this.set('similarTopics', null); return; @@ -41,6 +51,7 @@ export default WizardFieldValidator.extend({ if (lastKeyUp !== this._lastKeyUp) { return; } + this.set("typing", false); this.updateSimilarTopics(); }, 1000); diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 new file mode 100644 index 00000000..8bdc1416 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 @@ -0,0 +1,36 @@ +import Component from "@ember/component"; +import { bind } from "@ember/runloop"; +import { observes } from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNames: ['wizard-similar-topics'], + showTopics: true, + + didInsertElement() { + $(document).on("click", bind(this, this.documentClick)); + }, + + willDestroyElement() { + $(document).off("click", bind(this, this.documentClick)); + }, + + documentClick(e) { + if (this._state == "destroying") return; + let $target = $(e.target); + + if (!$target.hasClass('show-topics')) { + this.set('showTopics', false); + } + }, + + @observes('topics') + toggleShowWhenTopicsChange() { + this.set('showTopics', true); + }, + + actions: { + toggleShowTopics() { + this.set('showTopics', true); + } + } +}) \ No newline at end of file diff --git a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs index 5fda22b8..c19bc8c2 100644 --- a/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs +++ b/assets/javascripts/wizard/templates/components/similar-topics-validator.hbs @@ -1,13 +1,9 @@ {{#if loading}} -{{else if hasSimilarTopics}} +{{else if showSimilarTopics}} -
      - {{#each similarTopics as |similarTopic|}} -
    • {{wizard-similar-topic topic=similarTopic}}
    • - {{/each}} -
    -{{else if noSimilarTopics}} + {{wizard-similar-topics topics=similarTopics}} +{{else if showNoSimilarTopics}} {{else if showDefault}} diff --git a/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs b/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs new file mode 100644 index 00000000..045f973d --- /dev/null +++ b/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs @@ -0,0 +1,11 @@ +{{#if showTopics}} +
      + {{#each topics as |topic|}} +
    • {{wizard-similar-topic topic=topic}}
    • + {{/each}} +
    +{{else}} + + {{i18n 'realtime_validations.similar_topics.show'}} + +{{/if}} \ No newline at end of file diff --git a/assets/stylesheets/wizard/custom/validators.scss b/assets/stylesheets/wizard/custom/validators.scss index be1f3b3f..f5227688 100644 --- a/assets/stylesheets/wizard/custom/validators.scss +++ b/assets/stylesheets/wizard/custom/validators.scss @@ -1,31 +1,40 @@ .similar-topics-validator { position: relative; + display: flex; label { min-height: 20px; - display: inline-block; } } .wizard-similar-topics { - background-color: var(--tertiary-low); - padding: 5px; - margin: 0; - list-style: none; - position: absolute; - top: 25px; - box-shadow: shadow("dropdown"); - width: 100%; - box-sizing: border-box; - z-index: 100; + margin-left: 3px; - .title { - color: var(--primary); + ul { + background-color: var(--tertiary-low); + padding: 5px; + margin: 0; + list-style: none; + position: absolute; + left: 0; + top: 25px; + box-shadow: shadow("dropdown"); + width: 100%; + box-sizing: border-box; + z-index: 100; + + .title { + color: var(--primary); + } + + .blurb { + margin-left: 0.5em; + color: var(--primary-high); + font-size: $font-down-1; + } } - .blurb { - margin-left: 0.5em; - color: var(--primary-high); - font-size: $font-down-1; + .show-topics { + cursor: pointer; } } \ No newline at end of file diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3a2756e0..46633fc2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -520,9 +520,11 @@ en: yourself_confirm: title: "Did you forget to add recipients?" body: "Right now this message is only being sent to yourself!" + realtime_validations: similar_topics: default: "When you stop typing we'll look for similar topics." results: "Your topic is similar to..." no_results: "No similar topics." loading: "Looking for similar topics..." + show: "show"