livefyre 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in livefyre.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Mashable, Inc
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # Livefyre
2
+
3
+ Interface library for Livefyre's API. Currently a mishmash of the v2 and v3 APIs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'livefyre'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install livefyre
18
+
19
+ ## Documentation
20
+
21
+ Full documentation is available [on GitHub](http://mashable.github.com/livefyre/frames.html).
22
+
23
+ You can generate full documentation yourself from the source tree. Requires the yard-tomdoc plugin.
24
+
25
+ yard --plugin yard-tomdoc -o doc
26
+
27
+ ## Usage
28
+
29
+ You can set a default configuration object for Livefyre, which will prevent you from having to pass a client to
30
+ all your object proxies manually.
31
+
32
+ Livefyre.config = {
33
+ :network => "foo.fyre.co",
34
+ :network_key => "blorgons",
35
+ :site_id => 1234,
36
+ :site_key => "minerva",
37
+ :system_token => "your_long_lived_system_token",
38
+ :domain => "zor.t123.livefyre.com"
39
+ }
40
+
41
+ If you're using this gem from Rails, we recommend doing this from an initializer.
42
+
43
+ Once that's set, you're ready to start talking to Livefyre.
44
+
45
+ domain = Livefyre::Domain.new
46
+ domain.set_pull_url "http://foo.bar/users/{id}/pull/"
47
+
48
+ user = Livefyre::User.new("some_user_id")
49
+ user.refresh # Invoke ping-to-pull
50
+
51
+ ## Using with Rails
52
+
53
+ Integration with Rails is straightforward, but does require some setup.
54
+
55
+ ### Controller integration
56
+
57
+ You need to add a route to your routes file to handle profile pull requests from Livefyre. That'll look something like:
58
+
59
+ get "/livefyre/:id/pull", :to => "users#pull"
60
+
61
+ Of course, you need a matching controller action
62
+
63
+ def pull
64
+ # Checks the validity of the JWT that Livefyre sends with pull requests. Throws an exception if it's no good.
65
+ validate_livefyre_request!
66
+
67
+ user = User.find(params[:id])
68
+
69
+ # livefile_profile will attempt to generate valid Livefire profile dump from the passed user record by guessing at field names.
70
+ # You can pass overides in a hash as the second option, or you can always generate your own data structure.
71
+ render :json => livefire_profile(user, :image => user.profile_image_url).to_json
72
+ end
73
+
74
+ Finally, you'll need to set up a pull URL. Since this is done via the API, you are expected to do it manually. From a Rails console is fine, though
75
+ you may do it any other way you please, too. Livefyre will substitute the string "{id}" for the user ID it wants data for.
76
+
77
+ Livefyre::Domain.new.set_pull_url "http://your.domain.com/livefyre/{id}/pull"
78
+
79
+ You may want to invoke user updates when you save the user record.
80
+
81
+ def update
82
+ # ... user record updates here!
83
+ Livefyre::User.new( user._id ).refresh
84
+ end
85
+
86
+ Or you can have the gem do it automatically from the model:
87
+
88
+ class User < ActiveRecord::Base
89
+ # ...
90
+
91
+ livefyre_user :update_on => [:email, :display_name]
92
+ end
93
+
94
+ You can even have the gem do your ping updated asynchronously with Resque
95
+
96
+ class User < ActiveRecord::Base
97
+ # ...
98
+
99
+ livefyre_user :update_on => [:email, :display_name], :defer => true
100
+ end
101
+
102
+ This will enqueue a ping-to-pull job in the "livefyre" queue. Make sure you have a worker running that'll handle that queue!
103
+
104
+ ### View integration
105
+
106
+
107
+ In the location that you want to use your comment form, include something like the following:
108
+
109
+ <%= livefyre_comments post.id, post.title, post_url(post), post.tags %>
110
+
111
+ You'll also need to boot Livefyre with Javascript. In your application.js, you'll want to include the Livefyre loader in your manifest:
112
+
113
+ //=require livefyre.js
114
+
115
+ And then somewhere in your application.js, you'll want to actually boot:
116
+
117
+ window.initLivefyre({
118
+ login: function() {
119
+ // Things to do when the user clicks the "sign in" link. You probably want to
120
+ // take your user through a login cycle in a popup window, which includes calling
121
+ // the livefyre_login(user_id, user_name) method.
122
+ window.location = "/login";
123
+ },
124
+ logout: function() {
125
+ // things to do when the user clicks the "sign out" link. You probably want to take
126
+ // your user through the logout cycle, including a call to livefyre_logout.
127
+ window.location = "/logout";
128
+ },
129
+ viewProfile: function(handlers, author) {
130
+ // Handler for when a user's name is clicked in a comment
131
+ window.location = "/" + author;
132
+ },
133
+ editProfile: function(handlers, author) {
134
+ // Handler for when a user wants to edit their profile from the Livefyre user dropdown.
135
+ window.location = "/" + author + "/edit";
136
+ }
137
+ });
138
+
139
+ That's it!
140
+
141
+ ## Contributing
142
+
143
+ 1. Fork it
144
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
145
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
146
+ 4. Push to the branch (`git push origin my-new-feature`)
147
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+ rescue LoadError => e
7
+ # Pass
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,72 @@
1
+ defaultDelegate = (options) ->
2
+ authDelegate = new fyre.conv.RemoteAuthDelegate()
3
+ authDelegate.login = (handlers) ->
4
+ if options.login
5
+ options.login(handlers)
6
+
7
+ authDelegate.logout = (handlers) ->
8
+ if options.logout
9
+ options.logout(handlers)
10
+ authDelegate.viewProfile = (handlers, author) ->
11
+ if options.viewProfile
12
+ if options.viewProfile(handlers, author)
13
+ handlers.success()
14
+
15
+ authDelegate.editProfile = (handlers, author) ->
16
+ if options.editProfile
17
+ if options.editProfile(handlers, author)
18
+ handlers.success()
19
+
20
+ loadScriptAsync = null
21
+ (->
22
+ __loadedScripts = []
23
+ fjs = null
24
+ loadScriptAsync = (source, id, content, options) ->
25
+ content = null if !content
26
+ return if (document.getElementById(id))
27
+ return if __loadedScripts[id]
28
+ __loadedScripts[id] = true
29
+ fjs = document.getElementsByTagName('script')[0] unless fjs
30
+ js = document.createElement("script")
31
+ js.id = id
32
+ js.async = true
33
+ js.src = source
34
+ js.innerHTML = content
35
+ js[k] = v for k, v of options if options
36
+
37
+ fjs.parentNode.insertBefore(js, fjs)
38
+ js
39
+ )()
40
+
41
+ livefyreInitialized = false
42
+ @initLivefyre = (options) ->
43
+ if livefyreInitialized
44
+ throw "Livefyre has already been initialized"
45
+ livefyreInitialized = true
46
+ e = document.getElementById(options.element_id || "livefyre_comments")
47
+ if e
48
+ options.config ||=
49
+ checksum: e.getAttribute("data-checksum")
50
+ collectionMeta: e.getAttribute("data-collection-meta")
51
+ articleId: e.getAttribute("data-article-id")
52
+ siteId: e.getAttribute("data-site-id")
53
+ el: e.id
54
+
55
+ options.network ||= e.getAttribute("data-network")
56
+ options.domain ||= e.getAttribute("data-domain")
57
+ options.root ||= e.getAttribute("data-root")
58
+
59
+ @FYRE_LOADED_CB = ->
60
+ opts =
61
+ network: options.network
62
+ authDelegate: options.delegate || defaultDelegate(options)
63
+
64
+ fyre.conv.load opts, [options.config], ->
65
+ token = $.cookie(options.cookie_name || "livefyre_utoken")
66
+ if token
67
+ try
68
+ fyre.conv.login(token)
69
+ catch error
70
+ window.console.log "Error logging in:", e if window.console
71
+
72
+ element = loadScriptAsync "http://#{options.root}/wjs/v3.0/javascripts/livefyre.js", null, null, {"data-lf-domain": options.network}
@@ -0,0 +1,117 @@
1
+ // Generated by CoffeeScript 1.3.3
2
+ (function() {
3
+ var defaultDelegate, livefyreInitialized, loadScriptAsync;
4
+
5
+ defaultDelegate = function(options) {
6
+ var authDelegate;
7
+ authDelegate = new fyre.conv.RemoteAuthDelegate();
8
+ authDelegate.login = function(handlers) {
9
+ if (options.login) {
10
+ return options.login(handlers);
11
+ }
12
+ };
13
+ authDelegate.logout = function(handlers) {
14
+ if (options.logout) {
15
+ return options.logout(handlers);
16
+ }
17
+ };
18
+ authDelegate.viewProfile = function(handlers, author) {
19
+ if (options.viewProfile) {
20
+ if (options.viewProfile(handlers, author)) {
21
+ return handlers.success();
22
+ }
23
+ }
24
+ };
25
+ return authDelegate.editProfile = function(handlers, author) {
26
+ if (options.editProfile) {
27
+ if (options.editProfile(handlers, author)) {
28
+ return handlers.success();
29
+ }
30
+ }
31
+ };
32
+ };
33
+
34
+ loadScriptAsync = null;
35
+
36
+ (function() {
37
+ var fjs, __loadedScripts;
38
+ __loadedScripts = [];
39
+ fjs = null;
40
+ return loadScriptAsync = function(source, id, content, options) {
41
+ var js, k, v;
42
+ if (!content) {
43
+ content = null;
44
+ }
45
+ if (document.getElementById(id)) {
46
+ return;
47
+ }
48
+ if (__loadedScripts[id]) {
49
+ return;
50
+ }
51
+ __loadedScripts[id] = true;
52
+ if (!fjs) {
53
+ fjs = document.getElementsByTagName('script')[0];
54
+ }
55
+ js = document.createElement("script");
56
+ js.id = id;
57
+ js.async = true;
58
+ js.src = source;
59
+ js.innerHTML = content;
60
+ if (options) {
61
+ for (k in options) {
62
+ v = options[k];
63
+ js[k] = v;
64
+ }
65
+ }
66
+ fjs.parentNode.insertBefore(js, fjs);
67
+ return js;
68
+ };
69
+ })();
70
+
71
+ livefyreInitialized = false;
72
+
73
+ this.initLivefyre = function(options) {
74
+ var e, element;
75
+ if (livefyreInitialized) {
76
+ throw "Livefyre has already been initialized";
77
+ }
78
+ livefyreInitialized = true;
79
+ e = document.getElementById(options.element_id || "livefyre_comments");
80
+ if (e) {
81
+ options.config || (options.config = {
82
+ checksum: e.getAttribute("data-checksum"),
83
+ collectionMeta: e.getAttribute("data-collection-meta"),
84
+ articleId: e.getAttribute("data-article-id"),
85
+ siteId: e.getAttribute("data-site-id"),
86
+ el: e.id
87
+ });
88
+ options.network || (options.network = e.getAttribute("data-network"));
89
+ options.domain || (options.domain = e.getAttribute("data-domain"));
90
+ options.root || (options.root = e.getAttribute("data-root"));
91
+ this.FYRE_LOADED_CB = function() {
92
+ var opts;
93
+ opts = {
94
+ network: options.network,
95
+ authDelegate: options.delegate || defaultDelegate(options)
96
+ };
97
+ return fyre.conv.load(opts, [options.config], function() {
98
+ var token;
99
+ token = $.cookie(options.cookie_name || "livefyre_utoken");
100
+ if (token) {
101
+ try {
102
+ return fyre.conv.login(token);
103
+ } catch (error) {
104
+ if (window.console) {
105
+ return window.console.log("Error logging in:", e);
106
+ }
107
+ }
108
+ }
109
+ });
110
+ };
111
+ return element = loadScriptAsync("http://" + options.root + "/wjs/v3.0/javascripts/livefyre.js", null, null, {
112
+ "data-lf-domain": options.network
113
+ });
114
+ }
115
+ };
116
+
117
+ }).call(this);
@@ -0,0 +1,117 @@
1
+ module Livefyre
2
+ # Public: Primary interface to the Livefyre API
3
+ class Client
4
+ extend Forwardable
5
+ # Public: Valid roles for #set_user_role
6
+ ROLES = %w(admin member none outcast owner)
7
+
8
+ # Public: Valid scopes for #set_user_role
9
+ SCOPES = %w(domain site conv)
10
+
11
+ attr_accessor :host, :key, :options, :system_token, :http_client
12
+
13
+ def_delegators :http_client, :get, :post, :delete, :put
14
+
15
+ # Public: Create a new Livefyre client.
16
+ #
17
+ # options - [Hash] array of options to pass to the client for initialization
18
+ # :host - your Livefyre network_host
19
+ # :key - your Livefyre network_key
20
+ # :system_token - your Livefyre long-lived system user key
21
+ def initialize(options = {})
22
+ @options = options.clone
23
+ @host = options.delete(:network) || options.delete(:host)
24
+ raise "Invalid host" if @host.nil?
25
+ @http_client = Faraday.new(:url => "http://#{@host}")
26
+
27
+ @key = options.delete(:secret) || options.delete(:key) || options.delete(:network_key)
28
+ raise "Invalid secret key" if @key.nil?
29
+
30
+ @system_token = options.delete(:system_token)
31
+ raise "Invalid system token" if @system_token.nil?
32
+ end
33
+
34
+ # Public: Sign a data structure with this client's network key.
35
+ #
36
+ # Returns [String] A signed JWT token
37
+ def sign(data)
38
+ JWT.encode(data, @key)
39
+ end
40
+
41
+ # Public: Validates and decodes a JWT token
42
+ #
43
+ # Returns [Hash] A hash of data passed from the token
44
+ # Raises [JWT::DecodeError] if invalid token contents or signature
45
+ def validate(data)
46
+ JWT.decode(data, @key)
47
+ end
48
+
49
+ # Public: Create a {Livefyre::User} with this client's credentials.
50
+ #
51
+ # uid - the user ID to create a Livefyre user for. This should be the ID used to reference this user in Livefyre's system.
52
+ # display_name - the displayed name for this user. Optional.
53
+ #
54
+ # Returns [Livefyre::User]
55
+ def user(uid, display_name = nil)
56
+ User.new(uid, self, display_name)
57
+ end
58
+
59
+ # Public: Sets a user's role (affiliation) in a given scope.
60
+ #
61
+ # user_id - The user ID (without the host) to set roles for
62
+ # role - The {ROLES role} to set.
63
+ # scope - The {SCOPES scope} for which to set this role.
64
+ # scope_id - In the case that the given scope requires identification, specifies which scope to operate on.
65
+ #
66
+ # Examples
67
+ #
68
+ # set_user_role(1234, "owner", "domain")
69
+ # set_user_role(1234, "moderator", "site", site_id)
70
+ # set_user_role(1234, "moderator", "conv", conversation_id)
71
+ #
72
+ #
73
+ # Returns [Bool] true on success
74
+ # Raises APIException if the request failed
75
+ def set_user_role(user_id, role, scope = 'domain', scope_id = nil)
76
+ raise "Invalid scope" unless SCOPES.include? scope
77
+ raise "Invalid role" unless ROLES.include? role
78
+
79
+ post_data = {
80
+ :affiliation => role,
81
+ :lftoken => system_token,
82
+ }
83
+ case scope
84
+ when "domain"
85
+ post_data[:domain_wide] = 1
86
+ when "conv"
87
+ raise "Invalid scope_id" if scope_id.nil?
88
+ post_data[:conv_id] = scope_id
89
+ when "site"
90
+ raise "Invalid scope_id" if scope_id.nil?
91
+ post_data[:site_id] = scope_id
92
+ end
93
+ result = post "/api/v1.1/private/management/user/#{jid(user_id)}/role/", post_data
94
+ if result.success?
95
+ true
96
+ else
97
+ raise APIException.new(result.body)
98
+ end
99
+ end
100
+
101
+ # Public: Transform the given ID into a jid
102
+ #
103
+ # id - a string value to compose the JID with
104
+ #
105
+ # Returns [String] JID
106
+ def jid(id)
107
+ "%s@%s" % [id, host]
108
+ end
109
+
110
+ # Internal: Returns a cleaner string representation of this object
111
+ #
112
+ # Returns [String] representation of this class
113
+ def to_s
114
+ "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} host='#{host}' key='#{key}'>"
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,58 @@
1
+ module Livefyre
2
+ # Public: Controller extensions for Rails. Adds methods to be called from your controller to integrate with Livefyre.
3
+ module Controller
4
+ extend ActiveSupport::Concern
5
+
6
+ # Public: Creates the Livefyre session cookies. Should be called when the user logs in.
7
+ def livefyre_login(id, display_name)
8
+ cookie = (Livefyre.config[:cookie_options] || {}).clone || {:path => "/", :expires => Time.now + 1.year}
9
+ expiry = cookie.delete(:expires) || (Time.now + 1.year)
10
+
11
+ token = {
12
+ :domain => Livefyre.client.host,
13
+ :user_id => id,
14
+ :expires => expiry.to_i,
15
+ :display_name => display_name
16
+ }
17
+
18
+ name = cookie.delete(:name) || "livefyre_utoken"
19
+ cookies[name] = cookie.merge(:value => JWT.encode(token, Livefyre.client.key), :expires => expiry)
20
+ end
21
+
22
+ # Public: Destroys the Livefyre session cookies. Should be called when the user logs out
23
+ def livefyre_logout
24
+ name = (Livefyre.config[:cookie_options] || {})[:name] || "livefyre_utoken"
25
+ cookies.delete(name)
26
+ end
27
+
28
+ # Public: Attempt to generate valid Livefire profile dump from the passed user record by guessing at field names.
29
+ #
30
+ # user - The user record to generate data from. Assumes it's ActiveModel-ish.
31
+ # values - [Hash] of values to force values for, rather than guessing at.
32
+ #
33
+ # Returns [Hash] suitable for conversion to JSON
34
+ def livefire_profile(user, values = {})
35
+ {
36
+ :id => user.id,
37
+ :display_name => user.try(:display_name) || user.try(:name) || user.try(:username),
38
+ :email => user.try(:email),
39
+ :profile => url_for(user),
40
+ :settings_url => url_for(:edit, user),
41
+ :bio => user.try(:bio) || user.try(:about),
42
+ :name => {
43
+ :first_name => user.try(:first_name),
44
+ :last_name => user.try(:last_name),
45
+ }
46
+ }.merge defaults
47
+ end
48
+
49
+ # Public: Check the validity of the JWT that Livefyre sends with pull requests.
50
+ #
51
+ # Raises [JWT::DecodeError] if the token is invalid or missing.
52
+ def validate_livefyre_request!
53
+ token = JWT.decode params[:lftoken], Livefyre.client.key
54
+ raise JWT::DecodeError unless token["domain"] == Livefyre.client.host
55
+ return true
56
+ end
57
+ end
58
+ end