require 'net/dav'
require 'vortex_client/string_utils'
require 'highline/import'
require 'time'
# Utilities for managing content in the web content management system Vortex.
# All calls are done with the webdav protocol.
module Vortex
class Connection < Net::DAV
# Create a new connection to Vortex. Prompts for username and password if not
# supplied. Overrides Net::DAV.initialize()
#
# Examples:
#
# vortex = Vortex::Connection.new("https://www-dav.server.com",user,pass)
#
# vortex = Vortex::Connection.new("https://www-dav.server.com") =>
# Username: tiger
# Password: *****
def initialize(uri, *args)
@uri = uri
@uri = URI.parse(@uri) if @uri.is_a? String
@have_curl = false # This defaults to true in Net::DAV
@handler = NetHttpHandler.new(@uri)
@handler.verify_server = false # This defaults to true in Net::DAV
if(args != [])
@handler.user = args[0]
@handler.pass = args[1]
else
@handler.user = ask("Username: ") {|q| q.echo = true}
@handler.pass = ask("Password: ") {|q| q.echo = "*"} # false => no echo
end
return @handler
end
# Returns true if resource or collection exists.
#
# Example:
#
# vortex.exists?("https://www-dav.server.com/folder/index.html")
def exists?(uri)
uri = URI.parse(uri) if uri.is_a? String
begin
self.propfind(uri.path)
rescue Net::HTTPServerException => e
return false if(e.to_s =~ /404/)
end
return true
end
# Publish a document object to the web.
#
# Publishes a object by performing a PUT request to object.url with object.content
# and then performing a PROPPATCH request to object.url with object.properties
#
# Example:
#
# vortex = Vortex::Connection.new("https://www-dav.server.com")
# article = Vortex::StructuredArticle(:title=>"My title")
# vortex.publish(article)
def publish(object)
if(object.is_a? HtmlArticle or object.is_a? HtmlEvent)
uri = @uri.merge(object.url)
# puts "DEBUG: '" + object.class.to_s + "=" + object.properties
self.put_string(uri, object.content)
self.proppatch(uri, object.properties)
return uri.to_s
else
warn "Unknown vortex resource: " + object.class.to_s
end
end
# Creates collections
#
# Example:
#
# connection = Connection.new('https://host.com')
# collecion = ArticleListingCollection.new(:url => '/url')
# connection.create(collection)
def create(object)
if(object.is_a? Collection)
uri = @uri.merge(object.url)
self.mkdir(uri)
self.proppatch(uri, object.properties)
return uri.to_s
end
end
private
# Disable Net::DAV.credentials
def credentials(user, pass)
end
end
class Resource
end
# PlainFile: This is the same as 'File' in Vortex.
class PlainFile < Resource
# Named PlainFile so it won't get mixed up with standard File class.
end
# Plain HTML files with title, introduction and keywords set as webdav properties.
#
# Examples:
#
# article = HtmlArticle.new(:title => "Sample Title",
# :introduction => "Introduction",
# :body => "
Hello world
")
# vortex.publish(article)
class HtmlArticle < PlainFile
attr_accessor :title, :introduction, :body, :filename, :modifiedDate, :publishedDate, :owner, :url, :author, :date, :tags, :picture
# Create a new article of type html-article: plain html file with introduction stored as a webdav property.
def initialize(options={})
options.each{|k,v|send("#{k}=",v)}
end
def to_s
"#"
end
def url
if(@url)
@url
else
if(filename)
filename
end
if(title)
StringUtils.create_filename(title) + ".html"
else
warn "Article must have either a full url or title. "
end
end
end
def properties
props = 'article' +
'article' +
'utf-8'
if(@publishedDate and @publishedDate != "")
if(@publishedDate.kind_of? Time)
@publishedDate = @publishedDate.httpdate.to_s
end
props += '' + @publishedDate + ''
end
if(date and date != "")
if(date.kind_of? Time)
date = @date.httpdate.to_s
end
if(@publishedDate == nil or @publishedDate != "")
props += '' + date + ''
end
props += '' + date + '' +
'' + date + '' +
'' + date + '' +
'' + date + ''
end
if(picture)
props += '' + picture + ''
end
if(title)
props += '' + title + ''
end
if(owner)
props += '' + owner + ''
end
if(introduction and introduction != "")
props += '' + introduction + ''
end
if(author and author != "")
props += '' +
'' +
'' + author + '' +
'' +
''
end
if(tags and tags.kind_of?(Array) and tags.size > 0)
props += '' +
''
tags.each do |tag|
props += "#{tag}"
end
props += ''
end
return props
end
def content
content = '' +
'' + title + '' +
' ' +
''
if(body)
content += body
end
content += ''
end
end
# HtmlEvent: Event document. Article with location, map url, start and end dates.
#
# Examples:
#
# event = Vortex::HtmlEvent.new(:title => "Sample Event 1",
# :introduction => "Sample event introduction",
# :body => "
Hello world
",
# :startDate => Time.now, ## "22.01.2010 12:15",
# :endDate => Time.now + 60*60, ## "22.01.2010 13:00",
# :location => "Forskningsveien 3B",
# :mapUrl => "http://maps.google.com/123",
# :tags => ["vortex","testing"],
# :publishedDate => "05.01.2010 12:00")
# vortex.publish(event)
class HtmlEvent < PlainFile
attr_accessor :title, :introduction, :body, :filename, :modifiedDate, :publishedDate, :date,
:owner, :url, :tags, :startDate, :endDate, :location, :mapUrl
# Create a new article of type html-article: plain html file with
# introduction stored as a webdav property.
def initialize(options={})
options.each{|k,v|send("#{k}=",v)}
end
def to_s
"#"
end
def url
if(@url)
@url
else
if(filename)
filename
end
if(title)
StringUtils.create_filename(title) + ".html"
else
warn "Article must have either a full url or title. "
end
end
end
def properties
props = 'event' +
'event' +
'utf-8'
if(@publishedDate and @publishedDate != "")
if(@publishedDate.kind_of? Time)
@publishedDate = @publishedDate.httpdate.to_s
end
props += '' + @publishedDate + ''
end
if(@date and @date != "")
if(@date.kind_of? Time)
@date = @date.httpdate.to_s
end
if(@publishedDate == nil or @publishedDate != "")
props += '' + date + ''
end
props += '' + date + '' +
'' + date + '' +
'' + date + '' +
'' + date + ''
end
if(title)
props += '' + title + ''
end
if(owner)
props += '' + owner + ''
end
if(introduction and introduction != "")
props += '' + introduction + ''
end
if(tags and tags.kind_of?(Array) and tags.size > 0)
props += '' +
''
tags.each do |tag|
props += "#{tag}"
end
props += ''
end
if(@startDate and @startDate != "")
if(@startDate.kind_of? Time)
@startDate = @startDate.httpdate.to_s
end
props += '' + @startDate + ''
end
if(@endDate and @endDate != "")
if(@endDate.kind_of? Time)
@endDate = @endDate.httpdate.to_s
end
props += '' + @endDate + ''
end
if(@location and @location != "")
props += '' + @location + ''
end
if(@mapUrl and @mapUrl != "")
props += '' + @mapUrl + ''
end
return props
end
# TODO: Samme kode som i article... Bruk arv!
def content
content = '' +
'' + title + '' +
' ' +
''
if(body)
content += body
end
content += ''
end
end
# Vortex article stored as JSON data.
# TODO: Fill out the stub.
class StructuredArticle < PlainFile
attr_accessor :title, :introduction, :content, :filename, :modifiedDate, :publishedDate, :owner, :url
# Create an article
# Options:
#
# :title => "Title" mandatory
def initialize(options={})
options.each{|k,v|send("#{k}=",v)}
end
def to_s
"#"
end
def dav_properties
""
end
def dav_content
""
end
end
# Collection (folder)
class Collection < Resource
attr_accessor :title, :url, :foldername, :name, :introduction, :navigationTitle, :sortByDate, :sortByTitle, :owner
def url
if(@url)
return @url
end
if(@foldername)
return @foldername
end
if(@name)
return @name
end
if(@title)
return StringUtils.create_filename(title)
end
return "no-name"
end
def initialize(options={})
options.each{|k,v|send("#{k}=",v)}
end
def to_s
"#"
end
def properties()
# props = "collection" +
# "article-listing"
props = ""
if(title and title != "")
props += "#{title}"
end
if(navigationTitle and navigationTitle != "")
props += "#{navigationTitle}"
end
if(owner and owner != "")
props += "#{owner}"
end
return props
end
end
# Article listing collection
#
# Examaple:
#
# collection = ArticleListingCollection(:url => 'news')
# collection = ArticleListingCollection(:foldername => 'news')
# collection = ArticleListingCollection(:title => 'My articles')
# collection = ArticleListingCollection(:title => 'My articles',
# :foldername => 'articles',
# :navigationTitle => 'Read articles')
class ArticleListingCollection < Collection
def properties()
props = super
props += "article-listing" +
"article-listing"
return props
end
end
class EventListingCollection < Collection
def properties()
props = super
props += "event-listing" +
"event-listing"
return props
end
end
# Utilities
#
# Convert norwegian date to Time object with a forgiven regexp
#
# TODO: Move this somewhere.
#
# Examples:
#
# t = norwegian_date('1.1.2010')
# t = norwegian_date('22.01.2010')
# t = norwegian_date('22.01.2010 12:15')
# t = norwegian_date('22.01.2010 12:15:20')
def norwegian_date(date)
if /\A\s*
(\d\d?).(\d\d?).(-?\d+)
\s?
(\d\d?)?:?(\d\d?)?:?(\d\d?)?
\s*\z/ix =~ date
year = $3.to_i
mon = $2.to_i
day = $1.to_i
hour = $4.to_i
min = $5.to_i
sec = $6.to_i
# puts "Debug: #{year} #{mon} #{day} #{hour}:#{min}:#{sec}"
usec = 0
usec = $7.to_f * 1000000 if $7
if $8
zone = $8
year, mon, day, hour, min, sec =
apply_offset(year, mon, day, hour, min, sec, zone_offset(zone))
Time.utc(year, mon, day, hour, min, sec, usec)
else
Time.local(year, mon, day, hour, min, sec, usec)
end
else
raise ArgumentError.new("invalid date: #{date.inspect}")
end
end
end