diff --git a/app/assets/javascripts/app/controllers/_channel/form.js.coffee b/app/assets/javascripts/app/controllers/_channel/form.js.coffee
new file mode 100644
index 000000000..ebc5442a3
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_channel/form.js.coffee
@@ -0,0 +1,15 @@
+class App.ChannelForm extends App.Controller
+ constructor: ->
+ super
+ @title 'Form'
+ @render()
+
+ new App.SettingsArea(
+ el: @el.find('.js-settings')
+ area: 'Form::Base'
+ )
+
+ render: ->
+ @html App.view('channel/form')(
+ baseurl: window.location.origin
+ )
diff --git a/app/assets/javascripts/app/controllers/channel.js.coffee b/app/assets/javascripts/app/controllers/channel.js.coffee
index 50de9697a..3171311aa 100644
--- a/app/assets/javascripts/app/controllers/channel.js.coffee
+++ b/app/assets/javascripts/app/controllers/channel.js.coffee
@@ -6,8 +6,9 @@
#App.Config.set( 'Channels', { prio: 2500, parent: '#admin', name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBar' )
App.Config.set( 'Web', { prio: 1000, name: 'Web', parent: '#channels', target: '#channels/web', controller: App.ChannelWeb, role: ['Admin'] }, 'NavBarAdmin' )
-App.Config.set( 'Email', { prio: 2000, name: 'Email', parent: '#channels', target: '#channels/email', controller: App.ChannelEmail, role: ['Admin'] }, 'NavBarAdmin' )
-App.Config.set( 'Chat', { prio: 3000, name: 'Chat', parent: '#channels', target: '#channels/chat', controller: App.ChannelChat, role: ['Admin'] }, 'NavBarAdmin' )
-App.Config.set( 'Twitter', { prio: 4000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: App.ChannelTwitter, role: ['Admin'] }, 'NavBarAdmin' )
-App.Config.set( 'Facebook', { prio: 5000, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: App.ChannelFacebook, role: ['Admin'] }, 'NavBarAdmin' )
+App.Config.set( 'Form', { prio: 2000, name: 'Form', parent: '#channels', target: '#channels/form', controller: App.ChannelForm, role: ['Admin'] }, 'NavBarAdmin' )
+App.Config.set( 'Email', { prio: 3000, name: 'Email', parent: '#channels', target: '#channels/email', controller: App.ChannelEmail, role: ['Admin'] }, 'NavBarAdmin' )
+App.Config.set( 'Chat', { prio: 4000, name: 'Chat', parent: '#channels', target: '#channels/chat', controller: App.ChannelChat, role: ['Admin'] }, 'NavBarAdmin' )
+App.Config.set( 'Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: App.ChannelTwitter, role: ['Admin'] }, 'NavBarAdmin' )
+App.Config.set( 'Facebook', { prio: 6000, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: App.ChannelFacebook, role: ['Admin'] }, 'NavBarAdmin' )
diff --git a/app/assets/javascripts/app/views/channel/form.jst.eco b/app/assets/javascripts/app/views/channel/form.jst.eco
new file mode 100644
index 000000000..bd8ce9853
--- /dev/null
+++ b/app/assets/javascripts/app/views/channel/form.jst.eco
@@ -0,0 +1,42 @@
+
+
+
<%- @T('With form you can add a formular to your web page witch directly generates a Ticket for you.') %>
+
+
+
+
<%- @T('Settings') %>
+
+
+
<%- @T('You need to add the following Java Script code snipped to your web page') %>:
+
+
+<script id="zammad_form_script" src="<%= @baseurl %>/assets/form/form.js"></script>
+<script>
+$('#feedback-form').zammad_form({
+ lang: 'de-de',
+ debug: true,
+ modal: false,
+});
+</script>
+
\ No newline at end of file
diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb
new file mode 100644
index 000000000..6adbc3358
--- /dev/null
+++ b/app/controllers/form_controller.rb
@@ -0,0 +1,99 @@
+# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
+
+class FormController < ApplicationController
+
+ def config
+ return if !enabled?
+
+ api_path = Rails.configuration.api_path
+ http_type = Setting.get('http_type')
+ fqdn = Setting.get('fqdn')
+
+ endpoint = "#{http_type}://#{fqdn}#{api_path}/form_submit"
+
+ config = {
+ enabled: Setting.get('form_ticket_create'),
+ endpoint: endpoint,
+ }
+
+ render json: config, status: :ok
+ end
+
+ def submit
+ return if !enabled?
+
+ # validate input
+ errors = {}
+ if !params[:name] || params[:name].empty?
+ errors['name'] = 'required'
+ end
+ if !params[:email] || params[:email].empty?
+ errors['email'] = 'required'
+ end
+ if params[:email] !~ /@/
+ errors['email'] = 'invalid'
+ end
+ if !params[:body] || params[:body].empty?
+ errors['body'] = 'required'
+ end
+
+ if errors && !errors.empty?
+ render json: {
+ errors: errors
+ }, status: :ok
+ return
+ end
+
+ name = params[:name].strip
+ email = params[:email].strip.downcase
+
+ customer = User.find_by(email: email)
+ if !customer
+ roles = Role.where( name: 'Customer' )
+ customer = User.create(
+ firstname: name,
+ lastname: '',
+ email: email,
+ password: '',
+ active: true,
+ roles: roles,
+ updated_by_id: 1,
+ created_by_id: 1,
+ )
+ end
+
+ ticket = Ticket.create(
+ group_id: 1,
+ customer_id: customer.id,
+ title: '',
+ state_id: Ticket::State.find_by( name: 'new' ).id,
+ priority_id: Ticket::Priority.find_by( name: '2 normal' ).id,
+ updated_by_id: customer.id,
+ created_by_id: customer.id,
+ )
+
+ article = Ticket::Article.create(
+ ticket_id: ticket.id,
+ type_id: Ticket::Article::Type.find_by( name: 'web' ).id,
+ sender_id: Ticket::Article::Sender.find_by( name: 'Customer' ).id,
+ body: params[:body],
+ from: email,
+ subject: '',
+ internal: false,
+ updated_by_id: customer.id,
+ created_by_id: customer.id,
+ )
+
+ result = {}
+ render json: result, status: :ok
+ end
+
+ private
+
+ def enabled?
+ return true if Setting.get('form_ticket_create')
+ response_access_deny
+ false
+ end
+
+end
diff --git a/config/routes/form.rb b/config/routes/form.rb
new file mode 100644
index 000000000..70fac4fde
--- /dev/null
+++ b/config/routes/form.rb
@@ -0,0 +1,8 @@
+Zammad::Application.routes.draw do
+ api_path = Rails.configuration.api_path
+
+ # forms
+ match api_path + '/form_submit', to: 'form#submit', via: :post
+ match api_path + '/form_config', to: 'form#config', via: :get
+
+end
diff --git a/db/migrate/20150810000001_update_form.rb b/db/migrate/20150810000001_update_form.rb
new file mode 100644
index 000000000..3c69e6b52
--- /dev/null
+++ b/db/migrate/20150810000001_update_form.rb
@@ -0,0 +1,30 @@
+class UpdateForm < ActiveRecord::Migration
+ def up
+
+ Setting.create_if_not_exists(
+ title: 'Enable Ticket creation',
+ name: 'form_ticket_create',
+ area: 'Form::Base',
+ description: 'Defines if ticket can get created via web form.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'form_ticket_create',
+ tag: 'boolean',
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ frontend: false,
+ updated_by_id: 1,
+ created_by_id: 1,
+ )
+
+ end
+end
diff --git a/db/seeds.rb b/db/seeds.rb
index a28b1e794..5f96d478e 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -992,6 +992,29 @@ Setting.create_if_not_exists(
frontend: true
)
+Setting.create_if_not_exists(
+ title: 'Enable Ticket creation',
+ name: 'form_ticket_create',
+ area: 'Form::Base',
+ description: 'Defines if ticket can get created via web form.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'form_ticket_create',
+ tag: 'boolean',
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ frontend: false,
+)
+
Setting.create_if_not_exists(
title: 'Sender Format',
name: 'ticket_define_email_from',
diff --git a/public/assets/form/form.css b/public/assets/form/form.css
new file mode 100644
index 000000000..c6ac53fe2
--- /dev/null
+++ b/public/assets/form/form.css
@@ -0,0 +1,19 @@
+.zammad-form {
+ width: 300px;
+}
+
+.zammad-form .form-group {
+ margin-bottom: 15px;
+}
+
+.zammad-form .form-control {
+ display: block;
+ width: 100%;
+}
+
+.zammad-form .has-error .form-control {
+ border-color: #a94442;
+}
+.zammad-form .has-error label {
+ color: #a94442;
+}
\ No newline at end of file
diff --git a/public/assets/form/form.html b/public/assets/form/form.html
new file mode 100644
index 000000000..5a1a9ec04
--- /dev/null
+++ b/public/assets/form/form.html
@@ -0,0 +1,19 @@
+
+
+
+ Example Form
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/assets/form/form.js b/public/assets/form/form.js
new file mode 100644
index 000000000..182a9c313
--- /dev/null
+++ b/public/assets/form/form.js
@@ -0,0 +1,179 @@
+(function ($) {
+
+/*
+ provides feedback form for zammad
+*/
+
+ var pluginName = 'zammad_form',
+ defaults = {
+ debug: false,
+ loadCss: true,
+ };
+
+ function Plugin( element, options ) {
+ this.element = element;
+ this.$element = $(element)
+
+ this.options = $.extend( {}, defaults, options) ;
+
+ this._defaults = defaults;
+ this._name = pluginName;
+
+ this._endpoint_config = '/api/v1/form_config'
+ this._script_location = '/assets/form/form.js'
+
+ this._config = {}
+
+ this.attributes = [
+ {
+ display: 'Name',
+ name: 'name',
+ tag: 'input',
+ type: 'text',
+ placeholder: 'Your Name',
+ },
+ {
+ display: 'Email',
+ name: 'email',
+ tag: 'input',
+ type: 'email',
+ placeholder: 'Your Email',
+ },
+ {
+ display: 'Message',
+ name: 'body',
+ tag: 'textarea',
+ placeholder: 'Your Message...',
+ },
+ ]
+
+ this.init();
+ }
+
+
+ Plugin.prototype.init = function () {
+ var _this = this,
+ src = document.getElementById("zammad_form_script").src,
+ endpoint_config = src.replace(this._script_location, this._endpoint_config)
+
+ _this.log('init')
+
+ if (_this.options.loadCss) {
+ _this.loadCss('form.css')
+ }
+
+ _this.log('endpoint_config: ' + endpoint_config)
+
+ // load config
+ $.ajax({
+ url: endpoint_config,
+ }).done(function(data) {
+ _this.log('config:', data)
+ _this._config = data
+ _this.render()
+ }).fail(function() {
+ alert('Faild to load form config!')
+ });
+
+ // bind form submit
+ this.$element.on('submit', function (e) {
+ e.preventDefault()
+ _this.submit()
+ return true
+ })
+ }
+
+ // load css
+ Plugin.prototype.loadCss = function(filename) {
+ if (document.createStyleSheet) {
+ document.createStyleSheet(filename)
+ }
+ else {
+ $('').appendTo('head')
+ }
+ }
+
+ // send
+ Plugin.prototype.submit = function() {
+ var _this = this
+
+ _this.log('submit form', _this.getParams())
+
+ $.ajax({
+ method: 'post',
+ url: _this._config.endpoint,
+ data: _this.getParams(),
+ }).done(function(data) {
+ _this.log('ok done', _this._config.endpoint)
+
+ // removed errors
+ _this.$element.find('.has-error').removeClass('has-error')
+
+ // set errors
+ if (data.errors) {
+ $.each(data.errors, function( key, value ) {
+ _this.$element.find('[name=' + key + ']').closest('.form-group').addClass('has-error')
+ })
+ return
+ }
+
+ // ticket has been created
+ _this.thanks()
+ }).fail(function() {
+ alert('Faild to submit form!')
+ });
+ }
+
+ // get params
+ Plugin.prototype.getParams = function() {
+ var _this = this,
+ params = {}
+
+ $.each( _this.$element.find('form').serializeArray(), function( index, item ) {
+ params[item.name] = item.value
+ })
+ return params
+ }
+
+ // render form
+ Plugin.prototype.render = function(e) {
+ var form = $('')
+ $.each(this.attributes, function( index, value ) {
+ var item = $('')
+ if (value.tag == 'input') {
+ item.append('')
+ }
+ else if (value.tag == 'textarea') {
+ item.append('')
+ }
+ form.append(item)
+ })
+ form.append('Thank you for your inquery!')
+ this.$element.html(form)
+ return form
+ }
+
+ // log method
+ Plugin.prototype.log = function() {
+ if (this.options.debug) {
+ console.log(this._name, arguments)
+ }
+ }
+
+ $.fn[pluginName] = function ( options ) {
+ return this.each(function () {
+ if (!$.data(this, 'plugin_' + pluginName)) {
+ $.data(this, 'plugin_' + pluginName,
+ new Plugin( this, options ));
+ }
+ });
+ }
+
+}(jQuery));
\ No newline at end of file