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 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
+