livefyre 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.
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