viewpoint 0.1.26 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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