viewpoint 0.1.26 → 1.1.1

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 (112) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +203 -0
  3. data/lib/ews/calendar_accessors.rb +34 -0
  4. data/lib/ews/connection.rb +140 -0
  5. data/lib/ews/connection_helper.rb +35 -0
  6. data/lib/ews/convert_accessors.rb +56 -0
  7. data/lib/{exceptions/exceptions.rb → ews/errors.rb} +29 -19
  8. data/lib/ews/ews_client.rb +105 -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 +242 -0
  13. data/lib/ews/mailbox_accessors.rb +92 -0
  14. data/lib/ews/meeting_accessors.rb +39 -0
  15. data/lib/ews/message_accessors.rb +93 -0
  16. data/lib/ews/push_subscription_accessors.rb +33 -0
  17. data/lib/ews/room_accessors.rb +48 -0
  18. data/lib/ews/roomlist_accessors.rb +47 -0
  19. data/lib/ews/soap.rb +64 -0
  20. data/lib/ews/soap/builders/ews_builder.rb +1384 -0
  21. data/lib/ews/soap/ews_response.rb +84 -0
  22. data/lib/ews/soap/ews_soap_availability_response.rb +58 -0
  23. data/lib/ews/soap/ews_soap_free_busy_response.rb +119 -0
  24. data/lib/ews/soap/ews_soap_response.rb +103 -0
  25. data/lib/ews/soap/ews_soap_room_response.rb +53 -0
  26. data/lib/ews/soap/ews_soap_roomlist_response.rb +54 -0
  27. data/lib/ews/soap/exchange_availability.rb +61 -0
  28. data/lib/ews/soap/exchange_data_services.rb +780 -0
  29. data/lib/ews/soap/exchange_notification.rb +146 -0
  30. data/lib/ews/soap/exchange_synchronization.rb +93 -0
  31. data/lib/ews/soap/exchange_time_zones.rb +56 -0
  32. data/lib/ews/soap/exchange_user_configuration.rb +33 -0
  33. data/lib/ews/soap/exchange_web_service.rb +264 -0
  34. data/lib/{model/item_attachment.rb → ews/soap/parsers/ews_parser.rb} +24 -14
  35. data/lib/ews/soap/parsers/ews_sax_document.rb +70 -0
  36. data/lib/ews/soap/response_message.rb +80 -0
  37. data/lib/ews/soap/responses/create_attachment_response_message.rb +47 -0
  38. data/lib/{model/meeting_message.rb → ews/soap/responses/create_item_response_message.rb} +7 -10
  39. data/lib/ews/soap/responses/find_item_response_message.rb +80 -0
  40. data/lib/ews/soap/responses/get_events_response_message.rb +53 -0
  41. data/lib/ews/soap/responses/send_notification_response_message.rb +59 -0
  42. data/lib/{model/attachment.rb → ews/soap/responses/subscribe_response_message.rb} +17 -13
  43. data/lib/{model/attendee.rb → ews/soap/responses/sync_folder_hierarchy_response_message.rb} +14 -15
  44. data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
  45. data/lib/ews/templates/calendar_item.rb +79 -0
  46. data/lib/ews/templates/forward_item.rb +24 -0
  47. data/lib/ews/templates/message.rb +85 -0
  48. data/lib/ews/templates/reply_to_item.rb +25 -0
  49. data/lib/ews/templates/task.rb +74 -0
  50. data/lib/ews/types.rb +194 -0
  51. data/lib/ews/types/attachment.rb +77 -0
  52. data/lib/{model/meeting_cancellation.rb → ews/types/attendee.rb} +9 -8
  53. data/lib/ews/types/calendar_folder.rb +50 -0
  54. data/lib/ews/types/calendar_item.rb +130 -0
  55. data/lib/ews/types/contact.rb +7 -0
  56. data/lib/ews/types/contacts_folder.rb +8 -0
  57. data/lib/ews/types/copied_event.rb +51 -0
  58. data/lib/{soap/handsoap/builder.rb → ews/types/created_event.rb} +5 -2
  59. data/lib/ews/types/deleted_event.rb +24 -0
  60. data/lib/ews/types/distribution_list.rb +7 -0
  61. data/lib/ews/types/event.rb +62 -0
  62. data/lib/ews/types/export_items_response_message.rb +52 -0
  63. data/lib/ews/types/file_attachment.rb +65 -0
  64. data/lib/ews/types/folder.rb +60 -0
  65. data/lib/ews/types/free_busy_changed_event.rb +24 -0
  66. data/lib/ews/types/generic_folder.rb +418 -0
  67. data/lib/ews/types/item.rb +447 -0
  68. data/lib/ews/types/item_attachment.rb +84 -0
  69. data/lib/{model → ews/types}/item_field_uri_map.rb +2 -18
  70. data/lib/ews/types/mailbox_user.rb +156 -0
  71. data/lib/ews/types/meeting_cancellation.rb +7 -0
  72. data/lib/ews/types/meeting_message.rb +7 -0
  73. data/lib/ews/types/meeting_request.rb +7 -0
  74. data/lib/ews/types/meeting_response.rb +7 -0
  75. data/lib/ews/types/message.rb +7 -0
  76. data/lib/ews/types/modified_event.rb +48 -0
  77. data/lib/{extensions/string.rb → ews/types/moved_event.rb} +31 -15
  78. data/lib/ews/types/new_mail_event.rb +24 -0
  79. data/lib/ews/types/out_of_office.rb +147 -0
  80. data/lib/ews/types/post_item.rb +7 -0
  81. data/lib/ews/types/search_folder.rb +8 -0
  82. data/lib/ews/types/status_event.rb +39 -0
  83. data/lib/ews/types/task.rb +41 -0
  84. data/lib/ews/types/tasks_folder.rb +27 -0
  85. data/lib/viewpoint.rb +85 -108
  86. data/lib/{model/distribution_list.rb → viewpoint/logging.rb} +6 -3
  87. data/lib/{model/meeting_response.rb → viewpoint/logging/config.rb} +3 -3
  88. data/lib/viewpoint/string_utils.rb +76 -0
  89. metadata +156 -123
  90. data/README +0 -114
  91. data/lib/model/calendar_folder.rb +0 -67
  92. data/lib/model/calendar_item.rb +0 -267
  93. data/lib/model/contact.rb +0 -238
  94. data/lib/model/contacts_folder.rb +0 -46
  95. data/lib/model/event.rb +0 -116
  96. data/lib/model/file_attachment.rb +0 -53
  97. data/lib/model/folder.rb +0 -47
  98. data/lib/model/generic_folder.rb +0 -461
  99. data/lib/model/item.rb +0 -312
  100. data/lib/model/mailbox_user.rb +0 -146
  101. data/lib/model/meeting_request.rb +0 -39
  102. data/lib/model/message.rb +0 -142
  103. data/lib/model/model.rb +0 -269
  104. data/lib/model/search_folder.rb +0 -48
  105. data/lib/model/task.rb +0 -121
  106. data/lib/model/tasks_folder.rb +0 -44
  107. data/lib/soap/handsoap/builders/ews_build_helpers.rb +0 -383
  108. data/lib/soap/handsoap/builders/ews_builder.rb +0 -146
  109. data/lib/soap/handsoap/ews_service.rb +0 -790
  110. data/lib/soap/handsoap/parser.rb +0 -104
  111. data/lib/soap/handsoap/parsers/ews_parser.rb +0 -245
  112. data/lib/soap/soap_provider.rb +0 -64
