viewpoint 0.0.5 → 0.1.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 (59) hide show
  1. data/README +84 -45
  2. data/Rakefile +7 -5
  3. data/TODO +6 -0
  4. data/VERSION +1 -1
  5. data/lib/exceptions/exceptions.rb +34 -0
  6. data/lib/extensions/string.rb +37 -0
  7. data/lib/model/calendar_folder.rb +45 -0
  8. data/lib/model/calendar_item.rb +145 -0
  9. data/lib/model/contact.rb +58 -0
  10. data/lib/model/contacts_folder.rb +35 -0
  11. data/lib/model/distribution_list.rb +26 -0
  12. data/lib/model/event.rb +118 -0
  13. data/lib/model/folder.rb +36 -0
  14. data/lib/model/generic_folder.rb +302 -0
  15. data/lib/model/item.rb +126 -0
  16. data/lib/model/mailbox_user.rb +107 -0
  17. data/lib/model/meeting_cancellation.rb +28 -0
  18. data/lib/{viewpoint/errors.rb → model/meeting_message.rb} +10 -14
  19. data/lib/model/meeting_request.rb +26 -0
  20. data/lib/model/meeting_response.rb +26 -0
  21. data/lib/model/message.rb +73 -0
  22. data/lib/model/model.rb +161 -0
  23. data/lib/model/search_folder.rb +36 -0
  24. data/lib/model/task.rb +62 -0
  25. data/lib/model/task_list.rb +19 -0
  26. data/lib/model/tasks_folder.rb +33 -0
  27. data/lib/soap/handsoap/builder.rb +22 -0
  28. data/lib/soap/handsoap/builders/ews_build_helpers.rb +317 -0
  29. data/lib/soap/handsoap/builders/ews_builder.rb +86 -0
  30. data/lib/soap/handsoap/ews_service.rb +646 -0
  31. data/lib/soap/handsoap/parser.rb +106 -0
  32. data/lib/soap/handsoap/parsers/ews_parser.rb +165 -0
  33. data/lib/soap/soap_provider.rb +75 -0
  34. data/lib/viewpoint.rb +98 -3
  35. data/preamble +1 -1
  36. data/test/spec/basic_functions.spec +51 -0
  37. data/test/spec/folder_subscriptions.spec +35 -0
  38. data/test/spec/folder_synchronization.spec +28 -0
  39. data/utils/ewsWSDL2rb.rb +29 -0
  40. metadata +101 -43
  41. data/lib/exchwebserv.rb +0 -6
  42. data/lib/soap/viewpoint.conf +0 -3
  43. data/lib/viewpoint/calendar.rb +0 -128
  44. data/lib/viewpoint/calendar_item.rb +0 -118
  45. data/lib/viewpoint/event.rb +0 -0
  46. data/lib/viewpoint/exchange_headers.rb +0 -49
  47. data/lib/viewpoint/exchwebserv.rb +0 -236
  48. data/lib/viewpoint/folder.rb +0 -358
  49. data/lib/viewpoint/item.rb +0 -101
  50. data/lib/viewpoint/mail.rb +0 -117
  51. data/lib/viewpoint/message.rb +0 -132
  52. data/lib/viewpoint/task.rb +0 -0
  53. data/lib/viewpoint/tasks.rb +0 -0
  54. data/lib/wsdl/defaultMappingRegistry.rb +0 -10680
  55. data/lib/wsdl/exchangeServiceBinding.rb +0 -349
  56. data/lib/wsdl/exchangeServiceTypes.rb +0 -11013
  57. data/test/spec/basic_features_spec.rb +0 -37
  58. data/test/test_client.rb +0 -13
  59. data/test/testrestrict.rb +0 -75
