rews 0.2.12 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -7
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/lib/rews.rb +18 -0
- data/lib/rews/client.rb +175 -7
- data/lib/rews/folder.rb +0 -63
- data/lib/rews/item.rb +46 -1
- data/lib/rews/update.rb +46 -0
- data/lib/rews/util.rb +12 -2
- data/spec/request_proxy.rb +25 -1
- data/spec/rews/client_spec.rb +304 -0
- data/spec/rews/folder_spec.rb +2 -150
- data/spec/rews/item_spec.rb +120 -42
- data/spec/rews/update_spec.rb +20 -0
- data/spec/rews/util_spec.rb +11 -2
- data/spec/rews_spec.rb +21 -0
- data/spec/spec_helper.rb +1 -0
- metadata +34 -13
data/README.rdoc
CHANGED
@@ -29,15 +29,20 @@ to use :
|
|
29
29
|
:indexed_page_item_view=>{:max_entries_returned=>10, :offset=>0})
|
30
30
|
|
31
31
|
# get some properties for a bunch of messages in one hit
|
32
|
-
messages =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
messages = c.get_item(mids, :item_shape=>{
|
33
|
+
:base_shape=>:IdOnly,
|
34
|
+
:additional_properties=>[
|
35
|
+
[:field_uri, "item:Subject"],
|
36
|
+
[:field_uri, "item:DateTimeReceived"],
|
37
|
+
[:field_uri, "message:InternetMessageId"],
|
38
|
+
[:field_uri, "message:IsRead"],
|
39
|
+
[:field_uri, "message:IsReadReceiptRequested"]]})
|
40
|
+
|
41
|
+
# suppress read receipts on any messages which have requested them
|
42
|
+
c.suppress_read_receipt(messages)
|
38
43
|
|
39
44
|
# delete the items to the DeletedItems folder
|
40
|
-
|
45
|
+
c.delete_item(mids, :delete_type=>:MoveToDeletedItems)
|
41
46
|
|
42
47
|
== Install
|
43
48
|
|
data/Rakefile
CHANGED
@@ -13,10 +13,12 @@ begin
|
|
13
13
|
gem.add_dependency "savon", ">= 0.8.6"
|
14
14
|
gem.add_dependency "ntlm-http", ">= 0.1.2"
|
15
15
|
gem.add_dependency "fetch_in", ">= 0.2.0"
|
16
|
+
gem.add_runtime_dependency "rsxml", ">= 0.1.4"
|
16
17
|
gem.add_development_dependency "httpclient", ">= 2.1.7"
|
17
18
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
18
19
|
gem.add_development_dependency "rr", ">= 0.10.5"
|
19
20
|
gem.add_development_dependency "nokogiri", ">= 1.4.4"
|
21
|
+
|
20
22
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
23
|
end
|
22
24
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/rews.rb
CHANGED
@@ -4,18 +4,36 @@ require 'net/ntlm'
|
|
4
4
|
# require 'httpclient' # don't need httpclient now ntlm-http is fixed too
|
5
5
|
require 'savon'
|
6
6
|
require 'fetch_in'
|
7
|
+
require 'rsxml'
|
7
8
|
|
8
9
|
# Ruby Exchange Web Services
|
9
10
|
module Rews
|
10
11
|
WSDL = File.expand_path("../../Services.wsdl", __FILE__)
|
11
12
|
SCHEMA_MESSAGES = "http://schemas.microsoft.com/exchange/services/2006/messages"
|
12
13
|
SCHEMA_TYPES = "http://schemas.microsoft.com/exchange/services/2006/types"
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :logdev
|
17
|
+
end
|
18
|
+
|
19
|
+
module_function
|
20
|
+
|
21
|
+
def logger
|
22
|
+
return @logger if @logger
|
23
|
+
@logger = Logger.new(logdev) if logdev
|
24
|
+
end
|
25
|
+
|
26
|
+
def log
|
27
|
+
yield(logger) if logger
|
28
|
+
nil
|
29
|
+
end
|
13
30
|
end
|
14
31
|
|
15
32
|
require 'rews/util'
|
16
33
|
require 'rews/restriction'
|
17
34
|
require 'rews/shape'
|
18
35
|
require 'rews/sort_order'
|
36
|
+
require 'rews/update'
|
19
37
|
require 'rews/view'
|
20
38
|
require 'rews/folder'
|
21
39
|
require 'rews/item'
|
data/lib/rews/client.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Rews
|
2
2
|
class Client
|
3
|
+
include Util
|
4
|
+
|
3
5
|
attr_reader :endpoint
|
4
6
|
attr_reader :auth_type
|
5
7
|
attr_reader :user
|
6
8
|
attr_reader :password
|
7
9
|
attr_reader :savon_client
|
8
|
-
attr_accessor :logdev
|
9
10
|
|
10
11
|
# create a +Client+ to access Exchange Web Services
|
11
12
|
# * using NTLM authentication
|
@@ -39,14 +40,181 @@ module Rews
|
|
39
40
|
Folder::DistinguishedFolderId.new(self, id, mailbox_email)
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
CREATE_ITEM_OPTS={
|
44
|
+
:items=>nil,
|
45
|
+
:message_disposition=>nil,
|
46
|
+
:send_meeting_invitations=>nil
|
47
|
+
}
|
48
|
+
|
49
|
+
# create items, specifying a list of Rsxml expressions, one for each item in the Items list e.g.
|
50
|
+
#
|
51
|
+
# client.create_item(:items=>[[:suppress_read_receipt, [:reference_item_id, {:id=>"abc", :change_key=>"def"}]]])
|
52
|
+
def create_item(opts={})
|
53
|
+
opts = check_opts(CREATE_ITEM_OPTS, opts)
|
54
|
+
|
55
|
+
items = opts[:items].compact if opts[:items]
|
56
|
+
raise "no items!" if items.empty?
|
57
|
+
|
58
|
+
attrs = {}
|
59
|
+
attrs[:message_disposition] = opts[:message_disposition] if opts[:message_disposition]
|
60
|
+
attrs[:send_meeting_invitations] = opts[:send_meeting_invitations] if opts[:send_meeting_invitations]
|
61
|
+
|
62
|
+
r = with_error_check(self, :create_item_response, :response_messages, :create_item_response_message) do
|
63
|
+
savon_client.request(:wsdl, "CreateItem", attrs) do
|
64
|
+
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/CreateItem\"" # required by EWS 2007
|
65
|
+
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
66
|
+
|
67
|
+
xml = Builder::XmlMarkup.new
|
68
|
+
|
69
|
+
xml.wsdl :Items do
|
70
|
+
items.each do |item|
|
71
|
+
xml << Util.rsxml_to_xml(item)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
soap.body = xml.target!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
r
|
79
|
+
end
|
80
|
+
|
81
|
+
# +iids+ is a list of Items or ItemIds. If +iids+ is a list of Items,
|
82
|
+
# and those Items have +IsRead+ or +IsReadReceiptRequested+ properties then
|
83
|
+
# no +SuppressReadReceipt+ Item will be created if ( +IsRead+=true or
|
84
|
+
# +IsReadReceiptRequested+=false)
|
85
|
+
def suppress_read_receipt(iids)
|
86
|
+
items = iids.map do |item_or_item_id|
|
87
|
+
item_id = item_or_item_id.is_a?(Item::Item) ? item_or_item_id.item_id : item_or_item_id
|
88
|
+
srr = [:suppress_read_receipt, [:reference_item_id, {:id=>item_id.id, :change_key=>item_id.change_key}]]
|
89
|
+
if item_or_item_id.is_a?(Item::Item)
|
90
|
+
attributes = item_or_item_id.attributes
|
91
|
+
if (attributes.has_key?(:is_read) && attributes[:is_read]) ||
|
92
|
+
(attributes.has_key?(:is_read_receipt_requested) &&
|
93
|
+
!attributes[:is_read_receipt_requested])
|
94
|
+
next
|
95
|
+
else
|
96
|
+
srr
|
97
|
+
end
|
98
|
+
else
|
99
|
+
srr
|
100
|
+
end
|
101
|
+
end.compact
|
102
|
+
create_item(:items=>items) if items.length>0
|
45
103
|
end
|
46
104
|
|
47
|
-
|
48
|
-
|
49
|
-
|
105
|
+
GET_ITEM_OPTS = {
|
106
|
+
:item_shape=>Shape::ITEM_SHAPE_OPTS,
|
107
|
+
:ignore_change_keys=>nil
|
108
|
+
}
|
109
|
+
|
110
|
+
# retrieve a bunch of <tt>Item::Item</tt>s in one API hit.
|
111
|
+
# takes a list of <tt>Item::ItemId</tt>s, or a list of <tt>Item::Item</tt>,
|
112
|
+
# or a <tt>Folder::FindResult</tt> and options to specify +Shape::ItemShape+
|
113
|
+
def get_item(message_ids, opts={})
|
114
|
+
opts = check_opts(GET_ITEM_OPTS, opts)
|
115
|
+
message_ids = message_ids.result if message_ids.is_a?(Folder::FindResult)
|
116
|
+
|
117
|
+
r = with_error_check(self, :get_item_response,:response_messages,:get_item_response_message) do
|
118
|
+
self.savon_client.request(:wsdl, "GetItem") do
|
119
|
+
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/GetItem\"" # required by EWS 2007
|
120
|
+
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
121
|
+
|
122
|
+
xml = Builder::XmlMarkup.new
|
123
|
+
|
124
|
+
xml << Shape::ItemShape.new(opts[:item_shape]||{}).to_xml
|
125
|
+
xml.wsdl :ItemIds do
|
126
|
+
message_ids.each do |mid|
|
127
|
+
mid = mid.item_id if mid.is_a?(Item::Item)
|
128
|
+
xml << mid.to_xml(opts[:ignore_change_keys])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
soap.body = xml.target!
|
133
|
+
end
|
134
|
+
end
|
135
|
+
Item.read_get_item_response_messages(self, r)
|
50
136
|
end
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
UPDATE_ITEM_OPTS = {
|
141
|
+
:conflict_resolution => "AutoResolve",
|
142
|
+
:message_disposition => "SaveOnly",
|
143
|
+
:ignore_change_keys=>false,
|
144
|
+
:updates => nil,
|
145
|
+
}
|
146
|
+
|
147
|
+
# bulk update a bunch of Items in one API hit
|
148
|
+
# takes a list of <tt>Item::ItemId</tt>s, or a list of <tt>Item::Item</tt>,
|
149
|
+
# or a <tt>Folder::FindResult</tt> and applies the same set of updates to each of them
|
150
|
+
def update_item(message_ids, opts={})
|
151
|
+
opts = check_opts(UPDATE_ITEM_OPTS, opts)
|
152
|
+
message_ids = message_ids.result if message_ids.is_a?(Folder::FindResult)
|
153
|
+
|
154
|
+
updates = [*opts[:updates]].compact
|
155
|
+
raise "no updates!" if updates.empty?
|
156
|
+
r = with_error_check(self, :update_item_response, :response_messages, :update_item_response_message) do
|
157
|
+
self.savon_client.request(:wsdl, "UpdateItem",
|
158
|
+
:ConflictResolution=>opts[:conflict_resolution],
|
159
|
+
:MessageDisposition=>opts[:message_disposition]) do
|
160
|
+
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/UpdateItem\"" # required by EWS 2007
|
161
|
+
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
162
|
+
|
163
|
+
xml = Builder::XmlMarkup.new
|
164
|
+
|
165
|
+
xml.wsdl :ItemChanges do
|
166
|
+
message_ids.each do |mid|
|
167
|
+
mid = mid.item_id if mid.is_a?(Item::Item)
|
168
|
+
xml.t :ItemChange do
|
169
|
+
xml << mid.to_xml(opts[:ignore_change_keys])
|
170
|
+
xml.t :Updates do
|
171
|
+
updates.each do |update|
|
172
|
+
xml << update.to_xml
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
soap.body = xml.target!
|
180
|
+
end
|
181
|
+
end
|
182
|
+
r
|
183
|
+
end
|
184
|
+
|
185
|
+
DELETE_ITEM_OPTS = {
|
186
|
+
:delete_type! =>nil,
|
187
|
+
:ignore_change_keys=>false
|
188
|
+
}
|
189
|
+
|
190
|
+
# delete a bunch of Items in one API hit.
|
191
|
+
# takes a list of <tt>Item::ItemId</tt>s, or a list of <tt>Item::Item</tt>,
|
192
|
+
# or a <tt>Folder::FindResult</tt> and options to specify DeleteType
|
193
|
+
def delete_item(message_ids, opts={})
|
194
|
+
opts = check_opts(DELETE_ITEM_OPTS, opts)
|
195
|
+
message_ids = message_ids.result if message_ids.is_a?(Folder::FindResult)
|
196
|
+
|
197
|
+
r = with_error_check(self, :delete_item_response, :response_messages, :delete_item_response_message) do
|
198
|
+
self.savon_client.request(:wsdl, "DeleteItem", :DeleteType=>opts[:delete_type]) do
|
199
|
+
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/DeleteItem\"" # required by EWS 2007
|
200
|
+
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
201
|
+
|
202
|
+
xml = Builder::XmlMarkup.new
|
203
|
+
|
204
|
+
xml.wsdl :ItemIds do
|
205
|
+
message_ids.each do |mid|
|
206
|
+
mid = mid.item_id if mid.is_a?(Item::Item)
|
207
|
+
xml << mid.to_xml(opts[:ignore_change_keys])
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
soap.body = xml.target!
|
212
|
+
end
|
213
|
+
end
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
|
51
219
|
end
|
52
220
|
end
|
data/lib/rews/folder.rb
CHANGED
@@ -189,70 +189,7 @@ module Rews
|
|
189
189
|
r
|
190
190
|
end
|
191
191
|
|
192
|
-
GET_ITEM_OPTS = {
|
193
|
-
:item_shape=>Shape::ITEM_SHAPE_OPTS,
|
194
|
-
:ignore_change_keys=>nil
|
195
|
-
}
|
196
|
-
|
197
|
-
# retrieve a bunch of <tt>Item::Item</tt>s in one API hit.
|
198
|
-
# takes a list of <tt>Item::ItemId</tt>s, or a list of <tt>Item::Item</tt>,
|
199
|
-
# or a <tt>Folder::FindResult</tt> and options to specify +Shape::ItemShape+
|
200
|
-
def get_item(message_ids, opts={})
|
201
|
-
opts = check_opts(GET_ITEM_OPTS, opts)
|
202
|
-
message_ids = message_ids.result if message_ids.is_a?(FindResult)
|
203
|
-
|
204
|
-
r = with_error_check(client, :get_item_response,:response_messages,:get_item_response_message) do
|
205
|
-
client.savon_client.request(:wsdl, "GetItem") do
|
206
|
-
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/GetItem\"" # required by EWS 2007
|
207
|
-
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
208
|
-
|
209
|
-
xml = Builder::XmlMarkup.new
|
210
|
-
|
211
|
-
xml << Shape::ItemShape.new(opts[:item_shape]||{}).to_xml
|
212
|
-
xml.wsdl :ItemIds do
|
213
|
-
message_ids.each do |mid|
|
214
|
-
mid = mid.item_id if mid.is_a?(Item::Item)
|
215
|
-
xml << mid.to_xml(opts[:ignore_change_keys])
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
soap.body = xml.target!
|
220
|
-
end
|
221
|
-
end
|
222
|
-
Item.read_get_item_response_messages(client, r)
|
223
|
-
end
|
224
192
|
|
225
|
-
DELETE_ITEM_OPTS = {
|
226
|
-
:delete_type! =>nil,
|
227
|
-
:ignore_change_keys=>false
|
228
|
-
}
|
229
|
-
|
230
|
-
# delete a bunch of Items in one API hit.
|
231
|
-
# takes a list of <tt>Item::ItemId</tt>s, or a list of <tt>Item::Item</tt>,
|
232
|
-
# or a <tt>Folder::FindResult</tt> and options to specify DeleteType
|
233
|
-
def delete_item(message_ids, opts={})
|
234
|
-
opts = check_opts(DELETE_ITEM_OPTS, opts)
|
235
|
-
message_ids = message_ids.result if message_ids.is_a?(FindResult)
|
236
|
-
|
237
|
-
r = with_error_check(client, :delete_item_response, :response_messages, :delete_item_response_message) do
|
238
|
-
client.savon_client.request(:wsdl, "DeleteItem", :DeleteType=>opts[:delete_type]) do
|
239
|
-
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/DeleteItem\"" # required by EWS 2007
|
240
|
-
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
241
|
-
|
242
|
-
xml = Builder::XmlMarkup.new
|
243
|
-
|
244
|
-
xml.wsdl :ItemIds do
|
245
|
-
message_ids.each do |mid|
|
246
|
-
mid = mid.item_id if mid.is_a?(Item::Item)
|
247
|
-
xml << mid.to_xml(opts[:ignore_change_keys])
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
soap.body = xml.target!
|
252
|
-
end
|
253
|
-
end
|
254
|
-
true
|
255
|
-
end
|
256
193
|
end
|
257
194
|
|
258
195
|
# identifies a regular (non-distinguished) Folder on an Exchange server
|
data/lib/rews/item.rb
CHANGED
@@ -32,7 +32,7 @@ module Rews
|
|
32
32
|
@client = client
|
33
33
|
@item_id = ItemId.new(client, attributes[:item_id])
|
34
34
|
@item_class = item_class
|
35
|
-
@attributes = attributes
|
35
|
+
@attributes = attributes || {}
|
36
36
|
end
|
37
37
|
|
38
38
|
def ==(other)
|
@@ -134,6 +134,51 @@ module Rews
|
|
134
134
|
true
|
135
135
|
end
|
136
136
|
|
137
|
+
UPDATE_ITEM_OPTS = {
|
138
|
+
:conflict_resolution => "AutoResolve",
|
139
|
+
:message_disposition => "SaveOnly",
|
140
|
+
:ignore_change_keys=>false,
|
141
|
+
:updates => nil,
|
142
|
+
}
|
143
|
+
|
144
|
+
def update_item(opts={})
|
145
|
+
opts = check_opts(UPDATE_ITEM_OPTS, opts)
|
146
|
+
updates = [*opts[:updates]].compact
|
147
|
+
raise "no updates!" if updates.empty?
|
148
|
+
r = with_error_check(client, :update_item_response, :response_messages, :update_item_response_message) do
|
149
|
+
client.savon_client.request(:wsdl, "UpdateItem",
|
150
|
+
:ConflictResolution=>opts[:conflict_resolution],
|
151
|
+
:MessageDisposition=>opts[:message_disposition]) do
|
152
|
+
http.headers["SOAPAction"] = "\"#{SCHEMA_MESSAGES}/UpdateItem\"" # required by EWS 2007
|
153
|
+
soap.namespaces["xmlns:t"]=SCHEMA_TYPES
|
154
|
+
|
155
|
+
xml = Builder::XmlMarkup.new
|
156
|
+
|
157
|
+
xml.wsdl :ItemChanges do
|
158
|
+
xml.t :ItemChange do
|
159
|
+
xml << self.to_xml(opts[:ignore_change_keys])
|
160
|
+
xml.t :Updates do
|
161
|
+
updates.each do |update|
|
162
|
+
xml << update.to_xml
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
soap.body = xml.target!
|
169
|
+
end
|
170
|
+
end
|
171
|
+
r
|
172
|
+
end
|
173
|
+
|
174
|
+
# sets message:isReadReceiptRequested and message:isDeliveryReceiptRequested
|
175
|
+
# properties of a message to false
|
176
|
+
def set_is_read(is_read=true)
|
177
|
+
update_item(:conflict_resolution=>"AlwaysOverwrite",
|
178
|
+
:message_disposition=>"SaveOnly",
|
179
|
+
:updates=>[SetItemField.new("message:IsRead", [:message,[:is_read, is_read.to_s]])])
|
180
|
+
end
|
181
|
+
|
137
182
|
def to_xml(ignore_change_key=false)
|
138
183
|
xml = Builder::XmlMarkup.new
|
139
184
|
attrs = {:Id=>id.to_s}
|
data/lib/rews/update.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rews
|
2
|
+
class Update
|
3
|
+
attr_reader :field_uri
|
4
|
+
attr_reader :item_expr
|
5
|
+
|
6
|
+
def initialize(field_uri, item_expr=nil)
|
7
|
+
@field_uri = field_uri
|
8
|
+
@item_expr = item_expr
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"#<#{Xml.update_tag(self.class)} @field_uri=#{@field_uri}, @item_expr=#{@item_expr.inspect}>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_xml
|
16
|
+
Xml.write_update(self.class, field_uri, item_expr)
|
17
|
+
end
|
18
|
+
|
19
|
+
module Xml
|
20
|
+
module_function
|
21
|
+
|
22
|
+
def write_update(type, field_uri, item_expr)
|
23
|
+
xml = Builder::XmlMarkup.new
|
24
|
+
xml.t(update_tag(type)) do
|
25
|
+
xml.t :FieldURI, :FieldURI=>field_uri
|
26
|
+
xml << Util.rsxml_to_xml(item_expr)
|
27
|
+
end
|
28
|
+
xml.target!
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_tag(type)
|
32
|
+
# final component of module scoped classname
|
33
|
+
type.to_s[/(?:^|::)([^:]+)$/, 1].to_sym
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class SetItemField < Update
|
39
|
+
end
|
40
|
+
|
41
|
+
class AppendToItemField < Update
|
42
|
+
end
|
43
|
+
|
44
|
+
class DeleteItemField < Update
|
45
|
+
end
|
46
|
+
end
|