Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 09:20:29 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
b8e0291512
Commit
f6251ace06
22 geänderte Dateien mit 124 neuen und 85 gelöschten Zeilen
|
@ -1,5 +1,11 @@
|
||||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
const ACTION_TYPES = [
|
||||||
|
{ id: 'create_topic', name: 'create_topic *' },
|
||||||
|
{ id: 'update_profile', name: 'update_profile *' },
|
||||||
|
{ id: 'send_message', name: 'send_message *' }
|
||||||
|
];
|
||||||
|
|
||||||
const PROFILE_FIELDS = [
|
const PROFILE_FIELDS = [
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
|
@ -19,7 +25,7 @@ const PROFILE_FIELDS = [
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'wizard-custom-action',
|
classNames: 'wizard-custom-action',
|
||||||
types: ['create_topic', 'update_profile', 'send_message'],
|
types: ACTION_TYPES,
|
||||||
profileFields: PROFILE_FIELDS,
|
profileFields: PROFILE_FIELDS,
|
||||||
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
||||||
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
||||||
|
|
|
@ -102,7 +102,7 @@ const CustomWizard = Discourse.Model.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
CustomWizard.reopenClass({
|
CustomWizard.reopenClass({
|
||||||
findAll() {
|
all() {
|
||||||
return ajax("/admin/wizards/custom/all", {
|
return ajax("/admin/wizards/custom/all", {
|
||||||
type: 'GET'
|
type: 'GET'
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
|
@ -110,8 +110,8 @@ CustomWizard.reopenClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
findAllSubmissions() {
|
submissions(wizardId) {
|
||||||
return ajax("/admin/wizards/submissions/all", {
|
return ajax(`/admin/wizards/submissions/${wizardId}`, {
|
||||||
type: "GET"
|
type: "GET"
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
return result.submissions;
|
return result.submissions;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import CustomWizard from '../models/custom-wizard';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
return this.modelFor('admin-wizards-submissions').findBy('id', params.wizard_id);
|
return CustomWizard.submissions(params.wizard_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import CustomWizard from '../models/custom-wizard';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model() {
|
model() {
|
||||||
return CustomWizard.findAll();
|
return CustomWizard.all();
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import CustomWizard from '../models/custom-wizard';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model() {
|
model() {
|
||||||
return CustomWizard.findAllSubmissions();
|
return CustomWizard.all();
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel(model, transition) {
|
afterModel(model, transition) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='content-list wizard-list'>
|
<div class='content-list wizard-list'>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each model as |s|}}
|
{{#each model as |w|}}
|
||||||
<li>
|
<li>
|
||||||
{{#link-to "adminWizardSubmissions" s.id}}{{s.name}}{{/link-to}}
|
{{#link-to "adminWizardSubmissions" w.id}}{{w.name}}{{/link-to}}
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{combo-box value=action.type content=types}}
|
{{combo-box value=action.type content=types}}
|
||||||
|
<label>*{{i18n 'admin.wizard.action.requires_save'}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting full">
|
<div class="setting full">
|
||||||
<label>{{i18n "admin.wizard.action.create_topic.add_fields"}}</label>
|
<label>{{i18n "admin.wizard.action.add_fields" type='Topic'}}</label>
|
||||||
{{wizard-custom-input inputs=action.add_fields valueContent=wizardFields noneValue='admin.wizard.action.none'}}
|
{{wizard-custom-input inputs=action.add_fields valueContent=wizardFields noneValue='admin.wizard.action.none'}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -80,10 +81,16 @@
|
||||||
allowedUsers="true"}}
|
allowedUsers="true"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full">
|
||||||
|
<label>{{i18n "admin.wizard.action.add_fields" type='Message'}}</label>
|
||||||
|
{{wizard-custom-input inputs=action.add_fields valueContent=wizardFields noneValue='admin.wizard.action.none'}}
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if updateProfile}}
|
{{#if updateProfile}}
|
||||||
<div class="setting full">
|
<div class="setting full">
|
||||||
|
<label>{{i18n "admin.wizard.action.add_fields" type='Profile'}}</label>
|
||||||
{{wizard-custom-input inputs=action.profile_updates
|
{{wizard-custom-input inputs=action.profile_updates
|
||||||
valueContent=profileFields
|
valueContent=profileFields
|
||||||
keyContent=wizardFields
|
keyContent=wizardFields
|
||||||
|
|
|
@ -18,6 +18,7 @@ export default {
|
||||||
|
|
||||||
Router.map(function() {
|
Router.map(function() {
|
||||||
this.route('custom', { path: '/:wizard_id' }, function() {
|
this.route('custom', { path: '/:wizard_id' }, function() {
|
||||||
|
this.route('steps');
|
||||||
this.route('step', { path: '/steps/:step_id' });
|
this.route('step', { path: '/steps/:step_id' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,8 @@ export function findCustomWizard(wizardId) {
|
||||||
return ajax({ url: `/w/${wizardId}` }).then(result => {
|
return ajax({ url: `/w/${wizardId}` }).then(result => {
|
||||||
const wizard = result.wizard;
|
const wizard = result.wizard;
|
||||||
|
|
||||||
|
if (!wizard) return null;
|
||||||
|
|
||||||
if (!wizard.completed) {
|
if (!wizard.completed) {
|
||||||
wizard.steps = wizard.steps.map(step => {
|
wizard.steps = wizard.steps.map(step => {
|
||||||
const stepObj = Step.create(step);
|
const stepObj = Step.create(step);
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
const appModel = this.modelFor('custom');
|
const appModel = this.modelFor('custom');
|
||||||
|
|
||||||
|
if (appModel) {
|
||||||
if (appModel.completed) {
|
if (appModel.completed) {
|
||||||
this.set('completed', true);
|
this.set('completed', true);
|
||||||
} else if (appModel.start) {
|
} else if (appModel.start) {
|
||||||
this.replaceWith('custom.step', appModel.start);
|
this.replaceWith('custom.step', appModel.start);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller) {
|
setupController(controller) {
|
||||||
|
|
5
assets/javascripts/wizard/routes/custom-steps.js.es6
Normale Datei
5
assets/javascripts/wizard/routes/custom-steps.js.es6
Normale Datei
|
@ -0,0 +1,5 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
redirect() {
|
||||||
|
this.transitionTo('custom.index');
|
||||||
|
}
|
||||||
|
});
|
|
@ -16,8 +16,9 @@ export default Ember.Route.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
const background = model ? model.get('background') : 'AliceBlue';
|
||||||
Ember.run.scheduleOnce('afterRender', this, function(){
|
Ember.run.scheduleOnce('afterRender', this, function(){
|
||||||
$('body.custom-wizard').css('background', model.get('background'));
|
$('body.custom-wizard').css('background', background);
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
|
|
|
@ -33,6 +33,10 @@
|
||||||
width: 200px;
|
width: 200px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-step-form .wizard-btn {
|
.wizard-step-form .wizard-btn {
|
||||||
|
|
|
@ -44,9 +44,15 @@
|
||||||
width: 90px;
|
width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-value span {
|
.setting-value {
|
||||||
|
label {
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.full {
|
&.full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -64,21 +64,20 @@ en:
|
||||||
title: "Title"
|
title: "Title"
|
||||||
post: "Post"
|
post: "Post"
|
||||||
none: "Select a field"
|
none: "Select a field"
|
||||||
|
requires_save: "Requires 'Save' to be turned on."
|
||||||
|
add_fields: "Add Fields To {{type}}"
|
||||||
send_message:
|
send_message:
|
||||||
label: "Send Message"
|
label: "Send Message"
|
||||||
recipient: "Recipient"
|
recipient: "Recipient"
|
||||||
create_topic:
|
create_topic:
|
||||||
label: "Create Topic"
|
label: "Create Topic"
|
||||||
category: "Category"
|
category: "Category"
|
||||||
add_fields: "Add Fields"
|
|
||||||
update_profile:
|
update_profile:
|
||||||
label: "Update Profile"
|
label: "Update Profile"
|
||||||
wizard_field: "Wizard Field"
|
wizard_field: "Wizard Field"
|
||||||
profile_field: "Profile Field"
|
profile_field: "Profile Field"
|
||||||
|
|
||||||
wizard_js:
|
wizard_js:
|
||||||
wizard:
|
|
||||||
completed: "You have completed this wizard."
|
|
||||||
location:
|
location:
|
||||||
name:
|
name:
|
||||||
title: "Name (optional)"
|
title: "Name (optional)"
|
||||||
|
@ -109,3 +108,9 @@ en:
|
||||||
city: "Please enter a city or town."
|
city: "Please enter a city or town."
|
||||||
countrycode: "Please enter a country."
|
countrycode: "Please enter a country."
|
||||||
geo_location: "Search and select a result."
|
geo_location: "Search and select a result."
|
||||||
|
|
||||||
|
select_box:
|
||||||
|
filter_placeholder: "Search..."
|
||||||
|
|
||||||
|
wizard:
|
||||||
|
completed: "You have completed this wizard."
|
||||||
|
|
|
@ -3,3 +3,4 @@ en:
|
||||||
custom_title: "Wizard"
|
custom_title: "Wizard"
|
||||||
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."
|
||||||
|
|
|
@ -44,27 +44,13 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
render json: success_json.merge(wizards: wizards)
|
render json: success_json.merge(wizards: wizards)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_submissions
|
def submissions
|
||||||
params.require(:wizard_id)
|
params.require(:wizard_id)
|
||||||
|
|
||||||
wizard = PluginStore.get('custom_wizard_submissions', params[:wizard_id].underscore)
|
rows = PluginStoreRow.where(plugin_name: "#{params[:wizard_id]}_submissions").order(:id)
|
||||||
|
|
||||||
|
submissions = [*rows].map { |r| ::JSON.parse(r.value) }
|
||||||
|
|
||||||
render json: success_json.merge(submissions: submissions)
|
render json: success_json.merge(submissions: submissions)
|
||||||
end
|
end
|
||||||
|
|
||||||
def submissions
|
|
||||||
rows = PluginStoreRow.where(plugin_name: 'custom_wizard_submissions').order(:id)
|
|
||||||
|
|
||||||
all = [*rows].map do |r|
|
|
||||||
wizard = PluginStore.get('custom_wizard', r.key)
|
|
||||||
name = wizard ? wizard['name'] : r.key
|
|
||||||
{
|
|
||||||
id: r.key,
|
|
||||||
name: name,
|
|
||||||
submissions: ::JSON.parse(r.value)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: success_json.merge(submissions: all)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,14 +5,19 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
|
|
||||||
def wizard_page_title
|
def wizard_page_title
|
||||||
wizard = PluginStore.get('custom_wizard', params[:wizard_id].underscore)
|
wizard = PluginStore.get('custom_wizard', params[:wizard_id].underscore)
|
||||||
wizard['name'] || wizard['id']
|
wizard ? (wizard['name'] || wizard['id']) : I18n.t('wizard.custom_title')
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json do
|
format.json do
|
||||||
wizard = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore).build
|
template = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore)
|
||||||
|
if template.wizard.present?
|
||||||
|
wizard = template.build
|
||||||
render_serialized(wizard, WizardSerializer)
|
render_serialized(wizard, WizardSerializer)
|
||||||
|
else
|
||||||
|
render json: { error: I18n.t('wizard.none') }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
format.html {}
|
format.html {}
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
class CustomWizard::Builder
|
class CustomWizard::Builder
|
||||||
|
|
||||||
attr_accessor :wizard, :updater, :submission
|
attr_accessor :wizard, :updater, :submissions
|
||||||
|
|
||||||
def initialize(user, wizard_id)
|
def initialize(user, wizard_id)
|
||||||
data = PluginStore.get('custom_wizard', wizard_id)
|
data = PluginStore.get('custom_wizard', wizard_id)
|
||||||
|
|
||||||
|
return if data.blank?
|
||||||
|
|
||||||
@template = CustomWizard::Template.new(data)
|
@template = CustomWizard::Template.new(data)
|
||||||
@wizard = CustomWizard::Wizard.new(user,
|
@wizard = CustomWizard::Wizard.new(user,
|
||||||
id: wizard_id,
|
id: wizard_id,
|
||||||
|
@ -12,7 +15,7 @@ class CustomWizard::Builder
|
||||||
background: data["background"],
|
background: data["background"],
|
||||||
name: data["name"]
|
name: data["name"]
|
||||||
)
|
)
|
||||||
@submissions = Array.wrap(PluginStore.get("custom_wizard_submissions", wizard_id))
|
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sorted_handlers
|
def self.sorted_handlers
|
||||||
|
@ -121,7 +124,7 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
if s['actions'] && s['actions'].length
|
if s['actions'] && s['actions'].length
|
||||||
s['actions'].each do |a|
|
s['actions'].each do |a|
|
||||||
if a['type'] === 'create_topic'
|
if a['type'] === 'create_topic' && submission
|
||||||
title = submission[a['title']]
|
title = submission[a['title']]
|
||||||
post = submission[a['post']]
|
post = submission[a['post']]
|
||||||
|
|
||||||
|
@ -177,7 +180,7 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if a['type'] === 'send_message'
|
if a['type'] === 'send_message' && submission
|
||||||
title = submission[a['title']]
|
title = submission[a['title']]
|
||||||
post = submission[a['post']]
|
post = submission[a['post']]
|
||||||
|
|
||||||
|
@ -198,7 +201,7 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if a['type'] === 'update_profile' && a['profile_updates'].length
|
if a['type'] === 'update_profile' && a['profile_updates'].length && submission
|
||||||
user_updater = UserUpdater.new(user, user)
|
user_updater = UserUpdater.new(user, user)
|
||||||
attributes = {}
|
attributes = {}
|
||||||
a['profile_updates'].each do |pu|
|
a['profile_updates'].each do |pu|
|
||||||
|
@ -222,7 +225,7 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
|
|
||||||
@submissions.push(submission)
|
@submissions.push(submission)
|
||||||
PluginStore.set('custom_wizard_submissions', @wizard.id, @submissions)
|
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ class CustomWizard::StepUpdater
|
||||||
|
|
||||||
if success?
|
if success?
|
||||||
UserHistory.create(action: UserHistory.actions[:custom_wizard_step],
|
UserHistory.create(action: UserHistory.actions[:custom_wizard_step],
|
||||||
|
acting_user_id: @current_user.id,
|
||||||
context: @wizard.id,
|
context: @wizard.id,
|
||||||
subject: @step.id)
|
subject: @step.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,9 +46,10 @@ class CustomWizard::Wizard
|
||||||
def start
|
def start
|
||||||
completed = ::UserHistory.where(
|
completed = ::UserHistory.where(
|
||||||
acting_user_id: @user.id,
|
acting_user_id: @user.id,
|
||||||
action: ::UserHistory.actions[:custom_wizard_step]
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
).where(context: @steps.map(&:id))
|
context: @id,
|
||||||
.uniq.pluck(:context)
|
subject: @steps.map(&:id)
|
||||||
|
).uniq.pluck(:subject)
|
||||||
|
|
||||||
@steps.each do |s|
|
@steps.each do |s|
|
||||||
return s unless completed.include?(s.id)
|
return s unless completed.include?(s.id)
|
||||||
|
@ -62,9 +63,10 @@ class CustomWizard::Wizard
|
||||||
|
|
||||||
completed = ::UserHistory.where(
|
completed = ::UserHistory.where(
|
||||||
acting_user_id: @user.id,
|
acting_user_id: @user.id,
|
||||||
action: ::UserHistory.actions[:custom_wizard_step]
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
).where(context: steps)
|
context: @id,
|
||||||
.distinct.order(:context).pluck(:context)
|
subject: steps
|
||||||
|
).distinct.order(:subject).pluck(:subject)
|
||||||
|
|
||||||
steps.sort == completed
|
steps.sort == completed
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'as
|
||||||
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard')
|
config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard')
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
UserHistory.actions[:custom_wizard_step] = 100
|
UserHistory.actions[:custom_wizard_step] = 1000
|
||||||
|
|
||||||
require_dependency 'application_controller'
|
require_dependency 'application_controller'
|
||||||
module ::CustomWizard
|
module ::CustomWizard
|
||||||
|
@ -22,7 +22,7 @@ after_initialize do
|
||||||
|
|
||||||
CustomWizard::Engine.routes.draw do
|
CustomWizard::Engine.routes.draw do
|
||||||
get ':wizard_id' => 'wizard#index'
|
get ':wizard_id' => 'wizard#index'
|
||||||
get ':wizard_id/steps' => 'steps#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'
|
||||||
end
|
end
|
||||||
|
@ -41,8 +41,7 @@ after_initialize do
|
||||||
put 'admin/wizards/custom/save' => 'admin#save'
|
put 'admin/wizards/custom/save' => 'admin#save'
|
||||||
delete 'admin/wizards/custom/remove' => 'admin#remove'
|
delete 'admin/wizards/custom/remove' => 'admin#remove'
|
||||||
get 'admin/wizards/submissions' => 'admin#index'
|
get 'admin/wizards/submissions' => 'admin#index'
|
||||||
get 'admin/wizards/submissions/all' => 'admin#submissions'
|
get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions'
|
||||||
get 'admin/wizards/submissions/:wizard_id' => 'admin#find_submissions'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren