class Reactor::Cm::Obj

Constants

ATTR_LENGTH_CONSTRAINT
OBJ_ATTRS

Attributes

obj_id[R]

Public Class Methods

create(name, parent, objClass) click to toggle source
# File lib/reactor/cm/obj.rb, line 10
def self.create(name, parent, objClass)
  obj = Obj.new(name)
  obj.send(:create, parent, objClass)
  obj
end
delete_where(conditions) click to toggle source
# File lib/reactor/cm/obj.rb, line 37
def self.delete_where(conditions)
  request = XmlRequest.prepare do |xml|

    xml.tag!('obj-where') do
      conditions.each do |key, value|
        xml.tag!(key, value)
      end
    end
    xml.tag!("obj-delete")
  end
  request.execute!
end
exists?(path_or_id) click to toggle source
# File lib/reactor/cm/obj.rb, line 16
def self.exists?(path_or_id)
  obj = Obj.new
  begin
    obj.send(:load, path_or_id).ok?
  rescue
    return false
  end
end
get(path_or_id) click to toggle source
# File lib/reactor/cm/obj.rb, line 31
def self.get(path_or_id)
  obj = Obj.new
  obj.send(:load, path_or_id)
  obj
end
load(id) click to toggle source
# File lib/reactor/cm/obj.rb, line 25
def self.load(id)
  obj = Obj.new
  obj.instance_variable_set('@obj_id', id)
  obj