data/lib/model/folder.rb DELETED
@@ -1,47 +0,0 @@
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
20
- module EWS
21
- # This class represents a FolderType object in the Exchange Data store.
22
- # This is the type of folder that mail messages will be found in.
23
- class Folder < GenericFolder
24
-
25
- # Find folders of type Folder (message folders)
26
- # @see GenericFolder.find_folders
27
- # @param [String,Symbol] root An folder id, either a DistinguishedFolderId (must me a Symbol)
28
- # or a FolderId (String)
29
- # @param [String] traversal Shallow/Deep/SoftDeleted
30
- # @param [String] shape the shape to return IdOnly/Default/AllProperties
31
- # @param [optional, String] folder_type an optional folder type to limit the search to like 'IPF.Task'
32
- # @return [Array] Returns an Array of Folder or subclasses of Folder
33
- def self.find_folders(root = :msgfolderroot, traversal = 'Deep', shape = 'Default', folder_type = 'IPF.Note')
34
- super(root, traversal, shape, folder_type)
35
- end
36
-
37
-
38
- # Initialize with an item of FolderType. This is typically the folder
39
- # used to house mail messages.
40
- def initialize(folder)
41
- super(folder)
42
- define_int_var :unread_count
43
- end
44
-
45
- end # Folder
46
- end # EWS
47
- end # Viewpoint
@@ -1,461 +0,0 @@
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 class is inherited by all folder subtypes such as Mail, Calendar,
20
- # Tasks and Search. It will serve as the brain for all of the methods that
21
- # each of these folder types have in common.
22
- module Viewpoint
23
- module EWS
24
- # This class is a generic folder that should typically not be instantiated
25
- # on it's own. It represents all the commonalities among folders found
26
- # in the Exchange Data Store of which there are many.
27
- # @see http://msdn.microsoft.com/en-us/library/aa564009.aspx
28
- class GenericFolder
29
- include Viewpoint
30
- include Model
31
-
32
- @@distinguished_folder_ids = %w{calendar contacts deleteditems drafts inbox journal
33
- notes outbox sentitems tasks msgfolderroot root junkemail searchfolders voicemail
34
- recoverableitemsroot recoverableitemsdeletions recoverableitemsversions
35
- recoverableitemspurges archiveroot archivemsgfolderroot archivedeleteditems
36
- archiverecoverableitemsroot archiverecoverableitemsdeletions
37
- archiverecoverableitemsversions archiverecoverableitemspurges publicfoldersroot}
38
-
39
- @@event_types = %w{CopiedEvent CreatedEvent DeletedEvent ModifiedEvent MovedEvent NewMailEvent}
40
-
41
- # Get a specific folder by its ID.
42
- # @param [String,Symbol] folder_id either a DistinguishedFolderID or simply a FolderID
43
- # @param [String,nil] act_as User to act on behalf as. This user must have been given
44
- # delegate access to this folder or else this operation will fail.
45
- # @param [Hash] folder_shape
46
- # @option folder_shape [String] :base_shape IdOnly/Default/AllProperties
47
- # @raise [EwsError] raised when the backend SOAP method returns an error.
48
- def self.get_folder(folder_id, act_as = nil, folder_shape = {:base_shape => 'Default'})
49
- resp = (Viewpoint::EWS::EWS.instance).ews.get_folder( [normalize_id(folder_id)], folder_shape, act_as )
50
- if(resp.status == 'Success')
51
- folder = resp.items.first
52
- f_type = folder.keys.first.to_s.camel_case
53
- return(eval "#{f_type}.new(folder[folder.keys.first])")
54
- else
55
- raise EwsError, "Could not retrieve folder. #{resp.code}: #{resp.message}"
56
- end
57
- end
58
-
59
- # Find subfolders of the passed root folder. If no parameters are passed
60
- # this method will search from the Root folder.
61
- # @param [String,Symbol] root An folder id, either a DistinguishedFolderId (must me a Symbol)
62
- # or a FolderId (String). This is where to start the search from. Usually :root,:msgfolderroot,:publicfoldersroot
63
- # @param [String] traversal Shallow/Deep/SoftDeleted
64
- # @param [String] shape the shape to return IdOnly/Default/AllProperties
65
- # @param [optional, String] folder_type an optional folder type to limit the search to like 'IPF.Task'
66
- # @return [Array] Returns an Array of Folder or subclasses of Folder
67
- # @raise [EwsError] raised when the backend SOAP method returns an error.
68
- def self.find_folders(root = :msgfolderroot, traversal = 'Shallow', shape = 'Default', folder_type = nil)
69
- if( folder_type.nil? )
70
- resp = (Viewpoint::EWS::EWS.instance).ews.find_folder( [normalize_id(root)], traversal, {:base_shape => shape} )
71
- else
72
- restr = {:restriction =>
73
- {:is_equal_to => [{:field_uRI => {:field_uRI=>'folder:FolderClass'}}, {:field_uRI_or_constant=>{:constant => {:value => folder_type}}}]}
74
- }
75
- resp = (Viewpoint::EWS::EWS.instance).ews.find_folder( [normalize_id(root)], traversal, {:base_shape => shape}, restr)
76
- end
77
-
78
- if(resp.status == 'Success')
79
- folders = []
80
- resp.items.each do |f|
81
- f_type = f.keys.first.to_s.camel_case
82
- folders << (eval "#{f_type}.new(f[f.keys.first])")
83
- end
84
- return folders
85
- else
86
- raise EwsError, "Could not retrieve folders. #{resp.code}: #{resp.message}"
87
- end
88
- end
89
-
90
- # Return a list of folder names
91
- # @param [String,Symbol] root An folder id, either a DistinguishedFolderId (must me a Symbol)
92
- # or a FolderId (String). This is where to start the search from. Usually :root,:msgfolderroot,:publicfoldersroot
93
- # @return [Array<String>] Return an Array of folder names.
94
- # @raise [EwsError] raised when the backend SOAP method returns an error.
95
- def self.folder_names(root = :msgfolderroot)
96
- resp = (Viewpoint::EWS::EWS.instance).ews.find_folder([:msgfolderroot], 'Shallow')
97
- if(resp.status == 'Success')
98
- flds = []
99
- resp.items.each do |f|
100
- flds << f[f.keys.first][:display_name][:text]
101
- end
102
- flds
103
- else
104
- raise EwsError, "Could not retrieve folders. #{resp.code}: #{resp.message}"
105
- end
106
- end
107
-
108
- # Gets a folder by name. This name must match the folder name exactly.
109
- # @param [String] name The name of the folder to fetch.
110
- # @param [String,Symbol] root An folder id, either a DistinguishedFolderId (must me a Symbol)
111
- # or a FolderId (String). This is where to start the search from. Usually :root,:msgfolderroot,:publicfoldersroot
112
- # @param [String] shape the shape of the object to return IdOnly/Default/AllProperties
113
- # @return [GenericFolder] will return the folder by the given name.
114
- # @raise [EwsFolderNotFound] raised when a folder requested is not found
115
- # @raise [EwsError] raised when the backend SOAP method returns an error.
116
- def self.get_folder_by_name(name, root = :msgfolderroot, shape = 'Default', opts = {})
117
- opts[:traversal] = 'Deep' unless opts.has_key?(:traversal)
118
- # For now the :field_uRI and :field_uRI_or_constant must be in an Array for Ruby 1.8.7 because Hashes
119
- # are not positional at insertion until 1.9
120
- restr = {:restriction =>
121
- {:is_equal_to =>
122
- [{:field_uRI => {:field_uRI=>'folder:DisplayName'}}, {:field_uRI_or_constant =>{:constant => {:value=>name}}}]}}
123
- resp = (Viewpoint::EWS::EWS.instance).ews.find_folder([root], opts[:traversal], {:base_shape => shape}, restr)
124
- if(resp.status == 'Success')
125
- raise EwsFolderNotFound, "The folder requested is invalid or unavailable" if resp.items.empty?
126
- f = resp.items.first
127
- f_type = f.keys.first.to_s.camel_case
128
- eval "#{f_type}.new(f[f.keys.first])"
129
- else
130
- raise EwsError, "Could not retrieve folder. #{resp.code}: #{resp.message}"
131
- end
132
- end
133
-
134
- # Gets a folder by the given path. The default search path is :msgfolderroot so if you want to
135
- # specify a path at a different root change that parameter.
136
- # @param [String] path the fully qualified path to a folder at the given root
137
- # @example "/myfolders/folder a/personal calendar"
138
- # @param [String,Symbol] root An folder id, either a DistinguishedFolderId (must me a Symbol)
139
- # or a FolderId (String). This is where to start the search from. Usually :root,:msgfolderroot,:publicfoldersroot
140
- # @param [String] shape the shape of the object to return IdOnly/Default/AllProperties
141
- # @return [GenericFolder] will return the folder by the given path
142
- # @raise [EwsFolderNotFound] raised when a folder requested is not found
143
- # @raise [EwsError] raised when the backend SOAP method returns an error.
144
- def self.get_folder_by_path(path, root = :msgfolderroot, shape = 'Default')
145
- parts = path.split(/\//)
146
- parts = parts.slice(1..(parts.length)) if parts.first.empty?
147
- retfld = nil
148
- parts.each do |p|
149
- fld = self.get_folder_by_name(p, root, shape, {:traversal => 'Shallow'})
150
- root = fld.id
151
- retfld = fld if(fld.display_name.downcase == p.downcase)
152
- end
153
- retfld
154
- end
155
-
156
- attr_accessor :folder_id, :change_key, :parent_id, :sync_state
157
- attr_reader :subscription_id, :watermark
158
- alias :id :folder_id
159
-
160
- def initialize(ews_item)
161
- super() # Calls initialize in Model (creates @ews_methods Array)
162
- @ews_item = ews_item
163
- @folder_id = ews_item[:folder_id][:id]
164
- @ews_methods << :folder_id
165
- @ews_methods << :id
166
- @change_key = ews_item[:folder_id][:change_key]
167
- @ews_methods << :change_key
168
- unless ews_item[:parent_folder_id].nil?
169
- @parent_id = ews_item[:parent_folder_id]
170
- @ews_methods << :parent_id
171
- end
172
- define_str_var :display_name, :folder_class
173
- define_int_var :child_folder_count, :total_count
174
- # @todo Handle:
175
- # <EffectiveRights/>, <ExtendedProperty/>, <ManagedFolderInformation/>, <PermissionSet/>
176
-
177
- @sync_state = nil # Base-64 encoded sync data
178
- @synced = false # Whether or not the synchronization process is complete
179
- @subscription_id = nil
180
- @watermark = nil
181
- @shallow = true
182
- end
183
-
184
- # Subscribe this folder to events. This method initiates an Exchange pull
185
- # type subscription.
186
- #
187
- # @param [Array] event_types Which event types to subscribe to. By default
188
- # we subscribe to all Exchange event types: CopiedEvent, CreatedEvent,
189
- # DeletedEvent, ModifiedEvent, MovedEvent, NewMailEvent, FreeBusyChangedEvent
190
- # @return [Boolean] Did the subscription happen successfully?
191
- # @todo Add custom Exception for EWS
192
- def subscribe(event_types = @@event_types)
193
- # Refresh the subscription if already subscribed
194
- unsubscribe if subscribed?
195
-
196
- resp = (Viewpoint::EWS::EWS.instance).ews.subscribe([folder_id],event_types)
197
- if(resp.status == 'Success')
198
- @subscription_id = resp.items.first[:subscription_id][:text]
199
- @watermark = resp.items.first[:watermark][:text]
200
- return true
201
- else
202
- raise StandardError, "Error: #{resp.message}"
203
- end
204
- end
205
-
206
- # Check if there is a subscription for this folder.
207
- # @return [Boolean] Are we subscribed to this folder?
208
- def subscribed?
209
- ( @subscription_id.nil? or @watermark.nil? )? false : true
210
- end
211
-
212
-
213
- # Unsubscribe this folder from further Exchange events.
214
- # @return [Boolean] Did we unsubscribe successfully?
215
- # @todo Add custom Exception for EWS
216
- def unsubscribe
217
- return true if @subscription_id.nil?
218
-
219
- resp = (Viewpoint::EWS::EWS.instance).ews.unsubscribe(@subscription_id)
220
- if(resp.status == 'Success')
221
- @subscription_id, @watermark = nil, nil
222
- return true
223
- else
224
- raise StandardError, "Error: #{resp.message}"
225
- end
226
- end
227
-
228
- # Checks a subscribed folder for events
229
- # @return [Array] An array of Event items
230
- # @todo check for subscription expiry
231
- def get_events
232
- begin
233
- if(subscribed?)
234
- resp = (Viewpoint::EWS::EWS.instance).ews.get_events(@subscription_id, @watermark)
235
- parms = resp.items.shift
236
- @watermark = parms[:watermark]
237
- # @todo if parms[:more_events] # get more events
238
- return resp.items
239
- else
240
- raise StandardError, "Folder <#{self.display_name}> not subscribed to. Issue a Folder#subscribe before checking events."
241
- end
242
- rescue EwsSubscriptionTimeout => e
243
- @subscription_id, @watermark = nil, nil
244
- raise e
245
- end
246
- end
247
-
248
- # Find Items
249
- def find_items(opts = {})
250
- opts = opts.clone # clone the passed in object so we don't modify it in case it's being used in a loop
251
- item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'}
252
- unless item_shape.has_key?(:additional_properties) # Don't overwrite if specified by caller
253
- item_shape[:additional_properties] = {:field_uRI => ['item:ParentFolderId']}
254
- end
255
- resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts)
256
- if(resp.status == 'Success')
257
- parms = resp.items.shift
258
- items = []
259
- resp.items.each do |i|
260
- i_type = i.keys.first
261
- items << (eval "#{i_type.to_s.camel_case}.new(i[i_type])")
262
- end
263
- return items
264
- else
265
- raise EwsError, "Could not find items. #{resp.code}: #{resp.message}"
266
- end
267
- end
268
-
269
- # Fetch only items from today (since midnight)
270
- def todays_items(opts = {})
271
- #opts = {:query_string => ["Received:today"]}
272
- #This is a bit convoluted for pre-1.9.x ruby versions that don't support to_datetime
273
- items_since(DateTime.parse(Date.today.to_s), opts)
274
- end
275
-
276
- # Fetch items since a give DateTime
277
- # @param [DateTime] date_time the time to fetch Items since.
278
- def items_since(date_time, opts = {})
279
- restr = {:restriction =>
280
- {:is_greater_than_or_equal_to =>
281
- [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
282
- {:field_uRI_or_constant =>{:constant => {:value=>date_time}}}]
283
- }}
284
- find_items(opts.merge(restr))
285
- end
286
-
287
- # Fetch items between a given time period
288
- # @param [DateTime] start_date the time to start fetching Items from
289
- # @param [DateTime] end_date the time to stop fetching Items from
290
- def items_between(start_date, end_date, opts={})
291
- restr = {:restriction => {:and => [
292
- {:is_greater_than_or_equal_to =>
293
- [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
294
- {:field_uRI_or_constant=>{:constant => {:value =>start_date}}}]},
295
- {:is_less_than_or_equal_to =>
296
- [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
297
- {:field_uRI_or_constant=>{:constant => {:value =>end_date}}}]}
298
- ]}}
299
- find_items(opts.merge(restr))
300
- end
301
-
302
- # Search on the item subject
303
- # @param [String] match_str A simple string paramater to match against the subject. The search ignores
304
- # case and does not accept regexes... only strings.
305
- # @param [String,nil] exclude_str A string to exclude from matches against the subject. This is optional.
306
- def search_by_subject(match_str, exclude_str = nil)
307
- match = {:contains =>
308
- [
309
- {:containment_mode => 'Substring'},
310
- {:containment_comparison => 'IgnoreCase'},
311
- {:field_uRI => {:field_uRI=>'item:Subject'}},
312
- {:constant => {:value =>match_str}}
313
- ]
314
- }
315
- unless exclude_str.nil?
316
- excl = {:not =>
317
- {:contains =>
318
- [
319
- {:containment_mode => 'Substring'},
320
- {:containment_comparison => 'IgnoreCase'},
321
- {:field_uRI => {:field_uRI=>'item:Subject'}},
322
- {:constant => {:value =>exclude_str}}
323
- ]
324
- }
325
- }
326
-
327
- match[:and] = [{:contains => match.delete(:contains)}, excl]
328
- end
329
-
330
- find_items({:restriction => match})
331
- end
332
-
333
- # Get Item
334
- # @param [String] item_id the ID of the item to fetch
335
- # @param [String] change_key specify an optional change_key if you want to
336
- # make sure you are fetching a specific version of the object.
337
- def get_item(item_id, change_key = nil)
338
- item_shape = {:base_shape => 'Default', :additional_properties => {:field_uRI => ['item:ParentFolderId']}}
339
- resp = (Viewpoint::EWS::EWS.instance).ews.get_item([item_id], item_shape)
340
- if(resp.status == 'Success')
341
- item = resp.items.shift
342
- type = item.keys.first
343
- eval "#{type.to_s.camel_case}.new(item[type])"
344
- else
345
- raise EwsError, "Could not retrieve item. #{resp.code}: #{resp.message}"
346
- end
347
- end
348
-
349
- # Get Items
350
- # @param [String] item_ids is an array of Item IDs to fetch
351
- # @param [String] change_key specify an optional change_key if you want to
352
- # make sure you are fetching a specific version of the object.
353
- # @param [String] options specify an optional options hash. Supports the
354
- # key :item_shape that expects a hash value with :base_shape and other
355
- # optional parameters that specify the desired fields to return.
356
- def get_items(item_ids, change_key = nil, options={})
357
- item_shape = options[:item_shape] ||
358
- {:base_shape => 'Default', :additional_properties => {:field_uRI => ['item:ParentFolderId']}}
359
- shallow = item_shape[:base_shape] != 'AllProperties'
360
- resp = (Viewpoint::EWS::EWS.instance).ews.get_item(item_ids, item_shape)
361
- if(resp.status == 'Success')
362
- resp.items.map do |item|
363
- type = item.keys.first
364
- eval "#{type.to_s.camel_case}.new(item[type], :shallow => #{shallow})"
365
- end
366
- else
367
- raise EwsError, "Could not retrieve items. #{resp.code}: #{resp.message}"
368
- end
369
- end
370
-
371
-
372
- # Syncronize Items in this folder. If this method is issued multiple
373
- # times it will continue where the last sync completed.
374
- # @param [Integer] sync_amount The number of items to synchronize per sync
375
- # @param [Boolean] sync_all Whether to sync all the data by looping through.
376
- # The default is to just sync the first set. You can manually loop through
377
- # with multiple calls to #sync_items!
378
- # @return [Hash] Returns a hash with keys for each change type that ocurred.
379
- # Possible key values are (:create/:udpate/:delete). For create and update
380
- # changes the values are Arrays of Item or a subclass of Item. For deletes
381
- # an array of ItemIds are returned wich is a Hash in the form:
382
- # {:id=>"item id", :change_key=>"change key"}
383
- # See: http://msdn.microsoft.com/en-us/library/aa565609.aspx
384
- def sync_items!(sync_amount = 256, sync_all = false, opts = {})
385
- item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'}
386
- resp = (Viewpoint::EWS::EWS.instance).ews.sync_folder_items(@folder_id, @sync_state, sync_amount, item_shape)
387
- parms = resp.items.shift
388
- @sync_state = parms[:sync_state]
389
- @synced = parms[:includes_last_item_in_range]
390
- items = {}
391
- resp.items.each do |i|
392
- key = i.keys.first
393
- items[key] = [] unless items[key].is_a?(Array)
394
- if(key == :delete || key == :read_flag_change)
395
- items[key] << i[key][:item_id]
396
- else
397
- i_type = i[key].keys.first
398
- items[key] << (eval "#{i_type.to_s.camel_case}.new(i[key][i_type])")
399
- end
400
- end
401
- items
402
- end
403
-
404
- # This is basically a work-around for Microsoft's BPOS hosted Exchange, which does not support
405
- # subscriptions at the time of this writing. This is the best way I could think of to get
406
- # items from a specific period of time and track changes.
407
- # !! Before using this method I would suggest trying a GenericFolder#items_since then using
408
- # a subscription to track changes.
409
- # This method should be followed by subsequent calls to GenericFolder#sync_items! to fetch
410
- # additional items. Calling this method again will clear the sync_state and synchronize
411
- # everything again.
412
- # @return [Array<Item>] returns an array of Items
413
- def sync_items_since!(datetime, opts={})
414
- clear_sync_state!
415
-
416
- begin
417
- items = sync_items!
418
- end until items.empty?
419
-
420
- items_since(datetime, opts)
421
- end
422
-
423
- # Clears out the @sync_state so you can freshly synchronize this folder if needed
424
- def clear_sync_state!
425
- @sync_state = nil
426
- end
427
-
428
- # Create a subfolder of this folder
429
- #
430
- # @param [String] name The name of the new folder
431
- def add_subfolder(name)
432
- resp = (Viewpoint::EWS::EWS.instance).ews.create_folder(@folder_id, name)
433
- folder = resp.items.first
434
- ftype = folder.keys.first
435
- GenericFolder.get_folder(folder[ftype][:folder_id][:id])
436
- end
437
-
438
- # Deletes this folder from the Exchange Data Store
439
- # @param [Boolean] recycle_bin Send to the recycle bin instead of deleting (default: false)
440
- # @return [TrueClass] This will return true because if an issue occurs it
441
- # will be thrown in the SOAP Parser
442
- def delete!(recycle_bin = false)
443
- deltype = recycle_bin ? 'MoveToDeletedItems' : 'HardDelete'
444
- resp = (Viewpoint::EWS::EWS.instance).ews.delete_folder(@folder_id, deltype)
445
- true
446
- end
447
-
448
- private
449
-
450
- # Return the appropriate id based on whether it is a DistinguishedFolderId or
451
- # simply just a FolderId
452
- def self.normalize_id(folder_id)
453
- tfolder_id = folder_id.to_s.downcase
454
- # If the folder_id is a DistinguishedFolderId change it to a symbol so our SOAP
455
- # method does the right thing.
456
- @@distinguished_folder_ids.find_index(tfolder_id) ? tfolder_id.to_sym : folder_id
457
- end
458
-
459
- end # GenericFolder
460
- end # EWS
461
- end # Viewpoint