livefyre 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,43 +0,0 @@
1
- module Livefyre
2
- # Public: Proxy object for an item from a Conversation activity feed
3
- class Activity
4
- attr_accessor :id, :user, :conversation, :body, :state, :created_at
5
- def initialize(client, params = {})
6
- @client = Livefyre.client
7
- @params = params
8
- @id = params["activity_id"]
9
- @conversation = Conversation.new(@params["lf_conv_id"], @params["article_identifier"])
10
- @created_at = Time.at(@params["created"]) - Time.at(0).utc_offset
11
- end
12
-
13
- # Public: Cast this activity to a Comment
14
- #
15
- # Return [Comment]
16
- def comment
17
- Comment.new(@params["lf_comment_id"], conversation,
18
- :body => @params["body_text"],
19
- :user => user,
20
- :parent_id => @params["lf_parent_comment_id"],
21
- :author_ip => @params["author_ip"],
22
- :state => @params["state"]
23
- )
24
- end
25
-
26
- # Public: Fetch the user that created this record
27
- #
28
- # Returns [User]
29
- def user
30
- User.new((@params["lf_jid"] || "").split("@", 2).first, @client, @params["author"],
31
- :email => @params["author_email"],
32
- :url => @params["author_url"]
33
- )
34
- end
35
-
36
- # Internal: Test if this activity represented a comment
37
- #
38
- # Returns [Boolean]
39
- def comment?
40
- @params["activity_type"] == "comment-add"
41
- end
42
- end
43
- end
@@ -1,141 +0,0 @@
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, :site_key, :quill, :stream, :bootstrap, :search
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
- @quill = Faraday.new(:url => "http://quill.#{@host}")
27
- @stream = Faraday.new(:url => "http://stream.#{@host}")
28
- @search = Faraday.new(:url => "http://search.#{@host}")
29
- @bootstrap = Faraday.new(:url => "http://bootstrap.#{@host}")
30
- @site_key = options[:site_key]
31
-
32
- @key = options.delete(:secret) || options.delete(:key) || options.delete(:network_key)
33
- raise "Invalid secret key" if @key.nil?
34
-
35
- @system_token = options.delete(:system_token)
36
- raise "Invalid system token" if @system_token.nil?
37
- end
38
-
39
- # Public: Sign a data structure with this client's network key.
40
- #
41
- # Returns [String] A signed JWT token
42
- def sign(data)
43
- JWT.encode(data, @key)
44
- end
45
-
46
- # Public: Validates and decodes a JWT token
47
- #
48
- # Returns [Hash] A hash of data passed from the token
49
- # Raises [JWT::DecodeError] if invalid token contents or signature
50
- def validate(data)
51
- JWT.decode(data, @key)
52
- end
53
-
54
- # Public: Create a {Livefyre::User} with this client's credentials.
55
- #
56
- # uid - the user ID to create a Livefyre user for. This should be the ID used to reference this user in Livefyre's system.
57
- # display_name - the displayed name for this user. Optional.
58
- #
59
- # Returns [Livefyre::User]
60
- def user(uid, display_name = nil)
61
- User.new(uid, self, display_name)
62
- end
63
-
64
- # Public: Sets a user's role (affiliation) in a given scope.
65
- #
66
- # user_id - The user ID (without the host) to set roles for
67
- # role - The {ROLES role} to set.
68
- # scope - The {SCOPES scope} for which to set this role.
69
- # scope_id - In the case that the given scope requires identification, specifies which scope to operate on.
70
- #
71
- # Examples
72
- #
73
- # set_user_role(1234, "owner", "domain")
74
- # set_user_role(1234, "moderator", "site", site_id)
75
- # set_user_role(1234, "moderator", "conv", conversation_id)
76
- #
77
- #
78
- # Returns [Bool] true on success
79
- # Raises APIException if the request failed
80
- def set_user_role(user_id, role, scope = 'domain', scope_id = nil)
81
- raise "Invalid scope" unless SCOPES.include? scope
82
- raise "Invalid role" unless ROLES.include? role
83
-
84
- post_data = {
85
- :affiliation => role,
86
- :lftoken => system_token,
87
- }
88
- case scope
89
- when "domain"
90
- post_data[:domain_wide] = 1
91
- when "conv"
92
- raise "Invalid scope_id" if scope_id.nil?
93
- post_data[:conv_id] = scope_id
94
- when "site"
95
- raise "Invalid scope_id" if scope_id.nil?
96
- post_data[:site_id] = scope_id
97
- end
98
- result = post "/api/v1.1/private/management/user/#{jid(user_id)}/role/", post_data
99
- if result.success?
100
- true
101
- else
102
- raise APIException.new(result.body)
103
- end
104
- end
105
-
106
- # Public: Transform the given ID into a jid
107
- #
108
- # id - a string value to compose the JID with
109
- #
110
- # Returns [String] JID
111
- def jid(id)
112
- "%s@%s" % [id, host]
113
- end
114
-
115
- # Internal: Identifier to use to uniquely identify this client.
116
- #
117
- # Returns string ID
118
- def identifier
119
- @identifier ||= "RubyLib-#{Process.pid}-#{local_ip}-#{object_id}"
120
- end
121
-
122
- # Internal: Returns a cleaner string representation of this object
123
- #
124
- # Returns [String] representation of this class
125
- def to_s
126
- "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} host='#{host}' key='#{key}'>"
127
- end
128
-
129
- private
130
-
131
- def local_ip
132
- orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
133
- UDPSocket.open do |s|
134
- s.connect '64.233.187.99', 1
135
- s.addr.last
136
- end
137
- ensure
138
- Socket.do_not_reverse_lookup = orig
139
- end
140
- end
141
- end
@@ -1,190 +0,0 @@
1
- module Livefyre
2
- # Public: Proxy object for a [Comment] on a [Livefyre::Conversation]
3
- class Comment
4
- private
5
- SOURCES = %w(Livefyre Twitter Twitter Facebook Livefyre Livefyre Facebook Twitter Livefyre)
6
- VISIBILITIES = %w(None Everyone Owner Group)
7
- CONTENT_TYPES = %w(Message Opinion)
8
- PERMISSIONS = %w(Global Network Site Collection CollectionRule)
9
- REASONS = %w(disagree spam offensive off-topic)
10
-
11
- public
12
-
13
- attr_accessor :id, :body, :user, :parent_id, :ip, :conversation, :created_at
14
- def initialize(id, conversation, options = {})
15
- @id = id
16
- @body = options[:body]
17
- @user = options[:user]
18
- @parent_id = options[:parent_id]
19
- @ip = options[:author_ip]
20
- @conversation = conversation
21
- @created_at = options[:created_at]
22
- @client = options[:client] || Livefyre.client
23
- @options = options
24
- end
25
-
26
- # Public: Flag a comment
27
- #
28
- # reason - one of [disagree, spam, offensive, off-topic]
29
- # notes - String containing the reason for the flag
30
- # email - email address of the flagger
31
- # user - [User] If set, will include the user token for validation of the flag
32
- def flag(reason, notes, email, user = nil)
33
- raise "invalid reason" unless REASONS.include? reason
34
- payload = {
35
- :message_id => @id,
36
- :collection_id => @conversation.id,
37
- :flag => reason,
38
- :notes => notes,
39
- :email => email
40
- }
41
- payload[:lftoken] = user.token if user
42
- response = client.quill.post "/api/v3.0/message/25818122/flag/#{reason}/", payload.to_json
43
- if response.success?
44
- true
45
- else
46
- raise APIException.new(response.body)
47
- end
48
- end
49
-
50
- # Public: Delete this comment
51
- #
52
- # Returns [Boolean] true on success
53
- # Raises [APIException] on failure
54
- def delete!
55
- response = client.quill.post "/api/v3.0/message/#{id}/delete", {:lftoken => @client.system_token}
56
- if response.success?
57
- true
58
- else
59
- raise APIException.new(response.body)
60
- end
61
- end
62
-
63
- # Public: Update this comment's content
64
- #
65
- # Returns [Boolean] true on success
66
- # Raises [APIException] on failure
67
- def update(body)
68
- response = client.quill.post "/api/v3.0/message/#{id}/edit", {:lftoken => @client.system_token, :body => body}
69
- if response.success?
70
- true
71
- else
72
- raise APIException.new(response.body)
73
- end
74
- end
75
-
76
- # Public: Get the comment source as a string.
77
- # Currently only populated when created via ::create
78
- #
79
- # Returns [Enum<SOURCES>]
80
- def source
81
- source_id ? SOURCES[source_id] : nil
82
- end
83
-
84
- # Public: Get the comment source as an integer.
85
- # Currently only populated when created via ::create
86
- #
87
- # Returns [Integer]
88
- def source_id
89
- @options[:source]
90
- end
91
-
92
- # Public: Get the comment visibility as a string.
93
- # Currently only populated when created via ::create
94
- #
95
- # Returns [Enum<VISIBILITIES>]
96
- def visibility
97
- visibility_id ? VISIBILITIES[visibility_id] : nil
98
- end
99
-
100
- # Public: Get the comment visibility as an integer.
101
- # Currently only populated when created via ::create
102
- #
103
- # Returns [Integer]
104
- def visibility_id
105
- @options[:visibility]
106
- end
107
-
108
- # Public: Get the comment content type as a string.
109
- # Currently only populated when created via ::create
110
- #
111
- # Returns [Enum<CONTENT_TYPES>]
112
- def content_type
113
- content_type_id ? CONTENT_TYPES[content_type_id] : nil
114
- end
115
-
116
- # Public: Get the comment visibility as an integer.
117
- # Currently only populated when created via ::create
118
- #
119
- # Returns [Integer]
120
- def content_type_id
121
- @options[:type]
122
- end
123
-
124
- # Public: Likes a comment as the passed user
125
- #
126
- # Returns [Boolean] true on success
127
- # Raises [APIException] on failure
128
- def like!(user)
129
- response = @client.quill.post "/api/v3.0/message/#{id}/like/", {:collection_id => conversation.id, :lftoken => user.token}
130
- if response.success?
131
- true
132
- else
133
- raise APIException.new(response.body)
134
- end
135
- end
136
-
137
- # Public: Unlikes a comment as the passed user
138
- #
139
- # Returns [Boolean] true on success
140
- # Raises [APIException] on failure
141
- def unlike!(user)
142
- response = @client.quill.post "/api/v3.0/message/#{id}/unlike/", {:collection_id => conversation.id, :lftoken => user.token}
143
- if response.success?
144
- true
145
- else
146
- raise APIException.new(response.body)
147
- end
148
- end
149
-
150
- # Public: create a new comment on a conversation
151
- #
152
- # client - [Client] representing the site to use when creating the conversation
153
- # user - [User] to create the comment as
154
- # conversation - [Conversation] to create
155
- # body - [String] Comment body
156
- #
157
- # Returns [Comment]
158
- # Raises [APIException] when the API call fails
159
- def self.create(client, user, conversation, body, reply_to = nil)
160
- response = client.quill.post "/api/v3.0/collection/#{conversation.id}/post/", {:lftoken => user.token, :body => body, :_bi => client.identifier, :parent_id => reply_to}
161
- if response.success?
162
- puts JSON.parse(response.body).inspect
163
- data = JSON.parse(response.body)["data"]
164
-
165
- data["messages"].map do |entry|
166
- c = entry["content"]
167
- Comment.new(c["id"], conversation, {
168
- :body => c["bodyHtml"],
169
- :parent_id => c["parentId"],
170
- :user => User.new(c["authorId"], data["authors"].first.last["displayName"], data["authors"].first.last),
171
- :created_at => Time.at(c["createdAt"]),
172
- :source => entry["source"],
173
- :visibility => entry["vis"],
174
- :client => client,
175
- :type => entry["type"]
176
- })
177
- end.first
178
- else
179
- raise APIException.new(response.body)
180
- end
181
- end
182
-
183
- # Internal: Returns a cleaner string representation of this object
184
- #
185
- # Returns [String] representation of this class
186
- def to_s
187
- "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} id='#{id}' options=#{@options.inspect}>"
188
- end
189
- end
190
- end
@@ -1,82 +0,0 @@
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
- JWT::DecodeError if params[:lftoken].nil?
54
- token = JWT.decode params[:lftoken], Livefyre.client.key
55
- raise JWT::DecodeError unless token["domain"] == Livefyre.client.host
56
- return true
57
- end
58
-
59
- # Public: Used in your postback handler to parse the Livefyre postback body into an Activity stream for processing
60
- #
61
- # Returns [Array<Activity>] List of activities included in this postback.
62
- def parse_livefyre_postback
63
- JSON.parse(request.body).map {|item| Activity.new(client, item) }
64
- end
65
-
66
- module ClassMethods
67
- # Public: filter helper to validate postback signatures for Livefyre requests.
68
- #
69
- # options - valid before_filter options, plus :key
70
- #
71
- # Example:
72
- #
73
- # validate_postback_signature :only => [:postback], :key => "your_site_key"
74
- def validate_postback_signature(options = {})
75
- key = options.delete :key
76
- before_filter Proc.new {|c|
77
- Livefyre::Site.validate_signature(c.params, key)
78
- }, options
79
- end
80
- end
81
- end
82
- end
@@ -1,133 +0,0 @@
1
- module Livefyre
2
- # Public: Proxy object for a Livefyre [Conversation] (also called a Collection)
3
- class Conversation
4
- attr_accessor :id, :article_id
5
- def initialize(id, article_id)
6
- @id = id
7
- @article_id = article_id
8
- @client = Livefyre.client
9
- end
10
-
11
- # Public: Fetch a list of comments from a conversation
12
- # TODO: Not currently working.
13
- def comments
14
- response = @client.bootstrap.get "/bs3/#{@client.options[:domain]}/#{@client.options[:network]}/#{@client.options[:site_id]}/#{Base64.encode64 @article_id}/init"
15
- if response.success?
16
- JSON.parse response.body
17
- else
18
- raise APIException.new(response.body)
19
- end
20
- end
21
-
22
- # Public: Update this collection with new metadata
23
- #
24
- # Returns [Bool] true on success
25
- # Raises [APIException] on failure
26
- def update(title, link, tags = nil)
27
- meta = self.class.collectionMeta(@client, @article_id, title, link, tags)
28
- response = @client.quill.post "/api/v3.0/site/#{@client.options[:site_id]}/collection/update/", {:collectionMeta => meta, :articleId => @article_id}.to_json
29
- if response.success?
30
- true
31
- else
32
- raise APIException.new(response.body)
33
- end
34
- end
35
-
36
- # Public: Create a comment on this conversation
37
- #
38
- # user - [User] to create the comment as
39
- # body - [String] body of the content
40
- #
41
- # Returns [Comment]
42
- def create_comment(user, body)
43
- Comment.create(@client, user, self, body)
44
- end
45
-
46
- # Public: Follow this conversation as the passed user
47
- #
48
- # user - [User] to follow the conversation as
49
- #
50
- # Returns [Boolean] true on success
51
- # Raises [APIException] on failure
52
- def follow_as(user)
53
- response = @client.quill.post "/api/v3.0/collection/10584292/follow/", :lftoken => user.token, :collectionId => @id
54
- if response.success?
55
- true
56
- else
57
- raise APIException.new(response.body)
58
- end
59
- end
60
-
61
- # Public: Unfollow this conversation as the passed user
62
- #
63
- # user - [User] to unfollow the conversation as
64
- #
65
- # Returns [Boolean] true on success
66
- # Raises [APIException] on failure
67
- def unfollow_as(user)
68
- response = @client.quill.post "/api/v3.0/collection/10584292/unfollow/", :lftoken => user.token, :collectionId => @id
69
- if response.success?
70
- true
71
- else
72
- raise APIException.new(response.body)
73
- end
74
- end
75
-
76
- # Public: Create a new collection
77
- #
78
- # client - [Client] identifying the site to create the collection on
79
- # article_id - [String] ID to use to identify this article
80
- # title - [String] Article title
81
- # link - [String] Article link
82
- # tags - [String, Array] Article tags
83
- #
84
- # Returns [Conversation]
85
- def self.create(client, article_id, title, link, tags = nil)
86
- meta = collectionMeta(client, article_id, title, link, tags)
87
- response = client.quill.post "/api/v3.0/site/#{client.options[:site_id]}/collection/create", {:collectionMeta => meta, :articleId => article_id}.to_json
88
- if response.success?
89
- body = JSON.parse(response.body)
90
- Conversation.new(body["data"]["collectionId"], article_id)
91
- else
92
- error = JSON.parse(response.body)
93
- raise APIException.new(error["msg"])
94
- end
95
- end
96
-
97
- # Internal: Generate a signed collectionMeta
98
- #
99
- # client - [Client] identifying the site to create the collection on
100
- # title - [String] Article title
101
- # link - [String] Article link
102
- # tags - [String, Array] Article tags
103
- #
104
- # Returns [String] signed token
105
- def self.collectionMeta(client, article_id, title, link, tags)
106
- tag_str = case tags
107
- when Array
108
- tags.join ","
109
- when String
110
- tags
111
- when nil
112
- nil
113
- else
114
- raise "Invalid value given for tags: must be Array, String, or nil"
115
- end
116
-
117
- begin
118
- URI.parse(link)
119
- rescue URI::InvalidURIError => e
120
- raise "Invalid value for link: #{e.message}"
121
- end
122
-
123
- metadata = {
124
- :title => title,
125
- :url => link,
126
- :articleId => article_id,
127
- :tags => tag_str || "",
128
- }
129
-
130
- JWT.encode(metadata, client.site_key)
131
- end
132
- end
133
- end