Skip to content

Copying pages from existing rucksacks

jamesu edited this page Sep 12, 2010 · 2 revisions

Introduction

Got a load of stuff on an existing Rucksack that you want to copy over to a new one, and don’t want to spend hours manually cut + pasting content over?

Well here is the solution!

The solution

Using HTTParty, nokogiri, and a bit of XML i have come up with the following ruby script to copy pages from one Rucksack to another. It requires both HTTParty and nokogiri, so make sure you have both handy.

Ideally this could be extended for use in Rucksack itself, perhaps using something like RestClient. But for now, there is this.

To run, try something like the following:


require 'thescript'
RucksackDest.duplicate_page(RucksackSrc, 1)

Which duplicates all it can on page 1 to the destination Rucksack.

The code


# Rucksack copy

require 'httparty'
require 'nokogiri'


RUCKSACK_ORIGIN = "http://source"
RUCKSACK_DEST = "http://destination"

# Username + Passwords
RUCKSACK_ORIGIN_USER = "admin"
RUCKSACK_ORIGIN_PASS = "password"
RUCKSACK_DEST_USER = "admin"
RUCKSACK_DEST_PASS = "password"


class Rucksack

  def self.pages
    Nokogiri::XML.parse(get("/pages.xml")).xpath("/pages/page")
  end

  def self.slots_for_page(id)
    Nokogiri::XML.parse(get("/pages/#{id}.xml")).xpath('page/slots/slot')
  end

  def self.widget_path(widgetname)
    case widgetname
      when 'Note'
        return 'notes'
      when 'List'
        return 'lists'
      when 'Separator'
        return 'separators'
      when 'UploadedFile'
        return 'uploaded_files'
      when 'Email'
        return 'emails'
      when 'Album'
        return 'albums'
    end
  end

  def self.widgets_for_page(pid)
    slots_for_page(pid).collect do |path|
      id = path.xpath('rel-object-id').inner_text.to_i
      widgetname = path.xpath('rel-object-type').inner_text
      widgeturl = "/pages/#{pid}/#{widget_path(widgetname)}/#{id}.xml"

      res = nil
      begin
        res = {:type => widgetname, :data => Nokogiri::XML.parse(get(widgeturl)).xpath('/')}
        puts "GOT: #{widgeturl}"
      rescue Exception => e
        puts "Problem importing #{widgeturl} (#{e.to_s})"
      end

      res
    end
  end

  def self.duplicate_page(src, pid)
    page_str = Nokogiri::XML.parse(src.get("/pages/#{pid}.xml")).xpath('page').to_s

    # Make page on destination RuckSack
    new_page = Nokogiri::XML.parse(post("/pages.xml", {:headers => {'Content-Type' => 'text/xml'}, :body => page_str})).xpath('page')
    new_page_id = new_page.xpath('id').inner_text.to_i

    puts "New page id: #{new_page_id}"

    # Process widgets (Note: needs to be done in reverse order!)
    puts "Importing widgets..."
    src.widgets_for_page(pid).compact.reverse.each do |widget|
      widgetname = widget[:type]
      case widgetname
        when 'Note'
          print "Note -> ... "
          post("/pages/#{new_page_id}/#{widget_path(widgetname)}.xml", 
               {:headers => {'Content-Type' => 'text/xml'}, 
                :body => widget[:data].xpath('note').to_s})
          puts "Done"

        when 'Separator'
          print "Separator -> ..."
          post("/pages/#{new_page_id}/#{widget_path(widgetname)}.xml", 
               {:headers => {'Content-Type' => 'text/xml'}, 
                :body => widget[:data].xpath('separator').to_s})
          puts "Done"
        when 'List'
          print "List => ..."
          new_list = Nokogiri::XML.parse(post("/pages/#{new_page_id}/#{widget_path(widgetname)}.xml", 
               {:headers => {'Content-Type' => 'text/xml'}, 
                :body => widget[:data].xpath('list').to_s}))

          # Grab items for list
          list_id = widget[:data].xpath('id').inner_text.to_i
          new_list_id = new_list.xpath('list/id').inner_text.to_i
          print " id:#{list_id} -> #{new_list_id}..."
          Nokogiri::XML.parse(src.get("/pages/#{pid}/#{widget_path(widgetname)}/#{list_id}/items.xml")).xpath('list-items/list-item').each do |item|
            post("/pages/#{new_page_id}/#{widget_path(widgetname)}/#{new_list_id}/items.xml",
                {:headers => {'Content-Type' => 'text/xml'}, 
                :body => item.to_s})
            print "."
          end

          puts "Done"
        when 'UploadedFile'
          print "Skipping (file)"
        when 'Email'
          print "Skipping (email)"
          next
        when 'Album'
          print "Skipping (album)"
      end
    end
  end

  def self.users
    Nokogiri::XML.parse(get("/users.xml")).xpath("/pages/page")
  end


end

class RucksackSrc < Rucksack

  include HTTParty

  base_uri RUCKSACK_ORIGIN

  basic_auth RUCKSACK_ORIGIN_USER, RUCKSACK_ORIGIN_PASS
end

class RucksackDest < Rucksack

  include HTTParty

  base_uri RUCKSACK_DEST

  basic_auth RUCKSACK_DEST_USER, RUCKSACK_DEST_PASS
end