rexchange 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/RAKEFILE +1 -1
- data/lib/r_exchange.rb +2 -0
- data/lib/rexchange.rb +15 -1
- data/lib/rexchange/contact.rb +45 -0
- data/lib/rexchange/folder.rb +13 -5
- data/lib/rexchange/generic_item.rb +64 -0
- data/lib/rexchange/message.rb +12 -58
- metadata +5 -3
- data/lib/rexchange/extensions.rb +0 -13
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
-- 0.2.0:
|
2
|
+
* Fixed a bug in Message#move_to when passing folders
|
3
|
+
* Added support for Contact browsing
|
4
|
+
* Added GenericItem to abstract Contacts and Messages
|
5
|
+
* Renamed Folder#messages to Folder#get_messages to avoid naming collisions
|
6
|
+
|
1
7
|
-- 0.1.4:
|
2
8
|
* Moved Folder::normalize_folder_name to String#normalize, and added support for MixedCase sources.
|
3
9
|
* Fixed a bug where the normalized folder name was being used for the Folder#to_s result.
|
data/RAKEFILE
CHANGED
data/lib/r_exchange.rb
ADDED
data/lib/rexchange.rb
CHANGED
@@ -1,8 +1,22 @@
|
|
1
1
|
require 'rexchange/session'
|
2
2
|
|
3
|
+
class String
|
4
|
+
def ends_with?(partial)
|
5
|
+
self[self.size - partial.size..self.size] == partial
|
6
|
+
end
|
7
|
+
|
8
|
+
def ensure_ends_with(partial)
|
9
|
+
self.ends_with?(partial) ? self : self + partial
|
10
|
+
end
|
11
|
+
|
12
|
+
def normalize
|
13
|
+
self.split(/(?=[A-Z][a-z]*)/).join('_').tr('- ', '_').squeeze('_').downcase
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
3
17
|
module RExchange
|
4
18
|
# Use STDOUT or another stream if you'd like to capture the HTTP debug output
|
5
|
-
DEBUG_STREAM =
|
19
|
+
DEBUG_STREAM = $log
|
6
20
|
|
7
21
|
# A shortcut to RExchange::Session#new's block syntax
|
8
22
|
def self.open(uri, options = {})
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rexchange/generic_item'
|
2
|
+
|
3
|
+
module RExchange
|
4
|
+
class Contact < GenericItem
|
5
|
+
|
6
|
+
attribute_mappings :first_name => 'given_name',
|
7
|
+
:middle_name => 'middlename',
|
8
|
+
:last_name => 'sn',
|
9
|
+
:created_at => 'creationdate',
|
10
|
+
:address => 'mailingstreet',
|
11
|
+
:city => 'mailingcity',
|
12
|
+
:state => 'st',
|
13
|
+
:zip_code => 'mailingpostalcode',
|
14
|
+
:country => 'co',
|
15
|
+
:phone => 'home_phone',
|
16
|
+
:business_phone => 'telephone_number',
|
17
|
+
:fax => 'facsimiletelephonenumber',
|
18
|
+
:email => 'email1',
|
19
|
+
:website => 'businesshomepage',
|
20
|
+
:company => 'o'
|
21
|
+
|
22
|
+
def self.query(path)
|
23
|
+
<<-QBODY
|
24
|
+
<?xml version="1.0"?>
|
25
|
+
<D:searchrequest xmlns:D = "DAV:">
|
26
|
+
<D:sql>
|
27
|
+
SELECT "urn:schemas:contacts:givenName", "urn:schemas:contacts:middlename",
|
28
|
+
"urn:schemas:contacts:sn", "urn:schemas:contacts:title",
|
29
|
+
"urn:schemas:contacts:mailingstreet", "urn:schemas:contacts:mailingcity",
|
30
|
+
"urn:schemas:contacts:st", "urn:schemas:contacts:mailingpostalcode",
|
31
|
+
"urn:schemas:contacts:co", "urn:schemas:contacts:homePhone",
|
32
|
+
"urn:schemas:contacts:telephoneNumber", "urn:schemas:contacts:facsimiletelephonenumber",
|
33
|
+
"urn:schemas:contacts:mobile", "urn:schemas:contacts:email1",
|
34
|
+
"urn:schemas:contacts:businesshomepage", "urn:schemas:contacts:o", "DAV:creationdate"
|
35
|
+
FROM SCOPE('shallow traversal of "#{path}"')
|
36
|
+
WHERE "DAV:ishidden" = false
|
37
|
+
AND "DAV:isfolder" = false
|
38
|
+
AND "DAV:contentclass" = 'urn:content-classes:person'
|
39
|
+
</D:sql>
|
40
|
+
</D:searchrequest>
|
41
|
+
QBODY
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/rexchange/folder.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'rexml/document'
|
2
2
|
require 'net/https'
|
3
|
-
require 'rexchange/extensions'
|
4
3
|
require 'rexchange/dav_search_request'
|
5
4
|
require 'rexchange/message'
|
5
|
+
require 'rexchange/contact'
|
6
6
|
|
7
7
|
module RExchange
|
8
8
|
|
@@ -31,8 +31,12 @@ module RExchange
|
|
31
31
|
|
32
32
|
# Iterate through each RExchange::Message in this folder
|
33
33
|
def each
|
34
|
-
|
35
|
-
|
34
|
+
if @folder =~ /^contacts$/i || @parent =~ /\/contacts\//i
|
35
|
+
get_contacts
|
36
|
+
else
|
37
|
+
get_messages
|
38
|
+
end.each do |item|
|
39
|
+
yield item
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
@@ -46,13 +50,17 @@ module RExchange
|
|
46
50
|
def messages_in(folder)
|
47
51
|
folder.split('/').inject(@credentials.uri.path) do |final_path, current_path|
|
48
52
|
Folder.new(@credentials, final_path, current_path)
|
49
|
-
end.
|
53
|
+
end.get_messages
|
50
54
|
end
|
51
55
|
|
52
|
-
def
|
56
|
+
def get_messages
|
53
57
|
RExchange::Message::find(@credentials, to_s)
|
54
58
|
end
|
55
59
|
|
60
|
+
def get_contacts
|
61
|
+
RExchange::Contact::find(@credentials, to_s)
|
62
|
+
end
|
63
|
+
|
56
64
|
# Join the strings passed in with '/'s between them
|
57
65
|
def self.join(*args)
|
58
66
|
args.collect { |f| f.to_s.ensure_ends_with('/') }.to_s.squeeze('/')
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'rexchange/dav_move_request'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module RExchange
|
7
|
+
class GenericItem
|
8
|
+
include REXML
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
attr_accessor :attributes
|
12
|
+
|
13
|
+
# Used to access the attributes of the item as a hash.
|
14
|
+
def [](key)
|
15
|
+
return @attributes[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Used to access the attributes of the item.
|
19
|
+
def method_missing(sym, *args)
|
20
|
+
return @attributes[sym.to_s] if @attributes.include?(sym.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(session, dav_property_node)
|
24
|
+
@attributes = {}
|
25
|
+
@session = session
|
26
|
+
|
27
|
+
dav_property_node.elements.each do |element|
|
28
|
+
if element.name.normalize =~ /date$/i
|
29
|
+
@attributes[element.name.normalize] = Time::parse(element.text)
|
30
|
+
else
|
31
|
+
@attributes[element.name.normalize] = element.text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.attribute_mappings(mappings)
|
39
|
+
mappings.each_pair do |k,v|
|
40
|
+
define_method k do
|
41
|
+
@attributes[v]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.query(path)
|
47
|
+
raise 'YOU MUST DEFINE THIS'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Retrieve an Array of items (such as Contact, Message, etc)
|
51
|
+
def self.find(credentials, path)
|
52
|
+
response = DavSearchRequest.execute(credentials, :body => query(path))
|
53
|
+
|
54
|
+
items = []
|
55
|
+
xpath_query = "//a:propstat[a:status/text() = 'HTTP/1.1 200 OK']/a:prop"
|
56
|
+
|
57
|
+
Document.new(response.body).elements.each(xpath_query) do |m|
|
58
|
+
items << self.new(credentials, m)
|
59
|
+
end
|
60
|
+
|
61
|
+
return items
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/rexchange/message.rb
CHANGED
@@ -1,40 +1,13 @@
|
|
1
|
-
require '
|
2
|
-
require 'ostruct'
|
3
|
-
require 'rexchange/dav_move_request'
|
1
|
+
require 'rexchange/generic_item'
|
4
2
|
|
5
3
|
module RExchange
|
6
|
-
class Message
|
7
|
-
include REXML
|
8
|
-
include Enumerable
|
9
|
-
|
10
|
-
attr_accessor :attributes
|
4
|
+
class Message < GenericItem
|
11
5
|
|
12
6
|
def to_s
|
13
7
|
"To: #{to}, From: #{from}, Subject: #{subject}"
|
14
8
|
end
|
15
9
|
|
16
|
-
|
17
|
-
def [](key)
|
18
|
-
return @attributes[key]
|
19
|
-
end
|
20
|
-
|
21
|
-
# Used to access the attributes of the message. The following are available:
|
22
|
-
# * href
|
23
|
-
# * from
|
24
|
-
# * to
|
25
|
-
# * subject
|
26
|
-
# * message-id
|
27
|
-
# * date
|
28
|
-
# * importance
|
29
|
-
# * hasattachment
|
30
|
-
def method_missing(sym, *args)
|
31
|
-
return @attributes[sym.to_s] if @attributes.include?(sym.to_s)
|
32
|
-
end
|
33
|
-
|
34
|
-
# This is an alias for httpmail:hasattachment attribute.
|
35
|
-
def has_attachments?
|
36
|
-
return self['hasattachment']
|
37
|
-
end
|
10
|
+
attribute_mappings 'has_attachments?' => 'hasattachment'
|
38
11
|
|
39
12
|
# Returns the body of the message. This is either a httpmail:textdescription,
|
40
13
|
# or a httpmail:htmldescription
|
@@ -42,17 +15,6 @@ module RExchange
|
|
42
15
|
@attributes['textdescription'] || @attributes['htmldescription']
|
43
16
|
end
|
44
17
|
|
45
|
-
def initialize(session, dav_property_node)
|
46
|
-
@attributes = {}
|
47
|
-
@session = session
|
48
|
-
|
49
|
-
dav_property_node.elements.each do |element|
|
50
|
-
@attributes[element.name.tr('-', '_')] = element.text
|
51
|
-
end
|
52
|
-
|
53
|
-
return self
|
54
|
-
end
|
55
|
-
|
56
18
|
# Move this message to the specified folder.
|
57
19
|
# The folder can be a string such as 'inbox/archive' or a RExchange::Folder.
|
58
20
|
# === Example
|
@@ -61,15 +23,18 @@ module RExchange
|
|
61
23
|
# end
|
62
24
|
def move_to(folder)
|
63
25
|
source = URI.parse(self.href).path
|
64
|
-
destination =
|
26
|
+
destination = if folder.is_a?(RExchange::Folder)
|
27
|
+
folder.to_s.ensure_ends_with('/') + source.split('/').last
|
28
|
+
else
|
29
|
+
@session.uri.path.ensure_ends_with('/') + folder.to_s.ensure_ends_with('/') + source.split('/').last
|
30
|
+
end
|
65
31
|
|
32
|
+
$log.debug "move_to: source => \"#{source}\", destination => \"#{destination}\""
|
66
33
|
DavMoveRequest.execute(@session, source, destination)
|
67
34
|
end
|
68
35
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
body = <<DABODY
|
36
|
+
def self.query(path)
|
37
|
+
<<-QBODY
|
73
38
|
<?xml version="1.0"?>
|
74
39
|
<D:searchrequest xmlns:D = "DAV:">
|
75
40
|
<D:sql>
|
@@ -84,18 +49,7 @@ module RExchange
|
|
84
49
|
AND "DAV:contentclass" = 'urn:content-classes:message'
|
85
50
|
</D:sql>
|
86
51
|
</D:searchrequest>
|
87
|
-
|
88
|
-
|
89
|
-
response = DavSearchRequest.execute(credentials, :body => body)
|
90
|
-
|
91
|
-
mail_messages = []
|
92
|
-
xpath_query = "//a:propstat[a:status/text() = 'HTTP/1.1 200 OK']/a:prop"
|
93
|
-
|
94
|
-
Document.new(response.body).elements.each(xpath_query) do |m|
|
95
|
-
mail_messages << Message.new(credentials, m)
|
96
|
-
end
|
97
|
-
|
98
|
-
return mail_messages
|
52
|
+
QBODY
|
99
53
|
end
|
100
54
|
end
|
101
55
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rexchange
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-02-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2006-02-23
|
8
8
|
summary: "A simple wrapper around Microsoft Exchange Server's WebDAV API"
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -31,13 +31,15 @@ files:
|
|
31
31
|
- README
|
32
32
|
- CHANGELOG
|
33
33
|
- RAKEFILE
|
34
|
+
- lib/r_exchange.rb
|
34
35
|
- lib/rexchange.rb
|
36
|
+
- lib/rexchange/contact.rb
|
35
37
|
- lib/rexchange/credentials.rb
|
36
38
|
- lib/rexchange/dav_move_request.rb
|
37
39
|
- lib/rexchange/dav_search_request.rb
|
38
40
|
- lib/rexchange/exchange_request.rb
|
39
|
-
- lib/rexchange/extensions.rb
|
40
41
|
- lib/rexchange/folder.rb
|
42
|
+
- lib/rexchange/generic_item.rb
|
41
43
|
- lib/rexchange/message.rb
|
42
44
|
- lib/rexchange/session.rb
|
43
45
|
- test/functional.rb
|
data/lib/rexchange/extensions.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
class String
|
2
|
-
def ends_with?(partial)
|
3
|
-
self[self.size - partial.size..self.size] == partial
|
4
|
-
end
|
5
|
-
|
6
|
-
def ensure_ends_with(partial)
|
7
|
-
self.ends_with?(partial) ? self : self + partial
|
8
|
-
end
|
9
|
-
|
10
|
-
def normalize
|
11
|
-
self.split(/(?=[A-Z][a-z]*)/).join('_').tr('- ', '_').squeeze('_').downcase
|
12
|
-
end
|
13
|
-
end
|