rews 0.2.12 → 0.5.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/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
|