use person object with attributes to generate hcard

This commit is contained in:
Benjamin Neff 2015-07-02 02:03:32 +02:00
parent 6e6171fc93
commit 99d5ffdc04
7 changed files with 96 additions and 158 deletions

View file

@ -14,7 +14,7 @@ module DiasporaFederation
return render nothing: true, status: 404 if person.nil? return render nothing: true, status: 404 if person.nil?
logger.info "hcard profile request for: #{person.diaspora_handle}" logger.info "hcard profile request for: #{person.diaspora_handle}"
render html: WebFinger::HCard.from_profile(person.hcard_profile_hash).to_html.html_safe render html: WebFinger::HCard.from_person(person).to_html.html_safe
end end
end end
end end

View file

@ -84,18 +84,11 @@ module DiasporaFederation
def validate_config def validate_config
configuration_error "missing server_uri" unless @server_uri.respond_to? :host configuration_error "missing server_uri" unless @server_uri.respond_to? :host
validate_class(@person_class, "person_class", %i( validate_class(@person_class, "person_class", %i(
find_local_by_diaspora_handle find_local_by_diaspora_handle find_local_by_guid
find_local_by_guid diaspora_handle nickname guid public_key searchable
diaspora_handle alias_url hcard_url seed_url profile_url atom_url salmon_url
alias_url photo_large_url photo_medium_url photo_small_url
hcard_url full_name first_name last_name
seed_url
profile_url
atom_url
salmon_url
guid
public_key
hcard_profile_hash
)) ))
logger.info "successfully configured the federation engine" logger.info "successfully configured the federation engine"
end end

View file

@ -10,8 +10,7 @@ module DiasporaFederation
# #
# * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml} # * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml}
# * if the parsed XML from {WebFinger.from_xml} is incomplete # * if the parsed XML from {WebFinger.from_xml} is incomplete
# * if the params passed to {HCard.from_profile} or {HCard.from_html} # * if the html passed to {HCard.from_html} in some way is malformed, invalid or incomplete.
# are in some way malformed, invalid or incomplete.
class InvalidData < RuntimeError class InvalidData < RuntimeError
end end
end end

View file

