class DiasporaFederation::Discovery::HCard

This class provides the means of generating and parsing account data to and from the hCard format. hCard is based on +RFC 2426+ (vCard) which got superseded by +RFC 6350+. There is a draft for a new h-card format specification, that makes use of the new vCard standard.

@note The current implementation contains a huge amount of legacy elements

and classes, that should be removed and cleaned up in later iterations.

@todo This needs some radical restructuring. The generated HTML is not

correctly nested according to the hCard standard and class names are
partially wrong. Also, apart from that, it's just ugly.

@example Creating a hCard document from a person hash

hc = HCard.new(
  guid:             "0123456789abcdef",
  nickname:         "user",
  full_name:        "User Name",
  seed_url:         "https://server.example/",
  photo_large_url:  "https://server.example/uploads/l.jpg",
  photo_medium_url: "https://server.example/uploads/m.jpg",
  photo_small_url:  "https://server.example/uploads/s.jpg",
  public_key:       "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
  searchable:       true,
  first_name:       "User",
  last_name:        "Name"
)
html_string = hc.to_html

@example Create a HCard instance from an hCard document

hc = HCard.from_html(html_string)
...
full_name = hc.full_name
...

@see microformats.org/wiki/hCard “hCard 1.0” @see microformats.org/wiki/h-card “h-card” (draft) @see www.ietf.org/rfc/rfc2426.txt “vCard MIME Directory Profile” (obsolete) @see www.ietf.org/rfc/rfc6350.txt “vCard Format Specification”

Constants

SELECTORS

CSS selectors for finding all the hCard fields

Public Class Methods

from_html(html_string) click to toggle source

Creates a new HCard instance from the given HTML string @param html_string [String] HTML string @return [HCard] HCard instance @raise [InvalidData] if the HTML string is invalid or incomplete

# File lib/diaspora_federation/discovery/h_card.rb, line 149
def self.from_html(html_string)
  doc = parse_html_and_validate(html_string)

  new(
    guid:             content_from_doc(doc, :uid),
    nickname:         content_from_doc(doc, :nickname),
    full_name:        content_from_doc(doc, :fn),
    photo_large_url:  photo_from_doc(doc, :photo),
    photo_medium_url: photo_from_doc(doc, :photo_medium),
    photo_small_url:  photo_from_doc(doc, :photo_small),
    searchable:       (content_from_doc(doc, :searchable) == "true"),
    public_key:       content_from_doc(doc, :key),

    # TODO: remove first_name and last_name!
    first_name:       content_from_doc(doc, :given_name),
    last_name:        content_from_doc(doc, :family_name)
  )
end

Private Class Methods

content_from_doc(doc, content_selector) click to toggle source
# File lib/diaspora_federation/discovery/h_card.rb, line 261
                     def self.content_from_doc(doc, content_selector)
  element = element_from_doc(doc, content_selector)
  element.content if element
end
element_from_doc(doc, selector) click to toggle source
# File lib/diaspora_federation/discovery/h_card.rb, line 257
                     def self.element_from_doc(doc, selector)
  doc.at_css(SELECTORS[selector])
end
html_document_complete?(doc) click to toggle source

Make sure some of the most important elements are present in the parsed HTML document. @param [LibXML::XML::Document] doc HTML document @return [Boolean] validation result

# File lib/diaspora_federation/discovery/h_card.rb, line 244
                     def self.html_document_complete?(doc)
  !(doc.at_css(SELECTORS[:fn]).nil? || doc.at_css(SELECTORS[:photo]).nil?)
end
parse_html_and_validate(html_string) click to toggle source
# File lib/diaspora_federation/discovery/h_card.rb, line 248
                     def self.parse_html_and_validate(html_string)
  raise ArgumentError, "hcard html is not a string" unless html_string.instance_of?(String)

  doc = Nokogiri::HTML::Document.parse(html_string)
  raise InvalidData, "hcard html incomplete" unless html_document_complete?(doc)

  doc
end
photo_from_doc(doc, photo_selector) click to toggle source
# File lib/diaspora_federation/discovery/h_card.rb, line 266
                     def self.photo_from_doc(doc, photo_selector)
  element_from_doc(doc, photo_selector)["src"]
end

Public Instance Methods

to_html() click to toggle source

Create the HTML string from the current HCard instance @return [String] HTML string

# File lib/diaspora_federation/discovery/h_card.rb, line 121
def to_html
  builder = create_builder

  content = builder.doc.at_css("#content_inner")

  add_simple_property(content, :uid, "uid", @guid)
  add_simple_property(content, :nickname, "nickname", @nickname)
  add_simple_property(content, :full_name, "fn", @full_name)
  add_simple_property(content, :searchable, "searchable", @searchable)

  add_property(content, :key) do |html|
    html.pre(@public_key.to_s, class: "key")
  end

  # TODO: remove me!  ###################
  add_simple_property(content, :first_name, "given_name", @first_name)
  add_simple_property(content, :family_name, "family_name", @last_name)
  #######################################

  add_photos(content)

  builder.doc.to_xhtml(indent: 2, indent_text: " ")
end

Private Instance Methods

add_photos(container) click to toggle source

Calls {HCard#add_property} to add the photos @param container [Nokogiri::XML::Element] parent element @see HCard#add_property

# File lib/diaspora_federation/discovery/h_card.rb, line 226
def add_photos(container)
  add_property(container, :photo) do |html|
    html.img(class: "photo avatar", width: "300", height: "300", src: @photo_large_url.to_s)
  end

  add_property(container, :photo_medium) do |html|
    html.img(class: "photo avatar", width: "100", height: "100", src: @photo_medium_url.to_s)
  end

  add_property(container, :photo_small) do |html|
    html.img(class: "photo avatar", width: "50", height: "50", src: @photo_small_url.to_s)
  end
end
add_property(container, name) { |html| ... } click to toggle source

Add a property to the hCard document. The element will be added to the given container element and a “definition list” structure will be created around it. A Nokogiri::HTML::Builder instance will be passed to the given block, which should be used to add the element(s) containing the property data.

@param container [Nokogiri::XML::Element] parent element for added property HTML @param name [Symbol] property name @yield [Nokogiri::HTML::Builder] html builder

# File lib/diaspora_federation/discovery/h_card.rb, line 200
def add_property(container, name)
  Nokogiri::HTML::Builder.with(container) do |html|
    html.dl(class: "entity_#{name}") {
      html.dt(name.to_s.capitalize)
      html.dd {
        yield html
      }
    }
  end
end
add_simple_property(container, name, class_name, value) click to toggle source

Calls {HCard#add_property} for a simple text property @param container [Nokogiri::XML::Element] parent element @param name [Symbol] property name @param class_name [String] HTML class name @param value [#to_s] property value @see HCard#add_property

# File lib/diaspora_federation/discovery/h_card.rb, line 217
def add_simple_property(container, name, class_name, value)
  add_property(container, name) do |html|
    html.span(value.to_s, class: class_name)
  end
end
create_builder() click to toggle source

Creates the base HCard html structure @return [Nokogiri::HTML::Builder] HTML Builder instance

# File lib/diaspora_federation/discovery/h_card.rb, line 172
def create_builder
  Nokogiri::HTML::Builder.new do |html|
    html.html {
      html.head {
        html.meta(charset: "UTF-8")
        html.title(@full_name)
      }

      html.body {
        html.div(id: "content") {
          html.h1(@full_name)
          html.div(id: "content_inner", class: "entity_profile vcard author") {
            html.h2("User profile")
          }
        }
      }
    }
  end
end