viewpoint 0.0.5 → 0.1.0

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