stxt-viewpoint 1.0.0.beta.3
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/Changelog.txt +1004 -0
- data/README.md +201 -0
- data/TODO +17 -0
- data/lib/ews/calendar_accessors.rb +46 -0
- data/lib/ews/connection.rb +120 -0
- data/lib/ews/connection_helper.rb +35 -0
- data/lib/ews/convert_accessors.rb +56 -0
- data/lib/ews/errors.rb +56 -0
- data/lib/ews/ews_client.rb +101 -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 +231 -0
- data/lib/ews/mailbox_accessors.rb +92 -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/builders/ews_builder.rb +1257 -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/ews/soap/parsers/ews_parser.rb +43 -0
- 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/ews/soap/responses/create_item_response_message.rb +25 -0
- 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/ews/soap/responses/subscribe_response_message.rb +35 -0
- data/lib/ews/soap/responses/sync_folder_hierarchy_response_message.rb +36 -0
- data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
- data/lib/ews/soap.rb +63 -0
- data/lib/ews/templates/calendar_item.rb +78 -0
- data/lib/ews/templates/forward_item.rb +24 -0
- data/lib/ews/templates/message.rb +73 -0
- data/lib/ews/templates/reply_to_item.rb +25 -0
- data/lib/ews/templates/task.rb +74 -0
- data/lib/ews/types/attachment.rb +77 -0
- data/lib/ews/types/attendee.rb +27 -0
- data/lib/ews/types/calendar_folder.rb +50 -0
- data/lib/ews/types/calendar_item.rb +122 -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/ews/types/created_event.rb +24 -0
- 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 +420 -0
- data/lib/ews/types/item.rb +438 -0
- data/lib/ews/types/item_attachment.rb +84 -0
- data/lib/ews/types/item_field_uri_map.rb +224 -0
- 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/ews/types/moved_event.rb +51 -0
- data/lib/ews/types/new_mail_event.rb +24 -0
- data/lib/ews/types/out_of_office.rb +147 -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 +37 -0
- data/lib/ews/types/tasks_folder.rb +27 -0
- data/lib/ews/types.rb +191 -0
- data/lib/viewpoint/logging/config.rb +24 -0
- data/lib/viewpoint/logging.rb +27 -0
- data/lib/viewpoint/string_utils.rb +44 -0
- data/lib/viewpoint.rb +109 -0
- metadata +191 -0
@@ -0,0 +1,61 @@
|
|
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
|
+
module Viewpoint::EWS
|
19
|
+
|
20
|
+
# Generic Ews Error
|
21
|
+
class EwsError < StandardError; end
|
22
|
+
|
23
|
+
# Raise when authentication/authorization issues occur.
|
24
|
+
class EwsLoginError < EwsError; end
|
25
|
+
|
26
|
+
class EwsSubscriptionError < EwsError; end
|
27
|
+
|
28
|
+
# Raised when a user tries to query a folder subscription after the
|
29
|
+
# subscription has timed out.
|
30
|
+
class EwsSubscriptionTimeout < EwsSubscriptionError; end
|
31
|
+
|
32
|
+
# Represents a function in EWS that is not yet implemented in Viewpoint
|
33
|
+
class EwsNotImplemented < EwsError; end
|
34
|
+
|
35
|
+
# Raised when an method is called in the wrong way
|
36
|
+
class EwsBadArgumentError < EwsError; end
|
37
|
+
|
38
|
+
# Raised when an item that is asked for is not found
|
39
|
+
class EwsItemNotFound < EwsError; end
|
40
|
+
|
41
|
+
# Raised when a folder that is asked for is not found
|
42
|
+
class EwsFolderNotFound < EwsError; end
|
43
|
+
|
44
|
+
# Raise an Exchange Server version error. This is in case some functionality
|
45
|
+
# does not exist in a particular Server version but is called.
|
46
|
+
class EwsServerVersionError < EwsError; end
|
47
|
+
|
48
|
+
# Raised when #auto_deepen == false and a method is called for attributes
|
49
|
+
# that have not yet been fetched.
|
50
|
+
class EwsMinimalObjectError < EwsError; end
|
51
|
+
|
52
|
+
class EwsFrozenObjectError < EwsError; end
|
53
|
+
|
54
|
+
# Failed to save an object back to the EWS store.
|
55
|
+
class SaveFailed < EwsError; end
|
56
|
+
|
57
|
+
class EwsCreateItemError < EwsError; end
|
58
|
+
|
59
|
+
class EwsSendItemError < EwsError; end
|
60
|
+
|
61
|
+
end # Viewpoint::EWS
|
@@ -0,0 +1,264 @@
|
|
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
|
+
module Viewpoint::EWS::FolderAccessors
|
19
|
+
include Viewpoint::EWS
|
20
|
+
|
21
|
+
FOLDER_TYPE_MAP = {
|
22
|
+
:mail => 'IPF.Note',
|
23
|
+
:calendar => 'IPF.Appointment',
|
24
|
+
:task => 'IPF.Task',
|
25
|
+
}
|
26
|
+
|
27
|
+
# Find subfolders of the passed root folder. If no parameters are passed this
|
28
|
+
# method will search from the Root folder.
|
29
|
+
# @param [Hash] opts Misc options to control request
|
30
|
+
# @option opts [String,Symbol] :root Either a FolderId(String) or a
|
31
|
+
# DistinguishedFolderId(Symbol) . This is where to start the search from.
|
32
|
+
# Usually :root,:msgfolderroot, or :publicfoldersroot
|
33
|
+
# @option opts [Symbol] :traversal :shallow/:deep/:soft_deleted
|
34
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
35
|
+
# @option opts [optional, String] :folder_type an optional folder type to
|
36
|
+
# limit the search to like 'IPF.Task'
|
37
|
+
# @return [Array] Returns an Array of Folder or subclasses of Folder
|
38
|
+
# @raise [EwsError] raised when the backend SOAP method returns an error.
|
39
|
+
def folders(opts={})
|
40
|
+
opts = opts.clone
|
41
|
+
args = find_folders_args(opts)
|
42
|
+
obj = OpenStruct.new(opts: args, restriction: {})
|
43
|
+
yield obj if block_given?
|
44
|
+
merge_restrictions! obj
|
45
|
+
resp = ews.find_folder( args )
|
46
|
+
find_folders_parser(resp)
|
47
|
+
end
|
48
|
+
alias :find_folders :folders
|
49
|
+
|
50
|
+
# Get a specific folder by id or symbol
|
51
|
+
# @param [String,Symbol,Hash] folder_id Either a FolderId(String) or a
|
52
|
+
# DistinguishedFolderId(Symbol). You can also pass a Hash in the form:
|
53
|
+
# {id: <fold_id>, change_key: <change_key>}
|
54
|
+
# @param [Hash] opts Misc options to control request
|
55
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
56
|
+
# @option opts [String,nil] :act_as User to act on behalf as. This user must
|
57
|
+
# have been given delegate access to the folder or this operation will fail.
|
58
|
+
# @raise [EwsError] raised when the backend SOAP method returns an error.
|
59
|
+
def get_folder(folder_id, opts = {})
|
60
|
+
opts = opts.clone
|
61
|
+
args = get_folder_args(folder_id, opts)
|
62
|
+
resp = ews.get_folder(args)
|
63
|
+
get_folder_parser(resp)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get a specific folder by its name
|
67
|
+
# @param [String] name The folder name
|
68
|
+
# @param [Hash] opts Misc options to control request
|
69
|
+
# @option opts [String,Symbol] :parent Either a FolderId(String) or a
|
70
|
+
# DistinguishedFolderId(Symbol) . This is the parent folder.
|
71
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
72
|
+
# @option opts [String,nil] :act_as User to act on behalf as. This user must
|
73
|
+
# have been given delegate access to the folder or this operation will fail.
|
74
|
+
# @raise [EwsError] raised when the backend SOAP method returns an error.
|
75
|
+
def get_folder_by_name(name, opts={})
|
76
|
+
opts = opts.clone
|
77
|
+
opts[:root] = opts.delete(:parent)
|
78
|
+
folders(opts) do |obj|
|
79
|
+
obj.restriction = {
|
80
|
+
:is_equal_to =>
|
81
|
+
[
|
82
|
+
{:field_uRI => {:field_uRI=>'folder:DisplayName'}},
|
83
|
+
{:field_uRI_or_constant => {:constant => {:value=>name}}}
|
84
|
+
]
|
85
|
+
}
|
86
|
+
end.first
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param [String] name The name of the new folder
|
90
|
+
# @param [Hash] opts
|
91
|
+
# @option opts [String,Symbol] :parent Either a FolderId(String) or a
|
92
|
+
# DistinguishedFolderId(Symbol) . This is the parent folder.
|
93
|
+
# @option opts [Symbol] :type the type of folder to create. must be one of
|
94
|
+
# :folder, :calendar, :contacts, :search, or :tasks
|
95
|
+
# @see http://msdn.microsoft.com/en-us/library/aa580808.aspx
|
96
|
+
def make_folder(name, opts={})
|
97
|
+
parent = opts[:parent] || :msgfolderroot
|
98
|
+
resp = ews.create_folder :parent_folder_id => {:id => parent},
|
99
|
+
:folders => [folder_type(opts[:type]) => {:display_name => name}]
|
100
|
+
create_folder_parser(resp).first
|
101
|
+
end
|
102
|
+
alias :mkfolder :make_folder
|
103
|
+
|
104
|
+
# Get a specific folder by id or symbol
|
105
|
+
# @param [Hash] opts Misc options to control request
|
106
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
107
|
+
# @option opts [String,Symbol,Hash] :folder_id You can optionally specify a
|
108
|
+
# folder_id to limit the hierarchy synchronization to it. It must be a
|
109
|
+
# FolderId(String), a DistinguishedFolderId(Symbol) or you can pass a Hash
|
110
|
+
# in the form: {id: <fold_id>, change_key: <change_key>}
|
111
|
+
# @option opts [String] :sync_state an optional Base64 encoded SyncState
|
112
|
+
# String from a previous sync call.
|
113
|
+
# @yield [Hash] yields the formatted argument Hash for last-minute
|
114
|
+
# modification before calling the backend EWS method.
|
115
|
+
# @return [Hash] A hash with the following keys
|
116
|
+
# :all_synced, whether or not additional calls are needed to get all folders
|
117
|
+
# :sync_state, the sync state to use for the next call
|
118
|
+
# and the following optional keys depending on the changes
|
119
|
+
# :create, :update, :delete
|
120
|
+
# @raise [EwsError] raised when the backend SOAP method returns an error.
|
121
|
+
def sync_folders(opts = {})
|
122
|
+
opts = opts.clone
|
123
|
+
args = sync_folders_args(opts)
|
124
|
+
yield args if block_given?
|
125
|
+
resp = ews.sync_folder_hierarchy( args )
|
126
|
+
sync_folders_parser(resp)
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Build up the arguements for #find_folders
|
133
|
+
def find_folders_args(opts)
|
134
|
+
opts[:root] = opts[:root] || :msgfolderroot
|
135
|
+
opts[:traversal] = opts[:traversal] || :shallow
|
136
|
+
opts[:shape] = opts[:shape] || :default
|
137
|
+
folder_id = {:id => opts[:root]}
|
138
|
+
folder_id[:act_as] = opts[:act_as] if opts[:act_as]
|
139
|
+
if( opts[:folder_type] )
|
140
|
+
restr = { :is_equal_to =>
|
141
|
+
[
|
142
|
+
{:field_uRI => {:field_uRI=>'folder:FolderClass'}},
|
143
|
+
{:field_uRI_or_constant=>{:constant =>
|
144
|
+
{:value => map_folder_type(opts[:folder_type])}}},
|
145
|
+
]
|
146
|
+
}
|
147
|
+
end
|
148
|
+
args = {
|
149
|
+
:parent_folder_ids => [folder_id],
|
150
|
+
:traversal => opts[:traversal],
|
151
|
+
:folder_shape => {:base_shape => opts[:shape]}
|
152
|
+
}
|
153
|
+
args[:restriction] = restr if restr
|
154
|
+
args
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param [Viewpoint::EWS::SOAP::EwsSoapResponse] resp
|
158
|
+
def find_folders_parser(resp)
|
159
|
+
if resp.status == 'Success'
|
160
|
+
folders = resp.response_message[:elems][:root_folder][:elems][0][:folders][:elems]
|
161
|
+
return [] if folders.nil?
|
162
|
+
folders.collect do |f|
|
163
|
+
ftype = f.keys.first
|
164
|
+
class_by_name(ftype).new(ews, f[ftype])
|
165
|
+
end
|
166
|
+
else
|
167
|
+
raise EwsFolderNotFound, "Could not retrieve folders. #{resp.code}: #{resp.message}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def create_folder_parser(resp)
|
172
|
+
if resp.status == 'Success'
|
173
|
+
folders = resp.response_message[:elems][:folders][:elems]
|
174
|
+
folders.collect do |f|
|
175
|
+
ftype = f.keys.first
|
176
|
+
class_by_name(ftype).new(ews, f[ftype])
|
177
|
+
end
|
178
|
+
else
|
179
|
+
raise EwsError, "Could not create folder. #{resp.code}: #{resp.message}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Build up the arguements for #get_folder
|
184
|
+
def get_folder_args(folder_id, opts)
|
185
|
+
opts[:shape] ||= :default
|
186
|
+
default_args = {
|
187
|
+
:folder_shape => {:base_shape => opts[:shape]}
|
188
|
+
}
|
189
|
+
if folder_id.is_a?(Hash)
|
190
|
+
default_args[:folder_ids] = [folder_id]
|
191
|
+
else
|
192
|
+
default_args[:folder_ids] = [{:id => folder_id}]
|
193
|
+
end
|
194
|
+
default_args.merge opts
|
195
|
+
end
|
196
|
+
|
197
|
+
# @param [Viewpoint::EWS::SOAP::EwsSoapResponse] resp
|
198
|
+
def get_folder_parser(resp)
|
199
|
+
if(resp.status == 'Success')
|
200
|
+
f = resp.response_message[:elems][:folders][:elems][0]
|
201
|
+
ftype = f.keys.first
|
202
|
+
class_by_name(ftype).new(ews, f[ftype])
|
203
|
+
else
|
204
|
+
raise EwsFolderNotFound, "Could not retrieve folder. #{resp.code}: #{resp.message}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def sync_folders_args(opts)
|
209
|
+
opts[:shape] = opts[:shape] || :default
|
210
|
+
args = { :folder_shape => {:base_shape => opts[:shape]} }
|
211
|
+
if opts[:folder_id]
|
212
|
+
folder_id = opts[:folder_id]
|
213
|
+
if folder_id.is_a?(Hash)
|
214
|
+
args[:sync_folder_id] = folder_id
|
215
|
+
else
|
216
|
+
args[:sync_folder_id] = {:id => folder_id}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
args[:sync_state] = opts[:sync_state] if opts[:sync_state]
|
220
|
+
args
|
221
|
+
end
|
222
|
+
|
223
|
+
def sync_folders_parser(resp)
|
224
|
+
rmsg = resp.response_messages[0]
|
225
|
+
if rmsg.success?
|
226
|
+
rhash = {}
|
227
|
+
rhash[:all_synced] = rmsg.includes_last_folder_in_range?
|
228
|
+
rhash[:sync_state] = rmsg.sync_state
|
229
|
+
rmsg.changes.each do |c|
|
230
|
+
ctype = c.keys.first
|
231
|
+
rhash[ctype] = [] unless rhash.has_key?(ctype)
|
232
|
+
if ctype == :delete
|
233
|
+
rhash[ctype] << c[ctype][:elems][0][:folder_id][:attribs]
|
234
|
+
else
|
235
|
+
type = c[ctype][:elems][0].keys.first
|
236
|
+
item = class_by_name(type).new(ews, c[ctype][:elems][0][type])
|
237
|
+
rhash[ctype] << item
|
238
|
+
end
|
239
|
+
end
|
240
|
+
rhash
|
241
|
+
else
|
242
|
+
raise EwsError, "Could not synchronize folders. #{rmsg.response_code}: #{rmsg.message_text}"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Map a passed parameter to a know folder type mapping. If no mapping
|
247
|
+
# exits simply allow the passed in type to be passed to the SOAP call.
|
248
|
+
# @param [Symbol] type a symbol in FOLDER_TYPE_MAP
|
249
|
+
def map_folder_type(type)
|
250
|
+
FOLDER_TYPE_MAP[type] || type
|
251
|
+
end
|
252
|
+
|
253
|
+
def folder_type(type)
|
254
|
+
case type
|
255
|
+
when nil, :folder
|
256
|
+
:folder
|
257
|
+
when :calendar, :contacts, :search, :tasks
|
258
|
+
"#{type}_folder".to_sym
|
259
|
+
else
|
260
|
+
raise EwsBadArgumentError, "Not a proper folder type: :#{type}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Viewpoint::EWS
|
2
|
+
|
3
|
+
ConnectingSID = {
|
4
|
+
:UPN => 'PrincipalName',
|
5
|
+
:SID => 'SID',
|
6
|
+
:PSMTP => 'PrimarySmtpAddress',
|
7
|
+
:SMTP => 'SmtpAddress'
|
8
|
+
}
|
9
|
+
|
10
|
+
# @param connecting_type [String] should be one of the ConnectingSID variables
|
11
|
+
# ConnectingSID[:UPN] - use User Principal Name method
|
12
|
+
# ConnectingSID[:SID] - use Security Identifier method
|
13
|
+
# ConnectingSID[:PSMTP] - use primary Simple Mail Transfer Protocol method
|
14
|
+
# ConnectingSID[:SMTP] - use Simple Mail Transfer Protocol method
|
15
|
+
# you can add any other string, it will be converted into xml tag on soap request
|
16
|
+
# @param address [String] an address to include to requests for impersonation
|
17
|
+
def set_impersonation(connecting_type, address)
|
18
|
+
if ConnectingSID.has_value? connecting_type or connecting_type.is_a? String then
|
19
|
+
ews.impersonation_type = connecting_type
|
20
|
+
ews.impersonation_address = address
|
21
|
+
else
|
22
|
+
raise EwsBadArgumentError, "Not a proper connecting method: #{connecting_type.class}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_impersonation
|
27
|
+
ews.impersonation_type = ""
|
28
|
+
ews.impersonation_address = ""
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,231 @@
|
|
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
|
+
module Viewpoint::EWS::ItemAccessors
|
19
|
+
include Viewpoint::EWS
|
20
|
+
|
21
|
+
# This is a class method that fetches an existing Item from the
|
22
|
+
# Exchange Store.
|
23
|
+
# @param [String] item_id The id of the item. You can also pass a Hash in the
|
24
|
+
# form: {id: <fold_id>, change_key: <change_key>}
|
25
|
+
# @param [Hash] opts Misc options to control request
|
26
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
27
|
+
# @return [Item] Returns an Item or subclass of Item
|
28
|
+
# @todo Add support to fetch an item with a ChangeKey
|
29
|
+
def get_item(item_id, opts = {})
|
30
|
+
args = get_item_args(item_id, opts.clone)
|
31
|
+
obj = OpenStruct.new(opts: args)
|
32
|
+
yield obj if block_given?
|
33
|
+
resp = ews.get_item(args)
|
34
|
+
get_item_parser(resp)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Hash] opts Misc options to control request
|
38
|
+
# @option opts [Symbol] :folder_id
|
39
|
+
# @see GenericFolder#items
|
40
|
+
def find_items(opts = {})
|
41
|
+
args = find_items_args(opts.clone)
|
42
|
+
obj = OpenStruct.new(opts: args, restriction: {})
|
43
|
+
yield obj if block_given?
|
44
|
+
merge_restrictions! obj
|
45
|
+
resp = ews.find_item(args)
|
46
|
+
find_items_parser resp
|
47
|
+
end
|
48
|
+
|
49
|
+
# This is a class method that fetches an existing Item from the
|
50
|
+
# Exchange Store.
|
51
|
+
# @param [String] item_id The id of the item. You can also pass a Hash in the
|
52
|
+
# form: {id: <fold_id>, change_key: <change_key>}
|
53
|
+
# @param [Hash] opts Misc options to control request
|
54
|
+
# @option opts [Symbol] :shape :id_only/:default/:all_properties
|
55
|
+
# @return [Item] Returns an Item or subclass of Item
|
56
|
+
# @todo Add support to fetch an item with a ChangeKey
|
57
|
+
def get_items(item_ids, opts = {})
|
58
|
+
args = get_item_args(item_ids, opts.clone)
|
59
|
+
obj = OpenStruct.new(opts: args)
|
60
|
+
yield obj if block_given?
|
61
|
+
resp = ews.get_item(args)
|
62
|
+
get_items_parser(resp)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Copy an array of items to the specified folder
|
66
|
+
# @param items [Array] an array of EWS Items that you want to copy
|
67
|
+
# @param folder [String,Symbol,GenericFolder] The folder to copy to. This must
|
68
|
+
# be a subclass of GenericFolder, a DistinguishedFolderId (must me a Symbol)
|
69
|
+
# or a FolderId (String)
|
70
|
+
# @return [Array<Hash>] returns a Hash for each item passed
|
71
|
+
# on success:
|
72
|
+
# {:success => true, :item_id => <new_item_id>}
|
73
|
+
# on failure:
|
74
|
+
# {:success => false, :error_message => <the message>}
|
75
|
+
def copy_items(items, folder)
|
76
|
+
folder = folder.id if folder.kind_of?(Types::GenericFolder)
|
77
|
+
item_ids = items.collect{|i| {item_id: {id: i.id, change_key: i.change_key}}}
|
78
|
+
copy_opts = {
|
79
|
+
:to_folder_id => {:id => folder},
|
80
|
+
:item_ids => item_ids
|
81
|
+
}
|
82
|
+
resp = ews.copy_item(copy_opts)
|
83
|
+
copy_move_items_parser(resp)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Move an array of items to the specified folder
|
87
|
+
# @see #copy_items for parameter info
|
88
|
+
def move_items(items, folder)
|
89
|
+
folder = folder.id if folder.kind_of?(Types::GenericFolder)
|
90
|
+
item_ids = items.collect{|i| {item_id: {id: i.id, change_key: i.change_key}}}
|
91
|
+
move_opts = {
|
92
|
+
:to_folder_id => {:id => folder},
|
93
|
+
:item_ids => item_ids
|
94
|
+
}
|
95
|
+
resp = ews.move_item(move_opts)
|
96
|
+
copy_move_items_parser(resp, :move_item_response_message)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Exports an entire item into base64 string
|
100
|
+
# @param item_ids [Array] array of item ids. Can also be a single id value
|
101
|
+
# return [Array] array of bulk items
|
102
|
+
def export_items(item_ids)
|
103
|
+
args = export_items_args(item_ids)
|
104
|
+
|
105
|
+
resp = ews.export_items(args)
|
106
|
+
export_items_parser(resp)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def get_item_args(item_id, opts)
|
112
|
+
opts[:shape] ||= :default
|
113
|
+
default_args = {
|
114
|
+
:item_shape => {:base_shape => opts[:shape]}
|
115
|
+
}
|
116
|
+
default_args[:item_ids] = case item_id
|
117
|
+
when Hash
|
118
|
+
[{:item_id => item_id}]
|
119
|
+
when Array
|
120
|
+
item_id.map{|i| {:item_id => {:id => i}}}
|
121
|
+
else
|
122
|
+
[{:item_id => {:id => item_id}}]
|
123
|
+
end
|
124
|
+
default_args.merge opts
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_item_parser(resp)
|
128
|
+
rm = resp.response_messages[0]
|
129
|
+
|
130
|
+
if(rm && rm.status == 'Success')
|
131
|
+
i = rm.items.first
|
132
|
+
itype = i.keys.first
|
133
|
+
class_by_name(itype).new(ews, i[itype])
|
134
|
+
else
|
135
|
+
code = rm.respond_to?(:code) ? rm.code : "Unknown"
|
136
|
+
text = rm.respond_to?(:message_text) ? rm.message_text : "Unknown"
|
137
|
+
raise EwsItemNotFound, "Could not retrieve item. #{rm.code}: #{rm.message_text}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_items_parser(resp)
|
142
|
+
items = []
|
143
|
+
|
144
|
+
resp.response_messages.each do |rm|
|
145
|
+
if(rm && rm.status == 'Success')
|
146
|
+
rm.items.each do |i|
|
147
|
+
type = i.keys.first
|
148
|
+
items << class_by_name(type).new(ews, i[type])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
items
|
154
|
+
end
|
155
|
+
|
156
|
+
def find_items_args(opts)
|
157
|
+
default_args = {
|
158
|
+
:traversal => 'Shallow',
|
159
|
+
:item_shape => {:base_shape => 'Default'}
|
160
|
+
}
|
161
|
+
|
162
|
+
if opts[:folder_id].is_a?(Hash)
|
163
|
+
default_args[:parent_folder_ids] = [opts.delete(:folder_id)]
|
164
|
+
else
|
165
|
+
default_args[:parent_folder_ids] = [{:id => opts.delete(:folder_id)}]
|
166
|
+
end
|
167
|
+
default_args.merge(opts)
|
168
|
+
end
|
169
|
+
|
170
|
+
def find_items_parser(resp)
|
171
|
+
rm = resp.response_messages[0]
|
172
|
+
if rm.success?
|
173
|
+
items = []
|
174
|
+
rm.root_folder.items.each do |i|
|
175
|
+
type = i.keys.first
|
176
|
+
items << class_by_name(type).new(ews, i[type])
|
177
|
+
end
|
178
|
+
items
|
179
|
+
else
|
180
|
+
raise EwsError, "Could not retrieve folder. #{rm.code}: #{rm.message_text}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def copy_move_items_parser(resp, resp_type = :copy_item_response_message)
|
185
|
+
resp.response_messages.collect {|r|
|
186
|
+
obj = {}
|
187
|
+
if r.success?
|
188
|
+
obj[:success] = true
|
189
|
+
item = r.items.first
|
190
|
+
key = item.keys.first
|
191
|
+
obj[:item_id] = item[key][:elems][0][:item_id][:attribs][:id]
|
192
|
+
else
|
193
|
+
obj[:success] = false
|
194
|
+
obj[:error_message] = "#{r.response_code}: #{r.message_text}"
|
195
|
+
end
|
196
|
+
obj
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
def export_items_args(item_ids)
|
201
|
+
default_args = {}
|
202
|
+
default_args[:item_ids] = []
|
203
|
+
if item_ids.is_a?(Array) then
|
204
|
+
item_ids.each do |id|
|
205
|
+
default_args[:item_ids] = default_args[:item_ids] + [{:item_id => {:id => id}}]
|
206
|
+
end
|
207
|
+
else
|
208
|
+
default_args[:item_ids] = [{:item_id => {:id => item_ids}}]
|
209
|
+
end
|
210
|
+
default_args
|
211
|
+
end
|
212
|
+
|
213
|
+
def export_items_parser(resp)
|
214
|
+
rm = resp.response_messages
|
215
|
+
if(rm)
|
216
|
+
items = []
|
217
|
+
rm.each do |i|
|
218
|
+
if i.success? then
|
219
|
+
type = i.type
|
220
|
+
items << class_by_name(type).new(ews, i.message[:elems])
|
221
|
+
else
|
222
|
+
code = i.respond_to?(:code) ? i.code : "Unknown"
|
223
|
+
text = i.respond_to?(:message_text) ? i.message_text : "Unknown"
|
224
|
+
items << "Could not retrieve item. #{code}: #{text}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
items
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end # Viewpoint::EWS::ItemAccessors
|
@@ -0,0 +1,92 @@
|
|
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::EWS::MailboxAccessors
|
20
|
+
include Viewpoint::EWS
|
21
|
+
|
22
|
+
# Resolve contacts in the Exchange Data Store
|
23
|
+
# @param [String] ustring A string to resolve contacts to.
|
24
|
+
# @return [Array<MailboxUser>] It returns an Array of MailboxUsers.
|
25
|
+
def search_contacts(ustring)
|
26
|
+
resp = ews.resolve_names(:name => ustring)
|
27
|
+
|
28
|
+
users = []
|
29
|
+
if(resp.status == 'Success')
|
30
|
+
mb = resp.response_message[:elems][:resolution_set][:elems][0][:resolution][:elems][0]
|
31
|
+
users << Types::MailboxUser.new(ews, mb[:mailbox][:elems])
|
32
|
+
elsif(resp.code == 'ErrorNameResolutionMultipleResults')
|
33
|
+
resp.response_message[:elems][:resolution_set][:elems].each do |u|
|
34
|
+
if u[:resolution][:elems][0][:mailbox]
|
35
|
+
users << Types::MailboxUser.new(ews, u[:resolution][:elems][0][:mailbox][:elems])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
raise EwsError, "Find User produced an error: #{resp.code}: #{resp.message}"
|
40
|
+
end
|
41
|
+
users
|
42
|
+
end
|
43
|
+
|
44
|
+
# GetUserAvailability request
|
45
|
+
# @see http://msdn.microsoft.com/en-us/library/aa563800.aspx
|
46
|
+
# @param [Array<String>] emails A list of emails you want to retrieve free-busy info for.
|
47
|
+
# @param [Hash] opts
|
48
|
+
# @option opts [DateTime] :start_time
|
49
|
+
# @option opts [DateTime] :end_time
|
50
|
+
# @option opts [Symbol] :requested_view :merged_only/:free_busy/
|
51
|
+
# :free_busy_merged/:detailed/:detailed_merged
|
52
|
+
# @option opts [Hash] :time_zone The TimeZone data
|
53
|
+
# Example: {:bias => 'UTC offset in minutes',
|
54
|
+
# :standard_time => {:bias => 480, :time => '02:00:00',
|
55
|
+
# :day_order => 5, :month => 10, :day_of_week => 'Sunday'},
|
56
|
+
# :daylight_time => {same options as :standard_time}}
|
57
|
+
def get_user_availability(emails, opts)
|
58
|
+
opts = opts.clone
|
59
|
+
args = get_user_availability_args(emails, opts)
|
60
|
+
resp = ews.get_user_availability(args.merge(opts))
|
61
|
+
get_user_availability_parser(resp)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def get_user_availability_args(emails, opts)
|
68
|
+
unless opts.has_key?(:start_time) && opts.has_key?(:end_time) && opts.has_key?(:requested_view)
|
69
|
+
raise EwsBadArgumentError, "You must specify a start_time, end_time and requested_view."
|
70
|
+
end
|
71
|
+
|
72
|
+
default_args = {
|
73
|
+
mailbox_data: (emails.collect{|e| [email: {address: e}]}.flatten),
|
74
|
+
free_busy_view_options: {
|
75
|
+
time_window: {
|
76
|
+
start_time: opts[:start_time],
|
77
|
+
end_time: opts[:end_time]
|
78
|
+
},
|
79
|
+
requested_view: { :requested_free_busy_view => opts[:requested_view] },
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_user_availability_parser(resp)
|
85
|
+
if(resp.status == 'Success')
|
86
|
+
resp
|
87
|
+
else
|
88
|
+
raise EwsError, "GetUserAvailability produced an error: #{resp.code}: #{resp.message}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end # Viewpoint::EWS::MailboxAccessors
|