@ -14,21 +14,21 @@ module DiasporaFederation
# correctly nested according to the hCard standard and class names are # correctly nested according to the hCard standard and class names are
# partially wrong. Also, apart from that, it's just ugly. # partially wrong. Also, apart from that, it's just ugly.
# #
# @example Creating a hCard document from account data # @example Creating a hCard document from a person object
# hc = HCard.from_profile({ # html_string = HCard.from_person(person).to_html
# guid: "0123456789abcdef", #
# nickname: "user", # The person object should have the following attributes (with examples)
# full_name: "User Name", # guid: "0123456789abcdef",
# url: "https://server.example/", # nickname: "user",
# photo_large_url: "https://server.example/uploads/l.jpg", # full_name: "User Name",
# photo_medium_url: "https://server.example/uploads/m.jpg", # seed_url: "https://server.example/",
# photo_small_url: "https://server.example/uploads/s.jpg", # photo_large_url: "https://server.example/uploads/l.jpg",
# pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----", # photo_medium_url: "https://server.example/uploads/m.jpg",
# searchable: true, # photo_small_url: "https://server.example/uploads/s.jpg",
# first_name: "User", # public_key: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
# last_name: "Name" # searchable: true,
# }) # first_name: "User",
# html_string = hc.to_html # last_name: "Name"
# #
# @example Create a HCard instance from an hCard document # @example Create a HCard instance from an hCard document
# hc = HCard.from_html(html_string) # hc = HCard.from_html(html_string)
@ -68,7 +68,7 @@ module DiasporaFederation
# DER-encoded PKCS#1 key beginning with the text # DER-encoded PKCS#1 key beginning with the text
# "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----". # "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
# @return [String] public key # @return [String] public key
attr_reader :pubkey attr_reader :public_key
# @return [String] url to the big avatar (300x300) # @return [String] url to the big avatar (300x300)
attr_reader :photo_large_url attr_reader :photo_large_url
@ -128,7 +128,7 @@ module DiasporaFederation
add_simple_property(content, :searchable, "searchable", @searchable) add_simple_property(content, :searchable, "searchable", @searchable)
add_property(content, :key) do |html| add_property(content, :key) do |html|
html.pre(@pubkey.to_s, class: "key") html.pre(@public_key.to_s, class: "key")
end end
# TODO: remove me! ################### # TODO: remove me! ###################
@ -145,28 +145,28 @@ module DiasporaFederation
builder.doc.to_xhtml(indent: 2, indent_text: " ") builder.doc.to_xhtml(indent: 2, indent_text: " ")
end end
# Creates a new HCard instance from the given Hash containing profile data # Creates a new HCard instance from the given person
# @param [Hash] data account data # @param [Person] person the person object
# @return [HCard] HCard instance # @return [HCard] HCard instance
# @raise [InvalidData] if the account data Hash is invalid or incomplete # @raise [InvalidData] if the account data Hash is invalid or incomplete
def self.from_profile(data) def self.from_person(person)
raise InvalidData unless account_data_complete?(data) raise ArgumentError, "person is nil" if person.nil?
hc = allocate hc = allocate
hc.instance_eval { hc.instance_eval {
@guid = data[:guid] @guid = person.guid
@nickname = data[:nickname] @nickname = person.nickname
@full_name = data[:full_name] @full_name = person.full_name
@url = data[:url] @url = person.seed_url
@photo_large_url = data[:photo_large_url] @photo_large_url = person.photo_large_url
@photo_medium_url = data[:photo_medium_url] @photo_medium_url = person.photo_medium_url
@photo_small_url = data[:photo_small_url] @photo_small_url = person.photo_small_url
@pubkey = data[:pubkey] @public_key = person.public_key
@searchable = data[:searchable] @searchable = person.searchable
# TODO: remove me! ################### # TODO: remove me! ###################
@first_name = data[:first_name] @first_name = person.first_name
@last_name = data[:last_name] @last_name = person.last_name
####################################### #######################################
} }
hc hc
@ -188,7 +188,7 @@ module DiasporaFederation
@photo_large_url = photo_from_doc(doc, :photo) @photo_large_url = photo_from_doc(doc, :photo)
@photo_medium_url = photo_from_doc(doc, :photo_medium) @photo_medium_url = photo_from_doc(doc, :photo_medium)
@photo_small_url = photo_from_doc(doc, :photo_small) @photo_small_url = photo_from_doc(doc, :photo_small)
@pubkey = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil? @public_key = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil?
@searchable = content_from_doc(doc, :searchable) == "true" @searchable = content_from_doc(doc, :searchable) == "true"
# TODO: change me! ################### # TODO: change me! ###################
@ -271,19 +271,6 @@ module DiasporaFederation
end end
end end
# Checks the given account data Hash for correct type and completeness.
# @param [Hash] data account data
# @return [Boolean] validation result
def self.account_data_complete?(data)
data.instance_of?(Hash) &&
%i(
guid nickname full_name url
photo_large_url photo_medium_url photo_small_url
pubkey searchable first_name last_name
).all? {|k| data.key? k }
end
private_class_method :account_data_complete?
# Make sure some of the most important elements are present in the parsed # Make sure some of the most important elements are present in the parsed
# HTML document. # HTML document.
# @param [LibXML::XML::Document] doc HTML document # @param [LibXML::XML::Document] doc HTML document

View file

@ -25,7 +25,7 @@ module DiasporaFederation
end end
it "calls WebFinger::HCard.from_profile" do it "calls WebFinger::HCard.from_profile" do
expect(WebFinger::HCard).to receive(:from_profile).with(alice.hcard_profile_hash).and_call_original expect(WebFinger::HCard).to receive(:from_person).with(alice).and_call_original
get :hcard, "guid" => alice.guid get :hcard, "guid" => alice.guid
end end
end end

View file

