panmind-zendesk 1.0
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.
- data/README.md +143 -0
- data/Rakefile +48 -0
- data/lib/panmind/string_force_utf8_patch.rb +29 -0
- data/lib/panmind/zendesk.rb +144 -0
- data/rails/init.rb +5 -0
- metadata +85 -0
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
Zendesk on Rails - Dropbox and Remote Authentication
|
2
|
+
====================================================
|
3
|
+
|
4
|
+
Purpose
|
5
|
+
-------
|
6
|
+
|
7
|
+
The plugin implements the HTML generation code for the
|
8
|
+
[Zendesk dropbox](http://www.zendesk.com/blog/instant-support-access-with-drop-box)
|
9
|
+
and the necessary controller and routing code to implement
|
10
|
+
[Zendesk's remote authentication](http://www.zendesk.com/api/remote-authentication)
|
11
|
+
|
12
|
+
|
13
|
+
Installation
|
14
|
+
------------
|
15
|
+
|
16
|
+
Via RubyGems:
|
17
|
+
|
18
|
+
gem install panmind-zendesk
|
19
|
+
|
20
|
+
Or via Rails Plugin:
|
21
|
+
|
22
|
+
script/plugin install git://github.com/Panmind/zendesk.git
|
23
|
+
|
24
|
+
|
25
|
+
Configuration
|
26
|
+
-------------
|
27
|
+
|
28
|
+
In your `config/routes.rb`:
|
29
|
+
|
30
|
+
map.zendesk '/support', :controller => 'sessions'
|
31
|
+
|
32
|
+
The `/support` path will then respond to the methods that generate the query
|
33
|
+
string parameters required for Remote Authentication, while the `/support/exit`
|
34
|
+
path will simply set a notice in the flash and redirect the user to the app
|
35
|
+
`root_url`. You can override this behaviour by implementing a `zendesk_logout`
|
36
|
+
method in the controller.
|
37
|
+
|
38
|
+
You can define these methods in any of your controllers, in this example we
|
39
|
+
defined it in the `SessionsController`: so, in your `sessions_controller.rb`:
|
40
|
+
|
41
|
+
include Panmind::Zendesk::Controller if Panmind::Zendesk.enabled?
|
42
|
+
|
43
|
+
Finally, in your config/environment.rb:
|
44
|
+
|
45
|
+
Panmind::Zendesk.set(
|
46
|
+
:token => 'Your Zendesk token',
|
47
|
+
:hostname => 'example.zendesk.com',
|
48
|
+
:login => proc { [current_user.name, current_user.email] },
|
49
|
+
:login_url => :login_url,
|
50
|
+
:dropbox => {
|
51
|
+
:title => 'Dropbox title',
|
52
|
+
:email => proc { current_user.email rescue nil }
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
The required options are:
|
57
|
+
|
58
|
+
* `:token` -- your zendesk [shared secret](http://www.zendesk.com/api/remote-authentication)
|
59
|
+
* `:hostname` -- your zendesk account host name
|
60
|
+
* `:login` -- a `Proc` object evaluated in the controller instance context,
|
61
|
+
that must return an `Array` whose first element is the current user name and the second its email.
|
62
|
+
This `Proc` is evaluated iff the `logged_in?` instance method of your controller returns true.
|
63
|
+
* `:login_url` -- The name of the named route helper that generates the full URL of your login page.
|
64
|
+
We use `:ssl_login_url` thanks to our [SSL Helper](http://github.com/Panmind/ssl_helper) plugin.
|
65
|
+
|
66
|
+
The `:dropbox` option is for the [Zendesk dropbox](http://www.zendesk.com/blog/instant-support-access-with-drop-box)
|
67
|
+
configuration, it should be an hash with symbolized keys and it is converted to
|
68
|
+
`JSON` and bound to the `zendesk_params` Javascript variable.
|
69
|
+
|
70
|
+
To facilitate the Dropbox usage, the `:email` option can be a `Proc` object that
|
71
|
+
is then evaluated in the controller instance context and should return the current
|
72
|
+
user e-mail or nil if the user is not logged in. If the `:email` option is unset
|
73
|
+
no e-mail appears precompiled in the Dropbox for, or you could even set a static
|
74
|
+
email for every feedback request (dunno why should you do that, though :-).
|
75
|
+
|
76
|
+
|
77
|
+
Usage
|
78
|
+
-----
|
79
|
+
|
80
|
+
To embed the Dropbox tags, use the `zendesk_dropbox_tags` helper
|
81
|
+
in your layout:
|
82
|
+
|
83
|
+
<%= zendesk_dropbox_tags %>
|
84
|
+
|
85
|
+
To display a link that displays the dropbox when clicked use the
|
86
|
+
`zendesk_dropbox_link_to` helper in your views:
|
87
|
+
|
88
|
+
<%= zendesk_dropbox_link_to 'Send feedback' %>
|
89
|
+
|
90
|
+
To display a link that starts the Remote Authentication process
|
91
|
+
use the `zendesk_link_to` helper in your views:
|
92
|
+
|
93
|
+
<%= zendesk_link_to 'Support' %>
|
94
|
+
|
95
|
+
The second parameter of said helper is passed to Rails' `link_to`
|
96
|
+
one to customize the link tag attributes.
|
97
|
+
|
98
|
+
|
99
|
+
Caching
|
100
|
+
-------
|
101
|
+
|
102
|
+
If you implement asset minifization and concatenation, you'll surely
|
103
|
+
want to reduce HTTP requests to external services. While it's not the
|
104
|
+
best practice in the world to cache said services assets, to speed up
|
105
|
+
your application you may be forced to do it.
|
106
|
+
|
107
|
+
We're doing it on [Panmind](http://panmind.org) - both to reduce the
|
108
|
+
number of HTTP requests and because the lag with `assets0.zendesk.com`
|
109
|
+
from Europe is quite high.
|
110
|
+
|
111
|
+
To make a long story short, if you're including the Zendesk assets in
|
112
|
+
your minified blobs, you can use just the `zendesk_dropbox_config` view
|
113
|
+
helper to generate the dropbox configuration, placing it before the
|
114
|
+
inclusion of your JS blobs: this way the `zenbox_params` variable will
|
115
|
+
be already defined when the JS is evaluated.
|
116
|
+
|
117
|
+
If you're wondering how we're minifying and concatenating our JS and CSS
|
118
|
+
assets, follow us on GitHub: we plan to release that code as well. :-)
|
119
|
+
|
120
|
+
|
121
|
+
Compatibility
|
122
|
+
-------------
|
123
|
+
|
124
|
+
Tested with Rails 2.3.8 with the `rails_xss` plugin installed,
|
125
|
+
running under Ruby 1.9.1-p378.
|
126
|
+
|
127
|
+
|
128
|
+
TODO
|
129
|
+
----
|
130
|
+
|
131
|
+
* Your login action must implement a `redirect_to params[:return_to] || some_other_url`
|
132
|
+
or redirects to Zendesk won't work: remove relying on this assumption. Cleanly.
|
133
|
+
* Clean up configuration by requiring less variables: *convention over configuration*!
|
134
|
+
* Configuration of the `logged_in?` method name
|
135
|
+
* Remove the `String#force_utf8` patch and its usage
|
136
|
+
* Allow options passing to the `zendesk_dropbox_link_to` helper
|
137
|
+
* Code documentation
|
138
|
+
* Gem
|
139
|
+
* Tests (yuck!)
|
140
|
+
|
141
|
+
Please fork the project and send us a pull request if you check off any of these items
|
142
|
+
from the to-do list, or for any other improvement. Thanks! :-)
|
143
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
|
4
|
+
require 'lib/panmind/zendesk'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gemspec|
|
9
|
+
gemspec.name = 'panmind-zendesk'
|
10
|
+
|
11
|
+
gemspec.summary = 'Zendesk on Rails - Dropbox and Remote Authentication'
|
12
|
+
gemspec.description = 'The plugin implements the HTML generation code for the ' \
|
13
|
+
'Zendesk dropbox and the necessary controller and routing ' \
|
14
|
+
'code to implement remote authentication'
|
15
|
+
|
16
|
+
gemspec.authors = ['Marcello Barnaba']
|
17
|
+
gemspec.email = 'vjt@openssl.it'
|
18
|
+
gemspec.homepage = 'http://github.com/Panmind/zendesk'
|
19
|
+
|
20
|
+
gemspec.files = %w( README.md Rakefile rails/init.rb ) + Dir['lib/**/*']
|
21
|
+
gemspec.extra_rdoc_files = %w( README.md )
|
22
|
+
gemspec.has_rdoc = true
|
23
|
+
|
24
|
+
gemspec.version = Panmind::Zendesk::Version
|
25
|
+
gemspec.date = '2010-11-17'
|
26
|
+
|
27
|
+
gemspec.require_path = 'lib'
|
28
|
+
|
29
|
+
gemspec.add_dependency('rails', '~> 2.3.8')
|
30
|
+
end
|
31
|
+
rescue LoadError
|
32
|
+
puts 'Jeweler not available. Install it with: gem install jeweler'
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Generate the rdoc'
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
37
|
+
rdoc.rdoc_files.add %w( README.md lib/**/*.rb )
|
38
|
+
|
39
|
+
rdoc.main = 'README.md'
|
40
|
+
rdoc.title = 'Zendesk on Rails - Dropbox and Remote Authentication'
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Will someone help write tests?'
|
44
|
+
task :default do
|
45
|
+
puts
|
46
|
+
puts 'Can you help in writing tests? Please do :-)'
|
47
|
+
puts
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Adds a .force_utf8 to the String class: it forces the instance
|
2
|
+
# encoding to utf-8 and then normalizes in NFKC form.
|
3
|
+
#
|
4
|
+
# For now, we're using ActiveSupport because it has proven to be
|
5
|
+
# 33% faster than UnicodeUtils.. but in the future you can never
|
6
|
+
# know whether the AS::Multibyte::Chars class will be supported.
|
7
|
+
#
|
8
|
+
# Please read http://www.cl.cam.ac.uk/~mgk25/unicode.html#ucsutf
|
9
|
+
# for more information about Unicode Normalization Forms.
|
10
|
+
#
|
11
|
+
# - vjt Wed Jul 21 16:51:25 CEST 2010
|
12
|
+
#
|
13
|
+
unless 'the string'.respond_to?(:force_utf8)
|
14
|
+
class String
|
15
|
+
if '1.9'.respond_to?(:force_encoding)
|
16
|
+
#require 'unicode_utils/nfkc'
|
17
|
+
|
18
|
+
def force_utf8
|
19
|
+
#force_encoding('UTF-8')
|
20
|
+
#replace UnicodeUtils.nfkc(self)
|
21
|
+
replace ActiveSupport::Multibyte::Chars.new(self).normalize(:kc)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
def force_utf8
|
25
|
+
replace mb_chars.normalize(:kc)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Panmind
|
4
|
+
# Zendesk remote authentication helper for Rails. Implements JS generation,
|
5
|
+
# controller actions and route helpers. Have a look at the code, because it
|
6
|
+
# is more explanatory than a thousand words :-)
|
7
|
+
#
|
8
|
+
# Kudos to the Zendesk staff for such a simple and effective interface.
|
9
|
+
#
|
10
|
+
# (C) 2010 Panmind, Released under the terms of the Ruby License.
|
11
|
+
#
|
12
|
+
# - vjt Wed Jul 21 13:00:42 CEST 2010
|
13
|
+
#
|
14
|
+
module Zendesk
|
15
|
+
Version = 1.0
|
16
|
+
|
17
|
+
class ConfigurationError < StandardError; end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_reader :token, :hostname
|
21
|
+
|
22
|
+
def auth_url; @auth_url ||= "http://#{hostname}/access/remote/".freeze end
|
23
|
+
def return_url; @return_url ||= "http://#{hostname}/login".freeze end
|
24
|
+
def support_url; @support_url ||= "http://#{hostname}/home".freeze end
|
25
|
+
|
26
|
+
# TODO these should become attr_readers and we set @variables directly
|
27
|
+
attr_accessor :dropbox, :login, :login_url, :assets_path, :assets_name
|
28
|
+
|
29
|
+
def set(options)
|
30
|
+
self.token, self.hostname, self.login, self.login_url =
|
31
|
+
options.values_at(:token, :hostname, :login, :login_url)
|
32
|
+
|
33
|
+
if %w( token hostname login login_url ).any? {|conf| send(conf).blank?}
|
34
|
+
raise ConfigurationError, "Zendesk requires the API token, an hostname a proc to infer the user name "\
|
35
|
+
"and e-mail and the login route helper name" # TODO don't require all these things
|
36
|
+
end
|
37
|
+
|
38
|
+
self.dropbox = (options[:dropbox] || {}).reverse_merge(
|
39
|
+
:tab_id => 'feedback',
|
40
|
+
:tab_color => 'black',
|
41
|
+
:title => 'Support',
|
42
|
+
:text => "How may we help you? Please fill in details below, and we'll get back to you as soon as possible.",
|
43
|
+
:url => Zendesk.hostname
|
44
|
+
).freeze
|
45
|
+
|
46
|
+
# TODO better configuration
|
47
|
+
self.assets_path = options[:assets_path] || '//assets0.zendesk.com/external/zenbox'
|
48
|
+
self.assets_name = options[:assets_name] || 'overlay'
|
49
|
+
end
|
50
|
+
|
51
|
+
def enabled?
|
52
|
+
Rails.env.production?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def token=(token); @token = token.force_utf8.freeze rescue nil end
|
57
|
+
def hostname=(hostname); @hostname = hostname.freeze end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Helpers
|
61
|
+
def zendesk_dropbox_config
|
62
|
+
config = Zendesk.dropbox
|
63
|
+
if config[:email].kind_of?(Proc)
|
64
|
+
config = config.merge(:email => instance_exec(&config[:email]))
|
65
|
+
end
|
66
|
+
|
67
|
+
javascript_tag("var zenbox_params = #{config.to_json};").html_safe
|
68
|
+
end
|
69
|
+
|
70
|
+
def zendesk_dropbox_tags
|
71
|
+
return unless Zendesk.enabled?
|
72
|
+
|
73
|
+
%(#{zendesk_dropbox_config}
|
74
|
+
<style type='text/css'>@import url('#{Zendesk.assets_path}/#{Zendesk.assets_name}.css');</style>
|
75
|
+
<script type='text/javascript' src='#{Zendesk.assets_path}/#{Zendesk.assets_name}.js'></script>).html_safe
|
76
|
+
end
|
77
|
+
|
78
|
+
def zendesk_link_to(text, options = {})
|
79
|
+
return unless Zendesk.enabled?
|
80
|
+
link_to text, support_path, options
|
81
|
+
end
|
82
|
+
|
83
|
+
def zendesk_dropbox_link_to(text)
|
84
|
+
link_to text, '#', :onclick => 'Zenbox.render (); return false'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module Controller
|
89
|
+
def self.included(base)
|
90
|
+
base.before_filter :zendesk_handle_guests, :only => :zendesk_login
|
91
|
+
end
|
92
|
+
|
93
|
+
def zendesk_login
|
94
|
+
name, email = instance_exec(&Zendesk.login).map!(&:force_utf8)
|
95
|
+
|
96
|
+
now = params[:timestamp] || Time.now.to_i.to_s
|
97
|
+
hash = Digest::MD5.hexdigest(name + email + Zendesk.token + now)
|
98
|
+
back = params[:return_to] || Zendesk.return_url
|
99
|
+
|
100
|
+
auth_params = [
|
101
|
+
'?name=' + CGI.escape(name),
|
102
|
+
'&email=' + CGI.escape(email),
|
103
|
+
'×tamp=' + now,
|
104
|
+
'&hash=' + hash,
|
105
|
+
'&return_to=' + back
|
106
|
+
].join.force_utf8
|
107
|
+
|
108
|
+
redirect_to(Zendesk.auth_url + auth_params)
|
109
|
+
end
|
110
|
+
|
111
|
+
def zendesk_logout
|
112
|
+
flash[:notice] = "Thanks for visiting our support forum."
|
113
|
+
redirect_to root_url
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
def zendesk_handle_guests
|
118
|
+
return if logged_in? rescue false # TODO add another option
|
119
|
+
|
120
|
+
if params[:timestamp] && params[:return_to]
|
121
|
+
# User clicked on Zendesk "login", thus redirect to our
|
122
|
+
# login page, that'll redirect him/her back to Zendesk.
|
123
|
+
#
|
124
|
+
redirect_to send(Zendesk.login_url, :return_to => support_url)
|
125
|
+
else
|
126
|
+
# User clicked on our "support" link, and maybe doesn't
|
127
|
+
# have an account yet: redirect him/her to the support.
|
128
|
+
#
|
129
|
+
redirect_to Zendesk.support_url
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
module Routes
|
135
|
+
def zendesk(base, options)
|
136
|
+
return unless Zendesk.enabled?
|
137
|
+
|
138
|
+
self.support base, :controller => options[:controller], :action => :zendesk_login
|
139
|
+
self.connect "#{base}/exit", :controller => options[:controller], :action => :zendesk_logout
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
data/rails/init.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: panmind-zendesk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: "1.0"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Marcello Barnaba
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-17 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rails
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 19
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 3
|
32
|
+
- 8
|
33
|
+
version: 2.3.8
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: The plugin implements the HTML generation code for the Zendesk dropbox and the necessary controller and routing code to implement remote authentication
|
37
|
+
email: vjt@openssl.it
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- README.md
|
44
|
+
files:
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- lib/panmind/string_force_utf8_patch.rb
|
48
|
+
- lib/panmind/zendesk.rb
|
49
|
+
- rails/init.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://github.com/Panmind/zendesk
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.3.7
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Zendesk on Rails - Dropbox and Remote Authentication
|
84
|
+
test_files: []
|
85
|
+
|