class Tilia::VObject::Component::VCard

The VCard component.

This component represents the BEGIN:VCARD and END:VCARD found in every vcard.

Constants

DEFAULT_VERSION

VCards with version 2.1, 3.0 and 4.0 are found.

If the VCARD doesn't know its version, 2.1 is assumed.

Public Class Methods

new(*args) click to toggle source

sets instance variables

Calls superclass method
# File lib/tilia/v_object/component/v_card.rb, line 423
def initialize(*args)
  super
  @version = nil
end

Public Instance Methods

class_name_for_property_name(property_name) click to toggle source

Returns the default class for a property name.

@param [String] property_name

@return [String]

Calls superclass method
# File lib/tilia/v_object/component/v_card.rb, line 411
def class_name_for_property_name(property_name)
  class_name = super(property_name)

  # In vCard 4, BINARY no longer exists, and we need URI instead.
  if class_name == Property::Binary && document_type == VCARD40
    return Property::Uri
  end

  class_name
end
convert(target) click to toggle source

Converts the document to a different vcard version.

Use one of the VCARD constants for the target. This method will return a copy of the vcard in the new version.

At the moment the only supported conversion is from 3.0 to 4.0.

If input and output version are identical, a clone is returned.

@param [Fixnum] target

@return [VCard]

# File lib/tilia/v_object/component/v_card.rb, line 150
def convert(target)
  converter = VCardConverter.new
  converter.convert(self, target)
end
document_type() click to toggle source

Returns the current document type.

@return [Fixnum]

# File lib/tilia/v_object/component/v_card.rb, line 117
def document_type
  unless @version
    version = self['VERSION'].to_s

    case version
    when '2.1'
      @version = VCARD21
    when '3.0'
      @version = VCARD30
    when '4.0'
      @version = VCARD40
    else
      # We don't want to cache the version if it's unknown,
      # because we might get a version property in a bit.
      return UNKNOWN
    end
  end

  @version
end
json_serialize() click to toggle source

This method returns an array, with the representation as it should be encoded in json. This is used to create jCard or jCal documents.

@return [Array]

# File lib/tilia/v_object/component/v_card.rb, line 351
def json_serialize
  # A vcard does not have sub-components, so we're overriding this
  # method to remove that array element.
  properties = []

  children.each do |child|
    properties << child.json_serialize
  end

  [@name.downcase, properties]
end
preferred(property_name) click to toggle source

Returns a preferred field.

VCards can indicate wether a field such as ADR, TEL or EMAIL is preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x being a number between 1 and 100).

If neither of those parameters are specified, the first is returned, if a field with that name does not exist, null is returned.

@param [String] field_name

@return [Property, nil]

# File lib/tilia/v_object/component/v_card.rb, line 311
def preferred(property_name)
  preferred = nil
  last_pref = 101
  select(property_name).each do |field|
    pref = 101

    if field.key?('TYPE') && field['TYPE'].has('PREF')
      pref = 1
    elsif field.key?('PREF')
      pref = field['PREF'].value.to_i
    end

    if pref < last_pref || preferred.nil?
      preferred = field
      last_pref = pref
    end
  end

  preferred
end
validate(options = 0) click to toggle source

