viewpoint 1.2.0 → 2.0.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.
- checksums.yaml +4 -4
- data/README.md +11 -6
- data/lib/ews/calendar_accessors.rb +37 -33
- data/lib/ews/connection.rb +152 -125
- data/lib/ews/connection_helper.rb +36 -33
- data/lib/ews/convert_accessors.rb +58 -54
- data/lib/ews/errors.rb +59 -53
- data/lib/ews/ews_client.rb +92 -79
- data/lib/ews/exceptions/exceptions.rb +61 -61
- data/lib/ews/folder_accessors.rb +235 -234
- data/lib/ews/impersonation.rb +29 -23
- data/lib/ews/item_accessors.rb +215 -213
- data/lib/ews/mailbox_accessors.rb +82 -79
- data/lib/ews/meeting_accessors.rb +38 -31
- data/lib/ews/message_accessors.rb +83 -81
- data/lib/ews/push_subscription_accessors.rb +29 -27
- data/lib/ews/room_accessors.rb +48 -46
- data/lib/ews/roomlist_accessors.rb +47 -45
- data/lib/ews/soap/builders/ews_builder.rb +1191 -1176
- data/lib/ews/soap/ews_response.rb +68 -73
- data/lib/ews/soap/ews_soap_availability_response.rb +57 -57
- data/lib/ews/soap/ews_soap_free_busy_response.rb +115 -117
- data/lib/ews/soap/ews_soap_response.rb +99 -101
- data/lib/ews/soap/ews_soap_room_response.rb +45 -45
- data/lib/ews/soap/ews_soap_roomlist_response.rb +45 -46
- data/lib/ews/soap/exchange_availability.rb +54 -52
- data/lib/ews/soap/exchange_data_services.rb +693 -711
- data/lib/ews/soap/exchange_notification.rb +123 -128
- data/lib/ews/soap/exchange_synchronization.rb +82 -82
- data/lib/ews/soap/exchange_time_zones.rb +41 -44
- data/lib/ews/soap/exchange_user_configuration.rb +30 -27
- data/lib/ews/soap/exchange_web_service.rb +237 -233
- data/lib/ews/soap/parsers/ews_parser.rb +46 -42
- data/lib/ews/soap/parsers/ews_sax_document.rb +63 -58
- data/lib/ews/soap/response_message.rb +69 -68
- data/lib/ews/soap/responses/create_attachment_response_message.rb +45 -45
- data/lib/ews/soap/responses/create_item_response_message.rb +26 -25
- data/lib/ews/soap/responses/find_item_response_message.rb +76 -78
- data/lib/ews/soap/responses/get_events_response_message.rb +52 -51
- data/lib/ews/soap/responses/send_notification_response_message.rb +53 -51
- data/lib/ews/soap/responses/subscribe_response_message.rb +37 -34
- data/lib/ews/soap/responses/sync_folder_hierarchy_response_message.rb +38 -35
- data/lib/ews/soap/responses/sync_folder_items_response_message.rb +38 -35
- data/lib/ews/soap.rb +27 -26
- data/lib/ews/templates/calendar_item.rb +64 -60
- data/lib/ews/templates/forward_item.rb +19 -17
- data/lib/ews/templates/message.rb +61 -58
- data/lib/ews/templates/reply_to_item.rb +20 -18
- data/lib/ews/templates/task.rb +55 -55
- data/lib/ews/types/attachment.rb +75 -74
- data/lib/ews/types/attendee.rb +27 -25
- data/lib/ews/types/calendar_folder.rb +58 -59
- data/lib/ews/types/calendar_item.rb +133 -131
- data/lib/ews/types/contact.rb +11 -5
- data/lib/ews/types/contacts_folder.rb +10 -5
- data/lib/ews/types/copied_event.rb +47 -48
- data/lib/ews/types/created_event.rb +22 -20
- data/lib/ews/types/deleted_event.rb +22 -20
- data/lib/ews/types/distribution_list.rb +11 -5
- data/lib/ews/types/event.rb +60 -59
- data/lib/ews/types/export_items_response_message.rb +54 -50
- data/lib/ews/types/file_attachment.rb +63 -61
- data/lib/ews/types/folder.rb +58 -57
- data/lib/ews/types/free_busy_changed_event.rb +22 -20
- data/lib/ews/types/generic_folder.rb +348 -372
- data/lib/ews/types/item.rb +401 -397
- data/lib/ews/types/item_attachment.rb +87 -80
- data/lib/ews/types/item_field_uri_map.rb +201 -200
- data/lib/ews/types/mailbox_user.rb +146 -154
- data/lib/ews/types/meeting_cancellation.rb +11 -5
- data/lib/ews/types/meeting_message.rb +11 -5
- data/lib/ews/types/meeting_request.rb +11 -5
- data/lib/ews/types/meeting_response.rb +11 -5
- data/lib/ews/types/message.rb +11 -5
- data/lib/ews/types/modified_event.rb +43 -45
- data/lib/ews/types/moved_event.rb +47 -48
- data/lib/ews/types/new_mail_event.rb +22 -20
- data/lib/ews/types/out_of_office.rb +145 -144
- data/lib/ews/types/post_item.rb +11 -5
- data/lib/ews/types/search_folder.rb +10 -5
- data/lib/ews/types/status_event.rb +37 -36
- data/lib/ews/types/task.rb +39 -33
- data/lib/ews/types/tasks_folder.rb +31 -25
- data/lib/ews/types.rb +134 -139
- data/lib/viewpoint/logging/config.rb +19 -19
- data/lib/viewpoint/logging.rb +23 -17
- data/lib/viewpoint/string_utils.rb +31 -32
- data/lib/viewpoint.rb +18 -18
- metadata +50 -25
- data/Changelog.txt +0 -1004
|
@@ -1,418 +1,394 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This file is part of Viewpoint; the Ruby library for Microsoft Exchange Web Services.
|
|
4
|
+
#
|
|
5
|
+
# Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
3
18
|
|
|
4
|
-
|
|
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
|
|
19
|
+
require 'ews/item_accessors'
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
module Viewpoint
|
|
22
|
+
module EWS
|
|
23
|
+
module Types
|
|
24
|
+
# Generic Folder EWS data type.
|
|
25
|
+
module GenericFolder
|
|
26
|
+
include Viewpoint::EWS
|
|
27
|
+
include Viewpoint::EWS::Types
|
|
28
|
+
include Viewpoint::EWS::ItemAccessors
|
|
29
|
+
include Viewpoint::StringUtils
|
|
30
|
+
|
|
31
|
+
GFOLDER_KEY_PATHS = {
|
|
32
|
+
folder_id: %i[folder_id attribs],
|
|
33
|
+
id: %i[folder_id attribs id],
|
|
34
|
+
change_key: %i[folder_id attribs change_key],
|
|
35
|
+
parent_folder_id: %i[parent_folder_id attribs id],
|
|
36
|
+
parent_folder_change_key: %i[parent_folder_id attribs change_key],
|
|
37
|
+
folder_class: %i[folder_class text],
|
|
38
|
+
total_count: %i[total_count text],
|
|
39
|
+
child_folder_count: %i[child_folder_count text],
|
|
40
|
+
display_name: %i[display_name text]
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
GFOLDER_KEY_TYPES = {
|
|
44
|
+
total_count: lambda(&:to_i),
|
|
45
|
+
child_folder_count: lambda(&:to_i)
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
GFOLDER_KEY_ALIAS = {
|
|
49
|
+
name: :display_name,
|
|
50
|
+
ckey: :change_key
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
attr_accessor :subscription_id, :watermark, :sync_state
|
|
54
|
+
|
|
55
|
+
# @param [SOAP::ExchangeWebService] ews the EWS reference
|
|
56
|
+
# @param [Hash] ews_item the EWS parsed response document
|
|
57
|
+
def initialize(ews, ews_item)
|
|
58
|
+
super
|
|
59
|
+
simplify!
|
|
60
|
+
@sync_state = nil
|
|
61
|
+
@synced = false
|
|
62
|
+
end
|
|
11
63
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
=
|
|
64
|
+
def delete!
|
|
65
|
+
opts = {
|
|
66
|
+
folder_ids: [{ id: id }],
|
|
67
|
+
delete_type: 'HardDelete'
|
|
68
|
+
}
|
|
69
|
+
resp = @ews.delete_folder(opts)
|
|
70
|
+
resp.success? || raise(EwsError, "Could not delete folder. #{resp.code}: #{resp.message}")
|
|
71
|
+
end
|
|
18
72
|
|
|
19
|
-
|
|
73
|
+
def items(opts = {})
|
|
74
|
+
args = items_args(opts.clone)
|
|
75
|
+
obj = OpenStruct.new(opts: args, restriction: {})
|
|
76
|
+
yield obj if block_given?
|
|
77
|
+
merge_restrictions! obj
|
|
78
|
+
resp = ews.find_item(args)
|
|
79
|
+
items_parser resp
|
|
80
|
+
end
|
|
20
81
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
:parent_folder_change_key => [:parent_folder_id, :attribs, :change_key],
|
|
34
|
-
:folder_class => [:folder_class, :text],
|
|
35
|
-
:total_count => [:total_count, :text],
|
|
36
|
-
:child_folder_count => [:child_folder_count, :text],
|
|
37
|
-
:display_name => [:display_name, :text],
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
GFOLDER_KEY_TYPES = {
|
|
41
|
-
:total_count => ->(str){str.to_i},
|
|
42
|
-
:child_folder_count => ->(str){str.to_i},
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
GFOLDER_KEY_ALIAS = {
|
|
46
|
-
:name => :display_name,
|
|
47
|
-
:ckey => :change_key,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
attr_accessor :subscription_id, :watermark, :sync_state
|
|
51
|
-
|
|
52
|
-
# @param [SOAP::ExchangeWebService] ews the EWS reference
|
|
53
|
-
# @param [Hash] ews_item the EWS parsed response document
|
|
54
|
-
def initialize(ews, ews_item)
|
|
55
|
-
super
|
|
56
|
-
simplify!
|
|
57
|
-
@sync_state = nil
|
|
58
|
-
@synced = false
|
|
59
|
-
end
|
|
82
|
+
# Fetch items since a give DateTime
|
|
83
|
+
# @param [DateTime] date_time the time to fetch Items since.
|
|
84
|
+
def items_since(date_time, opts = {})
|
|
85
|
+
opts = opts.clone
|
|
86
|
+
raise EwsBadArgumentError, 'First argument must be a Date or DateTime' unless date_time.is_a?(Date)
|
|
87
|
+
|
|
88
|
+
restr = { restriction: { is_greater_than_or_equal_to: [
|
|
89
|
+
{ field_uRI: { field_uRI: 'item:DateTimeReceived' } },
|
|
90
|
+
{ field_uRI_or_constant: { constant: { value: date_time.to_datetime } } }
|
|
91
|
+
] } }
|
|
92
|
+
items(opts.merge(restr))
|
|
93
|
+
end
|
|
60
94
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
resp = @ews.delete_folder(opts)
|
|
67
|
-
if resp.success?
|
|
68
|
-
true
|
|
69
|
-
else
|
|
70
|
-
raise EwsError, "Could not delete folder. #{resp.code}: #{resp.message}"
|
|
71
|
-
end
|
|
72
|
-
end
|
|
95
|
+
# Fetch only items from today (since midnight)
|
|
96
|
+
def todays_items(_opts = {})
|
|
97
|
+
items_since(Date.today)
|
|
98
|
+
end
|
|
73
99
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
# Fetch items between a given time period
|
|
101
|
+
# @param [DateTime] start_date the time to start fetching Items from
|
|
102
|
+
# @param [DateTime] end_date the time to stop fetching Items from
|
|
103
|
+
def items_between(start_date, end_date, _opts = {})
|
|
104
|
+
items do |obj|
|
|
105
|
+
obj.restriction = { and: [
|
|
106
|
+
{ is_greater_than_or_equal_to: [
|
|
107
|
+
{ field_uRI: { field_uRI: 'item:DateTimeReceived' } },
|
|
108
|
+
{ field_uRI_or_constant: { constant: { value: start_date } } }
|
|
109
|
+
] },
|
|
110
|
+
{ is_less_than_or_equal_to: [
|
|
111
|
+
{ field_uRI: { field_uRI: 'item:DateTimeReceived' } },
|
|
112
|
+
{ field_uRI_or_constant: { constant: { value: end_date } } }
|
|
113
|
+
] }
|
|
114
|
+
] }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
82
117
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
# Search on the item subject
|
|
119
|
+
# @param [String] match_str A simple string paramater to match against the
|
|
120
|
+
# subject. The search ignores case and does not accept regexes... only strings.
|
|
121
|
+
# @param [String,nil] exclude_str A string to exclude from matches against
|
|
122
|
+
# the subject. This is optional.
|
|
123
|
+
def search_by_subject(match_str, exclude_str = nil)
|
|
124
|
+
items do |obj|
|
|
125
|
+
match = { contains: {
|
|
126
|
+
containment_mode: 'Substring',
|
|
127
|
+
containment_comparison: 'IgnoreCase',
|
|
128
|
+
field_uRI: { field_uRI: 'item:Subject' },
|
|
129
|
+
constant: { value: match_str }
|
|
130
|
+
} }
|
|
131
|
+
unless exclude_str.nil?
|
|
132
|
+
excl = { not: { contains: {
|
|
133
|
+
containment_mode: 'Substring',
|
|
134
|
+
containment_comparison: 'IgnoreCase',
|
|
135
|
+
field_uRI: { field_uRI: 'item:Subject' },
|
|
136
|
+
constant: { value: exclude_str }
|
|
137
|
+
} } }
|
|
138
|
+
|
|
139
|
+
match[:and] = [{ contains: match.delete(:contains) }, excl]
|
|
140
|
+
end
|
|
141
|
+
obj.restriction = match
|
|
142
|
+
end
|
|
143
|
+
end
|
|
97
144
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
def get_all_properties!
|
|
146
|
+
@ews_item = get_folder(base_shape: 'AllProperties')
|
|
147
|
+
simplify!
|
|
148
|
+
end
|
|
102
149
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
obj.restriction = { :and =>
|
|
109
|
-
[
|
|
110
|
-
{:is_greater_than_or_equal_to =>
|
|
111
|
-
[
|
|
112
|
-
{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
|
|
113
|
-
{:field_uRI_or_constant=>{:constant => {:value =>start_date}}}
|
|
114
|
-
]
|
|
150
|
+
def available_categories
|
|
151
|
+
opts = {
|
|
152
|
+
user_config_name: {
|
|
153
|
+
name: 'CategoryList',
|
|
154
|
+
distinguished_folder_id: { id: :calendar }
|
|
115
155
|
},
|
|
116
|
-
|
|
117
|
-
[
|
|
118
|
-
{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
|
|
119
|
-
{:field_uRI_or_constant=>{:constant => {:value =>end_date}}}
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Search on the item subject
|
|
128
|
-
# @param [String] match_str A simple string paramater to match against the
|
|
129
|
-
# subject. The search ignores case and does not accept regexes... only strings.
|
|
130
|
-
# @param [String,nil] exclude_str A string to exclude from matches against
|
|
131
|
-
# the subject. This is optional.
|
|
132
|
-
def search_by_subject(match_str, exclude_str = nil)
|
|
133
|
-
items do |obj|
|
|
134
|
-
match = {:contains => {
|
|
135
|
-
:containment_mode => 'Substring',
|
|
136
|
-
:containment_comparison => 'IgnoreCase',
|
|
137
|
-
:field_uRI => {:field_uRI=>'item:Subject'},
|
|
138
|
-
:constant => {:value =>match_str}
|
|
139
|
-
}}
|
|
140
|
-
unless exclude_str.nil?
|
|
141
|
-
excl = {:not =>
|
|
142
|
-
{:contains => {
|
|
143
|
-
:containment_mode => 'Substring',
|
|
144
|
-
:containment_comparison => 'IgnoreCase',
|
|
145
|
-
:field_uRI => {:field_uRI=>'item:Subject'},
|
|
146
|
-
:constant => {:value =>exclude_str}
|
|
147
|
-
}}
|
|
156
|
+
user_config_props: 'XmlData'
|
|
148
157
|
}
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
ews.get_user_configuration(opts)
|
|
159
|
+
# txt = resp.response_message[:elems][:get_user_configuration_response_message][:elems][1]
|
|
160
|
+
# [:user_configuration][:elems][1][:xml_data][:text]
|
|
161
|
+
# Base64.decode64 txt
|
|
151
162
|
end
|
|
152
|
-
obj.restriction = match
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
163
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
# Syncronize Items in this folder. If this method is issued multiple
|
|
165
|
+
# times it will continue where the last sync completed.
|
|
166
|
+
# @param [Integer] sync_amount The number of items to synchronize per sync
|
|
167
|
+
# @param [Boolean] sync_all Whether to sync all the data by looping through.
|
|
168
|
+
# The default is to just sync the first set. You can manually loop through
|
|
169
|
+
# with multiple calls to #sync_items!
|
|
170
|
+
# @return [Hash] Returns a hash with keys for each change type that ocurred.
|
|
171
|
+
# Possible key values are:
|
|
172
|
+
# (:create/:udpate/:delete/:read_flag_change).
|
|
173
|
+
# For :deleted and :read_flag_change items a simple hash with :id and
|
|
174
|
+
# :change_key is returned.
|
|
175
|
+
# See: http://msdn.microsoft.com/en-us/library/aa565609.aspx
|
|
176
|
+
# rubocop:disable Style/OptionalBooleanParameter -- public API
|
|
177
|
+
def sync_items!(sync_state = nil, sync_amount = 256, _sync_all = false, opts = {})
|
|
178
|
+
item_shape = opts.key?(:item_shape) ? opts.delete(:item_shape) : { base_shape: :default }
|
|
179
|
+
sync_state ||= @sync_state
|
|
180
|
+
|
|
181
|
+
resp = ews.sync_folder_items item_shape: item_shape, sync_folder_id: folder_id,
|
|
182
|
+
max_changes_returned: sync_amount, sync_state: sync_state
|
|
183
|
+
rmsg = resp.response_messages[0]
|
|
173
184
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
resp = ews.sync_folder_items item_shape: item_shape,
|
|
191
|
-
sync_folder_id: self.folder_id, max_changes_returned: sync_amount, sync_state: sync_state
|
|
192
|
-
rmsg = resp.response_messages[0]
|
|
193
|
-
|
|
194
|
-
if rmsg.success?
|
|
195
|
-
@synced = rmsg.includes_last_item_in_range?
|
|
196
|
-
@sync_state = rmsg.sync_state
|
|
197
|
-
rhash = {}
|
|
198
|
-
rmsg.changes.each do |c|
|
|
199
|
-
ctype = c.keys.first
|
|
200
|
-
rhash[ctype] = [] unless rhash.has_key?(ctype)
|
|
201
|
-
if ctype == :delete || ctype == :read_flag_change
|
|
202
|
-
rhash[ctype] << c[ctype][:elems][0][:item_id][:attribs]
|
|
203
|
-
else
|
|
204
|
-
type = c[ctype][:elems][0].keys.first
|
|
205
|
-
item = class_by_name(type).new(ews, c[ctype][:elems][0][type])
|
|
206
|
-
rhash[ctype] << item
|
|
185
|
+
raise EwsError, "Could not synchronize: #{rmsg.code}: #{rmsg.message_text}" unless rmsg.success?
|
|
186
|
+
|
|
187
|
+
@synced = rmsg.includes_last_item_in_range?
|
|
188
|
+
@sync_state = rmsg.sync_state
|
|
189
|
+
rhash = {}
|
|
190
|
+
rmsg.changes.each do |c|
|
|
191
|
+
ctype = c.keys.first
|
|
192
|
+
rhash[ctype] = [] unless rhash.key?(ctype)
|
|
193
|
+
if %i[delete read_flag_change].include?(ctype)
|
|
194
|
+
rhash[ctype] << c[ctype][:elems][0][:item_id][:attribs]
|
|
195
|
+
else
|
|
196
|
+
type = c[ctype][:elems][0].keys.first
|
|
197
|
+
item = class_by_name(type).new(ews, c[ctype][:elems][0][type])
|
|
198
|
+
rhash[ctype] << item
|
|
199
|
+
end
|
|
207
200
|
end
|
|
201
|
+
rhash
|
|
208
202
|
end
|
|
209
|
-
|
|
210
|
-
else
|
|
211
|
-
raise EwsError, "Could not synchronize: #{rmsg.code}: #{rmsg.message_text}"
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def synced?
|
|
216
|
-
@synced
|
|
217
|
-
end
|
|
203
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
|
218
204
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
# @param event_types [Array] Which event types to subscribe to. By default
|
|
223
|
-
# we subscribe to all Exchange event types: :all, :copied, :created,
|
|
224
|
-
# :deleted, :modified, :moved, :new_mail, :free_busy_changed
|
|
225
|
-
# @param watermark [String] pass a watermark if you wish to start the
|
|
226
|
-
# subscription at a specific point.
|
|
227
|
-
# @param timeout [Fixnum] the time in minutes that the subscription can
|
|
228
|
-
# remain idle between calls to #get_events. default: 240 minutes
|
|
229
|
-
# @return [Boolean] Did the subscription happen successfully?
|
|
230
|
-
def subscribe(evtypes = [:all], watermark = nil, timeout = 240)
|
|
231
|
-
# Refresh the subscription if already subscribed
|
|
232
|
-
unsubscribe if subscribed?
|
|
233
|
-
|
|
234
|
-
event_types = normalize_event_names(evtypes)
|
|
235
|
-
folder = {id: self.id, change_key: self.change_key}
|
|
236
|
-
resp = ews.pull_subscribe_folder(folder, event_types, timeout, watermark)
|
|
237
|
-
rmsg = resp.response_messages.first
|
|
238
|
-
if rmsg.success?
|
|
239
|
-
@subscription_id = rmsg.subscription_id
|
|
240
|
-
@watermark = rmsg.watermark
|
|
241
|
-
true
|
|
242
|
-
else
|
|
243
|
-
raise EwsSubscriptionError, "Could not subscribe: #{rmsg.code}: #{rmsg.message_text}"
|
|
244
|
-
end
|
|
245
|
-
end
|
|
205
|
+
def synced?
|
|
206
|
+
@synced
|
|
207
|
+
end
|
|
246
208
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
@
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
209
|
+
# Subscribe this folder to events. This method initiates an Exchange pull
|
|
210
|
+
# type subscription.
|
|
211
|
+
#
|
|
212
|
+
# @param event_types [Array] Which event types to subscribe to. By default
|
|
213
|
+
# we subscribe to all Exchange event types: :all, :copied, :created,
|
|
214
|
+
# :deleted, :modified, :moved, :new_mail, :free_busy_changed
|
|
215
|
+
# @param watermark [String] pass a watermark if you wish to start the
|
|
216
|
+
# subscription at a specific point.
|
|
217
|
+
# @param timeout [Fixnum] the time in minutes that the subscription can
|
|
218
|
+
# remain idle between calls to #get_events. default: 240 minutes
|
|
219
|
+
# @return [Boolean] Did the subscription happen successfully?
|
|
220
|
+
def subscribe(evtypes = [:all], watermark = nil, timeout = 240)
|
|
221
|
+
# Refresh the subscription if already subscribed
|
|
222
|
+
unsubscribe if subscribed?
|
|
223
|
+
|
|
224
|
+
event_types = normalize_event_names(evtypes)
|
|
225
|
+
folder = { id: id, change_key: change_key }
|
|
226
|
+
resp = ews.pull_subscribe_folder(folder, event_types, timeout, watermark)
|
|
227
|
+
rmsg = resp.response_messages.first
|
|
228
|
+
raise EwsSubscriptionError, "Could not subscribe: #{rmsg.code}: #{rmsg.message_text}" unless rmsg.success?
|
|
229
|
+
|
|
230
|
+
@subscription_id = rmsg.subscription_id
|
|
231
|
+
@watermark = rmsg.watermark
|
|
232
|
+
true
|
|
233
|
+
end
|
|
261
234
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
235
|
+
def push_subscribe(url, evtypes = [:all], watermark = nil, status_frequency = nil)
|
|
236
|
+
event_types = normalize_event_names(evtypes)
|
|
237
|
+
folder = { id: id, change_key: change_key }
|
|
238
|
+
resp = ews.push_subscribe_folder(folder, event_types, url, status_frequency, watermark)
|
|
239
|
+
rmsg = resp.response_messages.first
|
|
240
|
+
raise EwsSubscriptionError, "Could not subscribe: #{rmsg.code}: #{rmsg.message_text}" unless rmsg.success?
|
|
267
241
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
resp = ews.unsubscribe(@subscription_id)
|
|
274
|
-
rmsg = resp.response_messages.first
|
|
275
|
-
if rmsg.success?
|
|
276
|
-
@subscription_id, @watermark = nil, nil
|
|
277
|
-
true
|
|
278
|
-
else
|
|
279
|
-
raise EwsSubscriptionError, "Could not unsubscribe: #{rmsg.code}: #{rmsg.message_text}"
|
|
280
|
-
end
|
|
281
|
-
end
|
|
242
|
+
@subscription_id = rmsg.subscription_id
|
|
243
|
+
@watermark = rmsg.watermark
|
|
244
|
+
true
|
|
245
|
+
end
|
|
282
246
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if subscribed?
|
|
288
|
-
resp = ews.get_events(@subscription_id, @watermark)
|
|
289
|
-
rmsg = resp.response_messages[0]
|
|
290
|
-
@watermark = rmsg.new_watermark
|
|
291
|
-
# @todo if parms[:more_events] # get more events
|
|
292
|
-
rmsg.events.collect{|ev|
|
|
293
|
-
type = ev.keys.first
|
|
294
|
-
class_by_name(type).new(ews, ev[type])
|
|
295
|
-
}
|
|
296
|
-
else
|
|
297
|
-
raise EwsSubscriptionError, "Folder <#{self.display_name}> not subscribed to. Issue a Folder#subscribe before checking events."
|
|
247
|
+
# Check if there is a subscription for this folder.
|
|
248
|
+
# @return [Boolean] Are we subscribed to this folder?
|
|
249
|
+
def subscribed?
|
|
250
|
+
!@subscription_id.nil? && !@watermark.nil?
|
|
298
251
|
end
|
|
299
|
-
rescue EwsSubscriptionTimeout => e
|
|
300
|
-
@subscription_id, @watermark = nil, nil
|
|
301
|
-
raise e
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
252
|
|
|
253
|
+
# Unsubscribe this folder from further Exchange events.
|
|
254
|
+
# @return [Boolean] Did we unsubscribe successfully?
|
|
255
|
+
def unsubscribe
|
|
256
|
+
return true if @subscription_id.nil?
|
|
305
257
|
|
|
306
|
-
|
|
258
|
+
resp = ews.unsubscribe(@subscription_id)
|
|
259
|
+
rmsg = resp.response_messages.first
|
|
260
|
+
raise EwsSubscriptionError, "Could not unsubscribe: #{rmsg.code}: #{rmsg.message_text}" unless rmsg.success?
|
|
307
261
|
|
|
262
|
+
@subscription_id = nil
|
|
263
|
+
@watermark = nil
|
|
264
|
+
true
|
|
265
|
+
end
|
|
308
266
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
267
|
+
# Checks a subscribed folder for events
|
|
268
|
+
# @return [Array] An array of Event items
|
|
269
|
+
def get_events # rubocop:disable Naming/AccessorMethodName -- public API name
|
|
270
|
+
if subscribed?
|
|
271
|
+
resp = ews.get_events(@subscription_id, @watermark)
|
|
272
|
+
rmsg = resp.response_messages[0]
|
|
273
|
+
@watermark = rmsg.new_watermark
|
|
274
|
+
# @todo if parms[:more_events] # get more events
|
|
275
|
+
rmsg.events.collect { |ev|
|
|
276
|
+
type = ev.keys.first
|
|
277
|
+
class_by_name(type).new(ews, ev[type])
|
|
278
|
+
}
|
|
279
|
+
else
|
|
280
|
+
raise EwsSubscriptionError,
|
|
281
|
+
"Folder <#{display_name}> not subscribed to. Issue a Folder#subscribe before checking events."
|
|
282
|
+
end
|
|
283
|
+
rescue EwsSubscriptionTimeout => e
|
|
284
|
+
@subscription_id = nil
|
|
285
|
+
@watermark = nil
|
|
286
|
+
raise e
|
|
287
|
+
end
|
|
312
288
|
|
|
313
|
-
|
|
314
|
-
@key_types ||= super.merge(GFOLDER_KEY_TYPES)
|
|
315
|
-
end
|
|
289
|
+
private
|
|
316
290
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
291
|
+
def key_paths
|
|
292
|
+
@key_paths ||= super.merge(GFOLDER_KEY_PATHS)
|
|
293
|
+
end
|
|
320
294
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
295
|
+
def key_types
|
|
296
|
+
@key_types ||= super.merge(GFOLDER_KEY_TYPES)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def key_alias
|
|
300
|
+
@key_alias ||= super.merge(GFOLDER_KEY_ALIAS)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def simplify!
|
|
304
|
+
@ews_item = @ews_item[:elems].each_with_object({}) do |i, o|
|
|
305
|
+
key = i.keys.first
|
|
306
|
+
if o.key?(key)
|
|
307
|
+
if o[key].is_a?(Array)
|
|
308
|
+
o[key] << i[key]
|
|
309
|
+
else
|
|
310
|
+
o[key] = [o.delete(key), i[key]]
|
|
311
|
+
end
|
|
312
|
+
else
|
|
313
|
+
o[key] = i[key]
|
|
314
|
+
end
|
|
329
315
|
end
|
|
330
|
-
else
|
|
331
|
-
o[key] = i[key]
|
|
332
316
|
end
|
|
333
|
-
o
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
317
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
318
|
+
# Get a specific folder by its ID.
|
|
319
|
+
# @param [Hash] opts Misc options to control request
|
|
320
|
+
# @option opts [String] :base_shape IdOnly/Default/AllProperties
|
|
321
|
+
# @raise [EwsError] raised when the backend SOAP method returns an error.
|
|
322
|
+
def get_folder(opts = {})
|
|
323
|
+
args = get_folder_args(opts)
|
|
324
|
+
resp = ews.get_folder(args)
|
|
325
|
+
get_folder_parser(resp)
|
|
326
|
+
end
|
|
346
327
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
328
|
+
# Build up the arguements for #get_folder
|
|
329
|
+
# @todo: should we really pass the ChangeKey or do we want the freshest obj?
|
|
330
|
+
def get_folder_args(opts)
|
|
331
|
+
opts[:base_shape] ||= 'Default'
|
|
332
|
+
default_args = {
|
|
333
|
+
folder_ids: [{ id: id, change_key: change_key }],
|
|
334
|
+
folder_shape: { base_shape: opts[:base_shape] }
|
|
335
|
+
}
|
|
336
|
+
default_args.merge(opts)
|
|
337
|
+
end
|
|
357
338
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
f = resp.response_message[:elems][:folders][:elems][0]
|
|
361
|
-
f.values.first
|
|
362
|
-
else
|
|
363
|
-
raise EwsError, "Could not retrieve folder. #{resp.code}: #{resp.message}"
|
|
364
|
-
end
|
|
365
|
-
end
|
|
339
|
+
def get_folder_parser(resp)
|
|
340
|
+
raise EwsError, "Could not retrieve folder. #{resp.code}: #{resp.message}" unless resp.status == 'Success'
|
|
366
341
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
:traversal => 'Shallow',
|
|
371
|
-
:item_shape => {:base_shape => 'Default'}
|
|
372
|
-
}.merge(opts)
|
|
373
|
-
end
|
|
342
|
+
f = resp.response_message[:elems][:folders][:elems][0]
|
|
343
|
+
f.values.first
|
|
344
|
+
end
|
|
374
345
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
items << class_by_name(type).new(ews, i[type], self)
|
|
346
|
+
def items_args(opts)
|
|
347
|
+
{
|
|
348
|
+
parent_folder_ids: [{ id: id }],
|
|
349
|
+
traversal: 'Shallow',
|
|
350
|
+
item_shape: { base_shape: 'Default' }
|
|
351
|
+
}.merge(opts)
|
|
382
352
|
end
|
|
383
|
-
items
|
|
384
|
-
else
|
|
385
|
-
raise EwsError, "Could not retrieve folder. #{rm.code}: #{rm.message_text}"
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
353
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
merge_type => [
|
|
393
|
-
obj.opts.delete(:restriction),
|
|
394
|
-
obj.restriction
|
|
395
|
-
]
|
|
396
|
-
}
|
|
397
|
-
elsif !obj.restriction.empty?
|
|
398
|
-
obj.opts[:restriction] = obj.restriction
|
|
399
|
-
end
|
|
400
|
-
end
|
|
354
|
+
def items_parser(resp)
|
|
355
|
+
rm = resp.response_messages[0]
|
|
356
|
+
raise EwsError, "Could not retrieve folder. #{rm.code}: #{rm.message_text}" unless rm.status == 'Success'
|
|
401
357
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
358
|
+
items = []
|
|
359
|
+
rm.root_folder.items.each do |i|
|
|
360
|
+
type = i.keys.first
|
|
361
|
+
items << class_by_name(type).new(ews, i[type], self)
|
|
362
|
+
end
|
|
363
|
+
items
|
|
364
|
+
end
|
|
406
365
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
366
|
+
def merge_restrictions!(obj, merge_type = :and)
|
|
367
|
+
if obj.opts[:restriction] && !obj.opts[:restriction].empty? && !obj.restriction.empty?
|
|
368
|
+
obj.opts[:restriction] = {
|
|
369
|
+
merge_type => [
|
|
370
|
+
obj.opts.delete(:restriction),
|
|
371
|
+
obj.restriction
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
elsif !obj.restriction.empty?
|
|
375
|
+
obj.opts[:restriction] = obj.restriction
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def normalize_event_names(events)
|
|
380
|
+
events = %i[copied created deleted modified moved new_mail free_busy_changed] if events.include?(:all)
|
|
381
|
+
|
|
382
|
+
events.collect do |ev|
|
|
383
|
+
nev = ruby_case(ev)
|
|
384
|
+
if nev.end_with?('_event')
|
|
385
|
+
nev.to_sym
|
|
386
|
+
else
|
|
387
|
+
"#{nev}_event".to_sym
|
|
388
|
+
end
|
|
389
|
+
end
|
|
413
390
|
end
|
|
414
391
|
end
|
|
415
392
|
end
|
|
416
|
-
|
|
417
393
|
end
|
|
418
394
|
end
|