# Magnoline, Command line tool for the magnolia CMS #Copyright (C) 2007 Nicolas Modrzyk # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # #This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Nicolas Modrzyk hellonico at gmail dot com # # Things learn in this code: # 1. Command line parsing # 2. Multipart HTTP POST require 'rubygems' require 'cooloptions' require 'net/http' require 'ping' require 'http-access2' require 'yaml' include Net # # Simple client to import/export content of a magnolia repository. # This is using the import/export servlet accessible in magnolia module MagnoliaClient # # Parse the arguments to instanciate the proper ImportExport object class ImportExportSetUp CMD_IMPORT = "import" CMD_EXPORT = "export" IMPORT_BEHAVIORS = [:new,:remove,:replace] # # this method does the parsing of the arguments. Validates them # return the ImportExport object def parse(argv) options = CoolOptions.parse!('[options]') do |opt| opt.on 'action STRING','one of import/export' opt.on 'out STRING','path to directory where to put the content of the answer', "" opt.on 'repository-path STRING','path on the repository to take into account', "/help" opt.on 'workspace STRING',' the repository/workspace to take into account', "website" opt.on 'user STRING','user name to authenticate with', 'superuser' opt.on 'password STRING','password', 'superuser' opt.on 'import-file STRING','path to the xml file to import', '' opt.on 'server-url STRING','base url of the magnolia service', 'http://localhost:8080/magnoliaAuthor' opt.on 'behavior STRING', 'import behavior. One of new/remove/replace or 0/1/2', "0" opt.on 'verbose', 'give some more processing info on the command line', false opt.on 'console', 'output to standard output, bypass the out option', false opt.after do |r| r.out = File.expand_path(r.out) opt.error("Invalid action:"+r.action) unless (r.action == CMD_IMPORT || r.action == CMD_EXPORT) host = URI.parse(r.server_url).host opt.error("host is not reachable:" + host) unless Ping.pingecho(host,10) r.behavior = parse_behavior(r.behavior) if r.action == CMD_IMPORT end end return Export.new(options) if options.action == CMD_EXPORT return Import.new(options) if options.action == CMD_IMPORT end # # Parse the import behavior, from an integer or a string # Do this while parsing to get proper error output when needed def parse_behavior(value) IMPORT_BEHAVIORS.each_with_index { |behavior, index| return index if value.to_i == index return index if value.to_s == behavior.to_s } raise "Invalid behavior:#{value}" end end # # Class that does the actual import export class ImportExportBase MAGNOLIA_BASE_URL = "/.magnolia/pages/" MAGNOLIA_IMPORT_URL = MAGNOLIA_BASE_URL + "import.html" MAGNOLIA_EXPORT_URL = MAGNOLIA_BASE_URL + "export.html" # # No checking of options, this is done in the parser # assign values from the hash to attributes def initialize(options) @console = options.console @action = options.action @server_url = options.server_url @repository_path = options.repository_path @workspace = options.workspace @user = options.user @password = options.password @out = File.expand_path(options.out+'/'+@workspace+@repository_path.gsub('/','.')+'.xml') @verbose = options.verbose @options = options end # # prepare form data. Should return a hash with the needed parameters # this has to be implemented in the subclasses def prepare_form_data raise "Sub classing class need to implement this method" end # # trying to fix a bug in http-access2 where all the parameters values are loosing their last char # can't find a reason for that. def pad_data(data) data.each { |key,value| begin if not key==:mgnlFileImport value = value + " "; data[key] = value.to_s end rescue end } end # # main method # this create the necessary http client to simulate interaction and just post the # data collected from subclasses to the form. def exec client = HTTPAccess2::Client.new boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__') extheader = {'content-type' => "multipart/form-data; boundary=___#{ boundary }___"} client.set_basic_auth(@server_url, @user, @password) form_data = pad_data(prepare_form_data) verbose(form_data) if @verbose res = client.post_content(@full_url, form_data, extheader) if @console puts res else f = File.open(@out,'w') f.puts res f.close puts "Output of action "+@action+" has been written to "+@out if @verbose end end # # Verbose output before action def verbose(form_data) puts "------------ \tOptions data\t ---------------" puts @options.to_yaml puts "------------ \tPost data\t\t ---------------" puts form_data.to_yaml puts "------------ \tEnd data\t\t ---------------" end end # # Class responsible for import specific code class Import < ImportExportBase def initialize(options) super(options) @action = "importxml " @full_url = @server_url + MAGNOLIA_IMPORT_URL @import_file = options.import_file end def prepare_form_data form = { :mgnlFileImport=>open(@import_file), :mgnlRepository=>@workspace, :mgnlPath=>@repository_path, :mgnlKeepVersions=>false, :mgnlFormat=>false, :command=>@action } end end # # Class responsible for export specific code class Export < ImportExportBase def initialize(options) super(options) @action = "exportxml" @full_url = @server_url + MAGNOLIA_EXPORT_URL end def prepare_form_data post_data = { :mgnlRepository=>@workspace, :mgnlPath=>@repository_path, :command=>@action, :mgnlKeepVersions=>false, :mgnlFormat=>false, :ext=>'.xml' } end end end