diff --git a/lib/linked_in/api/update_methods.rb b/lib/linked_in/api/update_methods.rb
index 0a70273e..9b41b718 100644
--- a/lib/linked_in/api/update_methods.rb
+++ b/lib/linked_in/api/update_methods.rb
@@ -9,10 +9,26 @@ def add_share(share)
post(path, defaults.merge(share).to_json, "Content-Type" => "application/json")
end
+ # Creates a company share
+ #
+ # Returns an HttpResponse object with a body containing update_key and update_url
+ #
+ # @param company_id [String] The company id
+ # @param share [Hash] Share data
+ #
+ # @option share [String] comment Post must contain comment and/or (content/title and content/submitted-url). Max length is 700 characters.
+ # @option share [Hash] content Content Hash
+ # @option content [String] title Post must contain comment and/or (content/title and content/submitted-url). Max length is 200 characters.
+ # @option content [String] submitted-url Post must contain comment and/or (content/title and content/submitted-url).
+ # @option content [String] submitted-image-url Invalid without (content/title and content/submitted-url).
+ # @option content [String] description Max length of 256 characters.
+ # @option share [Hash] targets Hash containing target_code => ['target_value', ...]
+ #
+ # @return [HttpResponse] Response
def add_company_share(company_id, share)
path = "/companies/#{company_id}/shares"
defaults = {:visibility => {:code => "anyone"}}
- post(path, defaults.merge(share).to_json, "Content-Type" => "application/json")
+ hashify post(path, render(:company_share, defaults.merge(share)), 'x-li-format' => 'xml', "Content-Type" => "application/xml")
end
def follow_company(company_id)
@@ -74,6 +90,13 @@ def post_group_discussion(group_id, discussion)
post(path, discussion.to_json, "Content-Type" => "application/json")
end
+ private
+
+ def hashify response
+ response.body = Mash.from_response response
+ response
+ end
+
end
end
diff --git a/lib/linked_in/client.rb b/lib/linked_in/client.rb
index fdf99dca..ea01995d 100644
--- a/lib/linked_in/client.rb
+++ b/lib/linked_in/client.rb
@@ -8,6 +8,7 @@ class Client
include Api::QueryMethods
include Api::UpdateMethods
include Search
+ include Template
attr_reader :consumer_token, :consumer_secret, :consumer_options
diff --git a/lib/linked_in/helpers/request.rb b/lib/linked_in/helpers/request.rb
index 94f66592..c7b3318d 100644
--- a/lib/linked_in/helpers/request.rb
+++ b/lib/linked_in/helpers/request.rb
@@ -42,13 +42,13 @@ def raise_errors(response)
# in the HTTP answer (thankfully).
case response.code.to_i
when 401
- data = Mash.from_json(response.body)
+ data = Mash.from_response(response)
raise LinkedIn::Errors::UnauthorizedError.new(data), "(#{data.status}): #{data.message}"
when 400
- data = Mash.from_json(response.body)
+ data = Mash.from_response(response)
raise LinkedIn::Errors::GeneralError.new(data), "(#{data.status}): #{data.message}"
when 403
- data = Mash.from_json(response.body)
+ data = Mash.from_response(response)
raise LinkedIn::Errors::AccessDeniedError.new(data), "(#{data.status}): #{data.message}"
when 404
raise LinkedIn::Errors::NotFoundError, "(#{response.code}): #{response.message}"
diff --git a/lib/linked_in/mash.rb b/lib/linked_in/mash.rb
index 8079fff8..fc8e969e 100644
--- a/lib/linked_in/mash.rb
+++ b/lib/linked_in/mash.rb
@@ -1,5 +1,6 @@
require 'hashie'
require 'multi_json'
+require 'multi_xml'
module LinkedIn
class Mash < ::Hashie::Mash
@@ -10,6 +11,22 @@ def self.from_json(json_string)
new(result_hash)
end
+ # a simple helper to convert an xml string to a Mash
+ def self.from_xml(xml_string)
+ result_hash = ::MultiXml.parse(xml_string)
+
+ # Drop off the root element
+ new(result_hash[result_hash.keys.first])
+ end
+
+ def self.from_response(response)
+ if response['x-li-format'] == 'xml' or /\bxml\b/.match response['Content-Type']
+ from_xml(response.body)
+ else
+ from_json(response.body)
+ end
+ end
+
# returns a Date if we have year, month and day, and no conflicting key
def to_date
if !self.has_key?('to_date') && contains_date_fields?
diff --git a/lib/linked_in/template.rb b/lib/linked_in/template.rb
new file mode 100644
index 00000000..14c38adc
--- /dev/null
+++ b/lib/linked_in/template.rb
@@ -0,0 +1,37 @@
+require 'erb'
+require 'ostruct'
+require 'hashie'
+
+module LinkedIn
+ class TemplateBinding < ::Hashie::Mash
+ include ERB::Util
+ end
+
+ module Template
+
+ class << self
+ cache = {}
+ mutex = Mutex.new
+
+ define_method :load_template do |template|
+ return cache[template] if cache[template]
+ mutex.synchronize do
+ return cache[template] if cache[template]
+
+ file = File.join(LinkedIn.templates, "#{template.to_s}.xml.erb")
+ io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
+ erb = ERB.new(io)
+ erb.filename = file
+
+ cache[template] = erb
+ end
+ end
+ end
+
+ def render template, data
+ template = Template.load_template template
+ namespace = TemplateBinding.new data
+ template.result namespace.instance_eval { binding }
+ end
+ end
+end
diff --git a/lib/linked_in/templates/company_share.xml.erb b/lib/linked_in/templates/company_share.xml.erb
new file mode 100644
index 00000000..bb2a2f1a
--- /dev/null
+++ b/lib/linked_in/templates/company_share.xml.erb
@@ -0,0 +1,33 @@
+
+
+
+ <%=h visibility.code%>
+
+ <% if comment %>
+ <%= h comment %>
+ <% end %>
+ <% if content %>
+
+ <% if content["title"] %><%= h content["title"] %><% end %>
+ <%= h content["submitted-url"] %>
+ <% if content["description"] %><%= h content["description"] %><% end %>
+ <% if content["submitted-image-url"] %><%= h content["submitted-image-url"] %><% end %>
+
+ <% end %>
+ <% if targets %>
+
+
+ <% targets.each do |key, values| %>
+
+ <%= h key %>
+
+ <% values.each do |value| %>
+ <%= h value %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <% end %>
+
diff --git a/lib/linkedin.rb b/lib/linkedin.rb
index cd3524bf..1e7588f6 100644
--- a/lib/linkedin.rb
+++ b/lib/linkedin.rb
@@ -3,7 +3,7 @@
module LinkedIn
class << self
- attr_accessor :token, :secret, :default_profile_fields
+ attr_accessor :token, :secret, :default_profile_fields, :templates
# config/initializers/linkedin.rb (for instance)
#
@@ -22,6 +22,8 @@ def configure
end
end
+ @templates = File.join(File.expand_path(File.dirname(__FILE__)), 'linked_in', 'templates')
+
autoload :Api, "linked_in/api"
autoload :Client, "linked_in/client"
autoload :Mash, "linked_in/mash"
@@ -29,4 +31,5 @@ def configure
autoload :Helpers, "linked_in/helpers"
autoload :Search, "linked_in/search"
autoload :Version, "linked_in/version"
+ autoload :Template, "linked_in/template"
end
diff --git a/linkedin.gemspec b/linkedin.gemspec
index 91f69fd6..1aca42c1 100644
--- a/linkedin.gemspec
+++ b/linkedin.gemspec
@@ -4,6 +4,7 @@ require File.expand_path('../lib/linked_in/version', __FILE__)
Gem::Specification.new do |gem|
gem.add_dependency 'hashie', ['>= 1.2', '< 2.1']
gem.add_dependency 'multi_json', '~> 1.0'
+ gem.add_dependency 'multi_xml'
gem.add_dependency 'oauth', '~> 0.4'
# gem.add_development_dependency 'json', '~> 1.6'
gem.add_development_dependency 'rake', '~> 10'
diff --git a/spec/cases/api_spec.rb b/spec/cases/api_spec.rb
index e300859c..824d831f 100644
--- a/spec/cases/api_spec.rb
+++ b/spec/cases/api_spec.rb
@@ -68,13 +68,6 @@
response.code.should == "201"
end
- it "should be able to share a new company status" do
- stub_request(:post, "https://api.linkedin.com/v1/companies/123456/shares").to_return(:body => "", :status => 201)
- response = client.add_company_share("123456", { :comment => "Testing, 1, 2, 3" })
- response.body.should == nil
- response.code.should == "201"
- end
-
it "returns the shares for a person" do
stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates?type=SHAR&scope=self&after=1234&count=35").to_return(
:body => "{}")
@@ -192,6 +185,44 @@
response.code.should == "201"
end
+ it "should be able to share a new company status" do
+ stub_request(:post, "https://api.linkedin.com/v1/companies/2414183/shares").with(:headers => { 'Content-Type' => 'application/xml' }).to_return(:headers => {'Content-Type' => 'application/xml'}, :body => 'UNIU-c2414183-5811244423991812096-SHAREhttp://www.linkedin.com/company/2414183/comments?topic=5811244423991812096&type=U&scope=2414183&stype=C&a=FlWW', :status => 201)
+ response = client.add_company_share("2414183", { :comment => "Testing, 1, 2, 3" })
+ response.body.update_key.should == 'UNIU-c2414183-5811244423991812096-SHARE'
+ response.body.update_url.should == 'http://www.linkedin.com/company/2414183/comments?topic=5811244423991812096&type=U&scope=2414183&stype=C&a=FlWW'
+ response.code.should == "201"
+ end
+
+ it "should be able to handle an error" do
+ stub_request(:post, "https://api.linkedin.com/v1/companies/2414183/shares").with(:headers => { 'Content-Type' => 'application/xml' }).to_return(:headers => {'Content-Type' => 'application/xml'}, :body => ' 403 1386620304843 ZIBEJ5MXJ2 0 Member 172914333 cannot post updates on behalf of company 2414183 due to too few targeted followers ', :status => 403)
+
+ expect {
+ client.add_company_share("2414183", { :comment => "Testing, 1, 2, 3" })
+ }.to raise_error(LinkedIn::Errors::AccessDeniedError){ |error|
+ error.data.message.should_not be_nil
+ }
+ end
+
+ it "should be able to target a new company status" do
+ stub_request(:post, "https://api.linkedin.com/v1/companies/2414183/shares").with(:headers => { 'Content-Type' => 'application/xml' }).to_return(:headers => {'Content-Type' => 'application/xml'}, :body => 'UNIU-c2414183-5811244423991812096-SHAREhttp://www.linkedin.com/company/2414183/comments?topic=5811244423991812096&type=U&scope=2414183&stype=C&a=FlWW', :status => 201)
+ response = client.add_company_share("2414183", {
+ :comment => "Testing, 1, 2, 3",
+ :content => {
+ :"submitted-url" => "http://www.example.com/content.html",
+ :title => "Test Share with Content",
+ :description => "content description",
+ :"submitted-image-url" => "http://www.example.com/image.jpg"
+ },
+ :targets => {
+ :geos => ['as', 'eu'],
+ :jobFunc => ['acct', 'bd']
+ }
+ })
+ response.body.update_key.should == 'UNIU-c2414183-5811244423991812096-SHARE'
+ response.body.update_url.should == 'http://www.linkedin.com/company/2414183/comments?topic=5811244423991812096&type=U&scope=2414183&stype=C&a=FlWW'
+ response.code.should == "201"
+ end
+
end
context "Job API" do