zendesk_rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +136 -0
- data/Rakefile +17 -0
- data/app/assets/javascripts/zendesk_rails/application.js +3 -0
- data/app/assets/stylesheets/zendesk_rails/application.css +29 -0
- data/app/controllers/zendesk_rails/tickets_controller.rb +59 -0
- data/app/helpers/zendesk_rails/tickets_helper.rb +30 -0
- data/app/models/zendesk_rails/comment_handler.rb +40 -0
- data/app/models/zendesk_rails/ticket_handler.rb +74 -0
- data/app/views/layouts/zendesk_rails/tickets.html.erb +32 -0
- data/app/views/zendesk_rails/tickets/index.html.erb +37 -0
- data/app/views/zendesk_rails/tickets/new.html.erb +38 -0
- data/app/views/zendesk_rails/tickets/show.html.erb +43 -0
- data/config/locales/zendesk_rails.yml +48 -0
- data/config/routes.rb +4 -0
- data/lib/tasks/zendesk_rails_tasks.rake +4 -0
- data/lib/zendesk_rails.rb +11 -0
- data/lib/zendesk_rails/configuration.rb +91 -0
- data/lib/zendesk_rails/controller_helpers.rb +41 -0
- data/lib/zendesk_rails/engine.rb +15 -0
- data/lib/zendesk_rails/testing.rb +102 -0
- data/lib/zendesk_rails/version.rb +3 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7789387d91473a18513bc9a722fc13e3cf3531d6
|
4
|
+
data.tar.gz: 3955edcc59139c0dabd07f33051bca4025b01bc3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d4e34daf8e477af6e6afb20694130e871c20fd52d1087ec9584fdb4b359741dcc3db853a2a73058bf28329f78c4c95980d20489267c2f11d8ab761fc5533889
|
7
|
+
data.tar.gz: c95159cf2599910322ca2dbe44c44e27822eb7664ee78b54f67f2082e7fdfc1c4d0655376e81f64098cb27af70934e142daf9be488e307464b8d5066dc5c2e0d
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# Zendesk Rails
|
2
|
+
|
3
|
+
Zendesk Rails is a mountable engine for end-users to create, track, and comment on tickets. It uses the [Zendesk API Client](https://github.com/zendesk/zendesk_api_client_rb).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add `zendesk_rails` to your Gemfile.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'zendesk_rails'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Setup
|
14
|
+
|
15
|
+
Mount the engine within your Rails application by adding this line to `config/routes.rb`.
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
mount ZendeskRails::Engine, at: '/help'
|
19
|
+
```
|
20
|
+
|
21
|
+
Then you'll need to configure a few settings by creating an initializer.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
ZendeskRails.configure do
|
25
|
+
config.url = 'https://example.zendesk.com/api/v2'
|
26
|
+
|
27
|
+
# Basic / Token authentication
|
28
|
+
config.username = 'user@example.com'
|
29
|
+
|
30
|
+
# Choose one of the following depending on your authentication choice
|
31
|
+
config.token = 'your zendesk token'
|
32
|
+
config.password = 'your zendesk password'
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
You should be ready to go! Fire up your server and visit '/help'.
|
37
|
+
|
38
|
+
## Additional Configuration
|
39
|
+
|
40
|
+
#### Zendesk API Settings
|
41
|
+
|
42
|
+
The `configure` block accepts all settings from the [Zendesk API Client](https://github.com/zendesk/zendesk_api_client_rb). Take a look at their documentation for some additional configuration options.
|
43
|
+
|
44
|
+
#### Customization
|
45
|
+
|
46
|
+
##### config.app_name
|
47
|
+
|
48
|
+
Sets the name of the app in the navbar.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
config.app_name = 'My Application'
|
52
|
+
```
|
53
|
+
|
54
|
+
##### config.layout
|
55
|
+
|
56
|
+
Sets the path to a custom layout.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
config.layout = 'layouts/application'
|
60
|
+
```
|
61
|
+
|
62
|
+
##### config.devise_scope
|
63
|
+
|
64
|
+
By default, Zendesk Rails assumes your controller has a `current_user` method. If `config.devise_scope` were set to `:admin`, Zendesk Rails would use `current_admin`.
|
65
|
+
|
66
|
+
##### config.user_attributes
|
67
|
+
|
68
|
+
Your user model is expected to have `:name` and `:email` methods. Otherwise, you'll need to provide a hash with the values being the actual names of your methods.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
config.user_attributes = { name: :full_name, email: :email_address }
|
72
|
+
```
|
73
|
+
|
74
|
+
##### config.time_formatter
|
75
|
+
|
76
|
+
Times are displayed using `time_ago_in_words`. You can set this to either a string to be passed to `strftime`, or a proc that accepts a Time. Passing a proc allows you to use view helper methods.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
config.time_formatter = ->(time){ time.to_formatted_s }
|
80
|
+
```
|
81
|
+
|
82
|
+
##### config.ticket_list_options
|
83
|
+
|
84
|
+
Ticket list options are passed to the Zendesk API's search endpoint. By default, tickets are sorted by the created_at time in descending order.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
config.ticket_list_options = {
|
88
|
+
sort_by: :created_at,
|
89
|
+
sort_order: :desc
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
See http://developer.zendesk.com/documentation/rest_api/search.html
|
94
|
+
|
95
|
+
##### config.comment_list_options
|
96
|
+
|
97
|
+
Comment list options are passed to the Zendesk API's request comments. By default, comments are sorted by the created_at time in descending order.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
config.comment_list_options = {
|
101
|
+
sort_by: :created_at,
|
102
|
+
sort_order: :desc
|
103
|
+
}
|
104
|
+
```
|
105
|
+
|
106
|
+
See http://developer.zendesk.com/documentation/rest_api/requests.html#listing-comments
|
107
|
+
|
108
|
+
##### config.test_mode
|
109
|
+
|
110
|
+
When `config.test_mode` is true, a fake API will be used. All created tickets will be stored in memory. This setting is particularly useful for testing out Zendesk Rails. Do not use this setting in production.
|
111
|
+
|
112
|
+
### Overriding Controller Behavior
|
113
|
+
|
114
|
+
Zendesk Rails offers hooks that allow you to control what happens after a ticket is created/invalid. You can override the following methods in your `ApplicationController`.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
# The user will be redirected to this URL when a
|
118
|
+
# ticket is successfully created
|
119
|
+
def after_zendesk_ticket_created_path_for(ticket)
|
120
|
+
if user_signed_in?
|
121
|
+
ticket_path(ticket.id)
|
122
|
+
else
|
123
|
+
main_app.root_path
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Render will be called with these arguments when a ticket is invalid
|
128
|
+
def after_zendesk_ticket_invalid_template
|
129
|
+
return 'new' if user_signed_in?
|
130
|
+
['welcome/index', { layout: false }]
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
### Overriding Content
|
135
|
+
|
136
|
+
Zendesk Rails allows you to easily override content using I18n. Override keys from [config/locales/zendesk_rails.yml](config/locales/zendesk_rails.yml) in a file located in your `config/locales` directory.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ZendeskRails'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
*= require_tree .
|
3
|
+
*= require_self
|
4
|
+
*/
|
5
|
+
|
6
|
+
body {
|
7
|
+
padding-top: 70px;
|
8
|
+
}
|
9
|
+
|
10
|
+
.comment {
|
11
|
+
font-size: 17.5px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.comment-body p:last-child,
|
15
|
+
.comment-body ul:last-child,
|
16
|
+
.comment-body ol:last-child {
|
17
|
+
margin-bottom: 0;
|
18
|
+
}
|
19
|
+
|
20
|
+
.comment-details {
|
21
|
+
display: block;
|
22
|
+
font-size: 80%;
|
23
|
+
line-height: 1.42857143;
|
24
|
+
color: #777;
|
25
|
+
}
|
26
|
+
|
27
|
+
.comment-details:before {
|
28
|
+
content: '\2014 \00A0';
|
29
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
class TicketsController < ApplicationController
|
3
|
+
layout :zendesk_layout
|
4
|
+
|
5
|
+
def index
|
6
|
+
@tickets = TicketHandler.search(query: {
|
7
|
+
requester: zendesk_user_attribute(:email)
|
8
|
+
})
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
@ticket = TicketHandler.find_request(params[:id])
|
13
|
+
@handler = CommentHandler.new(@ticket)
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
@handler = TicketHandler.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@handler = TicketHandler.new(ticket_params)
|
22
|
+
|
23
|
+
if @ticket = @handler.create
|
24
|
+
flash_key = zendesk_user_signed_in? ? :authenticated : :unauthenticated
|
25
|
+
redirect_to after_zendesk_ticket_created_path_for(@ticket), flash: {
|
26
|
+
success: t("zendesk.tickets.create.#{flash_key}.message")
|
27
|
+
}
|
28
|
+
else
|
29
|
+
render *Array(after_zendesk_ticket_invalid_template)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
@ticket = TicketHandler.find_ticket(params[:id])
|
35
|
+
@handler = CommentHandler.new(@ticket, comment_params)
|
36
|
+
|
37
|
+
if @handler.save
|
38
|
+
redirect_to ticket_path(@ticket.id), flash: {
|
39
|
+
success: t('zendesk.comments.added')
|
40
|
+
}
|
41
|
+
else
|
42
|
+
render 'show'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def comment_params
|
49
|
+
params.require(:ticket).permit(:comment)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ticket_params
|
53
|
+
params.require(:ticket).permit(:subject, :body).merge(requester: {
|
54
|
+
name: (params[:ticket][:name].presence || zendesk_user_attribute(:name)),
|
55
|
+
email: (params[:ticket][:email].presence || zendesk_user_attribute(:email))
|
56
|
+
})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
module TicketsHelper
|
3
|
+
def format_time(time)
|
4
|
+
formatter = zendesk_config.time_formatter
|
5
|
+
if formatter.respond_to?(:call)
|
6
|
+
instance_exec time, &formatter
|
7
|
+
else
|
8
|
+
time.strftime(formatter)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def status_label_class(status)
|
13
|
+
t('zendesk.tickets.status')[status.try(:to_sym)] || 'default'
|
14
|
+
end
|
15
|
+
|
16
|
+
def form_group_with_errors(object, field, &block)
|
17
|
+
errors = object.errors.full_messages_for(field)
|
18
|
+
group_classes, help = 'form-group', ''
|
19
|
+
|
20
|
+
if errors.any?
|
21
|
+
group_classes << ' has-error has-feedback'
|
22
|
+
help = content_tag(:span, errors.first, class: 'help-block')
|
23
|
+
end
|
24
|
+
|
25
|
+
content_tag :div, class: group_classes do
|
26
|
+
capture(errors, &block).concat(help)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
class CommentHandler
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
extend ActiveModel::Naming
|
6
|
+
|
7
|
+
attr_reader :ticket
|
8
|
+
attr_accessor :comment, :requester_id
|
9
|
+
|
10
|
+
validates_presence_of :comment, :requester_id
|
11
|
+
|
12
|
+
def initialize(ticket, attributes = {})
|
13
|
+
@ticket = ticket
|
14
|
+
@requester_id = ticket.requester_id
|
15
|
+
|
16
|
+
attributes.each do |key, value|
|
17
|
+
send("#{key}=", value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def save
|
22
|
+
return false unless valid?
|
23
|
+
|
24
|
+
ticket.comment = {
|
25
|
+
body: comment,
|
26
|
+
author_id: ticket.requester_id
|
27
|
+
}
|
28
|
+
|
29
|
+
ticket.save
|
30
|
+
end
|
31
|
+
|
32
|
+
def comments
|
33
|
+
@comments ||= ticket.comments(ZendeskRails.config.comment_list_options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def persisted?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module ZendeskRails
|
4
|
+
class TicketHandler
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include ActiveModel::Conversion
|
7
|
+
extend ActiveModel::Naming
|
8
|
+
|
9
|
+
attr_reader :ticket
|
10
|
+
attr_accessor :name, :email, :subject, :body, :requester, :comment
|
11
|
+
|
12
|
+
validates :subject, :body, :requester, presence: true
|
13
|
+
validate :requester_email_presence, :requester_name_presence
|
14
|
+
|
15
|
+
def initialize(attributes = {})
|
16
|
+
attributes.each do |key, value|
|
17
|
+
send("#{key}=", value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@ticket = self.class.client.tickets.create(create_params) if valid?
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_params
|
26
|
+
{
|
27
|
+
subject: subject,
|
28
|
+
comment: { value: body },
|
29
|
+
requester: requester
|
30
|
+
}.merge(ZendeskRails.config.ticket_create_params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def persisted?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def requester_email_presence
|
38
|
+
if requester && requester[:email].blank?
|
39
|
+
errors.add(:email, "can't be blank")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def requester_name_presence
|
44
|
+
if requester && requester[:name].blank?
|
45
|
+
errors.add(:name, "can't be blank")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
delegate :client, to: ZendeskRails
|
51
|
+
|
52
|
+
def search(conditions = {})
|
53
|
+
conditions[:query] = to_query(conditions[:query])
|
54
|
+
conditions.merge!(ZendeskRails.config.ticket_list_options)
|
55
|
+
client.search(conditions)
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_request(id)
|
59
|
+
client.requests.find(id: id)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_ticket(id)
|
63
|
+
client.tickets.find(id: id)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def to_query(query)
|
69
|
+
return query unless query.respond_to?(:keys)
|
70
|
+
query.to_a.map { |a| a.join(':') }.join('+')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Help | <%= zendesk_config.app_name %></title>
|
5
|
+
<%= stylesheet_link_tag "http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" %>
|
6
|
+
<%= stylesheet_link_tag "zendesk_rails/application", media: "all" %>
|
7
|
+
<%= javascript_include_tag "zendesk_rails/application" %>
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
12
|
+
<div class="container">
|
13
|
+
<div class="navbar-header">
|
14
|
+
<%= link_to zendesk_config.app_name, main_app.root_path, class: 'navbar-brand' %>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="container" role="main">
|
20
|
+
<% flash.each do |type, message| %>
|
21
|
+
<div class="alert alert-<%= type %> fade in">
|
22
|
+
<button class="close" data-dismiss="alert">×</button>
|
23
|
+
<%= message %>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<%= yield %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<%= javascript_include_tag "http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js" %>
|
31
|
+
</body>
|
32
|
+
</html>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div class="jumbotron">
|
2
|
+
<h1><%= t 'zendesk.tickets.jumbotron.callout' %></h1>
|
3
|
+
<p><%= t 'zendesk.tickets.jumbotron.body' %></p>
|
4
|
+
<p>
|
5
|
+
<%= link_to t('zendesk.tickets.jumbotron.new'), new_ticket_path, class: "btn btn-primary btn-lg" %>
|
6
|
+
</p>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="row">
|
10
|
+
<div class="col-md-12">
|
11
|
+
<h1><%= t 'zendesk.tickets.list.heading' %></h1>
|
12
|
+
<div class="table-responsive">
|
13
|
+
<table class="table table-hover table-striped">
|
14
|
+
<thead>
|
15
|
+
<th><%= t 'zendesk.tickets.list.columns.id' %></th>
|
16
|
+
<th><%= t 'zendesk.tickets.list.columns.subject' %></th>
|
17
|
+
<th><%= t 'zendesk.tickets.list.columns.status' %></th>
|
18
|
+
<th><%= t 'zendesk.tickets.list.columns.last_updated' %></th>
|
19
|
+
</thead>
|
20
|
+
<tbody>
|
21
|
+
<% @tickets.each do |ticket| %>
|
22
|
+
<tr>
|
23
|
+
<td><%= link_to ticket.id, zendesk_rails.ticket_path(ticket.id) %></td>
|
24
|
+
<td><%= truncate ticket.subject, length: 300 %></td>
|
25
|
+
<td>
|
26
|
+
<span class="label label-<%= status_label_class(ticket.status) %>">
|
27
|
+
<%= ticket.status.try(:titleize) || 'New' %>
|
28
|
+
</span>
|
29
|
+
</td>
|
30
|
+
<td><%= format_time(ticket.updated_at || ticket.created_at) %></td>
|
31
|
+
</tr>
|
32
|
+
<% end %>
|
33
|
+
</tbody>
|
34
|
+
</table>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>New ticket</h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<div class="row">
|
6
|
+
<div class="col-md-8">
|
7
|
+
<%= form_for(@handler, as: :ticket, url: tickets_path) do |f| %>
|
8
|
+
<% unless zendesk_user_signed_in? %>
|
9
|
+
<%= form_group_with_errors f.object, :name do %>
|
10
|
+
<%= f.label :name, t('zendesk.tickets.new.name.label') %>
|
11
|
+
<%= f.text_field :name, class: "form-control", placeholder: t('zendesk.tickets.new.name.placeholder') %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<%= form_group_with_errors f.object, :email do %>
|
15
|
+
<%= f.label :email, t('zendesk.tickets.new.email.label') %>
|
16
|
+
<%= f.text_field :email, class: "form-control", placeholder: t('zendesk.tickets.new.email.placeholder') %>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<%= form_group_with_errors f.object, :subject do %>
|
21
|
+
<%= f.label :subject, t('zendesk.tickets.new.subject.label') %>
|
22
|
+
<%= f.text_field :subject, class: "form-control", placeholder: t('zendesk.tickets.new.subject.placeholder') %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<%= form_group_with_errors f.object, :body do %>
|
26
|
+
<%= f.label :body, t('zendesk.tickets.new.body.label') %>
|
27
|
+
<%= f.text_area :body, rows: 10, class: "form-control", placeholder: t('zendesk.tickets.new.body.placeholder') %>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<p><%= f.submit t('zendesk.tickets.new.submit'), class: "btn btn-large btn-primary" %></p>
|
31
|
+
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div class="col-md-4">
|
36
|
+
<div class="well"><%= t 'zendesk.tickets.new.sidebar' %></div>
|
37
|
+
</div>
|
38
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<div class='page-header'>
|
2
|
+
<h2>
|
3
|
+
<div class="label label-<%= status_label_class(@ticket.status) %>">#<%= @ticket.id -%></div>
|
4
|
+
<%= truncate @ticket.subject, length: 200 -%>
|
5
|
+
</h2>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="row">
|
9
|
+
<div class="col-md-12">
|
10
|
+
<div class="well">
|
11
|
+
<%= form_for(@handler, as: :ticket, method: :put, url: ticket_path(@ticket.id)) do |f| %>
|
12
|
+
|
13
|
+
<%= form_group_with_errors f.object, :comment do %>
|
14
|
+
<%= f.text_area :comment, rows: 5, class: "form-control", placeholder: t('zendesk.comments.new.placeholder') %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<p><%= submit_tag t('zendesk.comments.new.submit'), class: "btn btn-large btn-primary" %></p>
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="row">
|
24
|
+
<div class="col-md-12">
|
25
|
+
<% @handler.comments.each do |comment| %>
|
26
|
+
<div class="well comment">
|
27
|
+
<div class="row">
|
28
|
+
<% if photo = comment.author.photo %>
|
29
|
+
<div class="col-xs-2 col-md-1">
|
30
|
+
<%= image_tag photo.content_url, class: 'img-circle img-responsive' %>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
33
|
+
<div class="<%= photo ? 'col-xs-10 col-md-11' : 'col-md-12' %>">
|
34
|
+
<div class="comment-body"><%= raw comment.html_body %></div>
|
35
|
+
<small class="comment-details">
|
36
|
+
<%= t 'zendesk.comments.details', time: format_time(comment.created_at), user: comment.author.name %>
|
37
|
+
</small>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
43
|
+
</div>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
en:
|
2
|
+
zendesk:
|
3
|
+
app_name: Help
|
4
|
+
tickets:
|
5
|
+
status:
|
6
|
+
new: warning
|
7
|
+
open: danger
|
8
|
+
pending: info
|
9
|
+
hold: primary
|
10
|
+
solved: default
|
11
|
+
closed: default
|
12
|
+
jumbotron:
|
13
|
+
callout: Having trouble?
|
14
|
+
body: Our help desk is ready to answer any questions you might have. Create a new ticket to get help.
|
15
|
+
new: New Ticket »
|
16
|
+
list:
|
17
|
+
heading: My Tickets
|
18
|
+
columns:
|
19
|
+
id: '#'
|
20
|
+
subject: Subject
|
21
|
+
status: Status
|
22
|
+
last_updated: Last Updated
|
23
|
+
new:
|
24
|
+
sidebar: Just fill out the form, and we'll get back to you as soon as possible.
|
25
|
+
name:
|
26
|
+
label: Name
|
27
|
+
placeholder: Name
|
28
|
+
email:
|
29
|
+
label: Email
|
30
|
+
placeholder: Email
|
31
|
+
subject:
|
32
|
+
label: Subject
|
33
|
+
placeholder: Subject
|
34
|
+
body:
|
35
|
+
label: Describe your issue
|
36
|
+
placeholder: Please describe the issue you're experiencing...
|
37
|
+
submit: Send Your Question
|
38
|
+
create:
|
39
|
+
authenticated:
|
40
|
+
message: Thank you for your submission! You can track the status of your ticket from this page.
|
41
|
+
unauthenticated:
|
42
|
+
message: Thank you for your submission! You will receive an email at the provided address shortly.
|
43
|
+
comments:
|
44
|
+
details: '%{time} by %{user}'
|
45
|
+
added: Comment successfully submitted.
|
46
|
+
new:
|
47
|
+
placeholder: Enter your reply...
|
48
|
+
submit: Add Comment
|
data/config/routes.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
class Configuration
|
3
|
+
CLIENT_OPTIONS = [
|
4
|
+
:username,
|
5
|
+
:password,
|
6
|
+
:token,
|
7
|
+
:url,
|
8
|
+
:retry,
|
9
|
+
:logger,
|
10
|
+
:client_options,
|
11
|
+
:adapter,
|
12
|
+
:allow_http,
|
13
|
+
:access_token,
|
14
|
+
:url_based_access_token
|
15
|
+
]
|
16
|
+
|
17
|
+
DEFAULT_SORTING = {
|
18
|
+
sort_by: :created_at,
|
19
|
+
sort_order: :desc
|
20
|
+
}
|
21
|
+
|
22
|
+
DEFAULT_USER_ATTRIBUTES = {
|
23
|
+
name: :name,
|
24
|
+
email: :email
|
25
|
+
}
|
26
|
+
|
27
|
+
attr_accessor *CLIENT_OPTIONS
|
28
|
+
attr_accessor :layout
|
29
|
+
attr_writer :devise_scope
|
30
|
+
attr_writer :app_name
|
31
|
+
attr_writer :time_formatter
|
32
|
+
attr_writer :test_mode
|
33
|
+
attr_writer :user_attributes
|
34
|
+
attr_writer :ticket_list_options
|
35
|
+
attr_writer :comment_list_options
|
36
|
+
attr_writer :ticket_create_params
|
37
|
+
|
38
|
+
def devise_scope
|
39
|
+
@current_user_method || :user
|
40
|
+
end
|
41
|
+
|
42
|
+
def app_name
|
43
|
+
@app_name || I18n.translate('zendesk.app_name')
|
44
|
+
end
|
45
|
+
|
46
|
+
def time_formatter
|
47
|
+
@time_formatter || ->(time) { "#{time_ago_in_words(time)} ago" }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_mode?
|
51
|
+
@test_mode
|
52
|
+
end
|
53
|
+
|
54
|
+
def user_attributes
|
55
|
+
(@user_attributes || {}).reverse_merge(DEFAULT_USER_ATTRIBUTES)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ticket_create_params
|
59
|
+
@ticket_create_params || {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def ticket_list_options
|
63
|
+
(@ticket_list_options || {}).reverse_merge(DEFAULT_SORTING)
|
64
|
+
end
|
65
|
+
|
66
|
+
def comment_list_options
|
67
|
+
(@comment_list_options || {}).reverse_merge(DEFAULT_SORTING)
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_client
|
71
|
+
client_class.new do |client_config|
|
72
|
+
CLIENT_OPTIONS.each do |opt|
|
73
|
+
if value = send(opt)
|
74
|
+
client_config.send("#{opt}=", value)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def client_class
|
83
|
+
if test_mode?
|
84
|
+
require 'zendesk_rails/testing'
|
85
|
+
ZendeskRails::Testing::Client
|
86
|
+
else
|
87
|
+
::ZendeskAPI::Client
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
module ControllerHelpers
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
delegate :config, to: :ZendeskRails, prefix: :zendesk
|
7
|
+
delegate :layout, to: :zendesk_config, prefix: :zendesk
|
8
|
+
|
9
|
+
helper_method :zendesk_config,
|
10
|
+
:zendesk_current_user,
|
11
|
+
:zendesk_user_signed_in?,
|
12
|
+
:zendesk_user_attribute
|
13
|
+
end
|
14
|
+
|
15
|
+
def zendesk_current_user
|
16
|
+
send "current_#{zendesk_config.devise_scope}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def zendesk_user_signed_in?
|
20
|
+
send "#{zendesk_config.devise_scope}_signed_in?"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets the value of a user's name/emails based on
|
24
|
+
# configurable attribute names
|
25
|
+
def zendesk_user_attribute(attribute)
|
26
|
+
attr_name = zendesk_config.user_attributes[attribute]
|
27
|
+
zendesk_current_user.try(attr_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# The user will be redirected to this URL when a
|
31
|
+
# ticket is successfully created
|
32
|
+
def after_zendesk_ticket_created_path_for(ticket)
|
33
|
+
ticket_path(ticket.id)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Render will be called with these arguments when a ticket is invalid
|
37
|
+
def after_zendesk_ticket_invalid_template
|
38
|
+
'new'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'zendesk_api'
|
2
|
+
require 'zendesk_rails/configuration'
|
3
|
+
require 'zendesk_rails/controller_helpers'
|
4
|
+
|
5
|
+
module ZendeskRails
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace ZendeskRails
|
8
|
+
|
9
|
+
initializer "zendesk_rails.helpers" do
|
10
|
+
ActiveSupport.on_load(:action_controller) do
|
11
|
+
include ZendeskRails::ControllerHelpers
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module ZendeskRails
|
2
|
+
module Testing
|
3
|
+
class Resource
|
4
|
+
def initialize(attributes)
|
5
|
+
@attributes = attributes.symbolize_keys
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(method, *args, &block)
|
9
|
+
if attribute = @attributes[method]
|
10
|
+
attribute.respond_to?(:keys) ? OpenStruct.new(attribute) : attribute
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def all
|
18
|
+
(@all ||= []).sort { |a, b| b.created_at <=> a.created_at }
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate *Array.instance_methods(false), to: :all
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Comment < Resource
|
26
|
+
def initialize(attributes)
|
27
|
+
attributes.reverse_merge!(
|
28
|
+
created_at: Time.now,
|
29
|
+
updated_at: Time.now
|
30
|
+
)
|
31
|
+
super(attributes)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Ticket < Resource
|
36
|
+
def initialize(attributes)
|
37
|
+
attributes.reverse_merge!(
|
38
|
+
created_at: Time.now,
|
39
|
+
updated_at: Time.now,
|
40
|
+
priority: 'low',
|
41
|
+
status: 'new',
|
42
|
+
comments: []
|
43
|
+
)
|
44
|
+
super(attributes)
|
45
|
+
end
|
46
|
+
|
47
|
+
def save
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def requester_id
|
52
|
+
@requester_id ||= rand(1..1000)
|
53
|
+
end
|
54
|
+
|
55
|
+
def as_comment
|
56
|
+
@attributes.slice(:created_at, :updated_at).merge(
|
57
|
+
html_body: description,
|
58
|
+
author: { name: requester[:name] }
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def comments(_opts = {})
|
63
|
+
list = (@attributes[:comments] + [as_comment]).map { |c| Comment.new(c) }
|
64
|
+
list.sort { |a, b| b.created_at <=> a.created_at }
|
65
|
+
end
|
66
|
+
|
67
|
+
def comment=(opts)
|
68
|
+
@attributes[:comments].unshift(
|
69
|
+
html_body: opts[:body],
|
70
|
+
author: requester
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
def create(client_or_opts = {}, opts = nil)
|
76
|
+
opts ||= client_or_opts
|
77
|
+
id = (all.map(&:id).max || 0) + 1
|
78
|
+
opts.merge!(id: id, description: opts[:comment][:value])
|
79
|
+
ticket = new opts.slice(:subject, :requester, :description, :id)
|
80
|
+
@all.unshift ticket
|
81
|
+
ticket
|
82
|
+
end
|
83
|
+
|
84
|
+
def find(opts = {})
|
85
|
+
all.find do |ticket|
|
86
|
+
opts.map do |key, value|
|
87
|
+
ticket.send(key) == value || ticket.send(key).to_s == value
|
88
|
+
end.all?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Client
|
95
|
+
def tickets(_opts = {})
|
96
|
+
Ticket
|
97
|
+
end
|
98
|
+
alias_method :requests, :tickets
|
99
|
+
alias_method :search, :tickets
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zendesk_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ray Zane
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: zendesk_api
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jquery-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: combustion
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.5.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.5.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: capybara
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: poltergeist
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: A Rails engine to add help desk ticketing using the Zendesk API.
|
126
|
+
email:
|
127
|
+
- raymondzane@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- MIT-LICENSE
|
133
|
+
- README.md
|
134
|
+
- Rakefile
|
135
|
+
- app/assets/javascripts/zendesk_rails/application.js
|
136
|
+
- app/assets/stylesheets/zendesk_rails/application.css
|
137
|
+
- app/controllers/zendesk_rails/tickets_controller.rb
|
138
|
+
- app/helpers/zendesk_rails/tickets_helper.rb
|
139
|
+
- app/models/zendesk_rails/comment_handler.rb
|
140
|
+
- app/models/zendesk_rails/ticket_handler.rb
|
141
|
+
- app/views/layouts/zendesk_rails/tickets.html.erb
|
142
|
+
- app/views/zendesk_rails/tickets/index.html.erb
|
143
|
+
- app/views/zendesk_rails/tickets/new.html.erb
|
144
|
+
- app/views/zendesk_rails/tickets/show.html.erb
|
145
|
+
- config/locales/zendesk_rails.yml
|
146
|
+
- config/routes.rb
|
147
|
+
- lib/tasks/zendesk_rails_tasks.rake
|
148
|
+
- lib/zendesk_rails.rb
|
149
|
+
- lib/zendesk_rails/configuration.rb
|
150
|
+
- lib/zendesk_rails/controller_helpers.rb
|
151
|
+
- lib/zendesk_rails/engine.rb
|
152
|
+
- lib/zendesk_rails/testing.rb
|
153
|
+
- lib/zendesk_rails/version.rb
|
154
|
+
homepage: https://github.com/rzane/zendesk_rails
|
155
|
+
licenses:
|
156
|
+
- MIT
|
157
|
+
metadata: {}
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
requirements: []
|
173
|
+
rubyforge_project:
|
174
|
+
rubygems_version: 2.2.2
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: Rails Help Desk Ticketing using the Zendesk API
|
178
|
+
test_files: []
|