panmind-zendesk 1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ '&timestamp=' + 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
@@ -0,0 +1,5 @@
1
+ require 'panmind/zendesk'
2
+ require 'panmind/string_force_utf8_patch'
3
+
4
+ ActionView::Base.instance_eval { include Panmind::Zendesk::Helpers }
5
+ ActionController::Routing::RouteSet::Mapper.instance_eval { include Panmind::Zendesk::Routes }
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
+