@ -1,16 +1,6 @@
module DiasporaFederation module DiasporaFederation
describe WebFinger::HCard do describe WebFinger::HCard do
let(:guid) { "abcdef0123456789" } let(:person) { FactoryGirl.create(:person) }
let(:nickname) { "user" }
let(:first_name) { "Test" }
let(:last_name) { "Testington" }
let(:name) { "#{first_name} #{last_name}" }
let(:url) { "https://pod.example.tld/users/me" }
let(:photo_url) { "https://pod.example.tld/uploads/f.jpg" }
let(:photo_url_m) { "https://pod.example.tld/uploads/m.jpg" }
let(:photo_url_s) { "https://pod.example.tld/uploads/s.jpg" }
let(:key) { "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----" }
let(:searchable) { true }
let(:html) { let(:html) {
<<-HTML <<-HTML
@ -19,77 +9,77 @@ module DiasporaFederation
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>#{name}</title> <title>#{person.full_name}</title>
</head> </head>
<body> <body>
<div id="content"> <div id="content">
<h1>#{name}</h1> <h1>#{person.full_name}</h1>
<div id="content_inner" class="entity_profile vcard author"> <div id="content_inner" class="entity_profile vcard author">
<h2>User profile</h2> <h2>User profile</h2>
<dl class="entity_uid"> <dl class="entity_uid">
<dt>Uid</dt> <dt>Uid</dt>
<dd> <dd>
<span class="uid">#{guid}</span> <span class="uid">#{person.guid}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_nickname"> <dl class="entity_nickname">
<dt>Nickname</dt> <dt>Nickname</dt>
<dd> <dd>
<span class="nickname">#{nickname}</span> <span class="nickname">#{person.nickname}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_full_name"> <dl class="entity_full_name">
<dt>Full_name</dt> <dt>Full_name</dt>
<dd> <dd>
<span class="fn">#{name}</span> <span class="fn">#{person.full_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_searchable"> <dl class="entity_searchable">
<dt>Searchable</dt> <dt>Searchable</dt>
<dd> <dd>
<span class="searchable">#{searchable}</span> <span class="searchable">#{person.searchable}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_key"> <dl class="entity_key">
<dt>Key</dt> <dt>Key</dt>
<dd> <dd>
<pre class="key">#{key}</pre> <pre class="key">#{person.public_key}</pre>
</dd> </dd>
</dl> </dl>
<dl class="entity_first_name"> <dl class="entity_first_name">
<dt>First_name</dt> <dt>First_name</dt>
<dd> <dd>
<span class="given_name">#{first_name}</span> <span class="given_name">#{person.first_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_family_name"> <dl class="entity_family_name">
<dt>Family_name</dt> <dt>Family_name</dt>
<dd> <dd>
<span class="family_name">#{last_name}</span> <span class="family_name">#{person.last_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_url"> <dl class="entity_url">
<dt>Url</dt> <dt>Url</dt>
<dd> <dd>
<a id="pod_location" class="url" rel="me" href="#{url}">#{url}</a> <a id="pod_location" class="url" rel="me" href="#{person.seed_url}">#{person.seed_url}</a>
</dd> </dd>
</dl> </dl>
<dl class="entity_photo"> <dl class="entity_photo">
<dt>Photo</dt> <dt>Photo</dt>
<dd> <dd>
<img class="photo avatar" width="300" height="300" src="#{photo_url}" /> <img class="photo avatar" width="300" height="300" src="#{person.photo_large_url}" />
</dd> </dd>
</dl> </dl>
<dl class="entity_photo_medium"> <dl class="entity_photo_medium">
<dt>Photo_medium</dt> <dt>Photo_medium</dt>
<dd> <dd>
<img class="photo avatar" width="100" height="100" src="#{photo_url_m}" /> <img class="photo avatar" width="100" height="100" src="#{person.photo_medium_url}" />
</dd> </dd>
</dl> </dl>
<dl class="entity_photo_small"> <dl class="entity_photo_small">
<dt>Photo_small</dt> <dt>Photo_small</dt>
<dd> <dd>
<img class="photo avatar" width="50" height="50" src="#{photo_url_s}" /> <img class="photo avatar" width="50" height="50" src="#{person.photo_small_url}" />
</dd> </dd>
</dl> </dl>
</div> </div>
@ -105,60 +95,35 @@ HTML
context "generation" do context "generation" do
it "creates an instance from a data hash" do it "creates an instance from a data hash" do
hcard = WebFinger::HCard.from_profile( hcard = WebFinger::HCard.from_person(person)
guid: guid,
nickname: nickname,
full_name: name,
url: url,
photo_large_url: photo_url,
photo_medium_url: photo_url_m,
photo_small_url: photo_url_s,
pubkey: key,
searchable: searchable,
first_name: first_name,
last_name: last_name
)
expect(hcard.to_html).to eq(html) expect(hcard.to_html).to eq(html)
end end
it "fails if some params are missing" do
expect {
WebFinger::HCard.from_profile(
guid: guid,
nickname: nickname
)
}.to raise_error WebFinger::InvalidData
end
it "fails if nothing was given" do
expect { WebFinger::HCard.from_profile({}) }.to raise_error WebFinger::InvalidData
end
it "fails if nil was given" do it "fails if nil was given" do
expect { WebFinger::HCard.from_profile(nil) }.to raise_error WebFinger::InvalidData expect { WebFinger::HCard.from_person(nil) }.to raise_error ArgumentError
end end
end end
context "parsing" do context "parsing" do
it "reads its own output" do it "reads its own output" do
hcard = WebFinger::HCard.from_html(html) hcard = WebFinger::HCard.from_html(html)
expect(hcard.guid).to eq(guid) expect(hcard.guid).to eq(person.guid)
expect(hcard.nickname).to eq(nickname) expect(hcard.nickname).to eq(person.nickname)
expect(hcard.full_name).to eq(name) expect(hcard.full_name).to eq(person.full_name)
expect(hcard.url).to eq(url) expect(hcard.url).to eq(person.seed_url)
expect(hcard.photo_large_url).to eq(photo_url) expect(hcard.photo_large_url).to eq(person.photo_large_url)
expect(hcard.photo_medium_url).to eq(photo_url_m) expect(hcard.photo_medium_url).to eq(person.photo_medium_url)
expect(hcard.photo_small_url).to eq(photo_url_s) expect(hcard.photo_small_url).to eq(person.photo_small_url)
expect(hcard.pubkey).to eq(key) expect(hcard.public_key).to eq(person.public_key)
expect(hcard.searchable).to eq(searchable) expect(hcard.searchable).to eq(person.searchable)
expect(hcard.first_name).to eq(first_name) expect(hcard.first_name).to eq(person.first_name)
expect(hcard.last_name).to eq(last_name) expect(hcard.last_name).to eq(person.last_name)
end end
it "searchable is false, if it is empty in html" do it "searchable is false, if it is empty in html" do
changed_html = html.sub( changed_html = html.sub(
"class=\"searchable\">#{searchable}<", "class=\"searchable\">#{person.searchable}<",
"class=\"searchable\"><" "class=\"searchable\"><"
) )
@ -170,62 +135,62 @@ HTML
it "reads old-style HTML" do it "reads old-style HTML" do
historic_html = <<-HTML historic_html = <<-HTML
<div id="content"> <div id="content">
<h1>#{name}</h1> <h1>#{person.full_name}</h1>
<div id="content_inner"> <div id="content_inner">
<div class="entity_profile vcard author" id="i"> <div class="entity_profile vcard author" id="i">
<h2>User profile</h2> <h2>User profile</h2>
<dl class="entity_nickname"> <dl class="entity_nickname">
<dt>Nickname</dt> <dt>Nickname</dt>
<dd> <dd>
<a class="nickname url uid" href="#{url}" rel="me">#{name}</a> <a class="nickname url uid" href="#{person.seed_url}" rel="me">#{person.full_name}</a>
</dd> </dd>
</dl> </dl>
<dl class="entity_given_name"> <dl class="entity_given_name">
<dt>First name</dt> <dt>First name</dt>
<dd> <dd>
<span class="given_name">#{first_name}</span> <span class="given_name">#{person.first_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_family_name"> <dl class="entity_family_name">
<dt>Family name</dt> <dt>Family name</dt>
<dd> <dd>
<span class="family_name">#{last_name}</span> <span class="family_name">#{person.last_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_fn"> <dl class="entity_fn">
<dt>Full name</dt> <dt>Full name</dt>
<dd> <dd>
<span class="fn">#{name}</span> <span class="fn">#{person.full_name}</span>
</dd> </dd>
</dl> </dl>
<dl class="entity_url"> <dl class="entity_url">
<dt>URL</dt> <dt>URL</dt>
<dd> <dd>
<a class="url" href="#{url}" id="pod_location" rel="me">#{url}</a> <a class="url" href="#{person.seed_url}" id="pod_location" rel="me">#{person.seed_url}</a>
</dd> </dd>
</dl> </dl>
<dl class="entity_photo"> <dl class="entity_photo">
<dt>Photo</dt> <dt>Photo</dt>
<dd> <dd>
<img class="photo avatar" height="300px" src="#{photo_url}" width="300px"> <img class="photo avatar" height="300px" src="#{person.photo_large_url}" width="300px">
</dd> </dd>
</dl> </dl>
<dl class="entity_photo_medium"> <dl class="entity_photo_medium">
<dt>Photo</dt> <dt>Photo</dt>
<dd> <dd>
<img class="photo avatar" height="100px" src="#{photo_url_m}" width="100px"> <img class="photo avatar" height="100px" src="#{person.photo_medium_url}" width="100px">
</dd> </dd>
</dl> </dl>
<dl class="entity_photo_small"> <dl class="entity_photo_small">
<dt>Photo</dt> <dt>Photo</dt>
<dd> <dd>
<img class="photo avatar" height="50px" src="#{photo_url_s}" width="50px"> <img class="photo avatar" height="50px" src="#{person.photo_small_url}" width="50px">
</dd> </dd>
</dl> </dl>
<dl class="entity_searchable"> <dl class="entity_searchable">
<dt>Searchable</dt> <dt>Searchable</dt>
<dd> <dd>
<span class="searchable">#{searchable}</span> <span class="searchable">#{person.searchable}</span>
</dd> </dd>
</dl> </dl>
</div> </div>
@ -234,20 +199,20 @@ HTML
HTML HTML
hcard = WebFinger::HCard.from_html(historic_html) hcard = WebFinger::HCard.from_html(historic_html)
expect(hcard.url).to eq(url) expect(hcard.url).to eq(person.seed_url)
expect(hcard.photo_large_url).to eq(photo_url) expect(hcard.photo_large_url).to eq(person.photo_large_url)
expect(hcard.photo_medium_url).to eq(photo_url_m) expect(hcard.photo_medium_url).to eq(person.photo_medium_url)
expect(hcard.photo_small_url).to eq(photo_url_s) expect(hcard.photo_small_url).to eq(person.photo_small_url)
expect(hcard.searchable).to eq(searchable) expect(hcard.searchable).to eq(person.searchable)
expect(hcard.first_name).to eq(first_name) expect(hcard.first_name).to eq(person.first_name)
expect(hcard.last_name).to eq(last_name) expect(hcard.last_name).to eq(person.last_name)
end end
it "fails if the document is incomplete" do it "fails if the document is incomplete" do
invalid_html = <<-HTML invalid_html = <<-HTML
<div id="content"> <div id="content">
<span class="fn">#{name}</span> <span class="fn">#{person.full_name}</span>
</div> </div>
HTML HTML
expect { WebFinger::HCard.from_html(invalid_html) }.to raise_error WebFinger::InvalidData expect { WebFinger::HCard.from_html(invalid_html) }.to raise_error WebFinger::InvalidData

View file

@ -10,21 +10,15 @@ class Person < ActiveRecord::Base
alias_attribute :seed_url, :url alias_attribute :seed_url, :url
alias_attribute :public_key, :serialized_public_key alias_attribute :public_key, :serialized_public_key
def hcard_profile_hash def nickname; diaspora_handle.split("@")[0] end
{ def photo_large_url; "#{url}assets/user/default.png" end
guid: guid, def photo_medium_url; "#{url}assets/user/default.png" end
nickname: diaspora_handle.split("@")[0], def photo_small_url; "#{url}assets/user/default.png" end
full_name: "Dummy User",
url: url, def searchable; true end
photo_large_url: "#{url}assets/user/default.png", def full_name; "Dummy User" end
photo_medium_url: "#{url}assets/user/default.png", def first_name; "Dummy" end
photo_small_url: "#{url}assets/user/default.png", def last_name; "User" end
pubkey: serialized_public_key,
searchable: true,
first_name: "Dummy",
last_name: "User"
}
end
def self.find_local_by_diaspora_handle(identifier) def self.find_local_by_diaspora_handle(identifier)
# no remote? and closed_account? check ... this class is only for testing # no remote? and closed_account? check ... this class is only for testing