viewpoint2 1.3.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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +216 -0
  3. data/lib/ews/calendar_accessors.rb +34 -0
  4. data/lib/ews/connection.rb +130 -0
  5. data/lib/ews/connection_helper.rb +35 -0
  6. data/lib/ews/convert_accessors.rb +56 -0
  7. data/lib/ews/errors.rb +56 -0
  8. data/lib/ews/ews_client.rb +103 -0
  9. data/lib/ews/exceptions/exceptions.rb +61 -0
  10. data/lib/ews/folder_accessors.rb +264 -0
  11. data/lib/ews/impersonation.rb +30 -0
  12. data/lib/ews/item_accessors.rb +231 -0
  13. data/lib/ews/mailbox_accessors.rb +99 -0
  14. data/lib/ews/message_accessors.rb +93 -0
  15. data/lib/ews/push_subscription_accessors.rb +33 -0
  16. data/lib/ews/room_accessors.rb +48 -0
  17. data/lib/ews/roomlist_accessors.rb +47 -0
  18. data/lib/ews/soap.rb +64 -0
  19. data/lib/ews/soap/builders/ews_builder.rb +1351 -0
  20. data/lib/ews/soap/ews_response.rb +84 -0
  21. data/lib/ews/soap/ews_soap_availability_response.rb +58 -0
  22. data/lib/ews/soap/ews_soap_free_busy_response.rb +119 -0
  23. data/lib/ews/soap/ews_soap_response.rb +103 -0
  24. data/lib/ews/soap/ews_soap_room_response.rb +53 -0
  25. data/lib/ews/soap/ews_soap_roomlist_response.rb +54 -0
  26. data/lib/ews/soap/exchange_availability.rb +61 -0
  27. data/lib/ews/soap/exchange_data_services.rb +780 -0
  28. data/lib/ews/soap/exchange_notification.rb +146 -0
  29. data/lib/ews/soap/exchange_synchronization.rb +93 -0
  30. data/lib/ews/soap/exchange_time_zones.rb +56 -0
  31. data/lib/ews/soap/exchange_user_configuration.rb +33 -0
  32. data/lib/ews/soap/exchange_web_service.rb +264 -0
  33. data/lib/ews/soap/parsers/ews_parser.rb +43 -0
  34. data/lib/ews/soap/parsers/ews_sax_document.rb +70 -0
  35. data/lib/ews/soap/response_message.rb +80 -0
  36. data/lib/ews/soap/responses/create_attachment_response_message.rb +47 -0
  37. data/lib/ews/soap/responses/create_item_response_message.rb +25 -0
  38. data/lib/ews/soap/responses/find_item_response_message.rb +80 -0
  39. data/lib/ews/soap/responses/get_events_response_message.rb +53 -0
  40. data/lib/ews/soap/responses/send_notification_response_message.rb +59 -0
  41. data/lib/ews/soap/responses/subscribe_response_message.rb +35 -0
  42. data/lib/ews/soap/responses/sync_folder_hierarchy_response_message.rb +36 -0
  43. data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
  44. data/lib/ews/templates/calendar_item.rb +79 -0
  45. data/lib/ews/templates/forward_item.rb +24 -0
  46. data/lib/ews/templates/message.rb +76 -0
  47. data/lib/ews/templates/reply_to_item.rb +25 -0
  48. data/lib/ews/templates/task.rb +74 -0
  49. data/lib/ews/types.rb +194 -0
  50. data/lib/ews/types/attachment.rb +77 -0
  51. data/lib/ews/types/attendee.rb +41 -0
  52. data/lib/ews/types/calendar_folder.rb +50 -0
  53. data/lib/ews/types/calendar_item.rb +133 -0
  54. data/lib/ews/types/contact.rb +7 -0
  55. data/lib/ews/types/contacts_folder.rb +8 -0
  56. data/lib/ews/types/copied_event.rb +51 -0
  57. data/lib/ews/types/created_event.rb +24 -0
  58. data/lib/ews/types/deleted_event.rb +24 -0
  59. data/lib/ews/types/distribution_list.rb +7 -0
  60. data/lib/ews/types/event.rb +62 -0
  61. data/lib/ews/types/export_items_response_message.rb +52 -0
  62. data/lib/ews/types/file_attachment.rb +65 -0
  63. data/lib/ews/types/folder.rb +60 -0
  64. data/lib/ews/types/free_busy_changed_event.rb +24 -0
  65. data/lib/ews/types/generic_folder.rb +418 -0
  66. data/lib/ews/types/item.rb +450 -0
  67. data/lib/ews/types/item_attachment.rb +84 -0
  68. data/lib/ews/types/item_field_uri_map.rb +208 -0
  69. data/lib/ews/types/mailbox_user.rb +156 -0
  70. data/lib/ews/types/meeting_cancellation.rb +7 -0
  71. data/lib/ews/types/meeting_message.rb +7 -0
  72. data/lib/ews/types/meeting_request.rb +7 -0
  73. data/lib/ews/types/meeting_response.rb +7 -0
  74. data/lib/ews/types/message.rb +7 -0
  75. data/lib/ews/types/modified_event.rb +48 -0
  76. data/lib/ews/types/moved_event.rb +51 -0
  77. data/lib/ews/types/new_mail_event.rb +24 -0
  78. data/lib/ews/types/out_of_office.rb +147 -0
  79. data/lib/ews/types/post_item.rb +7 -0
  80. data/lib/ews/types/search_folder.rb +8 -0
  81. data/lib/ews/types/status_event.rb +39 -0
  82. data/lib/ews/types/task.rb +104 -0
  83. data/lib/ews/types/tasks_folder.rb +27 -0
  84. data/lib/viewpoint/logging.rb +27 -0
  85. data/lib/viewpoint/logging/config.rb +24 -0
  86. data/lib/viewpoint/string_utils.rb +76 -0
  87. data/lib/viewpoint2.rb +111 -0
  88. metadata +191 -0