(see Component#validate)

Calls superclass method
# File lib/tilia/v_object/component/v_card.rb, line 161
def validate(options = 0)
  warnings = []

  version_map = {
    VCARD21 => '2.1',
    VCARD30 => '3.0',
    VCARD40 => '4.0'
  }

  version = select('VERSION')
  if version.size == 1
    version = self['VERSION'].to_s
    unless ['2.1', '3.0', '4.0'].include?(version)
      warnings << {
        'level'   => 3,
        'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
        'node'    => self
      }
      if options & REPAIR > 0
        self['VERSION'] = version_map[DEFAULT_VERSION]
      end
    end

    if version == '2.1' && options & PROFILE_CARDDAV > 0
      warnings << {
        'level'   => 3,
        'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
        'node'    => self
      }
    end
  end

  uid = select('UID')
  if uid.size == 0
    if options & PROFILE_CARDDAV > 0
      # Required for CardDAV
      warning_level = 3
      message = 'vCards on CardDAV servers MUST have a UID property.'
    else
      # Not required for regular vcards
      warning_level = 2
      message = 'Adding a UID to a vCard property is recommended.'
    end

    if options & REPAIR > 0
      self['UID'] = UuidUtil.uuid
      warning_level = 1
    end

    warnings << {
      'level'   => warning_level,
      'message' => message,
      'node'    => self
    }
  end

  fn = select('FN')
  if fn.size != 1
    repaired = false

    if options & REPAIR > 0 && fn.size == 0
      # We're going to try to see if we can use the contents of the
      # N property.
      if key?('N')
        value = self['N'].to_s.split(';')
        if value[1]
          self['FN'] = value[1] + ' ' + value[0]
        else
          self['FN'] = value[0]
        end

        repaired = true

      # Otherwise, the ORG property may work
      elsif key?('ORG')
        self['FN'] = self['ORG'].to_s
        repaired = true
      end
    end

    warnings << {
      'level'   => repaired ? 1 : 3,
      'message' => 'The FN property must appear in the VCARD component exactly 1 time',
      'node'    => self
    }
  end

  w = super(options)
  w.concat warnings

  w
end
validation_rules() click to toggle source

(see Component#validation_rules)

# File lib/tilia/v_object/component/v_card.rb, line 255
def validation_rules
  {
    'ADR'          => '*',
    'ANNIVERSARY'  => '?',
    'BDAY'         => '?',
    'CALADRURI'    => '*',
    'CALURI'       => '*',
    'CATEGORIES'   => '*',
    'CLIENTPIDMAP' => '*',
    'EMAIL'        => '*',
    'FBURL'        => '*',
    'IMPP'         => '*',
    'GENDER'       => '?',
    'GEO'          => '*',
    'KEY'          => '*',
    'KIND'         => '?',
    'LANG'         => '*',
    'LOGO'         => '*',
    'MEMBER'       => '*',
    'N'            => '?',
    'NICKNAME'     => '*',
    'NOTE'         => '*',
    'ORG'          => '*',
    'PHOTO'        => '*',
    'PRODID'       => '?',
    'RELATED'      => '*',
    'REV'          => '?',
    'ROLE'         => '*',
    'SOUND'        => '*',
    'SOURCE'       => '*',
    'TEL'          => '*',
    'TITLE'        => '*',
    'TZ'           => '*',
    'URL'          => '*',
    'VERSION'      => '1',
    'XML'          => '*',

    # FN is commented out, because it's already handled by the
    # validate function, which may also try to repair it.
    # 'FN'           => '+',
    'UID'          => '?'
  }
end
xml_serialize(writer) click to toggle source

This method serializes the data into XML. This is used to create xCard or xCal documents.

@param [Tilia::Xml::Writer] writer XML writer.

@return [void]

# File lib/tilia/v_object/component/v_card.rb, line 369
def xml_serialize(writer)
  properties_by_group = {}

  children.each do |property|
    group = property.group

    properties_by_group[group] = [] unless properties_by_group[group]
    properties_by_group[group] << property
  end

  writer.start_element(@name.downcase)

  properties_by_group.each do |group, properties|
    unless group.blank?
      writer.start_element('group')
      writer.write_attribute('name', group.downcase)
    end

    properties.each do |property|
      case property.name
      when 'VERSION'
        next
      when 'XML'
        value = property.parts
        fragment = Tilia::Xml::Element::XmlFragment.new(value[0])
        writer.write(fragment)
      else
        property.xml_serialize(writer)
      end
    end

    writer.end_element unless group.blank?
  end

  writer.end_element
end

Protected Instance Methods

defaults() click to toggle source

This method should return a list of default property values.

@return [Hash]

# File lib/tilia/v_object/component/v_card.rb, line 337
def defaults
  {
    'VERSION' => '4.0',
    'PRODID'  => "-//Tilia//Tilia VObject #{Version::VERSION}//EN",
    'UID'     => "tilia-vobject-#{UuidUtil.uuid}"
  }
end