1
0
Fork 0

Merge branch 'master' into pr/46

Dieser Commit ist enthalten in:
Angus McLeod 2020-07-07 10:31:27 +10:00
Commit 58d0fda5af
38 geänderte Dateien mit 240 neuen und 132 gelöschten Zeilen

Datei anzeigen

@ -5,6 +5,8 @@ import { computed } from "@ember/object";
import wizardSchema from '../lib/wizard-schema'; import wizardSchema from '../lib/wizard-schema';
import UndoChanges from '../mixins/undo-changes'; import UndoChanges from '../mixins/undo-changes';
import Component from "@ember/component"; import Component from "@ember/component";
import { notificationLevels } from '../lib/wizard';
import I18n from "I18n";
export default Component.extend(UndoChanges, { export default Component.extend(UndoChanges, {
componentType: 'action', componentType: 'action',
@ -12,6 +14,7 @@ export default Component.extend(UndoChanges, {
visible: computed('currentActionId', function() { return this.action.id === this.currentActionId }), visible: computed('currentActionId', function() { return this.action.id === this.currentActionId }),
createTopic: equal('action.type', 'create_topic'), createTopic: equal('action.type', 'create_topic'),
updateProfile: equal('action.type', 'update_profile'), updateProfile: equal('action.type', 'update_profile'),
watchCategories: equal('action.type', 'watch_categories'),
sendMessage: equal('action.type', 'send_message'), sendMessage: equal('action.type', 'send_message'),
openComposer: equal('action.type', 'open_composer'), openComposer: equal('action.type', 'open_composer'),
sendToApi: equal('action.type', 'send_to_api'), sendToApi: equal('action.type', 'send_to_api'),
@ -31,6 +34,12 @@ export default Component.extend(UndoChanges, {
name: I18n.t(`admin.wizard.action.${type}.label`) name: I18n.t(`admin.wizard.action.${type}.label`)
}; };
}), }),
availableNotificationLevels: notificationLevels.map((type, index) => {
return {
id: type,
name: I18n.t(`admin.wizard.action.watch_categories.notification_level.${type}`)
};
}),
messageUrl: 'https://thepavilion.io/t/2810', messageUrl: 'https://thepavilion.io/t/2810',

Datei anzeigen

@ -63,7 +63,8 @@ export default Component.extend(UndoChanges, {
if (this.isDropdown) { if (this.isDropdown) {
options.wizardFieldSelection = 'key,value'; options.wizardFieldSelection = 'key,value';
options.userFieldOptionsSelection = 'output'; options.userFieldOptionsSelection = 'output';
options.inputTypes = 'association,assignment'; options.textSelection = 'key,value,output';
options.inputTypes = 'conditional,association,assignment';
options.pairConnector = 'association'; options.pairConnector = 'association';
options.keyPlaceholder = 'admin.wizard.key'; options.keyPlaceholder = 'admin.wizard.key';
options.valuePlaceholder = 'admin.wizard.value'; options.valuePlaceholder = 'admin.wizard.value';

Datei anzeigen

@ -1,5 +1,6 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { A } from "@ember/array"; import { A } from "@ember/array";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNames: ['container', 'export'], classNames: ['container', 'export'],

Datei anzeigen

@ -2,6 +2,7 @@ import { ajax } from 'discourse/lib/ajax';
import { default as discourseComputed } from 'discourse-common/utils/decorators'; import { default as discourseComputed } from 'discourse-common/utils/decorators';
import { notEmpty } from "@ember/object/computed"; import { notEmpty } from "@ember/object/computed";
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNames: ['container', 'import'], classNames: ['container', 'import'],

Datei anzeigen

@ -4,6 +4,7 @@ import { computed } from "@ember/object";
import { defaultConnector } from '../lib/wizard-mapper'; import { defaultConnector } from '../lib/wizard-mapper';
import { later } from "@ember/runloop"; import { later } from "@ember/runloop";
import { observes } from "discourse-common/utils/decorators"; import { observes } from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'], classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'],

Datei anzeigen

@ -6,6 +6,7 @@ import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper';
import { snakeCase, generateName, userProperties } from '../lib/wizard'; import { snakeCase, generateName, userProperties } from '../lib/wizard';
import Component from "@ember/component"; import Component from "@ember/component";
import { bind, later } from "@ember/runloop"; import { bind, later } from "@ember/runloop";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNameBindings: [':mapper-selector', 'activeType'], classNameBindings: [':mapper-selector', 'activeType'],

Datei anzeigen

@ -1,5 +1,6 @@
import { default as discourseComputed } from 'discourse-common/utils/decorators'; import { default as discourseComputed } from 'discourse-common/utils/decorators';
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNames: 'wizard-message', classNames: 'wizard-message',

Datei anzeigen

@ -3,6 +3,7 @@ import { notEmpty } from "@ember/object/computed";
import { userProperties } from '../lib/wizard'; import { userProperties } from '../lib/wizard';
import { scheduleOnce } from "@ember/runloop"; import { scheduleOnce } from "@ember/runloop";
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNames: 'wizard-text-editor', classNames: 'wizard-text-editor',

Datei anzeigen

@ -5,6 +5,7 @@ import { default as discourseComputed } from 'discourse-common/utils/decorators'
import { not, and, equal } from "@ember/object/computed"; import { not, and, equal } from "@ember/object/computed";
import { selectKitContent } from '../lib/wizard'; import { selectKitContent } from '../lib/wizard';
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n";
export default Controller.extend({ export default Controller.extend({
queryParams: ['refresh_list'], queryParams: ['refresh_list'],

Datei anzeigen

@ -3,8 +3,9 @@ import { popupAjaxError } from 'discourse/lib/ajax-error';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { notEmpty } from "@ember/object/computed"; import { notEmpty } from "@ember/object/computed";
import CustomWizardLogs from '../models/custom-wizard-logs'; import CustomWizardLogs from '../models/custom-wizard-logs';
import Controller from "@ember/controller";
export default Ember.Controller.extend({ export default Controller.extend({
refreshing: false, refreshing: false,
hasLogs: notEmpty("logs"), hasLogs: notEmpty("logs"),
page: 0, page: 0,

Datei anzeigen

@ -9,6 +9,7 @@ import { scheduleOnce, later } from "@ember/runloop";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import copyText from "discourse/lib/copy-text"; import copyText from "discourse/lib/copy-text";
import CustomWizard from '../models/custom-wizard'; import CustomWizard from '../models/custom-wizard';
import I18n from "I18n";
export default Controller.extend({ export default Controller.extend({
hasName: notEmpty('wizard.name'), hasName: notEmpty('wizard.name'),

Datei anzeigen

@ -1,58 +1,27 @@
import { default as discourseComputed } from 'discourse-common/utils/decorators'; import { default as discourseComputed } from 'discourse-common/utils/decorators';
import { scheduleOnce } from "@ember/runloop";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default Controller.extend({ export default Controller.extend({
title: 'admin.wizard.after_time_modal.title', title: 'admin.wizard.after_time_modal.title',
setup() { setup() {
const dateTime = this.get('model.dateTime'); this.set('bufferedDateTime', moment(this.model.dateTime));
const ROUNDING = 30 * 60 * 1000;
const nextInterval = moment(Math.ceil((+moment()) / ROUNDING) * ROUNDING);
const mDateTime = dateTime ? moment(dateTime) : nextInterval;
const mDateTimeLocal = mDateTime.local();
const date = mDateTimeLocal.format('YYYY-MM-DD');
const time = mDateTimeLocal.format('HH:mm');
this.setProperties({ date, time });
scheduleOnce('afterRender', this, () => {
const $timePicker = $("#time-picker");
$timePicker.timepicker({ timeFormat: 'H:i' });
$timePicker.timepicker('setTime', time);
$timePicker.change(() => this.set('time', $timePicker.val()));
});
}, },
@discourseComputed('date', 'time') @discourseComputed('bufferedDateTime')
dateTime: function(date, time) {
return moment(date + 'T' + time).format();
},
@discourseComputed('dateTime')
submitDisabled(dateTime) { submitDisabled(dateTime) {
return moment().isAfter(dateTime); return moment().isAfter(dateTime);
}, },
resetProperties() {
this.setProperties({
date: null,
time: null
});
},
actions: { actions: {
clear() {
this.resetProperties();
this.get('model.update')(null);
},
submit() { submit() {
const dateTime = this.get('dateTime'); const dateTime = this.get('bufferedDateTime');
const formatted = moment(dateTime).utc().toISOString(); this.get('model.update')(moment(dateTime).utc().toISOString());
this.get('model.update')(formatted);
this.resetProperties();
this.send("closeModal"); this.send("closeModal");
},
dateTimeChanged(dateTime) {
this.set('bufferedDateTime', dateTime);
} }
} }
}); });

Datei anzeigen

@ -1,5 +1,6 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { A } from "@ember/array"; import { A } from "@ember/array";
import I18n from "I18n";
// Inputs // Inputs

Datei anzeigen

@ -141,6 +141,16 @@ const action = {
profile_updates: null, profile_updates: null,
custom_fields: null custom_fields: null
}, },
watch_categories: {
categories: null,
notification_level: null,
mute_remainder: null
},
send_to_api: {
api: null,
api_endpoint: null,
api_body: null
},
add_to_group: { add_to_group: {
group: null group: null
}, },
@ -158,7 +168,9 @@ const action = {
'recipient', 'recipient',
'profile_updates', 'profile_updates',
'group', 'group',
'url' 'url',
'categories',
'mute_remainder'
], ],
advanced: [ advanced: [
'code', 'code',

Datei anzeigen

@ -46,7 +46,16 @@ const userProperties = [
'location', 'location',
'website', 'website',
'bio_raw', 'bio_raw',
'trust_level' 'trust_level',
'email_level'
];
const notificationLevels = [
'regular',
'watching',
'tracking',
'watching_first_post',
'muted'
]; ];
function listProperties(type, opts={}) { function listProperties(type, opts={}) {
@ -106,5 +115,6 @@ export {
snakeCase, snakeCase,
userProperties, userProperties,
listProperties, listProperties,
notificationLevels,
wizardFieldList wizardFieldList
}; };

Datei anzeigen

@ -1,11 +1,12 @@
import CustomWizardLogs from '../models/custom-wizard-logs'; import CustomWizardLogs from '../models/custom-wizard-logs';
import DiscourseRoute from "discourse/routes/discourse";
export default Discourse.Route.extend({ export default DiscourseRoute.extend({
model() { model() {
return CustomWizardLogs.list(); return CustomWizardLogs.list();
}, },
setupController(controller, model) { setupController(controller, model) {
controller.set('logs', model); controller.set('logs', model);
} }
}) })

Datei anzeigen

@ -1,6 +1,7 @@
import CustomWizard from '../models/custom-wizard'; import CustomWizard from '../models/custom-wizard';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import DiscourseRoute from "discourse/routes/discourse"; import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({ export default DiscourseRoute.extend({
model(params) { model(params) {

Datei anzeigen

@ -283,6 +283,63 @@
</div> </div>
{{/if}} {{/if}}
{{#if watchCategories}}
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.action.watch_categories.categories"}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=action.categories
property='categories'
onUpdate=(action 'mappedFieldUpdated')
options=(hash
textSelection='key,value'
wizardFieldSelection=true
userFieldSelection='key,value'
categorySelection='output'
context='action'
)}}
</div>
</div>
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.action.watch_categories.mute_remainder"}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=action.mute_remainder
property='mute_remainder'
onUpdate=(action 'mappedFieldUpdated')
options=(hash
context='action'
wizardFieldSelection=true
userFieldSelection='key,value'
)}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.action.watch_categories.notification_level.label"}}</label>
</div>
<div class="setting-value">
{{combo-box
value=action.notification_level
content=availableNotificationLevels
onChange=(action (mut action.notification_level))
options=(hash
isDisabled=action.custom_title_enabled
none='admin.wizard.action.watch_categories.select_a_notification_level'
)}}
</div>
</div>
{{/if}}
{{#if showAdvanced}} {{#if showAdvanced}}
{{wizard-advanced-toggle showAdvanced=action.showAdvanced}} {{wizard-advanced-toggle showAdvanced=action.showAdvanced}}

Datei anzeigen

@ -1,24 +1,16 @@
{{#d-modal-body class="next-session-time-modal" title=title}} {{#d-modal-body class="next-session-time-modal" title=title}}
<div class="date-time-card"> {{date-time-input
<div class="modal-date-time-set"> date=bufferedDateTime
<div class="modal-date-area"> onChange=(action "dateTimeChanged")
<label class="input-group-label"> showTime=true
{{i18n "admin.wizard.after_time_modal.date"}} clearable=true
</label> }}
{{date-picker value=date containerId="date-container"}}
</div>
<div class="modal-time-area">
<label class="input-group-label">
{{i18n "admin.wizard.after_time_modal.time"}}
</label>
<input type="text" id="time-picker"/>
</div>
</div>
<div id="date-container"/>
</div>
{{/d-modal-body}} {{/d-modal-body}}
<div class="modal-footer"> <div class="modal-footer">
{{d-button action="submit" class="btn-primary" label="admin.wizard.after_time_modal.done" disabled=submitDisabled}} {{d-button
<a class="clear" {{action 'clear'}}>{{i18n 'admin.wizard.after_time_modal.clear'}}</a> action="submit"
class="btn-primary"
label="admin.wizard.after_time_modal.done"
disabled=submitDisabled}}
</div> </div>

Datei anzeigen

@ -91,10 +91,12 @@
//= require discourse/app/components/input-tip //= require discourse/app/components/input-tip
//= require discourse/app/components/date-picker //= require discourse/app/components/date-picker
//= require discourse/app/components/text-field //= require discourse/app/components/text-field
//= require discourse/app/components/d-textarea
//= require discourse/app/templates/components/conditional-loading-spinner //= require discourse/app/templates/components/conditional-loading-spinner
//= require discourse/app/templates/components/d-button //= require discourse/app/templates/components/d-button
//= require discourse/app/templates/components/d-editor //= require discourse/app/templates/components/d-editor
//= require discourse/app/templates/components/date-picker
//= require discourse/app/templates/components/emoji-picker //= require discourse/app/templates/components/emoji-picker
//= require discourse/app/templates/components/popup-input-tip //= require discourse/app/templates/components/popup-input-tip
//= require discourse/app/templates/category-tag-autocomplete //= require discourse/app/templates/category-tag-autocomplete

Datei anzeigen

@ -1,6 +1,7 @@
import { default as computed, observes } from 'discourse-common/utils/decorators'; import { default as computed, observes } from 'discourse-common/utils/decorators';
import { renderAvatar } from 'discourse/helpers/user-avatar'; import { renderAvatar } from 'discourse/helpers/user-avatar';
import userSearch from '../lib/user-search'; import userSearch from '../lib/user-search';
import I18n from "I18n";
const template = function(params) { const template = function(params) {
const options = params.options; const options = params.options;

Datei anzeigen

@ -1,6 +1,6 @@
import ComposerEditor from 'discourse/components/composer-editor'; import ComposerEditor from 'discourse/components/composer-editor';
import { default as computed, on } from 'discourse-common/utils/decorators'; import { default as computed, on } from 'discourse-common/utils/decorators';
import { findRawTemplate } from "discourse/lib/raw-templates"; import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { throttle } from "@ember/runloop"; import { throttle } from "@ember/runloop";
import { scheduleOnce } from "@ember/runloop"; import { scheduleOnce } from "@ember/runloop";
import { safariHacksDisabled } from "discourse/lib/utilities"; import { safariHacksDisabled } from "discourse/lib/utilities";

Datei anzeigen

@ -1,5 +1,6 @@
import getUrl from "discourse-common/lib/get-url"; import getUrl from "discourse-common/lib/get-url";
import { getToken } from "wizard/lib/ajax"; import { getToken } from "wizard/lib/ajax";
import I18n from "I18n";
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: ["wizard-field-upload"], classNames: ["wizard-field-upload"],

Datei anzeigen

@ -2,6 +2,7 @@
import computed from "discourse-common/utils/decorators"; import computed from "discourse-common/utils/decorators";
import { siteDir, isRTL, isLTR } from "discourse/lib/text-direction"; import { siteDir, isRTL, isLTR } from "discourse/lib/text-direction";
import I18n from "I18n";
export default Ember.TextField.extend({ export default Ember.TextField.extend({
attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength', 'dir'], attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength', 'dir'],

Datei anzeigen

@ -23,7 +23,7 @@ export default {
const Store = requirejs("discourse/models/store").default; const Store = requirejs("discourse/models/store").default;
const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers; const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers;
const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default; const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default;
const Site = requirejs("discourse/models/site").default; const Site = requirejs("discourse/plugins/discourse-custom-wizard/wizard/models/site").default;
const RestAdapter = requirejs("discourse/adapters/rest").default; const RestAdapter = requirejs("discourse/adapters/rest").default;
Discourse.Model = EmberObject.extend(); Discourse.Model = EmberObject.extend();

Datei anzeigen

@ -0,0 +1,10 @@
import Site from "discourse/models/site";
export default Site.reopenClass({
// There is no site data actually loaded by the CW yet. This placeholder is
// needed by imported classes
createCurrent() {
const store = Discourse.__container__.lookup("service:store");
return store.createRecord("site", {});
},
})

Datei anzeigen

@ -1,3 +1,5 @@
import I18n from "I18n";
export default Ember.Route.extend({ export default Ember.Route.extend({
model(params) { model(params) {
const appModel = this.modelFor('custom'); const appModel = this.modelFor('custom');

Datei anzeigen

@ -0,0 +1,4 @@
{{date-picker
value=field.value
id=field.id
}}

Datei anzeigen

@ -481,54 +481,12 @@
min-height: 150px; min-height: 150px;
} }
.next-session-time-modal { .modal .modal-body.next-session-time-modal {
text-align: center; overflow: visible;
.date-time-card { .picker-container {
width: 270px; position: absolute;
padding: 10px 20px; top: 30px;
text-align: left;
}
.modal-date-time-set{
padding-top: 3px;
padding-bottom: 4px;
display: flex;
flex-direction: row;
.modal-date-area{
order: 1;
}
.modal-time-area{
order: 2;
margin-left: 10px;
.modal-time{
width: 127px;
}
}
}
.ui-timepicker-input {
width: 119px;
text-align: center;
}
.date-picker{
width: 121px;
}
.pika-single {
position: relative !important;
.pika-lendar {
border: 1px solid #eee;
padding: 14px;
margin: 0;
float: none;
width: auto;
}
} }
} }

Datei anzeigen

@ -163,6 +163,7 @@ en:
category: Category category: Category
group: Group group: Group
user_selector: User Selector user_selector: User Selector
date: Date
connector: connector:
and: "and" and: "and"
@ -208,6 +209,18 @@ en:
label: "Update Profile" label: "Update Profile"
setting: "Fields" setting: "Fields"
key: "field" key: "field"
watch_categories:
label: "Watch Categories"
categories: "Categories"
mute_remainder: "Mute Remainder"
notification_level:
label: "Notification Level"
regular: "Normal"
watching: "Watching"
tracking: "Tracking"
watching_first_post: "Watching First Post"
muted: "Muted"
select_a_notification_level: "Select level"
post_builder: post_builder:
checkbox: "Post Builder" checkbox: "Post Builder"
label: "Builder" label: "Builder"

Datei anzeigen

@ -116,12 +116,18 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:post, :post,
:post_builder, :post_builder,
:post_template, :post_template,
:notification_level,
:api,
:api_endpoint,
:api_body,
title: mapped_params, title: mapped_params,
category: mapped_params, category: mapped_params,
tags: mapped_params, tags: mapped_params,
custom_fields: mapped_params, custom_fields: mapped_params,
required: mapped_params, required: mapped_params,
recipient: mapped_params, recipient: mapped_params,
categories: mapped_params,
mute_remainder: mapped_params,
profile_updates: mapped_params, profile_updates: mapped_params,
group: mapped_params, group: mapped_params,
url: mapped_params url: mapped_params

Datei anzeigen

@ -2,7 +2,7 @@ module Jobs
class SetAfterTimeWizard < ::Jobs::Base class SetAfterTimeWizard < ::Jobs::Base
def execute(args) def execute(args)
if SiteSetting.custom_wizard_enabled if SiteSetting.custom_wizard_enabled
wizard = CustomWizard::Wizard.find(args[:wizard_id]) wizard = CustomWizard::Wizard.create(args[:wizard_id])
if wizard && wizard.after_time if wizard && wizard.after_time
user_ids = [] user_ids = []

Datei anzeigen

@ -97,19 +97,20 @@ class CustomWizard::Action
end end
def update_profile def update_profile
return unless (profile_updates = action['profile_updates']).length
params = {} params = {}
profile_updates.first[:pairs].each do |pair| if (profile_updates = action['profile_updates'])
if allowed_profile_field?(pair['key']) profile_updates.first[:pairs].each do |pair|
key = cast_profile_key(pair['key']) if allowed_profile_field?(pair['key'])
value = cast_profile_value(mapper.map_field(pair['value'], pair['value_type']), pair['key']) key = cast_profile_key(pair['key'])
value = cast_profile_value(mapper.map_field(pair['value'], pair['value_type']), pair['key'])
if user_field?(pair['key'])
params[:custom_fields] ||= {} if user_field?(pair['key'])
params[:custom_fields][key] = value params[:custom_fields] ||= {}
else params[:custom_fields][key] = value
params[key.to_sym] = value else
params[key.to_sym] = value
end
end end
end end
end end
@ -133,6 +134,36 @@ class CustomWizard::Action
end end
end end
def watch_categories
watched_categories = CustomWizard::Mapper.new(
inputs: action['categories'],
data: data,
user: user
).perform
notification_level = action['notification_level']
if notification_level.blank?
log_error("Notifcation Level was not set! Exiting wizard action")
return
end
mute_remainder = CustomWizard::Mapper.new(
inputs: action['mute_remainder'],
data: data,
user: user
).perform
Category.all.each do |category|
if watched_categories.present? && watched_categories.include?(category.id.to_s)
CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[notification_level.to_sym], category.id)
elsif mute_remainder
CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[:muted], category.id)
end
end
end
def send_to_api def send_to_api
api_body = nil api_body = nil

Datei anzeigen

@ -258,7 +258,9 @@ class CustomWizard::Builder
} }
).perform ).perform
if content.present? if content.present? &&
content[:result].present?
if content[:type] == 'association' if content[:type] == 'association'
content[:result] = content[:result].map do |item| content[:result] = content[:result].map do |item|
{ {
@ -324,6 +326,10 @@ class CustomWizard::Builder
if type === 'upload' && value.present? && !validate_file_type(value, file_types) if type === 'upload' && value.present? && !validate_file_type(value, file_types)
updater.errors.add(id, I18n.t('wizard.field.invalid_file', label: label, types: file_types)) updater.errors.add(id, I18n.t('wizard.field.invalid_file', label: label, types: file_types))
end end
if type === 'date' && value.present? && !validate_date(value)
updater.errors.add(id, I18n.t('wizard.field.invalid_date'))
end
CustomWizard::Builder.field_validators.each do |validator| CustomWizard::Builder.field_validators.each do |validator|
if type === validator[:type] if type === validator[:type]
@ -337,6 +343,15 @@ class CustomWizard::Builder
.map { |t| t.gsub('.', '') } .map { |t| t.gsub('.', '') }
.include?(File.extname(value['original_filename'])[1..-1]) .include?(File.extname(value['original_filename'])[1..-1])
end end
def validate_date(value)
begin
Date.parse(value)
true
rescue ArgumentError
false
end
end
def is_text_type(field) def is_text_type(field)
['text', 'textarea'].include? field['type'] ['text', 'textarea'].include? field['type']

Datei anzeigen

@ -11,6 +11,7 @@ class CustomWizard::Field
min_length: nil min_length: nil
}, },
text_only: {}, text_only: {},
date: {},
number: {}, number: {},
checkbox: {}, checkbox: {},
url: { url: {

Datei anzeigen

@ -1,7 +1,7 @@
class CustomWizard::Mapper class CustomWizard::Mapper
attr_accessor :inputs, :data, :user attr_accessor :inputs, :data, :user
USER_FIELDS = ['name', 'username', 'email', 'date_of_birth', 'title', 'locale', 'trust_level'] USER_FIELDS = ['name', 'username', 'email', 'date_of_birth', 'title', 'locale', 'trust_level', 'email_level']
PROFILE_FIELDS = ['location', 'website', 'bio_raw'] PROFILE_FIELDS = ['location', 'website', 'bio_raw']
def self.user_fields def self.user_fields
@ -188,7 +188,7 @@ class CustomWizard::Mapper
def map_user_field_options(value) def map_user_field_options(value)
if value.include?(User::USER_FIELD_PREFIX) if value.include?(User::USER_FIELD_PREFIX)
if field = UserField.find(value.split('_').last) if field = UserField.find_by(id: value.split('_').last)
field.user_field_options.map(&:value) field.user_field_options.map(&:value)
end end
end end

Datei anzeigen

@ -18,6 +18,7 @@ config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard"
if Rails.env.production? if Rails.env.production?
config.assets.precompile += %w{ config.assets.precompile += %w{
wizard-preload.js
wizard-custom-guest.js wizard-custom-guest.js
wizard-custom-lib.js wizard-custom-lib.js
wizard-custom.js wizard-custom.js

Datei anzeigen

@ -11,7 +11,8 @@
<%- if theme_ids.present? %> <%- if theme_ids.present? %>
<%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme) %> <%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme) %>
<%- end %> <%- end %>
<%= preload_script "locales/#{I18n.locale}" %>
<%= preload_script "ember_jquery" %> <%= preload_script "ember_jquery" %>
<%= preload_script "wizard-vendor" %> <%= preload_script "wizard-vendor" %>
<%= preload_script "wizard-application" %> <%= preload_script "wizard-application" %>
@ -19,7 +20,6 @@
<%= preload_script "wizard-custom" %> <%= preload_script "wizard-custom" %>
<%= preload_script "wizard-plugin" %> <%= preload_script "wizard-plugin" %>
<%= preload_script "pretty-text-bundle" %> <%= preload_script "pretty-text-bundle" %>
<%= preload_script "locales/#{I18n.locale}" %>
<script src="<%= ExtraLocalesController.url("wizard") %>"></script> <script src="<%= ExtraLocalesController.url("wizard") %>"></script>
<%= csrf_meta_tags %> <%= csrf_meta_tags %>