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.
- checksums.yaml +7 -0
- data/README.md +203 -0
- data/lib/ews/calendar_accessors.rb +34 -0
- data/lib/ews/connection.rb +140 -0
- data/lib/ews/connection_helper.rb +35 -0
- data/lib/ews/convert_accessors.rb +56 -0
- data/lib/{exceptions/exceptions.rb → ews/errors.rb} +29 -19
- data/lib/ews/ews_client.rb +105 -0
- data/lib/ews/exceptions/exceptions.rb +61 -0
- data/lib/ews/folder_accessors.rb +264 -0
- data/lib/ews/impersonation.rb +30 -0
- data/lib/ews/item_accessors.rb +242 -0
- data/lib/ews/mailbox_accessors.rb +92 -0
- data/lib/ews/meeting_accessors.rb +39 -0
- data/lib/ews/message_accessors.rb +93 -0
- data/lib/ews/push_subscription_accessors.rb +33 -0
- data/lib/ews/room_accessors.rb +48 -0
- data/lib/ews/roomlist_accessors.rb +47 -0
- data/lib/ews/soap.rb +64 -0
- data/lib/ews/soap/builders/ews_builder.rb +1384 -0
- data/lib/ews/soap/ews_response.rb +84 -0
- data/lib/ews/soap/ews_soap_availability_response.rb +58 -0
- data/lib/ews/soap/ews_soap_free_busy_response.rb +119 -0
- data/lib/ews/soap/ews_soap_response.rb +103 -0
- data/lib/ews/soap/ews_soap_room_response.rb +53 -0
- data/lib/ews/soap/ews_soap_roomlist_response.rb +54 -0
- data/lib/ews/soap/exchange_availability.rb +61 -0
- data/lib/ews/soap/exchange_data_services.rb +780 -0
- data/lib/ews/soap/exchange_notification.rb +146 -0
- data/lib/ews/soap/exchange_synchronization.rb +93 -0
- data/lib/ews/soap/exchange_time_zones.rb +56 -0
- data/lib/ews/soap/exchange_user_configuration.rb +33 -0
- data/lib/ews/soap/exchange_web_service.rb +264 -0
- data/lib/{model/item_attachment.rb → ews/soap/parsers/ews_parser.rb} +24 -14
- data/lib/ews/soap/parsers/ews_sax_document.rb +70 -0
- data/lib/ews/soap/response_message.rb +80 -0
- data/lib/ews/soap/responses/create_attachment_response_message.rb +47 -0
- data/lib/{model/meeting_message.rb → ews/soap/responses/create_item_response_message.rb} +7 -10
- data/lib/ews/soap/responses/find_item_response_message.rb +80 -0
- data/lib/ews/soap/responses/get_events_response_message.rb +53 -0
- data/lib/ews/soap/responses/send_notification_response_message.rb +59 -0
- data/lib/{model/attachment.rb → ews/soap/responses/subscribe_response_message.rb} +17 -13
- data/lib/{model/attendee.rb → ews/soap/responses/sync_folder_hierarchy_response_message.rb} +14 -15
- data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
- data/lib/ews/templates/calendar_item.rb +79 -0
- data/lib/ews/templates/forward_item.rb +24 -0
- data/lib/ews/templates/message.rb +85 -0
- data/lib/ews/templates/reply_to_item.rb +25 -0
- data/lib/ews/templates/task.rb +74 -0
- data/lib/ews/types.rb +194 -0
- data/lib/ews/types/attachment.rb +77 -0
- data/lib/{model/meeting_cancellation.rb → ews/types/attendee.rb} +9 -8
- data/lib/ews/types/calendar_folder.rb +50 -0
- data/lib/ews/types/calendar_item.rb +130 -0
- data/lib/ews/types/contact.rb +7 -0
- data/lib/ews/types/contacts_folder.rb +8 -0
- data/lib/ews/types/copied_event.rb +51 -0
- data/lib/{soap/handsoap/builder.rb → ews/types/created_event.rb} +5 -2
- data/lib/ews/types/deleted_event.rb +24 -0
- data/lib/ews/types/distribution_list.rb +7 -0
- data/lib/ews/types/event.rb +62 -0
- data/lib/ews/types/export_items_response_message.rb +52 -0
- data/lib/ews/types/file_attachment.rb +65 -0
- data/lib/ews/types/folder.rb +60 -0
- data/lib/ews/types/free_busy_changed_event.rb +24 -0
- data/lib/ews/types/generic_folder.rb +418 -0
- data/lib/ews/types/item.rb +447 -0
- data/lib/ews/types/item_attachment.rb +84 -0
- data/lib/{model → ews/types}/item_field_uri_map.rb +2 -18
- data/lib/ews/types/mailbox_user.rb +156 -0
- data/lib/ews/types/meeting_cancellation.rb +7 -0
- data/lib/ews/types/meeting_message.rb +7 -0
- data/lib/ews/types/meeting_request.rb +7 -0
- data/lib/ews/types/meeting_response.rb +7 -0
- data/lib/ews/types/message.rb +7 -0
- data/lib/ews/types/modified_event.rb +48 -0
- data/lib/{extensions/string.rb → ews/types/moved_event.rb} +31 -15
- data/lib/ews/types/new_mail_event.rb +24 -0
- data/lib/ews/types/out_of_office.rb +147 -0
- data/lib/ews/types/post_item.rb +7 -0
- data/lib/ews/types/search_folder.rb +8 -0
- data/lib/ews/types/status_event.rb +39 -0
- data/lib/ews/types/task.rb +41 -0
- data/lib/ews/types/tasks_folder.rb +27 -0
- data/lib/viewpoint.rb +85 -108
- data/lib/{model/distribution_list.rb → viewpoint/logging.rb} +6 -3
- data/lib/{model/meeting_response.rb → viewpoint/logging/config.rb} +3 -3
- data/lib/viewpoint/string_utils.rb +76 -0
- metadata +156 -123
- data/README +0 -114
- data/lib/model/calendar_folder.rb +0 -67
- data/lib/model/calendar_item.rb +0 -267
- data/lib/model/contact.rb +0 -238
- data/lib/model/contacts_folder.rb +0 -46
- data/lib/model/event.rb +0 -116
- data/lib/model/file_attachment.rb +0 -53
- data/lib/model/folder.rb +0 -47
- data/lib/model/generic_folder.rb +0 -461
- data/lib/model/item.rb +0 -312
- data/lib/model/mailbox_user.rb +0 -146
- data/lib/model/meeting_request.rb +0 -39
- data/lib/model/message.rb +0 -142
- data/lib/model/model.rb +0 -269
- data/lib/model/search_folder.rb +0 -48
- data/lib/model/task.rb +0 -121
- data/lib/model/tasks_folder.rb +0 -44
- data/lib/soap/handsoap/builders/ews_build_helpers.rb +0 -383
- data/lib/soap/handsoap/builders/ews_builder.rb +0 -146
- data/lib/soap/handsoap/ews_service.rb +0 -790
- data/lib/soap/handsoap/parser.rb +0 -104
- data/lib/soap/handsoap/parsers/ews_parser.rb +0 -245
- 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
|
data/lib/model/generic_folder.rb
DELETED
|
@@ -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
|