end
new(name=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 461
def initialize(name=nil)
  @name = name
  @attrs = {}
  @obj_attrs = {}
  @links = {}
  @removed_links = []
  @attr_options = {}
end

Protected Class Methods

extract_id(response) click to toggle source
# File lib/reactor/cm/obj.rb, line 457
def self.extract_id(response)
  response.xpath("//obj/id").text
end

Public Instance Methods

blob_ticket_id(content='edited') click to toggle source
# File lib/reactor/cm/obj.rb, line 381
def blob_ticket_id(content='edited')
  request = XmlRequest.prepare do |xml|
    xml.tag!('content-where') do
      xml.tag!('objectId', @obj_id)
      xml.tag!('state', content)
    end
    xml.tag!('content-get') do
      xml.tag!('blob')
    end
  end
  response = request.execute!
  possible_ticket_id = response.xpath('//blob').text
  encoding = response.xpath('//blob/@encoding').to_s
  # blob is smaller than
  # [systemConfig getKeys tuning.minStreamingDataLength]
  # and thus it is returned in-line
  if encoding != 'stream'
    raise Reactor::Cm::BlobTooSmallError
  end
  possible_ticket_id
end
commit!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 267
def commit!(msg=nil)
  simple_command("commit",msg)
end
composite_save(attrs, links_to_add, links_to_remove, links_to_set, links_modified=false) { |attrs, links_to_add, links_to_remove, links_to_set| ... } click to toggle source
# File lib/reactor/cm/obj.rb, line 148
def composite_save(attrs, links_to_add, links_to_remove, links_to_set, links_modified=false)
  set_multiple(attrs)

  skip_version_creation = @attrs.empty? && links_to_remove.empty? && links_to_set.empty? && !links_modified

  # The save procedure consists of two multiplexed CM requests.
  # Each request combines multiple operations for better performance.
  #
  # It is necessary to split the procedure into two request because
  # of the linklist handling. For objects with only released content
  # the edit operation copies all links (and generates new ids).
  # Only the new links from the edited content can be manipulated.
  # Thus it is neccessary after perform a read of links after the
  # edit operation.
  #
  # The second request may seem strange and redundant, but has very
  # important reasons for being structured the way it is.
  # The CM in all versions (as of this moment <= 7.0.1) contains
  # multiple race conditions regarding slave workers and the CRUL
  # interface.
  #
  # It is possible that the two requests are processed by two different
  # slave workers which have an invalid cache. Without careful
  # programing it may lead to unexpected results and/or data loss.
  # The reason behind it is simple: the cache invalidate requests
  # are distributed asynchronously between workers.
  #
  # It is impossible to ensure that the two requests are processed
  # by the same worker: a worker may get killed at any time, without
  # warning (producing errors on keep-alive connections). Furthermore
  # the workers regularly kill themselves (after X processed requests)
  # to contain memory leaks.
  #
  # Following cases are especially important to handle correctly:
  # Let Slave 1 be the worker receiving the first request and Slave 2
  # be the worker receiving the second request. Let Object be the
  # object on which the save operation is performed.
  #
  # 1. If the Object contains only released version, then Slave 2
  # may be unaware of the newly created edited version by the time
  # the second request is being processed. Thefore all operations
  # involving indirect content references for example adding links
  # (obj where ... editedContent addLinkTo) would get rejected
  # by the Slave 2.
  #
  # 2. If the Object contains an edited version, then Slave 2 may
  # be unaware of the changes perfomed on it, change of editor for
  # example, and can reject valid requests. The more dramatic
  # scenario involves Slave 2 persisting its invalid cached content
  # which would result in a data loss.
  #
  # The requests have been thus crafted to deal with those problems.
  # The solution is based on the precise source-code level knowledge
  # of the CM internals.
  resp = MultiXmlRequest.execute do |reqs|
    reqs.optional  {|xml| SimpleCommandRequest.build(xml, @obj_id, 'take') } unless skip_version_creation
    reqs.optional  {|xml| SimpleCommandRequest.build(xml, @obj_id, 'edit') } unless skip_version_creation


    reqs.mandatory {|xml| ObjSetRequest.build(xml, @obj_id, @obj_attrs) } unless @obj_attrs.empty? #important! requires different permissions
    reqs.mandatory {|xml| ContentSetRequest.build(xml, @obj_id, @attrs, @attr_options) } unless skip_version_creation
    reqs.mandatory  {|xml| ResolveRefsRequest.build(xml, @obj_id) } unless skip_version_creation
  end

  resp.assert_success

  yield(attrs, links_to_add, links_to_remove, links_to_set) if block_given?

  resp = MultiXmlRequest.execute do |reqs|
    reqs.optional  {|xml| SimpleCommandRequest.build(xml, @obj_id, 'take') }
    reqs.optional  {|xml| SimpleCommandRequest.build(xml, @obj_id, 'edit') }

    links_to_remove.each do |link_id|
      reqs.mandatory {|xml| LinkDeleteRequest.build(xml, link_id) }
    end

    links_to_set.each do |(link_id, link)|
      reqs.mandatory {|xml| LinkSetRequest.build(xml, link_id, link) }
    end

    links_to_add.each do |(attr, link)|
      reqs.mandatory {|xml| LinkAddRequest.build(xml, @obj_id, attr, link) }
    end
  end unless skip_version_creation || (links_to_remove.empty? && links_to_add.empty? && links_to_set.empty?)

  resp.assert_success
end
copy(new_parent, recursive = false, new_name = nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 288
def copy(new_parent, recursive = false, new_name = nil)
  request = XmlRequest.prepare do |xml|
    xml.tag!('obj-where') do
      xml.tag!("id", @obj_id)
    end
    xml.tag!("obj-copy") do
      xml.tag!("parent", new_parent)
      xml.tag!("name", new_name) if new_name
      xml.tag!("recursive", "1") if recursive
    end
  end
  response = request.execute!
  response.xpath("//obj/id").text
end
delete!() click to toggle source
# File lib/reactor/cm/obj.rb, line 303
def delete!
  simple_command("delete")
end
edit!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 255
def edit!(msg=nil)
  simple_command("edit",msg)
end
edited?() click to toggle source
# File lib/reactor/cm/obj.rb, line 335
def edited?
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    xml.get_key_tag!(base_name, 'isEdited')
  end
  response = request.execute!
  response.xpath("//isEdited").text == "1"
end
edited_content() click to toggle source
# File lib/reactor/cm/obj.rb, line 403
def edited_content
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    xml.get_key_tag!(base_name, 'editedContent')
  end
  response = request.execute!
  response.xpath("//editedContent").text
end
editor() click to toggle source
# File lib/reactor/cm/obj.rb, line 369
def editor
  request = XmlRequest.prepare do |xml|
    xml.tag!('content-where') do
      xml.tag!('objectId', @obj_id)
      xml.tag!('state', 'edited')
    end
    xml.get_key_tag!('content', 'editor')
  end
  response = request.execute!
  response.xpath('//editor').text
end
forward!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 263
def forward!(msg=nil)
  simple_command("forward",msg)
end
get(key) click to toggle source
# File lib/reactor/cm/obj.rb, line 58
def get(key)
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    xml.get_key_tag!(base_name, key)
  end
  response = request.execute!
  result = response.xpath("//#{key}")
  if result.children.map {|i| i.respond_to?(:name) && (i.name == "listitem") }.reduce(:&)
    result.children.map {|i| i.text.to_s }
  else
    result = result.text unless result.is_a? Array
    result
  end
end
path() click to toggle source
# File lib/reactor/cm/obj.rb, line 326
def path
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    xml.get_key_tag!(base_name, 'path')
  end
  response = request.execute!
  response.xpath("//obj/path").text
end
permission_clear(permission) click to toggle source
# File lib/reactor/cm/obj.rb, line 117
def permission_clear(permission)
  self.permission_set(permission, [])
end
permission_grant(permission, groups) click to toggle source
# File lib/reactor/cm/obj.rb, line 109
def permission_grant(permission, groups)
  self.permission_command('GrantTo', permission, groups)
end
permission_granted_to(user, permission) click to toggle source
# File lib/reactor/cm/obj.rb, line 83
def permission_granted_to(user, permission)
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    xml.get_tag!(base_name) do
      xml.tag!('permissionGrantedTo', :permission => permission, :user => user)
    end
  end
  response = request.execute!
  response.xpath("//permissionGrantedTo/text()") == "1"
end
permission_revoke(permission, groups) click to toggle source
# File lib/reactor/cm/obj.rb, line 113
def permission_revoke(permission, groups)
  self.permission_command('RevokeFrom', permission, groups)
end
permission_set(permission, groups) click to toggle source
# File lib/reactor/cm/obj.rb, line 94
def permission_set(permission, groups)
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, :id, @obj_id)

    options = {
      :permission => permission,
      :type => :list,
    }

    xml.set_key_tag!(base_name, :permission, groups, options)
  end

  request.execute!
end
reasons_for_incomplete_state() click to toggle source
# File lib/reactor/cm/obj.rb, line 344
def reasons_for_incomplete_state
  request = XmlRequest.prepare do |xml|
    xml.tag!('content-where') do
      xml.tag!('objectId', @obj_id)
      xml.tag!('state', 'edited')
    end
    xml.get_key_tag!('content', 'reasonsForIncompleteState')
  end
  response = request.execute!
  result = response.xpath('//reasonsForIncompleteState/*')
  result.kind_of?(Array) ? result.map(&:text).map(&:to_s) : [result.to_s]
end
reject!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 271
def reject!(msg=nil)
  simple_command("reject",msg)
end
release!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 247
def release!(msg=nil)
  simple_command("release",msg)
end
remove_active_contents!() click to toggle source
# File lib/reactor/cm/obj.rb, line 307
def remove_active_contents!
  simple_command("removeActiveContents")
end
remove_archived_contents!() click to toggle source
# File lib/reactor/cm/obj.rb, line 311
def remove_archived_contents!
  simple_command("removeArchivedContents")
end
resolve_refs!() click to toggle source
# File lib/reactor/cm/obj.rb, line 315
def resolve_refs!
  request = XmlRequest.prepare do |xml|
    xml.tag!('content-where') do
      xml.tag!('objectId', @obj_id)
      xml.tag!('state', 'edited')
    end
    xml.tag!('content-resolveRefs')
  end
  request.execute!
end
revert!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 275
def revert!(msg=nil)
  simple_command("revert",msg)
end
save!() click to toggle source
# File lib/reactor/cm/obj.rb, line 236
def save!
  links_to_remove = @removed_links.map {|l| l.link_id}
  links_to_add = @links.map do |attr, links|
    links.map do |link|
      [attr, {:destination_url => link.dest_url, :title => link.title, :target => link.target, :position => link.position}]
    end.flatten
  end
  composite_save([], links_to_add, links_to_remove, [])
end
set(key, value, options={}) click to toggle source
# File lib/reactor/cm/obj.rb, line 73
def set(key, value, options={})
  key = key.to_sym
  value = value[0, ATTR_LENGTH_CONSTRAINT[key]] if ATTR_LENGTH_CONSTRAINT[key] && value
  if OBJ_ATTRS.include?(key) then @obj_attrs[key] = value
  else
    @attrs[key] = value
  end
  @attr_options[key] = options
end
set_multiple(attrs) click to toggle source
# File lib/reactor/cm/obj.rb, line 144
def set_multiple(attrs)
  attrs.each {|a,(v,o)| set(a,v,o||{}) }
end
sign!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 279
def sign!(msg=nil)
  simple_command("sign",msg)
end
take!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 259
def take!(msg=nil)
  simple_command("take",msg)
end
unrelease!(msg=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 251
def unrelease!(msg=nil)
  simple_command("unrelease",msg)
end
upload(data_or_io, extension) click to toggle source
# File lib/reactor/cm/obj.rb, line 50
def upload(data_or_io, extension)
  data = (data_or_io.kind_of?IO) ? data_or_io.read : data_or_io
  base64_data = Base64.encode64(data)

  set(:contentType, extension)
  set(:blob, {base64_data=>{:encoding=>'base64'}})
end
valid_actions() click to toggle source
# File lib/reactor/cm/obj.rb, line 283
def valid_actions
  vcak = get('validControlActionKeys')
  (vcak || []).map(&:to_s)
end
workflow_comment() click to toggle source
# File lib/reactor/cm/obj.rb, line 357
def workflow_comment
  request = XmlRequest.prepare do |xml|
    xml.tag!('content-where') do
      xml.tag!('objectId', @obj_id)
      xml.tag!('state', 'released')
    end
    xml.get_key_tag!('content', 'workflowComment')
  end
  response = request.execute!
  response.xpath('//workflowComment/*').map {|x| x.text.to_s}.first
end

Protected Instance Methods

base_name() click to toggle source
# File lib/reactor/cm/obj.rb, line 427
def base_name
  'obj'
end
create(parent, objClass) click to toggle source
# File lib/reactor/cm/obj.rb, line 470
def create(parent, objClass)
  @request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'path', parent)
    xml.create_tag!(base_name) do
      xml.tag!('objClass') do
        xml.text!(objClass)
      end
      xml.tag!('name') do
        xml.text!(@name)
      end
    end
  end
  response = @request.execute!
  @obj_id = self.class.extract_id(response)
  response
end
get_content_attr_text(attr) click to toggle source
# File lib/reactor/cm/obj.rb, line 431
def get_content_attr_text(attr)
  content = edited_content
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!('content', 'id', content)
    xml.get_tag!('content') do
      xml.tag!(attr.to_s)
    end
  end
  response = request.execute!
  txt = response.xpath("//#{attr}/text()")
  txt.class.unnormalize(txt.to_s)
end
load(path_or_id) click to toggle source
# File lib/reactor/cm/obj.rb, line 487
def load(path_or_id)
  key = (/^\// =~ path_or_id.to_s) ? 'path' : 'id'
  value = path_or_id

  @request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, key.to_s, value.to_s)
    xml.get_key_tag!(base_name, 'id')
  end
  response = @request.execute!
  @obj_id = self.class.extract_id(response)
  response
end
permission_command(type, permission, groups) click to toggle source
# File lib/reactor/cm/obj.rb, line 500
def permission_command(type, permission, groups)
  request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)

    xml.tag!("#{base_name}-permission#{type}", :permission => permission) do
      groups.each do |name|
        xml.tag!(:group, name)
      end
    end
  end

  request.execute!
end
simple_command(cmd_name, comment=nil) click to toggle source
# File lib/reactor/cm/obj.rb, line 413
def simple_command(cmd_name, comment=nil)
  @request = XmlRequest.prepare do |xml|
    xml.where_key_tag!(base_name, 'id', @obj_id)
    if comment
      xml.tag!("#{base_name}-#{cmd_name}") do
        xml.tag!('comment', comment)
      end
    else
      xml.tag!("#{base_name}-#{cmd_name}")
    end
  end
  @request.execute!
end