@@ -0,0 +1,33 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ module Viewpoint::EWS::PushSubscriptionAccessors
20
+ include Viewpoint::EWS
21
+
22
+ def parse_send_notification(msg)
23
+ parser = Viewpoint::EWS::SOAP::EwsParser.new(msg)
24
+ resp = parser.parse response_class: Viewpoint::EWS::SOAP::EwsResponse
25
+ rmsg = resp.response_messages[0]
26
+ if rmsg.success?
27
+ rmsg
28
+ else
29
+ raise EwsSubscriptionError, "#{rmsg.code}: #{rmsg.message_text}"
30
+ end
31
+ end
32
+
33
+ end # Viewpoint::EWS::PushSubscriptionAccessors
@@ -0,0 +1,48 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2013 Camille Baldock <viewpoint@camillebaldock.co.uk>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ module Viewpoint::EWS::RoomAccessors
20
+ include Viewpoint::EWS
21
+
22
+ # Gets the rooms that are available within the specified room distribution list
23
+ # @see http://msdn.microsoft.com/en-us/library/dd899415.aspx
24
+ # @param [String] roomDistributionList
25
+ def get_rooms(roomDistributionList)
26
+ resp = ews.get_rooms(roomDistributionList)
27
+ get_rooms_parser(resp)
28
+ end
29
+
30
+ def room_name( room )
31
+ room[:room][:elems][:id][:elems][0][:name][:text]
32
+ end
33
+
34
+ def room_email( room )
35
+ room[:room][:elems][:id][:elems][1][:email_address][:text]
36
+ end
37
+
38
+ private
39
+
40
+ def get_rooms_parser(resp)
41
+ if resp.success?
42
+ resp
43
+ else
44
+ raise EwsError, "GetRooms produced an error: #{resp.code}: #{resp.message}"
45
+ end
46
+ end
47
+
48
+ end # Viewpoint::EWS::RoomAccessors
@@ -0,0 +1,47 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2013 Camille Baldock <viewpoint@camillebaldock.co.uk>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ module Viewpoint::EWS::RoomlistAccessors
20
+ include Viewpoint::EWS
21
+
22
+ # Gets the room lists that are available within the Exchange organization.
23
+ # @see http://msdn.microsoft.com/en-us/library/dd899416.aspx
24
+ def get_room_lists
25
+ resp = ews.get_room_lists
26
+ get_room_lists_parser(resp)
27
+ end
28
+
29
+ def roomlist_name( roomlist )
30
+ roomlist[:address][:elems][:name][:text]
31
+ end
32
+
33
+ def roomlist_email( roomlist )
34
+ roomlist[:address][:elems][:email_address][:text]
35
+ end
36
+
37
+ private
38
+
39
+ def get_room_lists_parser(resp)
40
+ if resp.success?
41
+ resp
42
+ else
43
+ raise EwsError, "GetRoomLists produced an error: #{resp.code}: #{resp.message}"
44
+ end
45
+ end
46
+
47
+ end # Viewpoint::EWS::RoomlistAccessors
@@ -0,0 +1,64 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ # This module defines some constants and other niceties to make available to
20
+ # the underlying SOAP classes and modules that do the actual work.
21
+ module Viewpoint
22
+ module EWS
23
+ module SOAP
24
+
25
+ # CONSTANTS
26
+
27
+ NS_SOAP = 'soap'.freeze
28
+ NS_EWS_TYPES = 't'.freeze
29
+ NS_EWS_MESSAGES = 'm'.freeze
30
+ NAMESPACES = {
31
+ "xmlns:#{NS_SOAP}" => 'http://schemas.xmlsoap.org/soap/envelope/',
32
+ "xmlns:#{NS_EWS_TYPES}" => 'http://schemas.microsoft.com/exchange/services/2006/types',
33
+ "xmlns:#{NS_EWS_MESSAGES}" => 'http://schemas.microsoft.com/exchange/services/2006/messages',
34
+ }.freeze
35
+
36
+ # used in ResolveNames to determine where names are resolved
37
+ ActiveDirectory = 'ActiveDirectory'
38
+ ActiveDirectoryContacts = 'ActiveDirectoryContacts'
39
+ Contacts = 'Contacts'
40
+ ContactsActiveDirectory = 'ContactsActiveDirectory'
41
+
42
+ # Target specific Exchange Server versions
43
+ # @see http://msdn.microsoft.com/en-us/library/bb891876(v=exchg.140).aspx
44
+ VERSION_2007 = 'Exchange2007'
45
+ VERSION_2007_SP1 = 'Exchange2007_SP1'
46
+ VERSION_2010 = 'Exchange2010'
47
+ VERSION_2010_SP1 = 'Exchange2010_SP1'
48
+ VERSION_2010_SP2 = 'Exchange2010_SP2'
49
+ VERSION_2013 = 'Exchange2013'
50
+ VERSION_2013_SP1 = 'Exchange2013_SP1'
51
+ VERSION_NONE = 'none'
52
+
53
+ HARD_DELETE = 'HardDelete'
54
+ SOFT_DELETE = 'SoftDelete'
55
+ MOVE_TO_DELETED_ITEMS = 'MoveToDeletedItems'
56
+
57
+ def initialize
58
+ @log = Logging.logger[self.class.name.to_s.to_sym]
59
+ @default_ns = NAMESPACES["xmlns:#{NS_EWS_MESSAGES}"]
60
+ end
61
+
62
+ end # SOAP
63
+ end # EWS
64
+ end # Viewpoint
@@ -0,0 +1,1351 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+ module Viewpoint::EWS::SOAP
19
+
20
+ # This class includes the element builders. The idea is that each element should
21
+ # know how to build themselves so each parent element can delegate creation of
22
+ # subelements to a method of the same name with a '!' after it.
23
+ class EwsBuilder
24
+ include Viewpoint::EWS
25
+ include Viewpoint::StringUtils
26
+
27
+ attr_reader :nbuild
28
+ def initialize
29
+ @nbuild = Nokogiri::XML::Builder.new
30
+ end
31
+
32
+ # Build the SOAP envelope and yield this object so subelements can be built. Once
33
+ # you have the EwsBuilder object you can use the nbuild object like shown in the
34
+ # example for the Header section. The nbuild object is the underlying
35
+ # Nokogiri::XML::Builder object.
36
+ # @param [Hash] opts
37
+ # @option opts [String] :server_version The version string that should get
38
+ # set in the Header. See ExchangeWebService#initialize
39
+ # @option opts [Hash] :time_zone_context TimeZoneDefinition. Format: !{id: time_zone_identifier}
40
+ # @example
41
+ # xb = EwsBuilder.new
42
+ # xb.build! do |part, b|
43
+ # if(part == :header)
44
+ # b.nbuild.MyVar('blablabla')
45
+ # else
46
+ # b.folder_shape!({:base_shape => 'Default'})
47
+ # end
48
+ # end
49
+ def build!(opts = {}, &block)
50
+ @nbuild.Envelope(NAMESPACES) do |node|
51
+ node.parent.namespace = parent_namespace(node)
52
+ node.Header {
53
+ set_version_header! opts[:server_version]
54
+ set_impersonation! opts[:impersonation_type], opts[:impersonation_mail]
55
+ set_time_zone_context_header! opts[:time_zone_context]
56
+ yield(:header, self) if block_given?
57
+ }
58
+ node.Body {
59
+ yield(:body, self) if block_given?
60
+ }
61
+ end
62
+ @nbuild.doc
63
+ end
64
+
65
+ # Build XML from a passed in Hash or Array in a specified format.
66
+ # @param [Array,Hash] elems The elements to add to the Builder. They must
67
+ # be specified like so:
68
+ #
69
+ # !{:top =>
70
+ # { :xmlns => 'http://stonesthrow/soap',
71
+ # :sub_elements => [
72
+ # {:elem1 => {:text => 'inside'}},
73
+ # {:elem2 => {:text => 'inside2'}}
74
+ # ],
75
+ # :id => '3232', :tx_dd => 23, :asdf => 'turkey'
76
+ # }
77
+ # }
78
+ # or
79
+ # [ {:first => {:text => 'hello'}},
80
+ # {:second => {:text => 'world'}}
81
+ # ]
82
+ #
83
+ # NOTE: there are specialized keys for text (:text), child elements
84
+ # (:sub_elements) and namespaces (:xmlns).
85
+ def build_xml!(elems)
86
+ case elems.class.name
87
+ when 'Hash'
88
+ if attendees = elems[:required_attendees]
89
+ required_attendees!(attendees)
90
+ else
91
+ keys = elems.keys
92
+ vals = elems.values
93
+ if(keys.length > 1 && !vals.is_a?(Hash))
94
+ raise "invalid input: #{elems}"
95
+ end
96
+ vals = vals.first.clone
97
+ se = vals.delete(:sub_elements)
98
+ txt = vals.delete(:text)
99
+ xmlns_attribute = vals.delete(:xmlns_attribute)
100
+
101
+ node = @nbuild.send(camel_case(keys.first), txt, vals) {|x|
102
+ build_xml!(se) if se
103
+ }
104
+
105
+ # Set node level namespace
106
+ node.xmlns = NAMESPACES["xmlns:#{xmlns_attribute}"] if xmlns_attribute
107
+ end
108
+ when 'Array'
109
+ elems.each do |e|
110
+ build_xml!(e)
111
+ end
112
+ else
113
+ raise "Unsupported type: #{elems.class.name}"
114
+ end
115
+ end
116
+
117
+ # Build the FolderShape element
118
+ # @see http://msdn.microsoft.com/en-us/library/aa494311.aspx
119
+ # @param [Hash] folder_shape The folder shape structure to build from
120
+ # @todo need fully support all options
121
+ def folder_shape!(folder_shape)
122
+ @nbuild.FolderShape {
123
+ @nbuild.parent.default_namespace = @default_ns
124
+ base_shape!(folder_shape[:base_shape])
125
+ if(folder_shape[:additional_properties])
126
+ additional_properties!(folder_shape[:additional_properties])
127
+ end
128
+ }
129
+ end
130
+
131
+ # Build the ItemShape element
132
+ # @see http://msdn.microsoft.com/en-us/library/aa565261.aspx
133
+ # @param [Hash] item_shape The item shape structure to build from
134
+ # @todo need fully support all options
135
+ def item_shape!(item_shape)
136
+ @nbuild[NS_EWS_MESSAGES].ItemShape {
137
+ @nbuild.parent.default_namespace = @default_ns
138
+ base_shape!(item_shape[:base_shape])
139
+ mime_content!(item_shape[:include_mime_content]) if item_shape.has_key?(:include_mime_content)
140
+ body_type!(item_shape[:body_type]) if item_shape[:body_type]
141
+ if(item_shape[:additional_properties])
142
+ additional_properties!(item_shape[:additional_properties])
143
+ end
144
+ }
145
+ end
146
+
147
+ # Build the IndexedPageItemView element
148
+ # @see http://msdn.microsoft.com/en-us/library/exchange/aa563549(v=exchg.150).aspx
149
+ # @todo needs peer check
150
+ def indexed_page_item_view!(indexed_page_item_view)
151
+ attribs = {}
152
+ indexed_page_item_view.each_pair {|k,v| attribs[camel_case(k)] = v.to_s}
153
+ @nbuild[NS_EWS_MESSAGES].IndexedPageItemView(attribs)
154
+ end
155
+
156
+ # Build the BaseShape element
157
+ # @see http://msdn.microsoft.com/en-us/library/aa580545.aspx
158
+ def base_shape!(base_shape)
159
+ @nbuild[NS_EWS_TYPES].BaseShape(camel_case(base_shape))
160
+ end
161
+
162
+ def mime_content!(include_mime_content)
163
+ @nbuild[NS_EWS_TYPES].IncludeMimeContent(include_mime_content.to_s.downcase)
164
+ end
165
+
166
+ def body_type!(body_type)
167
+ body_type = body_type.to_s
168
+ if body_type =~ /html/i
169
+ body_type = body_type.upcase
170
+ else
171
+ body_type = body_type.downcase.capitalize
172
+ end
173
+ nbuild[NS_EWS_TYPES].BodyType(body_type)
174
+ end
175
+
176
+ # Build the ParentFolderIds element
177
+ # @see http://msdn.microsoft.com/en-us/library/aa565998.aspx
178
+ def parent_folder_ids!(pfids)
179
+ @nbuild[NS_EWS_MESSAGES].ParentFolderIds {
180
+ pfids.each do |pfid|
181
+ dispatch_folder_id!(pfid)
182
+ end
183
+ }
184
+ end
185
+
186
+ # Build the ParentFolderId element
187
+ # @see http://msdn.microsoft.com/en-us/library/aa563268.aspx
188
+ def parent_folder_id!(pfid)
189
+ @nbuild.ParentFolderId {
190
+ dispatch_folder_id!(pfid)
191
+ }
192
+ end
193
+
194
+ # Build the FolderIds element
195
+ # @see http://msdn.microsoft.com/en-us/library/aa580509.aspx
196
+ def folder_ids!(fids, act_as=nil)
197
+ ns = @nbuild.parent.name.match(/subscription/i) ? NS_EWS_TYPES : NS_EWS_MESSAGES
198
+ @nbuild[ns].FolderIds {
199
+ fids.each do |fid|
200
+ fid[:act_as] = act_as if act_as != nil
201
+ dispatch_folder_id!(fid)
202
+ end
203
+ }
204
+ end
205
+
206
+ # Build the SyncFolderId element
207
+ # @see http://msdn.microsoft.com/en-us/library/aa580296.aspx
208
+ def sync_folder_id!(fid)
209
+ @nbuild.SyncFolderId {
210
+ dispatch_folder_id!(fid)
211
+ }
212
+ end
213
+
214
+ # Build the DistinguishedFolderId element
215
+ # @see http://msdn.microsoft.com/en-us/library/aa580808.aspx
216
+ # @todo add support for the Mailbox child object
217
+ def distinguished_folder_id!(dfid, change_key = nil, act_as = nil)
218
+ attribs = {'Id' => dfid.to_s}
219
+ attribs['ChangeKey'] = change_key if change_key
220
+ @nbuild[NS_EWS_TYPES].DistinguishedFolderId(attribs) {
221
+ if ! act_as.nil?
222
+ mailbox!({:email_address => act_as})
223
+ end
224
+ }
225
+ end
226
+
227
+ # Build the FolderId element
228
+ # @see http://msdn.microsoft.com/en-us/library/aa579461.aspx
229
+ def folder_id!(fid, change_key = nil)
230
+ attribs = {'Id' => fid}
231
+ attribs['ChangeKey'] = change_key if change_key
232
+ @nbuild[NS_EWS_TYPES].FolderId(attribs)
233
+ end
234
+
235
+ # @see http://msdn.microsoft.com/en-us/library/aa563525(v=EXCHG.140).aspx
236
+ def item_ids!(item_ids)
237
+ @nbuild.ItemIds {
238
+ item_ids.each do |iid|
239
+ dispatch_item_id!(iid)
240
+ end
241
+ }
242
+ end
243
+
244
+ def parent_item_id!(id)
245
+ nbuild.ParentItemId {|x|
246
+ x.parent['Id'] = id[:id]
247
+ x.parent['ChangeKey'] = id[:change_key] if id[:change_key]
248
+ }
249
+ end
250
+
251
+ # @see http://msdn.microsoft.com/en-us/library/aa580234(v=EXCHG.140).aspx
252
+ def item_id!(id)
253
+ nbuild[NS_EWS_TYPES].ItemId {|x|
254
+ x.parent['Id'] = id[:id]
255
+ x.parent['ChangeKey'] = id[:change_key] if id[:change_key]
256
+ }
257
+ end
258
+
259
+ # @see http://msdn.microsoft.com/en-us/library/ff709503(v=exchg.140).aspx
260
+ def export_item_ids!(item_ids)
261
+ ns = @nbuild.parent.name.match(/subscription/i) ? NS_EWS_TYPES : NS_EWS_MESSAGES
262
+ @nbuild[ns].ExportItems{
263
+ @nbuild.ItemIds {
264
+ item_ids.each do |iid|
265
+ dispatch_item_id!(iid)
266
+ end
267
+ }
268
+ }
269
+ end
270
+
271
+ # @see http://msdn.microsoft.com/en-us/library/aa580744(v=EXCHG.140).aspx
272
+ def occurrence_item_id!(id)
273
+ @nbuild[NS_EWS_TYPES].OccurrenceItemId {|x|
274
+ x.parent['RecurringMasterId'] = id[:recurring_master_id]
275
+ x.parent['ChangeKey'] = id[:change_key] if id[:change_key]
276
+ x.parent['InstanceIndex'] = id[:instance_index]
277
+ }
278
+ end
279
+
280
+ # @see http://msdn.microsoft.com/en-us/library/aa581019(v=EXCHG.140).aspx
281
+ def recurring_master_item_id!(id)
282
+ @nbuild[NS_EWS_TYPES].RecurringMasterItemId {|x|
283
+ x.parent['OccurrenceId'] = id[:occurrence_id]
284
+ x.parent['ChangeKey'] = id[:change_key] if id[:change_key]
285
+ }
286
+ end
287
+
288
+ # @see http://msdn.microsoft.com/en-us/library/aa565020(v=EXCHG.140).aspx
289
+ def to_folder_id!(to_fid)
290
+ @nbuild[NS_EWS_MESSAGES].ToFolderId {
291
+ dispatch_folder_id!(to_fid)
292
+ }
293
+ end
294
+
295
+ # @see http://msdn.microsoft.com/en-us/library/aa564009.aspx
296
+ def folders!(folders)
297
+ @nbuild.Folders {|x|
298
+ folders.each do |fold|
299
+ fold.each_pair do |ftype, vars| # convenience, should only be one pair
300
+ ftype = "#{ftype}!".to_sym
301
+ if self.respond_to? ftype
302
+ self.send ftype, vars
303
+ else
304
+ raise Viewpoint::EWS::EwsNotImplemented,
305
+ "#{ftype} not implemented as a builder."
306
+ end
307
+ end
308
+ end
309
+ }
310
+ end
311
+
312
+ def folder!(folder, type = :Folder)
313
+ nbuild[NS_EWS_TYPES].send(type) {|x|
314
+ folder.each_pair do |e,v|
315
+ ftype = "#{e}!".to_sym
316
+ if e == :folder_id
317
+ dispatch_folder_id!(v)
318
+ elsif self.respond_to?(ftype)
319
+ self.send ftype, v
320
+ else
321
+ raise Viewpoint::EWS::EwsNotImplemented,
322
+ "#{ftype} not implemented as a builder."
323
+ end
324
+ end
325
+ }
326
+ end
327
+
328
+ def calendar_folder!(folder)
329
+ folder! folder, :CalendarFolder
330
+ end
331
+
332
+ def contacts_folder!(folder)
333
+ folder! folder, :ContactsFolder
334
+ end
335
+
336
+ def search_folder!(folder)
337
+ folder! folder, :SearchFolder
338
+ end
339
+
340
+ def tasks_folder!(folder)
341
+ folder! folder, :TasksFolder
342
+ end
343
+
344
+ def display_name!(name)
345
+ nbuild[NS_EWS_TYPES].DisplayName(name)
346
+ end
347
+
348
+ # Build the AdditionalProperties element
349
+ # @see http://msdn.microsoft.com/en-us/library/aa563810.aspx
350
+ def additional_properties!(addprops)
351
+ @nbuild[NS_EWS_TYPES].AdditionalProperties {
352
+ addprops.each_pair {|k,v|
353
+ dispatch_field_uri!({k => v}, NS_EWS_TYPES)
354
+ }
355
+ }
356
+ end
357
+
358
+ # Build the Mailbox element.
359
+ # This element is commonly used for delegation. Typically passing an
360
+ # email_address is sufficient
361
+ # @see http://msdn.microsoft.com/en-us/library/aa565036.aspx
362
+ # @param [Hash] mailbox A well-formated hash
363
+ def mailbox!(mbox)
364
+ nbuild[NS_EWS_TYPES].Mailbox {
365
+ name!(mbox[:name]) if mbox[:name]
366
+ email_address!(mbox[:email_address]) if mbox[:email_address]
367
+ address!(mbox[:address]) if mbox[:address] # for Availability query
368
+ routing_type!(mbox[:routing_type]) if mbox[:routing_type]
369
+ mailbox_type!(mbox[:mailbox_type]) if mbox[:mailbox_type]
370
+ item_id!(mbox[:item_id]) if mbox[:item_id]
371
+ }
372
+ end
373
+
374
+ def name!(name)
375
+ nbuild[NS_EWS_TYPES].Name(name)
376
+ end
377
+
378
+ def email_address!(email)
379
+ nbuild[NS_EWS_TYPES].EmailAddress(email)
380
+ end
381
+
382
+ def address!(email)
383
+ nbuild[NS_EWS_TYPES].Address(email)
384
+ end
385
+
386
+ # This is stupid. The only valid value is "SMTP"
387
+ def routing_type!(type)
388
+ nbuild[NS_EWS_TYPES].RoutingType(type)
389
+ end
390
+
391
+ def mailbox_type!(type)Standard
392
+ nbuild[NS_EWS_TYPES].MailboxType(type)
393
+ end
394
+
395
+ def user_oof_settings!(opts)
396
+ nbuild.UserOofSettings { |node|
397
+ node.parent[:xmlns] = NAMESPACES["xmlns:#{NS_EWS_TYPES}"]
398
+ nbuild.OofState(camel_case(opts[:oof_state]))
399
+ nbuild.ExternalAudience(camel_case(opts[:external_audience])) if opts[:external_audience]
400
+ duration!(opts[:duration]) if opts[:duration]
401
+ nbuild.InternalReply {
402
+ nbuild.Message(opts[:internal_reply])
403
+ } if opts[:internal_reply]
404
+ nbuild.ExternalReply {
405
+ nbuild.Message(opts[:external_reply])
406
+ } if opts[:external_reply]
407
+ }
408
+ end
409
+
410
+ def duration!(opts)
411
+ nbuild.Duration {
412
+ nbuild.StartTime(format_time opts[:start_time])
413
+ nbuild.EndTime(format_time opts[:end_time])
414
+ }
415
+ end
416
+
417
+ def mailbox_data!(md)
418
+ nbuild[NS_EWS_TYPES].MailboxData {
419
+ nbuild[NS_EWS_TYPES].Email {
420
+ mbox = md[:email]
421
+ name!(mbox[:name]) if mbox[:name]
422
+ address!(mbox[:address]) if mbox[:address] # for Availability query
423
+ routing_type!(mbox[:routing_type]) if mbox[:routing_type]
424
+ }
425
+ nbuild[NS_EWS_TYPES].AttendeeType 'Required'
426
+ }
427
+ end
428
+
429
+ def free_busy_view_options!(opts)
430
+ nbuild[NS_EWS_TYPES].FreeBusyViewOptions {
431
+ nbuild[NS_EWS_TYPES].TimeWindow {
432
+ nbuild[NS_EWS_TYPES].StartTime(format_time opts[:time_window][:start_time])
433
+ nbuild[NS_EWS_TYPES].EndTime(format_time opts[:time_window][:end_time])
434
+ }
435
+ nbuild[NS_EWS_TYPES].RequestedView(camel_case(opts[:requested_view][:requested_free_busy_view]))
436
+ }
437
+ end
438
+
439
+ def suggestions_view_options!(opts)
440
+ end
441
+
442
+ def time_zone!(zone)
443
+ zone ||= {}
444
+ zone = {
445
+ bias: zone[:bias] || 480,
446
+ standard_time: {
447
+ bias: 0,
448
+ time: "02:00:00",
449
+ day_order: 5,
450
+ month: 10,
451
+ day_of_week: 'Sunday'
452
+ }.merge(zone[:standard_time] || {}),
453
+ daylight_time: {
454
+ bias: -60,
455
+ time: "02:00:00",
456
+ day_order: 1,
457
+ month: 4,
458
+ day_of_week: 'Sunday'
459
+ }.merge(zone[:daylight_time] || {})
460
+ }
461
+
462
+ nbuild[NS_EWS_TYPES].TimeZone {
463
+ nbuild[NS_EWS_TYPES].Bias(zone[:bias])
464
+ nbuild[NS_EWS_TYPES].StandardTime {
465
+ nbuild[NS_EWS_TYPES].Bias(zone[:standard_time][:bias])
466
+ nbuild[NS_EWS_TYPES].Time(zone[:standard_time][:time])
467
+ nbuild[NS_EWS_TYPES].DayOrder(zone[:standard_time][:day_order])
468
+ nbuild[NS_EWS_TYPES].Month(zone[:standard_time][:month])
469
+ nbuild[NS_EWS_TYPES].DayOfWeek(zone[:standard_time][:day_of_week])
470
+ }
471
+ nbuild[NS_EWS_TYPES].DaylightTime {
472
+ nbuild[NS_EWS_TYPES].Bias(zone[:daylight_time][:bias])
473
+ nbuild[NS_EWS_TYPES].Time(zone[:daylight_time][:time])
474
+ nbuild[NS_EWS_TYPES].DayOrder(zone[:daylight_time][:day_order])
475
+ nbuild[NS_EWS_TYPES].Month(zone[:daylight_time][:month])
476
+ nbuild[NS_EWS_TYPES].DayOfWeek(zone[:daylight_time][:day_of_week])
477
+ }
478
+ }
479
+ end
480
+
481
+ # Request all known time_zones from server
482
+ def get_server_time_zones!(get_time_zone_options)
483
+ nbuild[NS_EWS_MESSAGES].GetServerTimeZones('ReturnFullTimeZoneData' => get_time_zone_options[:full]) do
484
+ if get_time_zone_options[:ids] && get_time_zone_options[:ids].any?
485
+ nbuild[NS_EWS_MESSAGES].Ids do
486
+ get_time_zone_options[:ids].each do |id|
487
+ nbuild[NS_EWS_TYPES].Id id
488
+ end
489
+ end
490
+ end
491
+ end
492
+ end
493
+
494
+ # Specifies an optional time zone for the start time
495
+ # @param [Hash] attributes
496
+ # @option attributes :id [String] ID of the Microsoft well known time zone
497
+ # @option attributes :name [String] Optional name of the time zone
498
+ # @todo Implement sub elements Periods, TransitionsGroups and Transitions to override zone
499
+ # @see http://msdn.microsoft.com/en-us/library/exchange/dd899524.aspx
500
+ def start_time_zone!(zone)
501
+ attributes = {}
502
+ attributes['Id'] = zone[:id] if zone[:id]
503
+ attributes['Name'] = zone[:name] if zone[:name]
504
+ nbuild[NS_EWS_TYPES].StartTimeZone(attributes)
505
+ end
506
+
507
+ # Specifies an optional time zone for the end time
508
+ # @param [Hash] attributes
509
+ # @option attributes :id [String] ID of the Microsoft well known time zone
510
+ # @option attributes :name [String] Optional name of the time zone
511
+ # @todo Implement sub elements Periods, TransitionsGroups and Transitions to override zone
512
+ # @see http://msdn.microsoft.com/en-us/library/exchange/dd899434.aspx
513
+ def end_time_zone!(zone)
514
+ attributes = {}
515
+ attributes['Id'] = zone[:id] if zone[:id]
516
+ attributes['Name'] = zone[:name] if zone[:name]
517
+ nbuild[NS_EWS_TYPES].EndTimeZone(attributes)
518
+ end
519
+
520
+ # Specify a time zone
521
+ # @todo Implement subelements Periods, TransitionsGroups and Transitions to override zone
522
+ # @see http://msdn.microsoft.com/en-us/library/exchange/dd899488.aspx
523
+ def time_zone_definition!(zone)
524
+ attributes = {'Id' => zone[:id]}
525
+ attributes['Name'] = zone[:name] if zone[:name]
526
+ nbuild[NS_EWS_TYPES].TimeZoneDefinition(attributes)
527
+ end
528
+
529
+ # Build the Restriction element
530
+ # @see http://msdn.microsoft.com/en-us/library/aa563791.aspx
531
+ # @param [Hash] restriction a well-formatted Hash that can be fed to #build_xml!
532
+ def restriction!(restriction)
533
+ @nbuild[NS_EWS_MESSAGES].Restriction {
534
+ restriction.each_pair do |k,v|
535
+ self.send normalize_type(k), v
536
+ end
537
+ }
538
+ end
539
+
540
+ def and_r(expr)
541
+ and_or('And', expr)
542
+ end
543
+
544
+ def or_r(expr)
545
+ and_or('Or', expr)
546
+ end
547
+
548
+ def and_or(type, expr)
549
+ @nbuild[NS_EWS_TYPES].send(type) {
550
+ expr.each do |e|
551
+ type = e.keys.first
552
+ self.send normalize_type(type), e[type]
553
+ end
554
+ }
555
+ end
556
+
557
+ def not_r(expr)
558
+ @nbuild[NS_EWS_TYPES].Not {
559
+ type = expr.keys.first
560
+ self.send(type, expr[type])
561
+ }
562
+ end
563
+
564
+ def contains(expr)
565
+ @nbuild[NS_EWS_TYPES].Contains(
566
+ 'ContainmentMode' => expr.delete(:containment_mode),
567
+ 'ContainmentComparison' => expr.delete(:containment_comparison)) {
568
+ c = expr.delete(:constant) # remove constant 1st for ordering
569
+ type = expr.keys.first
570
+ self.send(type, expr[type])
571
+ constant(c)
572
+ }
573
+ end
574
+
575
+ def excludes(expr)
576
+ @nbuild[NS_EWS_TYPES].Excludes {
577
+ b = expr.delete(:bitmask) # remove bitmask 1st for ordering
578
+ type = expr.keys.first
579
+ self.send(type, expr[type])
580
+ bitmask(b)
581
+ }
582
+ end
583
+
584
+ def exists(expr)
585
+ @nbuild[NS_EWS_TYPES].Exists {
586
+ type = expr.keys.first
587
+ self.send(type, expr[type])
588
+ }
589
+ end
590
+
591
+ def bitmask(expr)
592
+ @nbuild[NS_EWS_TYPES].Bitmask('Value' => expr[:value])
593
+ end
594
+
595
+ def is_equal_to(expr)
596
+ restriction_compare('IsEqualTo',expr)
597
+ end
598
+
599
+ def is_greater_than(expr)
600
+ restriction_compare('IsGreaterThan',expr)
601
+ end
602
+
603
+ def is_greater_than_or_equal_to(expr)
604
+ restriction_compare('IsGreaterThanOrEqualTo',expr)
605
+ end
606
+
607
+ def is_less_than(expr)
608
+ restriction_compare('IsLessThan',expr)
609
+ end
610
+
611
+ def is_less_than_or_equal_to(expr)
612
+ restriction_compare('IsLessThanOrEqualTo',expr)
613
+ end
614
+
615
+ def is_not_equal_to(expr)
616
+ restriction_compare('IsNotEqualTo',expr)
617
+ end
618
+
619
+ def restriction_compare(type,expr)
620
+ nbuild[NS_EWS_TYPES].send(type) {
621
+ expr.each do |e|
622
+ e.each_pair do |k,v|
623
+ self.send(k, v)
624
+ end
625
+ end
626
+ }
627
+ end
628
+
629
+ def ews_types_builder
630
+ nbuild[NS_EWS_TYPES]
631
+ end
632
+
633
+ def field_uRI(expr)
634
+ value = expr.is_a?(Hash) ? (expr[:field_uRI] || expr[:field_uri]) : expr
635
+ ews_types_builder.FieldURI('FieldURI' => value)
636
+ end
637
+
638
+ alias_method :field_uri, :field_uRI
639
+
640
+ def indexed_field_uRI(expr)
641
+ nbuild[NS_EWS_TYPES].IndexedFieldURI(
642
+ 'FieldURI' => (expr[:field_uRI] || expr[:field_uri]),
643
+ 'FieldIndex' => expr[:field_index]
644
+ )
645
+ end
646
+
647
+ alias_method :indexed_field_uri, :indexed_field_uRI
648
+
649
+ def extended_field_uRI(expr)
650
+ nbuild[NS_EWS_TYPES].ExtendedFieldURI {
651
+ nbuild.parent['DistinguishedPropertySetId'] = expr[:distinguished_property_set_id] if expr[:distinguished_property_set_id]
652
+ nbuild.parent['PropertySetId'] = expr[:property_set_id] if expr[:property_set_id]
653
+ nbuild.parent['PropertyTag'] = expr[:property_tag] if expr[:property_tag]
654
+ nbuild.parent['PropertyName'] = expr[:property_name] if expr[:property_name]
655
+ nbuild.parent['PropertyId'] = expr[:property_id] if expr[:property_id]
656
+ nbuild.parent['PropertyType'] = expr[:property_type] if expr[:property_type]
657
+ }
658
+ end
659
+
660
+ alias_method :extended_field_uri, :extended_field_uRI
661
+
662
+ def extended_properties!(eprops)
663
+ eprops.each {|ep| extended_property!(ep)}
664
+ end
665
+
666
+ def extended_property!(eprop)
667
+ nbuild[NS_EWS_TYPES].ExtendedProperty {
668
+ key = eprop.keys.grep(/extended/i).first
669
+ dispatch_field_uri!({key => eprop[key]}, NS_EWS_TYPES)
670
+ if eprop[:values]
671
+ nbuild.Values {
672
+ eprop[:values].each do |v|
673
+ value! v
674
+ end
675
+ }
676
+ elsif eprop[:value]
677
+ value! eprop[:value]
678
+ end
679
+ }
680
+ end
681
+
682
+ def value!(val)
683
+ nbuild[NS_EWS_TYPES].Value(val)
684
+ end
685
+
686
+ def field_uRI_or_constant(expr)
687
+ nbuild[NS_EWS_TYPES].FieldURIOrConstant {
688
+ type = expr.keys.first
689
+ self.send(type, expr[type])
690
+ }
691
+ end
692
+
693
+ alias_method :field_uri_or_constant, :field_uRI_or_constant
694
+
695
+ def constant(expr)
696
+ nbuild[NS_EWS_TYPES].Constant('Value' => expr[:value])
697
+ end
698
+
699
+ # Build the CalendarView element
700
+ def calendar_view!(cal_view)
701
+ attribs = {}
702
+ cal_view.each_pair {|k,v| attribs[camel_case(k)] = v.to_s}
703
+ @nbuild[NS_EWS_MESSAGES].CalendarView(attribs)
704
+ end
705
+
706
+ # Build the ContactsView element
707
+ def contacts_view!(con_view)
708
+ attribs = {}
709
+ con_view.each_pair {|k,v| attribs[camel_case(k)] = v.to_s}
710
+ @nbuild[NS_EWS_MESSAGES].ContactsView(attribs)
711
+ end
712
+
713
+ # @see http://msdn.microsoft.com/en-us/library/aa579678(v=EXCHG.140).aspx
714
+ def event_types!(evtypes)
715
+ @nbuild[NS_EWS_TYPES].EventTypes {
716
+ evtypes.each do |et|
717
+ @nbuild[NS_EWS_TYPES].EventType(camel_case(et))
718
+ end
719
+ }
720
+ end
721
+
722
+ # @see http://msdn.microsoft.com/en-us/library/aa565886(v=EXCHG.140).aspx
723
+ def watermark!(wmark, ns = NS_EWS_TYPES)
724
+ @nbuild[ns].Watermark(wmark)
725
+ end
726
+
727
+ # @see http://msdn.microsoft.com/en-us/library/aa565201(v=EXCHG.140).aspx
728
+ def timeout!(tout)
729
+ @nbuild[NS_EWS_TYPES].Timeout(tout)
730
+ end
731
+
732
+ # @see http://msdn.microsoft.com/en-us/library/aa564048(v=EXCHG.140).aspx
733
+ def status_frequency!(freq)
734
+ @nbuild[NS_EWS_TYPES].StatusFrequency(freq)
735
+ end
736
+
737
+ # @see http://msdn.microsoft.com/en-us/library/aa566309(v=EXCHG.140).aspx
738
+ def uRL!(url)
739
+ @nbuild[NS_EWS_TYPES].URL(url)
740
+ end
741
+
742
+ # @see http://msdn.microsoft.com/en-us/library/aa563790(v=EXCHG.140).aspx
743
+ def subscription_id!(subid)
744
+ @nbuild.SubscriptionId(subid)
745
+ end
746
+
747
+ # @see http://msdn.microsoft.com/en-us/library/aa563455(v=EXCHG.140).aspx
748
+ def pull_subscription_request(subopts)
749
+ subscribe_all = subopts[:subscribe_to_all_folders] ? 'true' : 'false'
750
+ @nbuild.PullSubscriptionRequest('SubscribeToAllFolders' => subscribe_all) {
751
+ folder_ids!(subopts[:folder_ids]) if subopts[:folder_ids]
752
+ event_types!(subopts[:event_types]) if subopts[:event_types]
753
+ watermark!(subopts[:watermark]) if subopts[:watermark]
754
+ timeout!(subopts[:timeout]) if subopts[:timeout]
755
+ }
756
+ end
757
+
758
+ # @see http://msdn.microsoft.com/en-us/library/aa563599(v=EXCHG.140).aspx
759
+ def push_subscription_request(subopts)
760
+ subscribe_all = subopts[:subscribe_to_all_folders] ? 'true' : 'false'
761
+ @nbuild.PushSubscriptionRequest('SubscribeToAllFolders' => subscribe_all) {
762
+ folder_ids!(subopts[:folder_ids]) if subopts[:folder_ids]
763
+ event_types!(subopts[:event_types]) if subopts[:event_types]
764
+ watermark!(subopts[:watermark]) if subopts[:watermark]
765
+ status_frequency!(subopts[:status_frequency]) if subopts[:status_frequency]
766
+ uRL!(subopts[:uRL]) if subopts[:uRL]
767
+ }
768
+ end
769
+
770
+ # @see http://msdn.microsoft.com/en-us/library/ff406182(v=EXCHG.140).aspx
771
+ def streaming_subscription_request(subopts)
772
+ subscribe_all = subopts[:subscribe_to_all_folders] ? 'true' : 'false'
773
+ @nbuild.StreamingSubscriptionRequest('SubscribeToAllFolders' => subscribe_all) {
774
+ folder_ids!(subopts[:folder_ids]) if subopts[:folder_ids]
775
+ event_types!(subopts[:event_types]) if subopts[:event_types]
776
+ }
777
+ end
778
+
779
+ # @see http://msdn.microsoft.com/en-us/library/aa565970(v=EXCHG.140).aspx
780
+ def sync_state!(syncstate)
781
+ @nbuild.SyncState(syncstate)
782
+ end
783
+
784
+ # @see http://msdn.microsoft.com/en-us/library/aa563785(v=EXCHG.140).aspx
785
+ def ignore!(item_ids)
786
+ @nbuild.Ignore {
787
+ item_ids.each do |iid|
788
+ item_id!(iid)
789
+ end
790
+ }
791
+ end
792
+
793
+ # @see http://msdn.microsoft.com/en-us/library/aa566325(v=EXCHG.140).aspx
794
+ def max_changes_returned!(cnum)
795
+ @nbuild[NS_EWS_MESSAGES].MaxChangesReturned(cnum)
796
+ end
797
+
798
+ # @see http://msdn.microsoft.com/en-us/library/dd899531(v=EXCHG.140).aspx
799
+ def sync_scope!(scope)
800
+ @nbuild.SyncScope(scope)
801
+ end
802
+
803
+ # @see http://msdn.microsoft.com/en-us/library/aa580758(v=EXCHG.140).aspx
804
+ def saved_item_folder_id!(fid)
805
+ @nbuild.SavedItemFolderId {
806
+ dispatch_folder_id!(fid)
807
+ }
808
+ end
809
+
810
+ # @see http://msdn.microsoft.com/en-us/library/aa565652(v=exchg.140).aspx
811
+ def item!(item)
812
+ nbuild.Item {
813
+ item.each_pair {|k,v|
814
+ self.send("#{k}!", v)
815
+ }
816
+ }
817
+ end
818
+
819
+ def message!(item)
820
+ nbuild[NS_EWS_TYPES].Message {
821
+ if item[:extended_properties]
822
+ extended_properties! item.delete(:extended_properties)
823
+ end
824
+ item.each_pair {|k,v|
825
+ self.send("#{k}!", v)
826
+ }
827
+ }
828
+ end
829
+
830
+ def is_read!(read)
831
+ nbuild[NS_EWS_TYPES].IsRead(read)
832
+ end
833
+
834
+ def calendar_item!(item)
835
+ nbuild[NS_EWS_TYPES].CalendarItem {
836
+ item.each_pair {|k,v|
837
+ self.send("#{k}!", v)
838
+ }
839
+ }
840
+ end
841
+
842
+ def calendar_item_type!(type)
843
+ nbuild[NS_EWS_TYPES].CalendarItemType(type)
844
+ end
845
+
846
+ def recurrence!(item)
847
+ nbuild[NS_EWS_TYPES].Recurrence {
848
+ item.each_pair { |k, v|
849
+ self.send("#{k}!", v)
850
+ }
851
+ }
852
+ end
853
+
854
+ def daily_recurrence!(item)
855
+ nbuild[NS_EWS_TYPES].DailyRecurrence {
856
+ item.each_pair { |k, v|
857
+ self.send("#{k}!", v)
858
+ }
859
+ }
860
+ end
861
+
862
+ def weekly_recurrence!(item)
863
+ nbuild[NS_EWS_TYPES].WeeklyRecurrence {
864
+ item.each_pair { |k, v|
865
+ self.send("#{k}!", v)
866
+ }
867
+ }
868
+ end
869
+
870
+ def interval!(num)
871
+ nbuild[NS_EWS_TYPES].Interval(num)
872
+ end
873
+
874
+ def no_end_recurrence!(item)
875
+ nbuild[NS_EWS_TYPES].NoEndRecurrence {
876
+ item.each_pair { |k, v|
877
+ self.send("#{k}!", v)
878
+ }
879
+ }
880
+ end
881
+
882
+ def numbered_recurrence!(item)
883
+ nbuild[NS_EWS_TYPES].NumberedRecurrence {
884
+ item.each_pair { |k, v|
885
+ self.send("#{k}!", v)
886
+ }
887
+ }
888
+ end
889
+
890
+ def number_of_occurrences!(count)
891
+ nbuild[NS_EWS_TYPES].NumberOfOccurrences(count)
892
+ end
893
+
894
+
895
+ def task!(item)
896
+ nbuild[NS_EWS_TYPES].Task {
897
+ item.each_pair {|k, v|
898
+ self.send("#{k}!", v)
899
+ }
900
+ }
901
+ end
902
+
903
+ def forward_item!(item)
904
+ nbuild[NS_EWS_TYPES].ForwardItem {
905
+ item.each_pair {|k,v|
906
+ self.send("#{k}!", v)
907
+ }
908
+ }
909
+ end
910
+
911
+ def reply_to_item!(item)
912
+ nbuild[NS_EWS_TYPES].ReplyToItem {
913
+ item.each_pair {|k,v|
914
+ self.send("#{k}!", v)
915
+ }
916
+ }
917
+ end
918
+
919
+ def reply_all_to_item!(item)
920
+ nbuild[NS_EWS_TYPES].ReplyAllToItem {
921
+ item.each_pair {|k,v|
922
+ self.send("#{k}!", v)
923
+ }
924
+ }
925
+ end
926
+
927
+ def reference_item_id!(id)
928
+ nbuild[NS_EWS_TYPES].ReferenceItemId {|x|
929
+ x.parent['Id'] = id[:id]
930
+ x.parent['ChangeKey'] = id[:change_key] if id[:change_key]
931
+ }
932
+ end
933
+
934
+ def subject!(sub)
935
+ nbuild[NS_EWS_TYPES].Subject(sub)
936
+ end
937
+
938
+ def importance!(sub)
939
+ nbuild[NS_EWS_TYPES].Importance(sub)
940
+ end
941
+
942
+ def body!(b)
943
+ nbuild[NS_EWS_TYPES].Body(b[:text]) {|x|
944
+ x.parent['BodyType'] = b[:body_type] if b[:body_type]
945
+ }
946
+ end
947
+
948
+ def new_body_content!(b)
949
+ nbuild[NS_EWS_TYPES].NewBodyContent(b[:text]) {|x|
950
+ x.parent['BodyType'] = b[:body_type] if b[:body_type]
951
+ }
952
+ end
953
+
954
+ # @see http://msdn.microsoft.com/en-us/library/aa563719(v=exchg.140).aspx
955
+ # @param [Array] r An array of Mailbox type hashes to send to #mailbox!
956
+ def to_recipients!(r)
957
+ nbuild[NS_EWS_TYPES].ToRecipients {
958
+ r.each {|mbox| mailbox!(mbox[:mailbox]) }
959
+ }
960
+ end
961
+
962
+ def cc_recipients!(r)
963
+ nbuild[NS_EWS_TYPES].CcRecipients {
964
+ r.each {|mbox| mailbox!(mbox[:mailbox]) }
965
+ }
966
+ end
967
+
968
+ def bcc_recipients!(r)
969
+ nbuild[NS_EWS_TYPES].BccRecipients {
970
+ r.each {|mbox| mailbox!(mbox[:mailbox]) }
971
+ }
972
+ end
973
+
974
+ def from!(f)
975
+ nbuild[NS_EWS_TYPES].From {
976
+ mailbox! f
977
+ }
978
+ end
979
+
980
+ def required_attendees!(attendees)
981
+ nbuild[NS_EWS_TYPES].RequiredAttendees {
982
+ attendees.each {|a| attendee!(a[:attendee])}
983
+ }
984
+ end
985
+
986
+ def optional_attendees!(attendees)
987
+ nbuild[NS_EWS_TYPES].OptionalAttendees {
988
+ attendees.each {|a| attendee!(a[:attendee])}
989
+ }
990
+ end
991
+
992
+ def resources!(attendees)
993
+ nbuild[NS_EWS_TYPES].Resources {
994
+ attendees.each {|a| attendee!(a[:attendee])}
995
+ }
996
+ end
997
+
998
+ def attendee!(a)
999
+ wrap = Array.wrap(a)
1000
+ nbuild[NS_EWS_TYPES].Attendee {
1001
+ wrap.each do |item|
1002
+ if item[:mailbox]
1003
+ mailbox!(item[:mailbox])
1004
+ elsif item[:response_type]
1005
+ response_type!(item[:response_type])
1006
+ elsif item[:last_response_time]
1007
+ last_response_time!(item[:last_response_time])
1008
+ end
1009
+ end
1010
+ }
1011
+ end
1012
+
1013
+ def response_type!(type)
1014
+ nbuild[NS_EWS_TYPES].ResponseType(type)
1015
+ end
1016
+
1017
+ def last_response_time!(time)
1018
+ nbuild[NS_EWS_TYPES].LastResponseTime(format_time time)
1019
+ end
1020
+
1021
+ def start!(st)
1022
+ nbuild[NS_EWS_TYPES].Start(st[:text])
1023
+ end
1024
+
1025
+ def end!(et)
1026
+ nbuild[NS_EWS_TYPES].End(et[:text])
1027
+ end
1028
+
1029
+ def start_date!(sd)
1030
+ nbuild[NS_EWS_TYPES].StartDate sd[:text]
1031
+ end
1032
+
1033
+ def due_date!(dd)
1034
+ nbuild[NS_EWS_TYPES].DueDate format_time(dd[:text])
1035
+ end
1036
+
1037
+ def location!(loc)
1038
+ nbuild[NS_EWS_TYPES].Location(loc)
1039
+ end
1040
+
1041
+ def is_all_day_event!(all_day)
1042
+ nbuild[NS_EWS_TYPES].IsAllDayEvent(all_day)
1043
+ end
1044
+
1045
+ def reminder_is_set!(reminder)
1046
+ nbuild[NS_EWS_TYPES].ReminderIsSet reminder
1047
+ end
1048
+
1049
+ def reminder_due_by!(date)
1050
+ nbuild[NS_EWS_TYPES].ReminderDueBy format_time(date)
1051
+ end
1052
+
1053
+ def reminder_minutes_before_start!(minutes)
1054
+ nbuild[NS_EWS_TYPES].ReminderMinutesBeforeStart minutes
1055
+ end
1056
+
1057
+ # @see http://msdn.microsoft.com/en-us/library/aa566143(v=exchg.150).aspx
1058
+ # possible values Exchange Server 2010 = [Free, Tentative, Busy, OOF, NoData]
1059
+ # Exchange Server 2013 = [Free, Tentative, Busy, OOF, WorkingElsewhere, NoData]
1060
+ def legacy_free_busy_status!(state)
1061
+ nbuild[NS_EWS_TYPES].LegacyFreeBusyStatus(state)
1062
+ end
1063
+
1064
+ # @see http://msdn.microsoft.com/en-us/library/aa565428(v=exchg.140).aspx
1065
+ def item_changes!(changes)
1066
+ nbuild.ItemChanges {
1067
+ changes.each do |chg|
1068
+ item_change!(chg)
1069
+ end
1070
+ }
1071
+ end
1072
+
1073
+ # @see http://msdn.microsoft.com/en-us/library/aa581081(v=exchg.140).aspx
1074
+ def item_change!(change)
1075
+ @nbuild[NS_EWS_TYPES].ItemChange {
1076
+ updates = change.delete(:updates) # Remove updates so dispatch_item_id works correctly
1077
+ dispatch_item_id!(change)
1078
+ updates!(updates)
1079
+ }
1080
+ end
1081
+
1082
+ # @see http://msdn.microsoft.com/en-us/library/aa581074(v=exchg.140).aspx
1083
+ def updates!(updates)
1084
+ @nbuild[NS_EWS_TYPES].Updates {
1085
+ updates.each do |update|
1086
+ dispatch_update_type!(update)
1087
+ end
1088
+ }
1089
+ end
1090
+
1091
+ # @see http://msdn.microsoft.com/en-us/library/aa581317(v=exchg.140).aspx
1092
+ def append_to_item_field!(upd)
1093
+ uri = upd.select {|k,v| k =~ /_uri/i}
1094
+ raise EwsBadArgumentError, "Bad argument given for AppendToItemField." if uri.keys.length != 1
1095
+ upd.delete(uri.keys.first)
1096
+ @nbuild.AppendToItemField {
1097
+ dispatch_field_uri!(uri)
1098
+ dispatch_field_item!(upd)
1099
+ }
1100
+ end
1101
+
1102
+ # @see http://msdn.microsoft.com/en-us/library/aa581487(v=exchg.140).aspx
1103
+ def set_item_field!(upd)
1104
+ uri = upd.select {|k,v| k =~ /_uri/i}
1105
+ raise EwsBadArgumentError, "Bad argument given for SetItemField." if uri.keys.length != 1
1106
+ upd.delete(uri.keys.first)
1107
+ @nbuild[NS_EWS_TYPES].SetItemField {
1108
+ dispatch_field_uri!(uri, NS_EWS_TYPES)
1109
+ dispatch_field_item!(upd, NS_EWS_TYPES)
1110
+ }
1111
+ end
1112
+
1113
+ # @see http://msdn.microsoft.com/en-us/library/aa580330(v=exchg.140).aspx
1114
+ def delete_item_field!(upd)
1115
+ uri = upd.select {|k,v| k =~ /_uri/i}
1116
+ raise EwsBadArgumentError, "Bad argument given for SetItemField." if uri.keys.length != 1
1117
+ @nbuild[NS_EWS_TYPES].DeleteItemField {
1118
+ dispatch_field_uri!(uri, NS_EWS_TYPES)
1119
+ }
1120
+ end
1121
+
1122
+ # @see http://msdn.microsoft.com/en-us/library/ff709497(v=exchg.140).aspx
1123
+ def return_new_item_ids!(retval)
1124
+ @nbuild.ReturnNewItemIds(retval)
1125
+ end
1126
+
1127
+ def inline_attachment!(fa)
1128
+ @nbuild[NS_EWS_TYPES].FileAttachment {
1129
+ @nbuild[NS_EWS_TYPES].Name(fa.name)
1130
+ @nbuild[NS_EWS_TYPES].ContentId(fa.name)
1131
+ @nbuild[NS_EWS_TYPES].IsInline(true)
1132
+ @nbuild[NS_EWS_TYPES].Content(fa.content)
1133
+ }
1134
+ end
1135
+
1136
+ def file_attachment!(fa)
1137
+ @nbuild[NS_EWS_TYPES].FileAttachment {
1138
+ @nbuild[NS_EWS_TYPES].Name(fa.name)
1139
+ @nbuild[NS_EWS_TYPES].Content(fa.content)
1140
+ }
1141
+ end
1142
+
1143
+ def item_attachment!(ia)
1144
+ @nbuild[NS_EWS_TYPES].ItemAttachment {
1145
+ @nbuild[NS_EWS_TYPES].Name(ia.name)
1146
+ @nbuild[NS_EWS_TYPES].Item {
1147
+ item_id!(ia.item)
1148
+ }
1149
+ }
1150
+ end
1151
+
1152
+ # Build the AttachmentIds element
1153
+ # @see http://msdn.microsoft.com/en-us/library/aa580686.aspx
1154
+ def attachment_ids!(aids)
1155
+ @nbuild.AttachmentIds {
1156
+ @nbuild.parent.default_namespace = @default_ns
1157
+ aids.each do |aid|
1158
+ attachment_id!(aid)
1159
+ end
1160
+ }
1161
+ end
1162
+
1163
+ # Build the AttachmentId element
1164
+ # @see http://msdn.microsoft.com/en-us/library/aa580764.aspx
1165
+ def attachment_id!(aid)
1166
+ attribs = {'Id' => aid}
1167
+ @nbuild[NS_EWS_TYPES].AttachmentId(attribs)
1168
+ end
1169
+
1170
+ def user_configuration_name!(cfg_name)
1171
+ attribs = {'Name' => cfg_name.delete(:name)}
1172
+ @nbuild[NS_EWS_MESSAGES].UserConfigurationName(attribs) {
1173
+ fid = cfg_name.keys.first
1174
+ self.send "#{fid}!", cfg_name[fid][:id], cfg_name[fid][:change_key]
1175
+ }
1176
+ end
1177
+
1178
+ def user_configuration_properties!(cfg_prop)
1179
+ @nbuild[NS_EWS_MESSAGES].UserConfigurationProperties(cfg_prop)
1180
+ end
1181
+
1182
+ # ---------------------- Helpers -------------------- #
1183
+
1184
+ # A helper method to dispatch to a FolderId or DistinguishedFolderId correctly
1185
+ # @param [Hash] fid A folder_id
1186
+ # Ex: {:id => myid, :change_key => ck}
1187
+ def dispatch_folder_id!(fid)
1188
+ if(fid[:id].is_a?(String))
1189
+ folder_id!(fid[:id], fid[:change_key])
1190
+ elsif(fid[:id].is_a?(Symbol))
1191
+ distinguished_folder_id!(fid[:id], fid[:change_key], fid[:act_as])
1192
+ else
1193
+ raise EwsBadArgumentError, "Bad argument given for a FolderId. #{fid[:id].class}"
1194
+ end
1195
+ end
1196
+
1197
+ # A helper method to dispatch to an ItemId, OccurrenceItemId, or a RecurringMasterItemId
1198
+ # @param [Hash] iid The item id of some type
1199
+ def dispatch_item_id!(iid)
1200
+ type = iid.keys.first
1201
+ item = iid[type]
1202
+ case type
1203
+ when :item_id
1204
+ item_id!(item)
1205
+ when :occurrence_item_id
1206
+ occurrence_item_id!(
1207
+ item[:recurring_master_id], item[:change_key], item[:instance_index])
1208
+ when :recurring_master_item_id
1209
+ recurring_master_item_id!(item[:occurrence_id], item[:change_key])
1210
+ else
1211
+ raise EwsBadArgumentError, "Bad ItemId type. #{type}"
1212
+ end
1213
+ end
1214
+
1215
+ # A helper method to dispatch to a AppendToItemField, SetItemField, or
1216
+ # DeleteItemField
1217
+ # @param [Hash] update An update of some type
1218
+ def dispatch_update_type!(update)
1219
+ type = update.keys.first
1220
+ upd = update[type]
1221
+ case type
1222
+ when :append_to_item_field
1223
+ append_to_item_field!(upd)
1224
+ when :set_item_field
1225
+ set_item_field!(upd)
1226
+ when :delete_item_field
1227
+ delete_item_field!(upd)
1228
+ else
1229
+ raise EwsBadArgumentError, "Bad Update type. #{type}"
1230
+ end
1231
+ end
1232
+
1233
+ # A helper to dispatch to a FieldURI, IndexedFieldURI, or an ExtendedFieldURI
1234
+ # @todo Implement ExtendedFieldURI
1235
+ def dispatch_field_uri!(uri, ns=NS_EWS_MESSAGES)
1236
+ type = uri.keys.first
1237
+ vals = uri[type].is_a?(Array) ? uri[type] : [uri[type]]
1238
+ case type
1239
+ when :field_uRI, :field_uri
1240
+ vals.each do |val|
1241
+ value = val.is_a?(Hash) ? val[type] : val
1242
+ nbuild[ns].FieldURI('FieldURI' => value)
1243
+ end
1244
+ when :indexed_field_uRI, :indexed_field_uri
1245
+ vals.each do |val|
1246
+ nbuild[ns].IndexedFieldURI(
1247
+ 'FieldURI' => (val[:field_uRI] || val[:field_uri]),
1248
+ 'FieldIndex' => val[:field_index]
1249
+ )
1250
+ end
1251
+ when :extended_field_uRI, :extended_field_uri
1252
+ vals.each do |val|
1253
+ nbuild[ns].ExtendedFieldURI {
1254
+ nbuild.parent['DistinguishedPropertySetId'] = val[:distinguished_property_set_id] if val[:distinguished_property_set_id]
1255
+ nbuild.parent['PropertySetId'] = val[:property_set_id] if val[:property_set_id]
1256
+ nbuild.parent['PropertyTag'] = val[:property_tag] if val[:property_tag]
1257
+ nbuild.parent['PropertyName'] = val[:property_name] if val[:property_name]
1258
+ nbuild.parent['PropertyId'] = val[:property_id] if val[:property_id]
1259
+ nbuild.parent['PropertyType'] = val[:property_type] if val[:property_type]
1260
+ }
1261
+ end
1262
+ else
1263
+ raise EwsBadArgumentError, "Bad URI type. #{type}"
1264
+ end
1265
+ end
1266
+
1267
+ # Insert item, enforce xmlns attribute if prefix is present
1268
+ def dispatch_field_item!(item, ns_prefix = nil)
1269
+ item.values.first[:xmlns_attribute] = ns_prefix if ns_prefix
1270
+ build_xml!(item)
1271
+ end
1272
+
1273
+ def room_list!(cfg_prop)
1274
+ @nbuild[NS_EWS_MESSAGES].RoomList {
1275
+ email_address!(cfg_prop)
1276
+ }
1277
+ end
1278
+
1279
+ def room_lists!
1280
+ @nbuild[NS_EWS_MESSAGES].GetRoomLists
1281
+ end
1282
+
1283
+
1284
+ private
1285
+
1286
+ def parent_namespace(node)
1287
+ node.parent.namespace_definitions.find {|ns| ns.prefix == NS_SOAP}
1288
+ end
1289
+
1290
+ def set_version_header!(version)
1291
+ if version && !(version == 'none')
1292
+ nbuild[NS_EWS_TYPES].RequestServerVersion {|x|
1293
+ x.parent['Version'] = version
1294
+ }
1295
+ end
1296
+ end
1297
+
1298
+ def set_impersonation!(type, address)
1299
+ if type && type != ""
1300
+ nbuild[NS_EWS_TYPES].ExchangeImpersonation {
1301
+ nbuild[NS_EWS_TYPES].ConnectingSID {
1302
+ nbuild[NS_EWS_TYPES].method_missing type, address
1303
+ }
1304
+ }
1305
+ end
1306
+ end
1307
+
1308
+ # Set TimeZoneContext Header
1309
+ # @param time_zone_def [Hash] !{id: time_zone_identifier, name: time_zone_name}
1310
+ def set_time_zone_context_header!(time_zone_def)
1311
+ if time_zone_def
1312
+ nbuild[NS_EWS_TYPES].TimeZoneContext do
1313
+ time_zone_definition! time_zone_def
1314
+ end
1315
+ end
1316
+ end
1317
+
1318
+ def meeting_time_zone!(mtz)
1319
+ nbuild[NS_EWS_TYPES].MeetingTimeZone do |x|
1320
+ x.parent['TimeZoneName'] = mtz[:time_zone_name] if mtz[:time_zone_name]
1321
+ nbuild[NS_EWS_TYPES].BaseOffset(mtz[:base_offset][:text]) if mtz[:base_offset]
1322
+ end
1323
+ end
1324
+
1325
+ # some methods need special naming so they use the '_r' suffix like 'and'
1326
+ def normalize_type(type)
1327
+ case type
1328
+ when :and, :or, :not
1329
+ "#{type}_r".to_sym
1330
+ else
1331
+ type
1332
+ end
1333
+ end
1334
+
1335
+ def format_time(time)
1336
+ case time
1337
+ when Time, Date, DateTime
1338
+ time.to_datetime.new_offset(0).iso8601
1339
+ when String
1340
+ begin
1341
+ DateTime.parse(time).new_offset(0).iso8601
1342
+ rescue ArgumentError
1343
+ raise EwsBadArgumentError, "Invalid Time argument (#{time})"
1344
+ end
1345
+ else
1346
+ raise EwsBadArgumentError, "Invalid Time argument (#{time})"
1347
+ end
1348
+ end
1349
+
1350
+ end # EwsBuilder
1351
+ end # Viewpoint::EWS::SOAP