@@ -0,0 +1,58 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Viewpoint
22
+ module EWS
23
+ class Contact < Item
24
+
25
+ # Create a Contact in the Exchange Data Store
26
+ #
27
+ # @param [String] subject The task subject
28
+ # @param [String] body The task body
29
+ def self.add_contact()
30
+ item = {}
31
+
32
+ conn = Viewpoint::EWS::EWS.instance
33
+ resp = conn.ews.create_contact_item()
34
+
35
+ if(resp.status == 'Success')
36
+ resp = resp.items.shift
37
+ self.new(resp[resp.keys.first])
38
+ else
39
+ raise EwsError, "Could not add contact. #{resp.code}: #{resp.message}"
40
+ end
41
+ end
42
+
43
+
44
+
45
+ # Initialize an Exchange Web Services item of type Contact
46
+ def initialize(ews_item)
47
+ super(ews_item)
48
+ end
49
+
50
+ private
51
+
52
+ def init_methods
53
+ super()
54
+ end
55
+
56
+ end # Contact
57
+ end # EWS
58
+ end # Viewpoint
@@ -0,0 +1,35 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Viewpoint
22
+ module EWS
23
+ class ContactsFolder < GenericFolder
24
+
25
+ # initialize with an item of CalendarFolderType
26
+ def initialize(folder)
27
+ super(folder)
28
+
29
+ # @todo Handle:
30
+ # <SharingEffectiveRights/>
31
+ end
32
+
33
+ end # ContactsFolder
34
+ end # EWS
35
+ end # Viewpoint
@@ -0,0 +1,26 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Viewpoint
22
+ module EWS
23
+ class DistributionList < Item
24
+ end # DistributionList
25
+ end # EWS
26
+ end # Viewpoint
@@ -0,0 +1,118 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Viewpoint
22
+ module EWS
23
+ class Event < Item
24
+
25
+ attr_reader :subject, :organizer, :location, :start, :end, :cal_item_type
26
+
27
+ # Initialize an Exchange Web Services item
28
+ def initialize(ews_item, parent_folder)
29
+ raise InvalidEWSItemError if ews_item.nil?
30
+
31
+ @subject = ews_item.subject # String
32
+ @location = ews_item.location if ews_item.location
33
+ @cal_item_type = ews_item.calendarItemType
34
+ @organizer = ews_item.organizer.mailbox.name if ews_item.organizer and ews_item.organizer.mailbox # String
35
+ @start = ews_item.start if ews_item.start # DateTime
36
+ @end = ews_item.v_end if ews_item.v_end # DateTime
37
+ @busy_status = ews_item.legacyFreeBusyStatus if ews_item.legacyFreeBusyStatus # String
38
+ @date_time_recieved = ews_item.dateTimeReceived if ews_item.dateTimeReceived # DateTime
39
+
40
+ # This is where the event object gets loaded if it
41
+ # is requested. Think of it like IMAP downloading the
42
+ # body when the message is viewed.
43
+ @message = nil
44
+
45
+ super(ews_item, parent_folder)
46
+ end
47
+
48
+
49
+
50
+ def body
51
+ get_calitem if @message == nil
52
+ @message.body
53
+ end
54
+
55
+ def sender
56
+ @organizer
57
+ end
58
+
59
+
60
+ # Convert item to iCal format: http://www.ietf.org/rfc/rfc2445.txt
61
+ # Returns Icalendar::Event object
62
+ def to_ical_event
63
+ get_calitem if @message == nil
64
+ iev = Icalendar::Event.new
65
+ iev.uid = @message.uID
66
+ unless(@message.organizer == nil)
67
+ iev.organizer = "CN=\"#{@message.organizer.mailbox.name}\"" if @message.organizer.mailbox.name
68
+ iev.organizer += ":MAILTO:#{@message.organizer.mailbox.emailAddress}" if @message.organizer.mailbox.emailAddress
69
+ end
70
+ # TODO: Handle EWS Timezones better. TZ_HASH in viewpoint.rb is the start of this
71
+ require 'time'
72
+ #tzoffset = @message.timeZone.sub(/^\(GMT([^\)]+)\).*$/,'\1')
73
+ dtstart = Time.at(@message.start.strftime('%s').to_i)
74
+ dtend = Time.at(@message.v_end.strftime('%s').to_i)
75
+ dtstamp = Time.at(@message.dateTimeStamp.strftime('%s').to_i)
76
+ last_modified = Time.at(@message.lastModifiedTime.strftime('%s').to_i)
77
+ timestr = "%Y%m%dT%H%M%S"
78
+ iev.dtstart = dtstart.strftime(timestr)
79
+ iev.dtend = dtend.strftime(timestr)
80
+ iev.tzid = dtstart.strftime('%Z')
81
+ iev.dtstamp = dtstamp.strftime(timestr)
82
+ iev.last_modified = last_modified.strftime(timestr)
83
+ iev.location = @message.location if @message.location
84
+ iev.klass = @message.sensitivity if @message.sensitivity
85
+ iev.summary = @message.subject if @message.subject
86
+ iev.description = @message.body if @message.body
87
+ iev.transp = @message.legacyFreeBusyStatus if @message.legacyFreeBusyStatus
88
+ iev.duration = @message.duration if @message.duration
89
+ iev.sequence = @message.appointmentSequenceNumber if @message.appointmentSequenceNumber
90
+ iev.status = @message.myResponseType if @message.myResponseType
91
+ #iev.attach @message.attachments if @message.hasAttachments
92
+ @message.requiredAttendees.each do |pers|
93
+ output = "ROLE=REQ-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
94
+ output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
95
+ iev.attendees << output if output
96
+ end if @message.requiredAttendees
97
+ @message.optionalAttendees.each do |pers|
98
+ output = "ROLE=OPT-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
99
+ output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
100
+ iev.attendees << output if output
101
+ end if @message.optionalAttendees
102
+
103
+ #iev.categories = @message.categories if @message.categories
104
+ #iev.resources = @message.resources if @message.resources
105
+ return iev
106
+ end
107
+
108
+
109
+ # These methods are marked 'private' because they return EWS Types and I am trying to
110
+ # hide those because they are not elegant and a bit too tedious for the public interface.
111
+ private
112
+
113
+ def get_calitem
114
+ @message = @parent_folder.get_item(@item_id)
115
+ end
116
+ end # Event
117
+ end # EWS
118
+ end # Viewpoint
@@ -0,0 +1,36 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Viewpoint
22
+ module EWS
23
+ # This class represents a FolderType object in the Exchange Data store.
24
+ # This is the type of folder that mail messages will be found in.
25
+ class Folder < GenericFolder
26
+
27
+ # Initialize with an item of FolderType. This is typically the folder
28
+ # used to house mail messages.
29
+ def initialize(folder)
30
+ super(folder)
31
+ define_int_var :unread_count
32
+ end
33
+
34
+ end # Folder
35
+ end # EWS
36
+ end # Viewpoint
@@ -0,0 +1,302 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ # This class is inherited by all folder subtypes such as Mail, Calendar,
22
+ # Tasks and Search. It will serve as the brain for all of the methods that
23
+ # each of these folder types have in common.
24
+ module Viewpoint
25
+ module EWS
26
+ # This class is a generic folder that should typically not be instantiated
27
+ # on it's own. It represents all the commonalities among folders found
28
+ # in the Exchange Data Store of which there are many.
29
+ # @see http://msdn.microsoft.com/en-us/library/aa564009.aspx
30
+ class GenericFolder
31
+ include Viewpoint
32
+ include Model
33
+
34
+ @@distinguished_folder_ids = %w{calendar contacts deleteditems drafts inbox journal
35
+ notes outbox sentitems tasks msgfolderroot root junkemail searchfolders voicemail
36
+ recoverableitemsroot recoverableitemsdeletions recoverableitemsversions
37
+ recoverableitemspurges archiveroot archivemsgfolderroot archivedeleteditems
38
+ archiverecoverableitemsroot archiverecoverableitemsdeletions
39
+ archiverecoverableitemsversions archiverecoverableitemspurges}
40
+
41
+ @@event_types = %w{CopiedEvent CreatedEvent DeletedEvent ModifiedEvent MovedEvent NewMailEvent}
42
+
43
+ def self.get_folder(folder_id, act_as = nil, folder_shape = {:base_shape => 'Default'})
44
+ resp = (Viewpoint::EWS::EWS.instance).ews.get_folder( [normalize_id(folder_id)], folder_shape, act_as )
45
+ if(resp.status == 'Success')
46
+ folder = resp.items.first
47
+ f_type = folder.keys.first.to_s.camel_case
48
+ return(eval "#{f_type}.new(folder[folder.keys.first])")
49
+ else
50
+ raise EwsError, "Could not retrieve folder. #{resp.code}: #{resp.message}"
51
+ end
52
+ end
53
+
54
+ # Find subfolders of the passed root folder. If no parameters are passed
55
+ # this method will search from the Root folder.
56
+ # @param [Array] root An folder id, either a DistinguishedFolderId (must me a Symbol)
57
+ # or a FolderId (String)
58
+ # @param [String] traversal Shallow/Deep/SoftDeleted
59
+ # @return [Array] Returns an Array of Folder or subclasses of Folder
60
+ def self.find_folders(root = :root, traversal = 'Shallow')
61
+ resp = (Viewpoint::EWS::EWS.instance).ews.find_folder( [normalize_id(root)], traversal )
62
+ if(resp.status == 'Success')
63
+ folders = []
64
+ resp.items.each do |f|
65
+ f_type = f.keys.first.to_s.camel_case
66
+ folders << (eval "#{f_type}.new(f[f.keys.first])")
67
+ end
68
+ return folders
69
+ else
70
+ raise EwsError, "Could not retrieve folders. #{resp.code}: #{resp.message}"
71
+ end
72
+ end
73
+
74
+ attr_accessor :folder_id, :change_key, :parent_id
75
+ attr_reader :subscription_id, :watermark, :sync_state
76
+ alias :id :folder_id
77
+
78
+ def initialize(ews_item)
79
+ super() # Calls initialize in Model (creates @ews_methods Array)
80
+ @ews_item = ews_item
81
+ @folder_id = ews_item[:folder_id][:id]
82
+ @ews_methods << :folder_id
83
+ @ews_methods << :id
84
+ @change_key = ews_item[:folder_id][:change_key]
85
+ @ews_methods << :change_key
86
+ unless ews_item[:parent_folder_id].nil?
87
+ @parent_id = ews_item[:parent_folder_id]
88
+ @ews_methods << :parent_id
89
+ end
90
+ define_str_var :display_name, :folder_class
91
+ define_int_var :child_folder_count, :total_count
92
+ # @todo Handle:
93
+ # <EffectiveRights/>, <ExtendedProperty/>, <ManagedFolderInformation/>, <PermissionSet/>
94
+
95
+ @sync_state = nil # Base-64 encoded sync data
96
+ @synced = false # Whether or not the synchronization process is complete
97
+ @subscription_id = nil
98
+ @watermark = nil
99
+ @shallow = true
100
+ end
101
+
102
+ # Subscribe this folder to events. This method initiates an Exchange pull
103
+ # type subscription.
104
+ #
105
+ # @param [Array] event_types Which event types to subscribe to. By default
106
+ # we subscribe to all Exchange event types: CopiedEvent, CreatedEvent,
107
+ # DeletedEvent, ModifiedEvent, MovedEvent, NewMailEvent, FreeBusyChangedEvent
108
+ # @return [Boolean] Did the subscription happen successfully?
109
+ # @todo Add custom Exception for EWS
110
+ def subscribe(event_types = @@event_types)
111
+ # Refresh the subscription if already subscribed
112
+ unsubscribe if subscribed?
113
+
114
+ resp = (Viewpoint::EWS::EWS.instance).ews.subscribe([folder_id],event_types)
115
+ if(resp.status == 'Success')
116
+ @subscription_id = resp.items.first[:subscription_id][:text]
117
+ @watermark = resp.items.first[:watermark][:text]
118
+ return true
119
+ else
120
+ raise StandardError, "Error: #{resp.message}"
121
+ end
122
+ end
123
+
124
+ # Check if there is a subscription for this folder.
125
+ # @return [Boolean] Are we subscribed to this folder?
126
+ def subscribed?
127
+ ( @subscription_id.nil? or @watermark.nil? )? false : true
128
+ end
129
+
130
+
131
+ # Unsubscribe this folder from further Exchange events.
132
+ # @return [Boolean] Did we unsubscribe successfully?
133
+ # @todo Add custom Exception for EWS
134
+ def unsubscribe
135
+ return true if @subscription_id.nil?
136
+
137
+ resp = (Viewpoint::EWS::EWS.instance).ews.unsubscribe(@subscription_id)
138
+ if(resp.status == 'Success')
139
+ @subscription_id, @watermark = nil, nil
140
+ return true
141
+ else
142
+ raise StandardError, "Error: #{resp.message}"
143
+ end
144
+ end
145
+
146
+ # Checks a subscribed folder for events
147
+ # @returns [Array] An array of Event items
148
+ # @todo check for subscription expiry
149
+ def get_events
150
+ begin
151
+ if(subscribed?)
152
+ resp = (Viewpoint::EWS::EWS.instance).ews.get_events(@subscription_id, @watermark)
153
+ parms = resp.items.shift
154
+ @watermark = parms[:watermark]
155
+ # @todo if parms[:more_events] # get more events
156
+ return resp.items
157
+ else
158
+ raise StandardError, "Folder <#{self.display_name}> not subscribed to. Issue a Folder#subscribe before checking events."
159
+ end
160
+ rescue EwsSubscriptionTimeout => e
161
+ @subscription_id, @watermark = nil, nil
162
+ raise e
163
+ end
164
+ end
165
+
166
+ # Find Items
167
+ def find_items(opts = {})
168
+ resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', {:base_shape => 'Default'}, opts)
169
+ if(resp.status == 'Success')
170
+ parms = resp.items.shift
171
+ items = []
172
+ resp.items.each do |i|
173
+ i_type = i.keys.first
174
+ items << (eval "#{i_type.to_s.camel_case}.new(i[i_type])")
175
+ end
176
+ return items
177
+ else
178
+ raise EwsError, "Could not find items. #{resp.code}: #{resp.message}"
179
+ end
180
+ end
181
+
182
+ def todays_items
183
+ #opts = {:query_string => ["Received:today"]}
184
+ items_since(Date.today.to_datetime)
185
+ end
186
+
187
+ def items_since(date_time)
188
+ restr = {:restriction =>
189
+ {:is_greater_than_or_equal_to =>
190
+ {:field_uRI => {:field_uRI=>'item:DateTimeReceived'}, :field_uRI_or_constant =>{:constant => {:value=>date_time}}}}}
191
+ find_items(restr)
192
+ end
193
+
194
+ def items_between(start_date, end_date)
195
+ restr = {:restriction => {:and => [
196
+ {:is_greater_than_or_equal_to => {:field_uRI => {:field_uRI=>'item:DateTimeReceived'},:field_uRI_or_constant=>{:constant => {:value =>start_date}}}},
197
+ {:is_less_than_or_equal_to => {:field_uRI => {:field_uRI=>'item:DateTimeReceived'},:field_uRI_or_constant=>{:constant => {:value =>end_date}}}}
198
+ ]}}
199
+ find_items(restr)
200
+ end
201
+
202
+ # Search on the item subject
203
+ # @param [String] match_str A simple string paramater to match against the subject. The search ignores
204
+ # case and does not accept regexes... only strings.
205
+ # @param [String,nil] exclude_str A string to exclude from matches against the subject. This is optional.
206
+ def search_by_subject(match_str, exclude_str = nil)
207
+ match = {:contains =>
208
+ {
209
+ :containment_mode => 'Substring',
210
+ :containment_comparison => 'IgnoreCase',
211
+ :field_uRI => {:field_uRI=>'item:Subject'},
212
+ :constant => {:value =>match_str}
213
+ }
214
+ }
215
+ unless exclude_str.nil?
216
+ excl = {:not =>
217
+ {:contains =>
218
+ {
219
+ :containment_mode => 'Substring',
220
+ :containment_comparison => 'IgnoreCase',
221
+ :field_uRI => {:field_uRI=>'item:Subject'},
222
+ :constant => {:value =>exclude_str}
223
+ }
224
+ }
225
+ }
226
+
227
+ match[:and] = [{:contains => match.delete(:contains)}, excl]
228
+ end
229
+
230
+ find_items({:restriction => match})
231
+ end
232
+
233
+ # Get Item
234
+ def get_item(item_id, change_key = nil)
235
+ resp = (Viewpoint::EWS::EWS.instance).ews.get_item([item_id])
236
+ if(resp.status == 'Success')
237
+ item = resp.items.shift
238
+ type = item.keys.first
239
+ return eval "#{type.to_s.camel_case}.new(item[type])"
240
+ else
241
+ raise EwsError, "Could not retrieve item. #{resp.code}: #{resp.message}"
242
+ end
243
+ end
244
+
245
+ # Syncronize Items in this folder. If this method is issued multiple
246
+ # times it will continue where the last sync completed.
247
+ # @param [Integer] sync_amount The number of items to synchronize per sync
248
+ # @param [Boolean] sync_all Whether to sync all the data by looping through.
249
+ # The default is to just sync the first set. You can manually loop through
250
+ # with multiple calls to #sync_items!
251
+ # @return [Hash] Returns a hash with keys for each change type that ocurred.
252
+ # Possible key values are (:create/:udpate/:delete). For create and update
253
+ # changes the values are Arrays of Item or a subclass of Item. For deletes
254
+ # an array of ItemIds are returned wich is a Hash in the form:
255
+ # {:id=>"item id", :change_key=>"change key"}
256
+ # See: http://msdn.microsoft.com/en-us/library/aa565609.aspx
257
+ def sync_items!(sync_amount = 256, sync_all = false)
258
+ resp = (Viewpoint::EWS::EWS.instance).ews.sync_folder_items(@folder_id, @sync_state, sync_amount)
259
+ parms = resp.items.shift
260
+ @sync_state = parms[:sync_state]
261
+ @synced = parms[:includes_last_item_in_range]
262
+ items = {}
263
+ resp.items.each do |i|
264
+ key = i.keys.first
265
+ items[key] = [] unless items[key].is_a?(Array)
266
+ if(key == :delete || key == :read_flag_change)
267
+ items[key] << i[key][:item_id]
268
+ else
269
+ i_type = i[key].keys.first
270
+ items[key] << (eval "#{i_type.to_s.camel_case}.new(i[key][i_type])")
271
+ end
272
+ end
273
+ items
274
+ end
275
+
276
+ # Create a subfolder of this folder
277
+ #
278
+ # @param [String] name The name of the new folder
279
+ def add_subfolder(name)
280
+ resp = (Viewpoint::EWS::EWS.instance).ews.create_folder(@folder_id, name)
281
+ end
282
+
283
+
284
+ # Deletes this folder from the Exchange Data Store
285
+ def delete!
286
+ resp = (Viewpoint::EWS::EWS.instance).ews.delete_folder(@folder_id)
287
+ end
288
+
289
+ private
290
+
291
+ # Return the appropriate id based on whether it is a DistinguishedFolderId or
292
+ # simply just a FolderId
293
+ def self.normalize_id(folder_id)
294
+ tfolder_id = folder_id.to_s.downcase
295
+ # If the folder_id is a DistinguishedFolderId change it to a symbol so our SOAP
296
+ # method does the right thing.
297
+ @@distinguished_folder_ids.find_index(tfolder_id) ? tfolder_id.to_sym : folder_id
298
+ end
299
+
300
+ end # GenericFolder
301
+ end # EWS
302
+ end # Viewpoint