Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-25 10:40:28 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
a09376e645
Commit
be81aa7f4d
25 geänderte Dateien mit 657 neuen und 94 gelöschten Zeilen
|
@ -1,4 +1,5 @@
|
||||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import showModal from 'discourse/lib/show-modal';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
@computed('model.id', 'model.name')
|
@computed('model.id', 'model.name')
|
||||||
|
@ -6,6 +7,12 @@ export default Ember.Controller.extend({
|
||||||
return window.location.origin + '/w/' + Ember.String.dasherize(wizardId);
|
return window.location.origin + '/w/' + Ember.String.dasherize(wizardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed('model.after_time_scheduled')
|
||||||
|
nextSessionScheduledLabel(scheduled) {
|
||||||
|
return scheduled ? moment(scheduled).format('MMMM Do, HH:mm') :
|
||||||
|
I18n.t('admin.wizard.after_time_time_label');
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -21,7 +28,6 @@ export default Ember.Controller.extend({
|
||||||
this.send("refreshWizard");
|
this.send("refreshWizard");
|
||||||
}
|
}
|
||||||
}).catch((result) => {
|
}).catch((result) => {
|
||||||
console.log(result)
|
|
||||||
this.set('saving', false);
|
this.set('saving', false);
|
||||||
this.set('error', I18n.t(`admin.wizard.error.${result.error}`));
|
this.set('error', I18n.t(`admin.wizard.error.${result.error}`));
|
||||||
Ember.run.later(() => this.set('error', null), 10000);
|
Ember.run.later(() => this.set('error', null), 10000);
|
||||||
|
@ -29,9 +35,21 @@ export default Ember.Controller.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this.get('model').remove().then(() => {
|
const wizard = this.get('model');
|
||||||
|
wizard.remove().then(() => {
|
||||||
this.send("refreshAllWizards");
|
this.send("refreshAllWizards");
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
setNextSessionScheduled() {
|
||||||
|
let controller = showModal('next-session-scheduled', {
|
||||||
|
model: {
|
||||||
|
dateTime: this.get('model.after_time_scheduled'),
|
||||||
|
update: (dateTime) => this.set('model.after_time_scheduled', dateTime)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.setup();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
title: 'admin.wizard.after_time_modal.title',
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const dateTime = this.get('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 });
|
||||||
|
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, () => {
|
||||||
|
const $timePicker = $("#time-picker");
|
||||||
|
$timePicker.timepicker({ timeFormat: 'H:i' });
|
||||||
|
$timePicker.timepicker('setTime', time);
|
||||||
|
$timePicker.change(() => this.set('time', $timePicker.val()));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('date', 'time')
|
||||||
|
dateTime: function(date, time) {
|
||||||
|
return moment(date + 'T' + time).format();
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('dateTime')
|
||||||
|
submitDisabled(dateTime) {
|
||||||
|
return moment().isAfter(dateTime);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetProperties() {
|
||||||
|
this.setProperties({
|
||||||
|
date: null,
|
||||||
|
time: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
clear() {
|
||||||
|
this.resetProperties();
|
||||||
|
this.get('model.update')(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
const dateTime = this.get('dateTime');
|
||||||
|
const formatted = moment(dateTime).utc().toISOString();
|
||||||
|
this.get('model.update')(formatted);
|
||||||
|
this.resetProperties();
|
||||||
|
this.send("closeModal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,6 +1,5 @@
|
||||||
import { registerUnbound } from 'discourse-common/lib/helpers';
|
import { registerUnbound } from 'discourse-common/lib/helpers';
|
||||||
|
|
||||||
registerUnbound('dasherize', function(string) {
|
registerUnbound('dasherize', function(string) {
|
||||||
console.log(string)
|
|
||||||
return Ember.String.dasherize(string);
|
return Ember.String.dasherize(string);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export default {
|
||||||
|
name: "custom-wizard-redirect",
|
||||||
|
after: "message-bus",
|
||||||
|
|
||||||
|
initialize: function (container) {
|
||||||
|
const messageBus = container.lookup('message-bus:main');
|
||||||
|
|
||||||
|
if (!messageBus) { return; }
|
||||||
|
|
||||||
|
messageBus.subscribe("/redirect_to_wizard", function (wizardId) {
|
||||||
|
const wizardUrl = window.location.origin + '/w/' + wizardId;
|
||||||
|
window.location.href = wizardUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,5 +1,16 @@
|
||||||
import { ajax } from 'discourse/lib/ajax';
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
|
||||||
|
const wizardProperties = [
|
||||||
|
'name',
|
||||||
|
'background',
|
||||||
|
'save_submissions',
|
||||||
|
'multiple_submissions',
|
||||||
|
'after_signup',
|
||||||
|
'after_time',
|
||||||
|
'after_time_scheduled',
|
||||||
|
'required'
|
||||||
|
];
|
||||||
|
|
||||||
const CustomWizard = Discourse.Model.extend({
|
const CustomWizard = Discourse.Model.extend({
|
||||||
save() {
|
save() {
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
|
@ -8,14 +19,22 @@ const CustomWizard = Discourse.Model.extend({
|
||||||
|
|
||||||
let wizard = { id: id.underscore() };
|
let wizard = { id: id.underscore() };
|
||||||
|
|
||||||
|
wizardProperties.forEach((p) => {
|
||||||
|
const value = this.get(p);
|
||||||
|
if (value) wizard[p] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (wizard['after_time'] && wizard['after_time_scheduled']) {
|
||||||
|
return reject({ error: 'after_time_need_time' });
|
||||||
|
};
|
||||||
|
|
||||||
const steps = this.get('steps');
|
const steps = this.get('steps');
|
||||||
if (steps.length > 0) {
|
if (steps.length > 0) {
|
||||||
const stepsResult = this.buildSteps(steps);
|
const stepsResult = this.buildSteps(steps);
|
||||||
console.log(stepsResult)
|
|
||||||
if (stepsResult.error) {
|
if (stepsResult.error) {
|
||||||
reject({ error: stepsResult.error })
|
reject({ error: stepsResult.error });
|
||||||
} else {
|
} else {
|
||||||
wizard['steps'] = stepsResult;
|
wizard['steps'] = stepsResult.steps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,25 +42,12 @@ const CustomWizard = Discourse.Model.extend({
|
||||||
return reject({ error: 'steps_required' });
|
return reject({ error: 'steps_required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = this.get('name');
|
|
||||||
if (name) wizard['name'] = name;
|
|
||||||
|
|
||||||
const background = this.get('background');
|
|
||||||
if (background) wizard['background'] = background;
|
|
||||||
|
|
||||||
const save_submissions = this.get('save_submissions');
|
|
||||||
if (save_submissions) wizard['save_submissions'] = save_submissions;
|
|
||||||
|
|
||||||
const multiple_submissions = this.get('multiple_submissions');
|
|
||||||
if (multiple_submissions) wizard['multiple_submissions'] = multiple_submissions;
|
|
||||||
|
|
||||||
ajax("/admin/wizards/custom/save", {
|
ajax("/admin/wizards/custom/save", {
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
wizard: JSON.stringify(wizard)
|
wizard: JSON.stringify(wizard)
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
console.log(result)
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result);
|
reject(result);
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,10 +92,10 @@ const CustomWizard = Discourse.Model.extend({
|
||||||
|
|
||||||
if (f.type === 'dropdown') {
|
if (f.type === 'dropdown') {
|
||||||
const choices = f.choices;
|
const choices = f.choices;
|
||||||
//if ((!choices || choices.length < 1) && !f.choices_key && !f.choices_categories) {
|
if ((!choices || choices.length < 1) && !f.choices_key && !f.choices_categories) {
|
||||||
//error = 'field.need_choices';
|
error = 'field.need_choices';
|
||||||
//return;
|
return;
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete f.isNew;
|
delete f.isNew;
|
||||||
|
@ -166,10 +172,10 @@ CustomWizard.reopenClass({
|
||||||
if (w) {
|
if (w) {
|
||||||
props['id'] = w.id;
|
props['id'] = w.id;
|
||||||
props['existingId'] = true;
|
props['existingId'] = true;
|
||||||
props['name'] = w.name;
|
|
||||||
props['background'] = w.background;
|
wizardProperties.forEach((p) => {
|
||||||
props['save_submissions'] = w.save_submissions;
|
props[p] = w[p];
|
||||||
props['multiple_submissions'] = w.multiple_submissions;
|
});
|
||||||
|
|
||||||
if (w.steps && w.steps.length) {
|
if (w.steps && w.steps.length) {
|
||||||
w.steps.forEach((s) => {
|
w.steps.forEach((s) => {
|
||||||
|
@ -226,6 +232,9 @@ CustomWizard.reopenClass({
|
||||||
props['background'] = '';
|
props['background'] = '';
|
||||||
props['save_submissions'] = true;
|
props['save_submissions'] = true;
|
||||||
props['multiple_submissions'] = false;
|
props['multiple_submissions'] = false;
|
||||||
|
props['after_signup'] = false;
|
||||||
|
props['after_time'] = false;
|
||||||
|
props['required'] = false;
|
||||||
props['steps'] = Ember.A();
|
props['steps'] = Ember.A();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default Discourse.Route.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
let fields = ['user_id', 'completed'];
|
let fields = ['user'];
|
||||||
|
|
||||||
model.wizard.steps.forEach((s) => {
|
model.wizard.steps.forEach((s) => {
|
||||||
if (s.fields) {
|
if (s.fields) {
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting full">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.multiple_submissions'}}</h3>
|
<h3>{{i18n 'admin.wizard.multiple_submissions'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,6 +51,37 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.required'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.required}}
|
||||||
|
<span for="save">{{i18n 'admin.wizard.required_label'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.after_signup'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.after_signup}}
|
||||||
|
<span for="save">{{i18n 'admin.wizard.after_signup_label'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.after_time'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.after_time}}
|
||||||
|
<span for="save">{{i18n 'admin.wizard.after_time_label'}}</span>
|
||||||
|
{{d-button action='setNextSessionScheduled' translatedLabel=nextSessionScheduledLabel icon='calendar-o'}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting full">
|
<div class="setting full">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.url'}}</h3>
|
<h3>{{i18n 'admin.wizard.url'}}</h3>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{{#d-modal-body class="next-session-time-modal" title=title}}
|
||||||
|
<div class="date-time-card">
|
||||||
|
<div class="modal-date-time-set">
|
||||||
|
<div class="modal-date-area">
|
||||||
|
<label class="input-group-label">
|
||||||
|
{{i18n "admin.wizard.after_time_modal.date"}}
|
||||||
|
</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}}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
{{d-button action="submit" class="btn-primary" label="admin.wizard.after_time_modal.done" disabled=submitDisabled}}
|
||||||
|
<a class="clear" {{action 'clear'}}>{{i18n 'admin.wizard.after_time_modal.clear'}}</a>
|
||||||
|
</div>
|
|
@ -19,14 +19,6 @@ export default StepController.extend({
|
||||||
|
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
this.set('stepMessage', message);
|
this.set('stepMessage', message);
|
||||||
},
|
|
||||||
|
|
||||||
finished(result) {
|
|
||||||
let url = "/";
|
|
||||||
if (result.topic_id) {
|
|
||||||
url += `t/${result.topic_id}`;
|
|
||||||
}
|
|
||||||
window.location.href = getUrl(url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,6 +32,12 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
WizardStep.reopen({
|
WizardStep.reopen({
|
||||||
|
showQuitButton: function() {
|
||||||
|
const index = this.get('step.index');
|
||||||
|
const required = this.get('wizard.required');
|
||||||
|
return index === 0 && !required;
|
||||||
|
}.property('step.index', 'wizard.required'),
|
||||||
|
|
||||||
bannerImage: function() {
|
bannerImage: function() {
|
||||||
const src = this.get('step.banner');
|
const src = this.get('step.banner');
|
||||||
if (!src) return;
|
if (!src) return;
|
||||||
|
@ -53,7 +59,7 @@ export default {
|
||||||
this.get('step').save()
|
this.get('step').save()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (this.get('finalStep')) {
|
if (this.get('finalStep')) {
|
||||||
this.sendAction('finished', response);
|
this.get('wizard').finished(response);
|
||||||
} else {
|
} else {
|
||||||
this.sendAction('goNext', response);
|
this.sendAction('goNext', response);
|
||||||
}
|
}
|
||||||
|
@ -64,8 +70,12 @@ export default {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
quit() {
|
quit() {
|
||||||
this.set('finalStep', true);
|
if ($(event.target).hasClass('quit')) {
|
||||||
this.send('nextStep');
|
this.get('wizard').skip();
|
||||||
|
} else {
|
||||||
|
this.set('finalStep', true);
|
||||||
|
this.send('nextStep');
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import getUrl from 'discourse-common/lib/get-url';
|
||||||
import WizardField from 'wizard/models/wizard-field';
|
import WizardField from 'wizard/models/wizard-field';
|
||||||
import { ajax } from 'wizard/lib/ajax';
|
import { ajax } from 'wizard/lib/ajax';
|
||||||
import Step from 'wizard/models/step';
|
import Step from 'wizard/models/step';
|
||||||
|
|
||||||
const CustomWizard = Ember.Object.extend({
|
const CustomWizard = Ember.Object.extend({
|
||||||
@computed('steps.length')
|
@computed('steps.length')
|
||||||
totalSteps: length => length
|
totalSteps: length => length,
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
if (this.get('required')) return;
|
||||||
|
const id = this.get('id');
|
||||||
|
ajax({ url: `/w/${id}/skip`, type: 'PUT' }).then((result) => {
|
||||||
|
this.finished(result);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
finished(result) {
|
||||||
|
let url = "/";
|
||||||
|
if (result.redirect_to) {
|
||||||
|
url = result.redirect_to;
|
||||||
|
}
|
||||||
|
window.location.href = getUrl(url);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function findCustomWizard(wizardId) {
|
export function findCustomWizard(wizardId) {
|
||||||
|
|
7
assets/lib/jquery.timepicker.min.js
gevendort
Normale Datei
7
assets/lib/jquery.timepicker.min.js
gevendort
Normale Datei
Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist
72
assets/lib/jquery.timepicker.scss
Normale Datei
72
assets/lib/jquery.timepicker.scss
Normale Datei
|
@ -0,0 +1,72 @@
|
||||||
|
.ui-timepicker-wrapper {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 150px;
|
||||||
|
width: 6.5em;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);
|
||||||
|
-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);
|
||||||
|
box-shadow:0 5px 10px rgba(0,0,0,0.2);
|
||||||
|
outline: none;
|
||||||
|
z-index: 10001;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-wrapper.ui-timepicker-with-duration {
|
||||||
|
width: 13em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30,
|
||||||
|
.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 {
|
||||||
|
width: 11em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-duration {
|
||||||
|
margin-left: 5px; color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list:hover .ui-timepicker-duration {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list li {
|
||||||
|
padding: 3px 0 3px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #000;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list:hover .ui-timepicker-selected {
|
||||||
|
background: #fff; color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.ui-timepicker-selected,
|
||||||
|
.ui-timepicker-list li:hover,
|
||||||
|
.ui-timepicker-list .ui-timepicker-selected:hover {
|
||||||
|
background: #1980EC; color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.ui-timepicker-selected .ui-timepicker-duration,
|
||||||
|
.ui-timepicker-list li:hover .ui-timepicker-duration {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list li.ui-timepicker-disabled,
|
||||||
|
.ui-timepicker-list li.ui-timepicker-disabled:hover,
|
||||||
|
.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled {
|
||||||
|
color: #888;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-timepicker-list li.ui-timepicker-disabled:hover,
|
||||||
|
.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled {
|
||||||
|
background: #f2f2f2;
|
||||||
|
}
|
|
@ -55,6 +55,11 @@
|
||||||
span {
|
span {
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 5px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.full {
|
&.full {
|
||||||
|
@ -162,3 +167,54 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.next-session-time-modal {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.date-time-card {
|
||||||
|
width: 270px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,22 @@ en:
|
||||||
background: "Background"
|
background: "Background"
|
||||||
background_placeholder: "Background css property"
|
background_placeholder: "Background css property"
|
||||||
save_submissions: "Save"
|
save_submissions: "Save"
|
||||||
save_submissions_label: "Save wizard submissions"
|
save_submissions_label: "Save wizard submissions."
|
||||||
multiple_submissions: "Multiple"
|
multiple_submissions: "Multiple"
|
||||||
multiple_submissions_label: "Allow multiple submissions by the same user"
|
multiple_submissions_label: "Allow multiple submissions by the same user."
|
||||||
|
after_signup: "After Signup"
|
||||||
|
after_signup_label: "Users are directed to wizard after signup."
|
||||||
|
after_time: "After Time"
|
||||||
|
after_time_label: "Users are directed to wizard after the start time until wizard is completed or skipped."
|
||||||
|
after_time_time_label: "Start Time"
|
||||||
|
after_time_modal:
|
||||||
|
title: "Wizard Start Time"
|
||||||
|
date: "Date"
|
||||||
|
time: "Time"
|
||||||
|
done: "Set Time"
|
||||||
|
clear: "Clear"
|
||||||
|
required: "Required"
|
||||||
|
required_label: "Users cannot skip the wizard."
|
||||||
save: "Save Changes"
|
save: "Save Changes"
|
||||||
remove: "Delete Wizard"
|
remove: "Delete Wizard"
|
||||||
header: "Wizard"
|
header: "Wizard"
|
||||||
|
@ -32,6 +45,8 @@ en:
|
||||||
name_required: "Wizards must have a name."
|
name_required: "Wizards must have a name."
|
||||||
steps_required: "Wizards must have at least one step."
|
steps_required: "Wizards must have at least one step."
|
||||||
id_required: "All wizards, steps, fields and actions need an id."
|
id_required: "All wizards, steps, fields and actions need an id."
|
||||||
|
after_time_need_time: "After time is enabled but no time is set."
|
||||||
|
after_time_invalid: "After time is invalid."
|
||||||
field:
|
field:
|
||||||
need_choices: "All dropdowns need choices."
|
need_choices: "All dropdowns need choices."
|
||||||
choices_label_empty: "Custom choice labels cannot be empty."
|
choices_label_empty: "Custom choice labels cannot be empty."
|
||||||
|
|
|
@ -4,3 +4,4 @@ en:
|
||||||
field:
|
field:
|
||||||
too_short: "%{label} must be at least %{min} characters"
|
too_short: "%{label} must be at least %{min} characters"
|
||||||
none: "We couldn't find a wizard at that address."
|
none: "We couldn't find a wizard at that address."
|
||||||
|
no_skip: "Wizard can't be skipped"
|
||||||
|
|
|
@ -17,18 +17,30 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
|
|
||||||
error = nil
|
error = nil
|
||||||
|
|
||||||
if !wizard["id"] || wizard["id"].empty?
|
if wizard["id"].blank?
|
||||||
error = 'id_required'
|
error = 'id_required'
|
||||||
elsif !wizard["name"] || wizard["name"].empty?
|
elsif wizard["name"].blank?
|
||||||
error = 'name_required'
|
error = 'name_required'
|
||||||
elsif !wizard["steps"] || wizard["steps"].empty?
|
elsif wizard["steps"].blank?
|
||||||
error = 'steps_required'
|
error = 'steps_required'
|
||||||
|
elsif wizard["after_time"]
|
||||||
|
if !wizard["after_time_scheduled"]
|
||||||
|
error = 'after_time_need_time'
|
||||||
|
else
|
||||||
|
after_time_scheduled = Time.parse(wizard["after_time_scheduled"]).utc
|
||||||
|
begin
|
||||||
|
if after_time_scheduled < Time.now.utc
|
||||||
|
error = 'after_time_invalid'
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
error = 'after_time_invalid'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return render json: { error: error } if error
|
return render json: { error: error } if error
|
||||||
|
|
||||||
wizard["steps"].each do |s|
|
wizard["steps"].each do |s|
|
||||||
puts "HERE IS THE ID: #{s["id"]}"
|
|
||||||
if s["id"].blank?
|
if s["id"].blank?
|
||||||
error = 'id_required'
|
error = 'id_required'
|
||||||
break
|
break
|
||||||
|
@ -63,6 +75,17 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
|
|
||||||
return render json: { error: error } if error
|
return render json: { error: error } if error
|
||||||
|
|
||||||
|
existing = PluginStore.get('custom_wizard', params[:id])
|
||||||
|
|
||||||
|
if wizard['after_time'] && after_time_scheduled != Time.parse(existing['after_time_scheduled']).utc
|
||||||
|
Jobs.cancel_scheduled_job(:set_after_time_wizard)
|
||||||
|
Jobs.enqueue_at(after_time_scheduled, :set_after_time_wizard, wizard_id: wizard['id'])
|
||||||
|
end
|
||||||
|
|
||||||
|
if existing['after_time'] && !wizard['after_time']
|
||||||
|
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id'])
|
||||||
|
end
|
||||||
|
|
||||||
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
||||||
|
|
||||||
render json: success_json
|
render json: success_json
|
||||||
|
@ -71,6 +94,12 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
def remove
|
def remove
|
||||||
params.require(:id)
|
params.require(:id)
|
||||||
|
|
||||||
|
wizard = PluginStore.get('custom_wizard', params[:id])
|
||||||
|
|
||||||
|
if wizard['after_time']
|
||||||
|
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id'])
|
||||||
|
end
|
||||||
|
|
||||||
PluginStore.remove('custom_wizard', params[:id])
|
PluginStore.remove('custom_wizard', params[:id])
|
||||||
|
|
||||||
render json: success_json
|
render json: success_json
|
||||||
|
|
|
@ -11,9 +11,9 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
def index
|
def index
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json do
|
format.json do
|
||||||
template = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore)
|
builder = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore)
|
||||||
if template.wizard.present?
|
if builder.wizard.present?
|
||||||
wizard = template.build
|
wizard = builder.build
|
||||||
render_serialized(wizard, WizardSerializer)
|
render_serialized(wizard, WizardSerializer)
|
||||||
else
|
else
|
||||||
render json: { error: I18n.t('wizard.none') }
|
render json: { error: I18n.t('wizard.none') }
|
||||||
|
@ -22,4 +22,34 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
format.html {}
|
format.html {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## clean up if user skips wizard
|
||||||
|
def skip
|
||||||
|
wizard_id = params[:wizard_id]
|
||||||
|
|
||||||
|
wizard = PluginStore.get('custom_wizard', wizard_id.underscore)
|
||||||
|
|
||||||
|
if wizard['required']
|
||||||
|
return render json: { error: I18n.t('wizard.no_skip') }
|
||||||
|
end
|
||||||
|
|
||||||
|
user = current_user
|
||||||
|
result = success_json
|
||||||
|
submission = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)).last
|
||||||
|
|
||||||
|
if submission && submission['redirect_to']
|
||||||
|
result.merge!(redirect_to: submission['redirect_to'])
|
||||||
|
end
|
||||||
|
|
||||||
|
if submission && !wizard['save_submissions']
|
||||||
|
PluginStore.remove("#{wizard['id']}_submissions", user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if user.custom_fields['redirect_to_wizard'] === wizard_id
|
||||||
|
user.custom_fields.delete('redirect_to_wizard')
|
||||||
|
user.save_custom_fields(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
14
jobs/clear_next_session_wizard.rb
Normale Datei
14
jobs/clear_next_session_wizard.rb
Normale Datei
|
@ -0,0 +1,14 @@
|
||||||
|
module Jobs
|
||||||
|
class ClearNextSessionWizard < Jobs::Base
|
||||||
|
sidekiq_options queue: 'critical'
|
||||||
|
|
||||||
|
def execute(args)
|
||||||
|
User.human_users.each do |u|
|
||||||
|
if u.custom_fields['redirect_to_wizard'] === args[:wizard_id]
|
||||||
|
u.custom_fields.delete('redirect_to_wizard')
|
||||||
|
u.save_custom_fields(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
jobs/set_next_session_wizard.rb
Normale Datei
15
jobs/set_next_session_wizard.rb
Normale Datei
|
@ -0,0 +1,15 @@
|
||||||
|
module Jobs
|
||||||
|
class SetNextSessionWizard < Jobs::Base
|
||||||
|
def execute(args)
|
||||||
|
if PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: args[:wizard_id])
|
||||||
|
user_ids = []
|
||||||
|
User.human_users.each do |u|
|
||||||
|
u.custom_fields['redirect_to_wizard'] = args[:wizard_id]
|
||||||
|
u.save_custom_fields(true)
|
||||||
|
user_ids.push(u.id)
|
||||||
|
end
|
||||||
|
MessageBus.publish "/redirect_to_wizard", args[:wizard_id], user_ids: user_ids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,13 +7,16 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
return if data.blank?
|
return if data.blank?
|
||||||
|
|
||||||
@template = CustomWizard::Template.new(data)
|
@steps = data['steps']
|
||||||
@wizard = CustomWizard::Wizard.new(user,
|
@wizard = CustomWizard::Wizard.new(user,
|
||||||
id: wizard_id,
|
id: wizard_id,
|
||||||
save_submissions: data['save_submissions'],
|
save_submissions: data['save_submissions'],
|
||||||
multiple_submissions: data['multiple_submissions'],
|
multiple_submissions: data['multiple_submissions'],
|
||||||
background: data["background"],
|
background: data["background"],
|
||||||
name: data["name"]
|
name: data["name"],
|
||||||
|
after_time: data["after_time"],
|
||||||
|
after_signup: data["after_signup"],
|
||||||
|
required: data["required"]
|
||||||
)
|
)
|
||||||
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
|
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
|
||||||
end
|
end
|
||||||
|
@ -32,9 +35,9 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
|
|
||||||
def build
|
def build
|
||||||
unless (@wizard.completed? && !@template.respond_to?(:multiple_submissions)) ||
|
unless (@wizard.completed? && !@wizard.respond_to?(:multiple_submissions)) ||
|
||||||
!@template.steps
|
!@steps
|
||||||
@template.steps.each do |s|
|
@steps.each do |s|
|
||||||
@wizard.append_step(s['id']) do |step|
|
@wizard.append_step(s['id']) do |step|
|
||||||
step.title = s['title'] if s['title']
|
step.title = s['title'] if s['title']
|
||||||
step.description = s['description'] if s['description']
|
step.description = s['description'] if s['description']
|
||||||
|
@ -53,7 +56,7 @@ class CustomWizard::Builder
|
||||||
params[:description] = f['description'] if f['description']
|
params[:description] = f['description'] if f['description']
|
||||||
params[:key] = f['key'] if f['key']
|
params[:key] = f['key'] if f['key']
|
||||||
|
|
||||||
if @submissions.last && @submissions.last['completed'] === false
|
if @submissions.last
|
||||||
submission = @submissions.last
|
submission = @submissions.last
|
||||||
params[:value] = submission[f['id']] if submission[f['id']]
|
params[:value] = submission[f['id']] if submission[f['id']]
|
||||||
end
|
end
|
||||||
|
@ -122,7 +125,14 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
next if updater.errors.any?
|
next if updater.errors.any?
|
||||||
|
|
||||||
data = @wizard.save_submissions ? submission : step_input
|
if @wizard.save_submissions
|
||||||
|
data = submission
|
||||||
|
else
|
||||||
|
data = step_input
|
||||||
|
|
||||||
|
# Allow redirect to be passed to wizard that doesn't save submissions.
|
||||||
|
data['redirect_to'] = submission['redirect_to'] if submission['redirect_to']
|
||||||
|
end
|
||||||
|
|
||||||
if s['actions'] && s['actions'].length
|
if s['actions'] && s['actions'].length
|
||||||
s['actions'].each do |a|
|
s['actions'].each do |a|
|
||||||
|
@ -177,7 +187,8 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
post.topic.save_custom_fields(true)
|
post.topic.save_custom_fields(true)
|
||||||
end
|
end
|
||||||
updater.result = { topic_id: post.topic.id }
|
|
||||||
|
data['redirect_to'] = post.topic.url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -198,7 +209,7 @@ class CustomWizard::Builder
|
||||||
if creator.errors.present?
|
if creator.errors.present?
|
||||||
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
||||||
else
|
else
|
||||||
updater.result = { topic_id: post.topic_id }
|
data['redirect_to'] = post.topic.url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -214,20 +225,32 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if updater.errors.empty?
|
||||||
|
updater.result = { redirect_to: data['redirect_to'] }
|
||||||
|
end
|
||||||
|
|
||||||
if @wizard.save_submissions && updater.errors.empty?
|
if @wizard.save_submissions && updater.errors.empty?
|
||||||
@submissions.pop(1) if submission && submission['completed'] === false
|
|
||||||
|
|
||||||
submission['user_id'] = @wizard.user.id
|
|
||||||
submission['completed'] = updater.step.next.nil?
|
|
||||||
|
|
||||||
if step_input
|
if step_input
|
||||||
step_input.each do |key, value|
|
step_input.each do |key, value|
|
||||||
submission[key] = value
|
data[key] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@submissions.push(submission)
|
if data.present?
|
||||||
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
|
@submissions.pop(1) if @wizard.unfinished?
|
||||||
|
@submissions.push(data)
|
||||||
|
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ensure there is no submission left over after the user has completed a wizard with save_submissions off
|
||||||
|
if !@wizard.save_submissions && updater.step.next.nil?
|
||||||
|
PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @wizard.after_time && updater.step.next.nil?
|
||||||
|
@wizard.user.custom_fields.delete('redirect_to_wizard');
|
||||||
|
@wizard.user.save_custom_fields(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,27 @@
|
||||||
class CustomWizard::Template
|
class CustomWizard::Template
|
||||||
|
|
||||||
attr_reader :id, :name, :steps, :background, :save_submissions, :multiple_submissions, :custom
|
attr_reader :id,
|
||||||
|
:name,
|
||||||
|
:steps,
|
||||||
|
:background,
|
||||||
|
:save_submissions,
|
||||||
|
:multiple_submissions,
|
||||||
|
:after_signup,
|
||||||
|
:after_time,
|
||||||
|
:after_time_scheduled,
|
||||||
|
:required
|
||||||
|
|
||||||
def initialize(data)
|
def initialize(data)
|
||||||
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
||||||
@id = data['id']
|
@id = data['id']
|
||||||
@name = data['name']
|
@name = data['name']
|
||||||
@background = data['background']
|
|
||||||
@save_submissions = data['save_submissions']
|
|
||||||
@multiple_submissions = data['multiple_submissions']
|
|
||||||
@steps = data['steps']
|
@steps = data['steps']
|
||||||
|
@background = data['background']
|
||||||
|
@save_submissions = data['save_submissions'] || false
|
||||||
|
@multiple_submissions = data['multiple_submissions'] || false
|
||||||
|
@after_signup = data['after_signup']
|
||||||
|
@after_time = data['after_time']
|
||||||
|
@after_time_scheduled = data['after_time_scheduled']
|
||||||
|
@required = data['required']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,17 +6,24 @@ require_dependency 'wizard/builder'
|
||||||
class CustomWizard::Wizard
|
class CustomWizard::Wizard
|
||||||
|
|
||||||
attr_reader :steps, :user
|
attr_reader :steps, :user
|
||||||
attr_accessor :id, :name, :background, :save_submissions, :multiple_submissions
|
attr_accessor :id,
|
||||||
|
:name,
|
||||||
|
:background,
|
||||||
|
:save_submissions,
|
||||||
|
:multiple_submissions,
|
||||||
|
:after_time,
|
||||||
|
:after_signup,
|
||||||
|
:required
|
||||||
|
|
||||||
def initialize(user, attrs = {})
|
def initialize(user, attrs = {})
|
||||||
@steps = []
|
@steps = []
|
||||||
@user = user
|
@user = user
|
||||||
@first_step = nil
|
@first_step = nil
|
||||||
@id = attrs[:id] if attrs[:id]
|
|
||||||
@name = attrs[:name] if attrs[:name]
|
attrs.each do |key, value|
|
||||||
@save_submissions = attrs[:save_submissions] if attrs[:save_submissions]
|
setter = "#{key}="
|
||||||
@multiple_submissions = attrs[:multiple_submissions] if attrs[:multiple_submissions]
|
send(setter, value) if respond_to?(setter.to_sym, false)
|
||||||
@background = attrs[:background] if attrs[:background]
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_step(step_name)
|
def create_step(step_name)
|
||||||
|
@ -58,26 +65,59 @@ class CustomWizard::Wizard
|
||||||
@first_step
|
@first_step
|
||||||
end
|
end
|
||||||
|
|
||||||
def completed_steps?(steps)
|
|
||||||
steps = [steps].flatten.uniq
|
|
||||||
|
|
||||||
completed = ::UserHistory.where(
|
|
||||||
acting_user_id: @user.id,
|
|
||||||
action: ::UserHistory.actions[:custom_wizard_step],
|
|
||||||
context: @id,
|
|
||||||
subject: steps
|
|
||||||
).distinct.order(:subject).pluck(:subject)
|
|
||||||
|
|
||||||
steps.sort == completed
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_updater(step_id, fields)
|
def create_updater(step_id, fields)
|
||||||
step = @steps.find { |s| s.id == step_id.dasherize }
|
step = @steps.find { |s| s.id == step_id }
|
||||||
wizard = self
|
wizard = self
|
||||||
CustomWizard::StepUpdater.new(@user, wizard, step, fields)
|
CustomWizard::StepUpdater.new(@user, wizard, step, fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unfinished?
|
||||||
|
most_recent = ::UserHistory.where(
|
||||||
|
acting_user_id: @user.id,
|
||||||
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
|
context: @id,
|
||||||
|
).distinct.order(:updated_at).first
|
||||||
|
|
||||||
|
if most_recent
|
||||||
|
last_finished_step = most_recent.subject
|
||||||
|
last_step = CustomWizard::Wizard.step_ids(@id).last
|
||||||
|
last_finished_step != last_step
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def completed?
|
def completed?
|
||||||
completed_steps?(@steps.map(&:id))
|
steps = CustomWizard::Wizard.step_ids(@id)
|
||||||
|
|
||||||
|
history = ::UserHistory.where(
|
||||||
|
acting_user_id: @user.id,
|
||||||
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
|
context: @id
|
||||||
|
)
|
||||||
|
|
||||||
|
if @completed_after
|
||||||
|
history.where("updated_at > ?", @completed_after)
|
||||||
|
end
|
||||||
|
|
||||||
|
completed = history.distinct.order(:subject).pluck(:subject)
|
||||||
|
|
||||||
|
(steps - completed).empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.after_signup
|
||||||
|
rows = PluginStoreRow.where(plugin_name: 'custom_wizard')
|
||||||
|
wizards = [*rows].select { |r| r.value['after_signup'] }
|
||||||
|
if wizards.any?
|
||||||
|
wizards.first.key
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.step_ids(wizard_id)
|
||||||
|
data = PluginStore.get('custom_wizard', wizard_id)
|
||||||
|
steps = data['steps'] || []
|
||||||
|
steps.map { |s| s['id'] }.flatten.uniq
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,28 @@
|
||||||
|
require_dependency 'wizard'
|
||||||
require_dependency 'wizard/field'
|
require_dependency 'wizard/field'
|
||||||
require_dependency 'wizard/step'
|
require_dependency 'wizard/step'
|
||||||
|
|
||||||
|
::Wizard.class_eval do
|
||||||
|
def self.user_requires_completion?(user)
|
||||||
|
wizard_result = self.new(user).requires_completion?
|
||||||
|
return wizard_result if wizard_result
|
||||||
|
|
||||||
|
custom_redirect = nil
|
||||||
|
|
||||||
|
if user && wizard_id = CustomWizard::Wizard.after_signup
|
||||||
|
custom_redirect = wizard_id.dasherize
|
||||||
|
|
||||||
|
if CustomWizard::Wizard.new(user, id: wizard_id).completed?
|
||||||
|
custom_redirect = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$redis.set('custom_wizard_redirect', custom_redirect)
|
||||||
|
|
||||||
|
!!custom_redirect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
::Wizard::Field.class_eval do
|
::Wizard::Field.class_eval do
|
||||||
attr_reader :label, :description, :key, :min_length
|
attr_reader :label, :description, :key, :min_length
|
||||||
|
|
||||||
|
@ -24,7 +46,7 @@ class ::Wizard::Step
|
||||||
end
|
end
|
||||||
|
|
||||||
::WizardSerializer.class_eval do
|
::WizardSerializer.class_eval do
|
||||||
attributes :id, :background, :completed
|
attributes :id, :background, :completed, :required
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id
|
object.id
|
||||||
|
@ -57,6 +79,10 @@ end
|
||||||
def include_steps?
|
def include_steps?
|
||||||
!include_completed?
|
!include_completed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def required
|
||||||
|
object.required
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
::WizardStepSerializer.class_eval do
|
::WizardStepSerializer.class_eval do
|
||||||
|
|
51
plugin.rb
51
plugin.rb
|
@ -4,6 +4,8 @@
|
||||||
# authors: Angus McLeod
|
# authors: Angus McLeod
|
||||||
|
|
||||||
register_asset 'stylesheets/wizard_custom_admin.scss'
|
register_asset 'stylesheets/wizard_custom_admin.scss'
|
||||||
|
register_asset 'lib/jquery.timepicker.min.js'
|
||||||
|
register_asset 'lib/jquery.timepicker.scss'
|
||||||
|
|
||||||
config = Rails.application.config
|
config = Rails.application.config
|
||||||
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts')
|
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts')
|
||||||
|
@ -22,6 +24,7 @@ after_initialize do
|
||||||
|
|
||||||
CustomWizard::Engine.routes.draw do
|
CustomWizard::Engine.routes.draw do
|
||||||
get ':wizard_id' => 'wizard#index'
|
get ':wizard_id' => 'wizard#index'
|
||||||
|
put ':wizard_id/skip' => 'wizard#skip'
|
||||||
get ':wizard_id/steps' => 'wizard#index'
|
get ':wizard_id/steps' => 'wizard#index'
|
||||||
get ':wizard_id/steps/:step_id' => 'wizard#index'
|
get ':wizard_id/steps/:step_id' => 'wizard#index'
|
||||||
put ':wizard_id/steps/:step_id' => 'steps#update'
|
put ':wizard_id/steps/:step_id' => 'steps#update'
|
||||||
|
@ -45,6 +48,8 @@ after_initialize do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
load File.expand_path('../jobs/clear_after_time_wizard.rb', __FILE__)
|
||||||
|
load File.expand_path('../jobs/set_after_time_wizard.rb', __FILE__)
|
||||||
load File.expand_path('../lib/builder.rb', __FILE__)
|
load File.expand_path('../lib/builder.rb', __FILE__)
|
||||||
load File.expand_path('../lib/field.rb', __FILE__)
|
load File.expand_path('../lib/field.rb', __FILE__)
|
||||||
load File.expand_path('../lib/step_updater.rb', __FILE__)
|
load File.expand_path('../lib/step_updater.rb', __FILE__)
|
||||||
|
@ -54,4 +59,50 @@ after_initialize do
|
||||||
load File.expand_path('../controllers/wizard.rb', __FILE__)
|
load File.expand_path('../controllers/wizard.rb', __FILE__)
|
||||||
load File.expand_path('../controllers/steps.rb', __FILE__)
|
load File.expand_path('../controllers/steps.rb', __FILE__)
|
||||||
load File.expand_path('../controllers/admin.rb', __FILE__)
|
load File.expand_path('../controllers/admin.rb', __FILE__)
|
||||||
|
|
||||||
|
::UsersController.class_eval do
|
||||||
|
def wizard_path
|
||||||
|
if custom_wizard_redirect = $redis.get('custom_wizard_redirect')
|
||||||
|
"#{Discourse.base_url}/w/#{custom_wizard_redirect}"
|
||||||
|
else
|
||||||
|
"#{Discourse.base_url}/wizard"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module InvitesControllerCustomWizard
|
||||||
|
def path(url)
|
||||||
|
if Wizard.user_requires_completion?(@user)
|
||||||
|
wizard_path = $redis.get('custom_wizard_redirect')
|
||||||
|
unless url === '/'
|
||||||
|
PluginStore.set("#{wizard_path.underscore}_submissions", @user.id, [{ redirect_to: url }])
|
||||||
|
end
|
||||||
|
url = "/w/#{wizard_path}"
|
||||||
|
end
|
||||||
|
super(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def post_process_invite(user)
|
||||||
|
super(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require_dependency 'invites_controller'
|
||||||
|
class ::InvitesController
|
||||||
|
prepend InvitesControllerCustomWizard
|
||||||
|
end
|
||||||
|
|
||||||
|
class ::ApplicationController
|
||||||
|
before_action :redirect_to_wizard_if_required, if: :current_user
|
||||||
|
|
||||||
|
def redirect_to_wizard_if_required
|
||||||
|
@wizard_id ||= current_user.custom_fields['redirect_to_wizard']
|
||||||
|
if @wizard_id && request.original_url !~ /w/ && request.original_url !~ /admin/
|
||||||
|
redirect_to "/w/#{@wizard_id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_to_serializer(:current_user, :redirect_to_wizard) { object.custom_fields['redirect_to_wizard'] }
|
||||||
end
|
end
|
||||||
|
|
Laden …
In neuem Issue referenzieren