viewpoint 1.0.0.beta.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +16 -11
  3. data/lib/ews/connection.rb +15 -6
  4. data/lib/ews/convert_accessors.rb +56 -0
  5. data/lib/ews/errors.rb +56 -0
  6. data/lib/ews/ews_client.rb +33 -3
  7. data/lib/ews/exceptions/exceptions.rb +2 -0
  8. data/lib/ews/folder_accessors.rb +66 -1
  9. data/lib/ews/impersonation.rb +30 -0
  10. data/lib/ews/item_accessors.rb +77 -3
  11. data/lib/ews/mailbox_accessors.rb +6 -1
  12. data/lib/ews/message_accessors.rb +9 -2
  13. data/lib/ews/room_accessors.rb +48 -0
  14. data/lib/ews/roomlist_accessors.rb +47 -0
  15. data/lib/ews/soap.rb +1 -0
  16. data/lib/ews/soap/builders/ews_builder.rb +303 -48
  17. data/lib/ews/soap/ews_response.rb +2 -1
  18. data/lib/ews/soap/ews_soap_free_busy_response.rb +13 -3
  19. data/lib/ews/soap/ews_soap_response.rb +1 -1
  20. data/lib/ews/soap/ews_soap_room_response.rb +53 -0
  21. data/lib/ews/soap/ews_soap_roomlist_response.rb +54 -0
  22. data/lib/ews/soap/exchange_data_services.rb +49 -11
  23. data/lib/ews/soap/exchange_synchronization.rb +93 -0
  24. data/lib/ews/soap/exchange_time_zones.rb +56 -0
  25. data/lib/ews/soap/exchange_web_service.rb +36 -66
  26. data/lib/ews/soap/parsers/ews_sax_document.rb +8 -4
  27. data/lib/ews/soap/response_message.rb +3 -1
  28. data/lib/ews/soap/responses/create_attachment_response_message.rb +2 -1
  29. data/lib/ews/soap/responses/send_notification_response_message.rb +2 -1
  30. data/lib/{extensions/string.rb → ews/soap/responses/sync_folder_hierarchy_response_message.rb} +18 -17
  31. data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
  32. data/lib/ews/templates/calendar_item.rb +78 -0
  33. data/lib/ews/templates/message.rb +8 -1
  34. data/lib/ews/templates/task.rb +74 -0
  35. data/lib/ews/types.rb +59 -11
  36. data/lib/ews/types/calendar_folder.rb +42 -0
  37. data/lib/ews/types/calendar_item.rb +93 -1
  38. data/lib/ews/types/export_items_response_message.rb +52 -0
  39. data/lib/ews/types/generic_folder.rb +70 -2
  40. data/lib/ews/types/item.rb +64 -4
  41. data/lib/ews/types/item_attachment.rb +41 -3
  42. data/lib/ews/types/item_field_uri_map.rb +1 -1
  43. data/lib/ews/types/task.rb +30 -0
  44. data/lib/ews/types/tasks_folder.rb +19 -0
  45. data/lib/viewpoint.rb +14 -14
  46. data/lib/viewpoint/logging.rb +27 -0
  47. data/lib/viewpoint/logging/config.rb +24 -0
  48. data/lib/viewpoint/string_utils.rb +76 -0
  49. metadata +82 -76
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d907a62a27fdf84a5f753b2319e08bc2cbee8cf
4
+ data.tar.gz: 9bcbc3643c74e3bcb3cbb59390025196f39fa8eb
5
+ SHA512:
6
+ metadata.gz: abb2801cebc4a0cc1e2cffae1271c0f7cac0410dcf59cfef1752cea8f57db0b052ae23ea20e8d85afd8dd5ade056e00f4884c8a3675d2f0f523e31bd6ebbe60a
7
+ data.tar.gz: 9cf44489f68a0f70894674d1d82080ffe352e340f96ba22053e003810c88088beada8f4a43491feacce88add6a59da369259301a34c3ec356879c618183cd5d1
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- **ATTENTION:** If you want to fix a bug in the currently released gem please use the 0.1.x-support branch. The master is the current development branch and is not ready for prime-time just yet.
1
+ [![Build Status](https://travis-ci.org/WinRb/Viewpoint.svg?branch=master)](https://travis-ci.org/WinRb/Viewpoint)
2
2
 
3
3
  # Viewpoint for Exchange Web Services 1.0
4
4
  http://github.com/zenchild/Viewpoint/wiki
@@ -33,13 +33,18 @@ out of the picture this is no longer required. Go crazy ;)
33
33
  ## Enhanced in 1.0
34
34
 
35
35
  * *Delegate access is supported*
36
- One thing that was often asked for, but missing from the previous version
37
- was delegate access to mailboxes and calendars. This is now supported via
38
- the 'act_as' parameter to the GenericFolder::get_folder method. For example:
39
- ofolder = Folder.get_folder(:inbox,'otheruser@test.com')
40
- If your user has delegate access to the Inbox for otheruser@test.com this
41
- operation will retrieve their inbox and allow you to manipulate it as you
42
- would with your own Inbox.
36
+ One thing that was often asked for, but missing from the previous version was delegate access to mailboxes and calendars. This is now supported via the 'act_as' parameter to the GenericFolder::get_folder method.
37
+
38
+ >> Inbox example:
39
+ ```ofolder = Folder.get_folder(:inbox, opts = {act_as: "user@host.com"})```
40
+
41
+ >> If your user has delegate access to the Inbox for user@host.com this operation will retrieve their inbox and allow you to manipulate it as you would with your own Inbox.
42
+
43
+ >> Calendar example:
44
+ ```oCalendar = cli.get_folder(:calendar, opts = {act_as: "user@host.com"})```
45
+
46
+ >> If your user has delegate access to the Calendar for user@host.com this operation will retrieve their calendar and allow you to manipulate it as you would with your own Calendar, depending on the permissions the other user has granted you.
47
+
43
48
 
44
49
  * There is also some support for manipulation of delegate access itself via
45
50
  the methods MailboxUser#add_delegate!, MailboxUser#update_delegate!, and
@@ -127,12 +132,12 @@ items = inbox.items
127
132
  inbox.todays_items
128
133
 
129
134
  # since a specific date
130
- sd = Date.iso8061 '2013-01-01'
135
+ sd = Date.iso8601 '2013-01-01'
131
136
  inbox.items_since sd
132
137
 
133
138
  # between 2 dates
134
- sd = Date.iso8061 '2013-01-01'
135
- ed = Date.iso8061 '2013-02-01'
139
+ sd = Date.iso8601 '2013-01-01'
140
+ ed = Date.iso8601 '2013-02-01'
136
141
  inbox.items_between sd, ed
137
142
  ```
138
143
  ### Free/Busy Calendar Accessors
@@ -25,10 +25,17 @@ class Viewpoint::EWS::Connection
25
25
  # @param [String] endpoint the URL of the web service.
26
26
  # @example https://<site>/ews/Exchange.asmx
27
27
  # @param [Hash] opts Misc config options (mostly for developement)
28
- # @option opts [Fixnum] ssl_verify_mode
28
+ # @option opts [Fixnum] :ssl_verify_mode
29
+ # @option opts [Array] :trust_ca an array of hashed dir paths or a file
29
30
  def initialize(endpoint, opts = {})
30
31
  @log = Logging.logger[self.class.name.to_s.to_sym]
31
32
  @httpcli = HTTPClient.new
33
+ if opts[:trust_ca]
34
+ @httpcli.ssl_config.clear_cert_store
35
+ opts[:trust_ca].each do |ca|
36
+ @httpcli.ssl_config.add_trust_ca ca
37
+ end
38
+ end
32
39
  @httpcli.ssl_config.verify_mode = opts[:ssl_verify_mode] if opts[:ssl_verify_mode]
33
40
  # Up the keep-alive so we don't have to do the NTLM dance as often.
34
41
  @httpcli.keep_alive_timeout = 60
@@ -91,16 +98,18 @@ class Viewpoint::EWS::Connection
91
98
  resp.body
92
99
  when 302
93
100
  # @todo redirect
94
- raise "Unhandled HTTP Redirect"
101
+ raise Errors::UnhandledResponseError.new("Unhandled HTTP Redirect", resp)
102
+ when 401
103
+ raise Errors::UnauthorizedResponseError.new("Unauthorized request", resp)
95
104
  when 500
96
- if resp.headers['Content-Type'].include?('xml')
105
+ if resp.headers['Content-Type'] =~ /xml/
97
106
  err_string, err_code = parse_soap_error(resp.body)
98
- raise "SOAP Error: Message: #{err_string} Code: #{err_code}"
107
+ raise Errors::SoapResponseError.new("SOAP Error: Message: #{err_string} Code: #{err_code}", resp, err_code, err_string)
99
108
  else
100
- raise "Internal Server Error. Message: #{resp.body}"
109
+ raise Errors::ServerError.new("Internal Server Error. Message: #{resp.body}", resp)
101
110
  end
102
111
  else
103
- raise "HTTP Error Code: #{resp.status}, Msg: #{resp.body}"
112
+ raise Errors::ResponseError.new("HTTP Error Code: #{resp.status}, Msg: #{resp.body}", resp)
104
113
  end
105
114
  end
106
115
 
@@ -0,0 +1,56 @@
1
+ =begin
2
+ This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
3
+
4
+ Copyright © 2013 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::ConvertAccessors
19
+ include Viewpoint::EWS
20
+
21
+ # This is a class method that converts identifiers between formats.
22
+ # @param [String] id The id to be converted
23
+ # @param [Hash] opts Misc options to control request
24
+ # @option opts [Symbol] :format :ews_legacy_id/:ews_id/:entry_id/:hex_entry_id/:store_id/:owa_id
25
+ # @option opts [Symbol] :destination_format :ews_legacy_id/:ews_id/:entry_id/:hex_entry_id/:store_id/:owa_id
26
+ # @option opts [String] :mailbox Mailbox, if required
27
+ # @return [EwsResponse] Returns an EwsResponse containing the convert response message
28
+
29
+ def convert_id(id, opts = {})
30
+ args = convert_id_args(id, opts.clone)
31
+ obj = OpenStruct.new(opts: args)
32
+ yield obj if block_given?
33
+ resp = ews.convert_id(args)
34
+ convert_id_parser(resp)
35
+ end
36
+
37
+ private
38
+
39
+ def convert_id_args(id, opts)
40
+ { id: id }.merge opts
41
+ end
42
+
43
+ def convert_id_parser(resp)
44
+ rm = resp.response_messages[0]
45
+
46
+ if(rm && rm.status == 'Success')
47
+ # @todo create custom response class
48
+ rm
49
+ else
50
+ code = rm.respond_to?(:code) ? rm.code : "Unknown"
51
+ text = rm.respond_to?(:message_text) ? rm.message_text : "Unknown"
52
+ raise EwsError, "Could not convert id. #{rm.code}: #{rm.message_text}"
53
+ end
54
+ end
55
+
56
+ end # Viewpoint::EWS::ItemAccessors
@@ -0,0 +1,56 @@
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::Errors
20
+ class ResponseError < RuntimeError
21
+ attr_reader :response
22
+
23
+ def initialize(message, response)
24
+ super(message)
25
+ @response = response
26
+ end
27
+
28
+ def status
29
+ response.status
30
+ end
31
+
32
+ def body
33
+ response.body
34
+ end
35
+ end
36
+
37
+ class UnhandledResponseError < ResponseError
38
+ end
39
+
40
+ class ServerError < ResponseError
41
+ end
42
+
43
+ class UnauthorizedResponseError < ResponseError
44
+ end
45
+
46
+ class SoapResponseError < ResponseError
47
+ attr_reader :faultcode,
48
+ :faultstring
49
+
50
+ def initialize(message, response, faultcode, faultstring)
51
+ super(message, response)
52
+ @faultcode = faultcode
53
+ @faultstring = faultstring
54
+ end
55
+ end
56
+ end
@@ -4,6 +4,9 @@ require 'ews/message_accessors'
4
4
  require 'ews/mailbox_accessors'
5
5
  require 'ews/push_subscription_accessors'
6
6
  require 'ews/calendar_accessors'
7
+ require 'ews/room_accessors'
8
+ require 'ews/roomlist_accessors'
9
+ require 'ews/convert_accessors'
7
10
 
8
11
  # This class is the glue between the Models and the Web Service.
9
12
  class Viewpoint::EWSClient
@@ -14,8 +17,12 @@ class Viewpoint::EWSClient
14
17
  include Viewpoint::EWS::MailboxAccessors
15
18
  include Viewpoint::EWS::PushSubscriptionAccessors
16
19
  include Viewpoint::EWS::CalendarAccessors
20
+ include Viewpoint::EWS::RoomAccessors
21
+ include Viewpoint::EWS::RoomlistAccessors
22
+ include Viewpoint::EWS::ConvertAccessors
23
+ include Viewpoint::StringUtils
17
24
 
18
- # The instance of Viewpoint::EWS::SOAP::ExchangeWebService
25
+ # The instance of Viewpoint::EWS::SOAP::ExchangeWebService
19
26
  attr_reader :ews
20
27
 
21
28
  # Initialize the EWSClient instance.
@@ -31,16 +38,39 @@ class Viewpoint::EWSClient
31
38
  # @option opts [Object] :http_class specify an alternate HTTP connection class.
32
39
  # @option opts [Hash] :http_opts options to pass to the connection
33
40
  def initialize(endpoint, user = nil, pass = nil, opts = {})
41
+ # dup all. @see ticket https://github.com/zenchild/Viewpoint/issues/68
42
+ endpoint, user, pass = endpoint.dup, user.dup, pass.dup
43
+ opts = opts.dup
34
44
  http_klass = opts[:http_class] || Viewpoint::EWS::Connection
35
45
  con = http_klass.new(endpoint, opts[:http_opts] || {})
36
46
  con.set_auth(user,pass) if(user && pass)
37
47
  @ews = SOAP::ExchangeWebService.new(con, opts)
38
48
  end
39
49
 
50
+ # @param deepen [Boolean] true to autodeepen, false otherwise
51
+ # @param behavior [Symbol] :raise, :nil When setting autodeepen to false you
52
+ # can choose what the behavior is when an attribute does not exist. The
53
+ # default is to raise a EwsMinimalObjectError.
54
+ def set_auto_deepen(deepen, behavior = :raise)
55
+ if deepen
56
+ @ews.auto_deepen = true
57
+ else
58
+ behavior = [:raise, :nil].include?(behavior) ? behavior : :raise
59
+ @ews.no_auto_deepen_behavior = behavior
60
+ @ews.auto_deepen = false
61
+ end
62
+ end
63
+
40
64
  def auto_deepen=(deepen)
41
- @ews.auto_deepen = (deepen ? true : false)
65
+ set_auto_deepen deepen
42
66
  end
43
67
 
68
+ # Specify a default time zone context for all time attributes
69
+ # @param id [String] Identifier of a Microsoft well known time zone (e.g: 'UTC', 'W. Europe Standard Time')
70
+ # @note A list of time zones known by the server can be requested via {EWS::SOAP::ExchangeTimeZones#get_time_zones}
71
+ def set_time_zone(microsoft_time_zone_id)
72
+ @ews.set_time_zone_context microsoft_time_zone_id
73
+ end
44
74
 
45
75
  private
46
76
 
@@ -49,7 +79,7 @@ class Viewpoint::EWSClient
49
79
  # in there that I didn't want to include directly in this class.
50
80
  def class_by_name(cname)
51
81
  if(cname.instance_of? Symbol)
52
- cname = cname.to_s.camel_case
82
+ cname = camel_case(cname)
53
83
  end
54
84
  Viewpoint::EWS::Types.const_get(cname)
55
85
  end
@@ -49,6 +49,8 @@ module Viewpoint::EWS
49
49
  # that have not yet been fetched.
50
50
  class EwsMinimalObjectError < EwsError; end
51
51
 
52
+ class EwsFrozenObjectError < EwsError; end
53
+
52
54
  # Failed to save an object back to the EWS store.
53
55
  class SaveFailed < EwsError; end
54
56
 
@@ -101,6 +101,31 @@ module Viewpoint::EWS::FolderAccessors
101
101
  end
102
102
  alias :mkfolder :make_folder
103
103
 
104
+ # Get a specific folder by id or symbol
105
+ # @param [Hash] opts Misc options to control request
106
+ # @option opts [Symbol] :shape :id_only/:default/:all_properties
107
+ # @option opts [String,Symbol,Hash] :folder_id You can optionally specify a
108
+ # folder_id to limit the hierarchy synchronization to it. It must be a
109
+ # FolderId(String), a DistinguishedFolderId(Symbol) or you can pass a Hash
110
+ # in the form: {id: <fold_id>, change_key: <change_key>}
111
+ # @option opts [String] :sync_state an optional Base64 encoded SyncState
112
+ # String from a previous sync call.
113
+ # @yield [Hash] yields the formatted argument Hash for last-minute
114
+ # modification before calling the backend EWS method.
115
+ # @return [Hash] A hash with the following keys
116
+ # :all_synced, whether or not additional calls are needed to get all folders
117
+ # :sync_state, the sync state to use for the next call
118
+ # and the following optional keys depending on the changes
119
+ # :create, :update, :delete
120
+ # @raise [EwsError] raised when the backend SOAP method returns an error.
121
+ def sync_folders(opts = {})
122
+ opts = opts.clone
123
+ args = sync_folders_args(opts)
124
+ yield args if block_given?
125
+ resp = ews.sync_folder_hierarchy( args )
126
+ sync_folders_parser(resp)
127
+ end
128
+
104
129
 
105
130
  private
106
131
 
@@ -109,6 +134,8 @@ private
109
134
  opts[:root] = opts[:root] || :msgfolderroot
110
135
  opts[:traversal] = opts[:traversal] || :shallow
111
136
  opts[:shape] = opts[:shape] || :default
137
+ folder_id = {:id => opts[:root]}
138
+ folder_id[:act_as] = opts[:act_as] if opts[:act_as]
112
139
  if( opts[:folder_type] )
113
140
  restr = { :is_equal_to =>
114
141
  [
@@ -119,7 +146,7 @@ private
119
146
  }
120
147
  end
121
148
  args = {
122
- :parent_folder_ids => [{:id => opts[:root]}],
149
+ :parent_folder_ids => [folder_id],
123
150
  :traversal => opts[:traversal],
124
151
  :folder_shape => {:base_shape => opts[:shape]}
125
152
  }
@@ -178,6 +205,44 @@ private
178
205
  end
179
206
  end
180
207
 
208
+ def sync_folders_args(opts)
209
+ opts[:shape] = opts[:shape] || :default
210
+ args = { :folder_shape => {:base_shape => opts[:shape]} }
211
+ if opts[:folder_id]
212
+ folder_id = opts[:folder_id]
213
+ if folder_id.is_a?(Hash)
214
+ args[:sync_folder_id] = folder_id
215
+ else
216
+ args[:sync_folder_id] = {:id => folder_id}
217
+ end
218
+ end
219
+ args[:sync_state] = opts[:sync_state] if opts[:sync_state]
220
+ args
221
+ end
222
+
223
+ def sync_folders_parser(resp)
224
+ rmsg = resp.response_messages[0]
225
+ if rmsg.success?
226
+ rhash = {}
227
+ rhash[:all_synced] = rmsg.includes_last_folder_in_range?
228
+ rhash[:sync_state] = rmsg.sync_state
229
+ rmsg.changes.each do |c|
230
+ ctype = c.keys.first
231
+ rhash[ctype] = [] unless rhash.has_key?(ctype)
232
+ if ctype == :delete
233
+ rhash[ctype] << c[ctype][:elems][0][:folder_id][:attribs]
234
+ else
235
+ type = c[ctype][:elems][0].keys.first
236
+ item = class_by_name(type).new(ews, c[ctype][:elems][0][type])
237
+ rhash[ctype] << item
238
+ end
239
+ end
240
+ rhash
241
+ else
242
+ raise EwsError, "Could not synchronize folders. #{rmsg.response_code}: #{rmsg.message_text}"
243
+ end
244
+ end
245
+
181
246
  # Map a passed parameter to a know folder type mapping. If no mapping
182
247
  # exits simply allow the passed in type to be passed to the SOAP call.
183
248
  # @param [Symbol] type a symbol in FOLDER_TYPE_MAP
@@ -0,0 +1,30 @@
1
+ module Viewpoint::EWS
2
+
3
+ ConnectingSID = {
4
+ :UPN => 'PrincipalName',
5
+ :SID => 'SID',
6
+ :PSMTP => 'PrimarySmtpAddress',
7
+ :SMTP => 'SmtpAddress'
8
+ }
9
+
10
+ # @param connecting_type [String] should be one of the ConnectingSID variables
11
+ # ConnectingSID[:UPN] - use User Principal Name method
12
+ # ConnectingSID[:SID] - use Security Identifier method
13
+ # ConnectingSID[:PSMTP] - use primary Simple Mail Transfer Protocol method
14
+ # ConnectingSID[:SMTP] - use Simple Mail Transfer Protocol method
15
+ # you can add any other string, it will be converted into xml tag on soap request
16
+ # @param address [String] an address to include to requests for impersonation
17
+ def set_impersonation(connecting_type, address)
18
+ if ConnectingSID.has_value? connecting_type or connecting_type.is_a? String then
19
+ ews.impersonation_type = connecting_type
20
+ ews.impersonation_address = address
21
+ else
22
+ raise EwsBadArgumentError, "Not a proper connecting method: #{connecting_type.class}"
23
+ end
24
+ end
25
+
26
+ def remove_impersonation
27
+ ews.impersonation_type = ""
28
+ ews.impersonation_address = ""
29
+ end
30
+ end
@@ -46,6 +46,22 @@ module Viewpoint::EWS::ItemAccessors
46
46
  find_items_parser resp
47
47
  end
48
48
 
49
+ # This is a class method that fetches an existing Item from the
50
+ # Exchange Store.
51
+ # @param [String] item_id The id of the item. You can also pass a Hash in the
52
+ # form: {id: <fold_id>, change_key: <change_key>}
53
+ # @param [Hash] opts Misc options to control request
54
+ # @option opts [Symbol] :shape :id_only/:default/:all_properties
55
+ # @return [Item] Returns an Item or subclass of Item
56
+ # @todo Add support to fetch an item with a ChangeKey
57
+ def get_items(item_ids, opts = {})
58
+ args = get_item_args(item_ids, opts.clone)
59
+ obj = OpenStruct.new(opts: args)
60
+ yield obj if block_given?
61
+ resp = ews.get_item(args)
62
+ get_items_parser(resp)
63
+ end
64
+
49
65
  # Copy an array of items to the specified folder
50
66
  # @param items [Array] an array of EWS Items that you want to copy
51
67
  # @param folder [String,Symbol,GenericFolder] The folder to copy to. This must
@@ -80,6 +96,15 @@ module Viewpoint::EWS::ItemAccessors
80
96
  copy_move_items_parser(resp, :move_item_response_message)
81
97
  end
82
98
 
99
+ # Exports an entire item into base64 string
100
+ # @param item_ids [Array] array of item ids. Can also be a single id value
101
+ # return [Array] array of bulk items
102
+ def export_items(item_ids)
103
+ args = export_items_args(item_ids)
104
+
105
+ resp = ews.export_items(args)
106
+ export_items_parser(resp)
107
+ end
83
108
 
84
109
  private
85
110
 
@@ -88,10 +113,13 @@ private
88
113
  default_args = {
89
114
  :item_shape => {:base_shape => opts[:shape]}
90
115
  }
91
- if item_id.is_a?(Hash)
92
- default_args[:item_ids] = [{:item_id => item_id}]
116
+ default_args[:item_ids] = case item_id
117
+ when Hash
118
+ [{:item_id => item_id}]
119
+ when Array
120
+ item_id.map{|i| {:item_id => {:id => i}}}
93
121
  else
94
- default_args[:item_ids] = [{:item_id => {:id => item_id}}]
122
+ [{:item_id => {:id => item_id}}]
95
123
  end
96
124
  default_args.merge opts
97
125
  end
@@ -110,6 +138,21 @@ private
110
138
  end
111
139
  end
112
140
 
141
+ def get_items_parser(resp)
142
+ items = []
143
+
144
+ resp.response_messages.each do |rm|
145
+ if(rm && rm.status == 'Success')
146
+ rm.items.each do |i|
147
+ type = i.keys.first
148
+ items << class_by_name(type).new(ews, i[type])
149
+ end
150
+ end
151
+ end
152
+
153
+ items
154
+ end
155
+
113
156
  def find_items_args(opts)
114
157
  default_args = {
115
158
  :traversal => 'Shallow',
@@ -154,4 +197,35 @@ private
154
197
  }
155
198
  end
156
199
 
200
+ def export_items_args(item_ids)
201
+ default_args = {}
202
+ default_args[:item_ids] = []
203
+ if item_ids.is_a?(Array) then
204
+ item_ids.each do |id|
205
+ default_args[:item_ids] = default_args[:item_ids] + [{:item_id => {:id => id}}]
206
+ end
207
+ else
208
+ default_args[:item_ids] = [{:item_id => {:id => item_ids}}]
209
+ end
210
+ default_args
211
+ end
212
+
213
+ def export_items_parser(resp)
214
+ rm = resp.response_messages
215
+ if(rm)
216
+ items = []
217
+ rm.each do |i|
218
+ if i.success? then
219
+ type = i.type
220
+ items << class_by_name(type).new(ews, i.message[:elems])
221
+ else
222
+ code = i.respond_to?(:code) ? i.code : "Unknown"
223
+ text = i.respond_to?(:message_text) ? i.message_text : "Unknown"
224
+ items << "Could not retrieve item. #{code}: #{text}"
225
+ end
226
+ end
227
+ items
228
+ end
229
+ end
230
+
157
231
  end # Viewpoint::EWS::ItemAccessors