viewpoint 1.0.0.beta.1 → 1.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +16 -11
  3. data/lib/ews/connection.rb +15 -6
  4. data/lib/ews/convert_accessors.rb +56 -0
  5. data/lib/ews/errors.rb +56 -0
  6. data/lib/ews/ews_client.rb +33 -3
  7. data/lib/ews/exceptions/exceptions.rb +2 -0
  8. data/lib/ews/folder_accessors.rb +66 -1
  9. data/lib/ews/impersonation.rb +30 -0
  10. data/lib/ews/item_accessors.rb +77 -3
  11. data/lib/ews/mailbox_accessors.rb +6 -1
  12. data/lib/ews/message_accessors.rb +9 -2
  13. data/lib/ews/room_accessors.rb +48 -0
  14. data/lib/ews/roomlist_accessors.rb +47 -0
  15. data/lib/ews/soap.rb +1 -0
  16. data/lib/ews/soap/builders/ews_builder.rb +303 -48
  17. data/lib/ews/soap/ews_response.rb +2 -1
  18. data/lib/ews/soap/ews_soap_free_busy_response.rb +13 -3
  19. data/lib/ews/soap/ews_soap_response.rb +1 -1
  20. data/lib/ews/soap/ews_soap_room_response.rb +53 -0
  21. data/lib/ews/soap/ews_soap_roomlist_response.rb +54 -0
  22. data/lib/ews/soap/exchange_data_services.rb +49 -11
  23. data/lib/ews/soap/exchange_synchronization.rb +93 -0
  24. data/lib/ews/soap/exchange_time_zones.rb +56 -0
  25. data/lib/ews/soap/exchange_web_service.rb +36 -66
  26. data/lib/ews/soap/parsers/ews_sax_document.rb +8 -4
  27. data/lib/ews/soap/response_message.rb +3 -1
  28. data/lib/ews/soap/responses/create_attachment_response_message.rb +2 -1
  29. data/lib/ews/soap/responses/send_notification_response_message.rb +2 -1
  30. data/lib/{extensions/string.rb → ews/soap/responses/sync_folder_hierarchy_response_message.rb} +18 -17
  31. data/lib/ews/soap/responses/sync_folder_items_response_message.rb +36 -0
  32. data/lib/ews/templates/calendar_item.rb +78 -0
  33. data/lib/ews/templates/message.rb +8 -1
  34. data/lib/ews/templates/task.rb +74 -0
  35. data/lib/ews/types.rb +59 -11
  36. data/lib/ews/types/calendar_folder.rb +42 -0
  37. data/lib/ews/types/calendar_item.rb +93 -1
  38. data/lib/ews/types/export_items_response_message.rb +52 -0
  39. data/lib/ews/types/generic_folder.rb +70 -2
  40. data/lib/ews/types/item.rb +64 -4
  41. data/lib/ews/types/item_attachment.rb +41 -3
  42. data/lib/ews/types/item_field_uri_map.rb +1 -1
  43. data/lib/ews/types/task.rb +30 -0
  44. data/lib/ews/types/tasks_folder.rb +19 -0
  45. data/lib/viewpoint.rb +14 -14
  46. data/lib/viewpoint/logging.rb +27 -0
  47. data/lib/viewpoint/logging/config.rb +24 -0
  48. data/lib/viewpoint/string_utils.rb +76 -0
  49. metadata +82 -76
@@ -22,6 +22,7 @@ module Viewpoint::EWS::SOAP
22
22
  # 132% faster than the DOM-based parser for large documents.
23
23
  class EwsSaxDocument < Nokogiri::XML::SAX::Document
24
24
  include Viewpoint::EWS
25
+ include Viewpoint::StringUtils
25
26
 
26
27
  attr_reader :struct
27
28
 
@@ -31,7 +32,10 @@ module Viewpoint::EWS::SOAP
31
32
  end
32
33
 
33
34
  def characters(string)
34
- string.strip!
35
+ # FIXME: Move white space removal to somewhere else.
36
+ # This function can be called multiple times. In this case newlines in Text Bodies get stripped.
37
+ # See: https://github.com/zenchild/Viewpoint/issues/90
38
+ #string.strip!
35
39
  return if string.empty?
36
40
  if @elems.last[:text]
37
41
  @elems.last[:text] += string
@@ -41,18 +45,18 @@ module Viewpoint::EWS::SOAP
41
45
  end
42
46
 
43
47
  def start_element_namespace(name, attributes = [], prefix = nil, uri = nil, ns = [])
44
- name = name.ruby_case.to_sym
48
+ name = ruby_case(name).to_sym
45
49
  elem = {}
46
50
  unless attributes.empty?
47
51
  elem[:attribs] = attributes.collect{|a|
48
- {a.localname.ruby_case.to_sym => a.value}
52
+ { ruby_case(a.localname).to_sym => a.value}
49
53
  }.inject(&:merge)
50
54
  end
51
55
  @elems << elem
52
56
  end
53
57
 
54
58
  def end_element_namespace name, prefix=nil, uri=nil
55
- name = name.ruby_case.to_sym
59
+ name = ruby_case(name).to_sym
56
60
  elem = @elems.pop
57
61
  if @elems.empty?
58
62
  @struct[name] = elem
@@ -19,7 +19,7 @@
19
19
  module Viewpoint::EWS::SOAP
20
20
  class ResponseMessage
21
21
 
22
- attr_reader :message
22
+ attr_reader :message, :type
23
23
 
24
24
  def initialize(message)
25
25
  @type = message.keys.first
@@ -76,3 +76,5 @@ require_relative './responses/find_item_response_message'
76
76
  require_relative './responses/subscribe_response_message'
77
77
  require_relative './responses/get_events_response_message'
78
78
  require_relative './responses/send_notification_response_message'
79
+ require_relative './responses/sync_folder_items_response_message'
80
+ require_relative './responses/sync_folder_hierarchy_response_message'
@@ -19,6 +19,7 @@
19
19
  module Viewpoint::EWS::SOAP
20
20
 
21
21
  class CreateAttachmentResponseMessage < ResponseMessage
22
+ include Viewpoint::StringUtils
22
23
 
23
24
  def attachments
24
25
  return @attachments if @attachments
@@ -34,7 +35,7 @@ module Viewpoint::EWS::SOAP
34
35
  def parse_attachments(att)
35
36
  att.collect do |a|
36
37
  type = a.keys.first
37
- klass = Viewpoint::EWS::Types.const_get(type.to_s.camel_case)
38
+ klass = Viewpoint::EWS::Types.const_get(camel_case(type))
38
39
  item = OpenStruct.new
39
40
  item.ews = nil
40
41
  klass.new(item, a[type])
@@ -18,6 +18,7 @@
18
18
 
19
19
  module Viewpoint::EWS::SOAP
20
20
  class SendNotificationResponseMessage < ResponseMessage
21
+ include Viewpoint::StringUtils
21
22
 
22
23
  def notification
23
24
  safe_hash_access message, [:elems, :notification, :elems]
@@ -49,7 +50,7 @@ module Viewpoint::EWS::SOAP
49
50
  @events ||=
50
51
  notification[3..-1].collect do |ev|
51
52
  type = ev.keys.first
52
- klass = Viewpoint::EWS::Types.const_get(type.to_s.camel_case)
53
+ klass = Viewpoint::EWS::Types.const_get(camel_case(type))
53
54
  klass.new(nil, ev[type])
54
55
  end
55
56
  end
@@ -16,20 +16,21 @@
16
16
  limitations under the License.
17
17
  =end
18
18
 
19
- # Custom extensions to the class String
20
- class String
21
- # Change CamelCased strings to ruby_cased strings
22
- # It uses the lookahead assertion ?= In this case it basically says match
23
- # anything followed by a capital letter, but not the capital letter itself.
24
- # @see http://www.pcre.org/pcre.txt The PCRE guide for more details
25
- def ruby_case
26
- self.split(/(?=[A-Z])/).join('_').downcase
27
- end
28
-
29
- # Change a ruby_cased string to CamelCased
30
- def camel_case
31
- self.split(/_/).map { |i|
32
- i.sub(/^./) { |s| s.upcase }
33
- }.join
34
- end
35
- end # String
19
+ module Viewpoint::EWS::SOAP
20
+ class SyncFolderHierarchyResponseMessage < ResponseMessage
21
+
22
+ def sync_state
23
+ safe_hash_access message, [:elems, :sync_state, :text]
24
+ end
25
+
26
+ def includes_last_folder_in_range?
27
+ ans = safe_hash_access message, [:elems, :includes_last_folder_in_range, :text]
28
+ ans.downcase == 'true'
29
+ end
30
+
31
+ def changes
32
+ safe_hash_access(message, [:elems, :changes, :elems]) || []
33
+ end
34
+
35
+ end # SyncFolderHierarchyResponseMessage
36
+ end # Viewpoint::EWS::SOAP
@@ -0,0 +1,36 @@
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::SOAP
20
+ class SyncFolderItemsResponseMessage < ResponseMessage
21
+
22
+ def sync_state
23
+ safe_hash_access message, [:elems, :sync_state, :text]
24
+ end
25
+
26
+ def includes_last_item_in_range?
27
+ ans = safe_hash_access message, [:elems, :includes_last_item_in_range, :text]
28
+ ans.downcase == 'true'
29
+ end
30
+
31
+ def changes
32
+ safe_hash_access(message, [:elems, :changes, :elems]) || []
33
+ end
34
+
35
+ end # SyncFolderItemsResponseMessage
36
+ end # Viewpoint::EWS::SOAP
@@ -0,0 +1,78 @@
1
+ module Viewpoint::EWS
2
+ module Template
3
+ # Template for creating CalendarItems
4
+ # @see http://msdn.microsoft.com/en-us/library/exchange/aa564765.aspx
5
+ class CalendarItem < OpenStruct
6
+
7
+ # Available parameters with the required ordering
8
+ PARAMETERS = %w{mime_content item_id parent_folder_id item_class subject sensitivity body attachments
9
+ date_time_received size categories in_reply_to is_submitted is_draft is_from_me is_resend is_unmodified
10
+ internet_message_headers date_time_sent date_time_created response_objects reminder_due_by reminder_is_set
11
+ reminder_minutes_before_start display_cc display_to has_attachments extended_property culture start end
12
+ original_start is_all_day_event legacy_free_busy_status location when is_meeting is_cancelled is_recurring
13
+ meeting_request_was_sent is_response_requested calendar_item_type my_response_type organizer
14
+ required_attendees optional_attendees resources conflicting_meeting_count adjacent_meeting_count
15
+ conflicting_meetings adjacent_meetings duration time_zone appointment_reply_time appointment_sequence_number
16
+ appointment_state recurrence first_occurrence last_occurrence modified_occurrences deleted_occurrences
17
+ meeting_time_zone start_time_zone end_time_zone conference_type allow_new_time_proposal is_online_meeting
18
+ meeting_workspace_url net_show_url effective_rights last_modified_name last_modified_time is_associated
19
+ web_client_read_form_query_string web_client_edit_form_query_string conversation_id unique_body
20
+ }.map(&:to_sym).freeze
21
+
22
+ # Returns a new CalendarItem template
23
+ def initialize(opts = {})
24
+ super opts.dup
25
+ end
26
+
27
+ # EWS CreateItem container
28
+ # @return [Hash]
29
+ def to_ews_create
30
+ structure = {}
31
+ structure[:message_disposition] = (draft ? 'SaveOnly' : 'SendAndSaveCopy')
32
+ structure[:send_meeting_invitations] = 'SendToNone'
33
+
34
+ if self.saved_item_folder_id
35
+ if self.saved_item_folder_id.kind_of?(Hash)
36
+ structure[:saved_item_folder_id] = saved_item_folder_id
37
+ else
38
+ structure[:saved_item_folder_id] = {id: saved_item_folder_id}
39
+ end
40
+ end
41
+
42
+ structure[:items] = [{calendar_item: to_ews_item}]
43
+ structure
44
+ end
45
+
46
+ # EWS Item hash
47
+ #
48
+ # Puts all known parameters in the required ordering and structure
49
+ # @return [Hash]
50
+ def to_ews_item
51
+ item_parameters = {}
52
+ PARAMETERS.each do |key|
53
+ if !(value = self.send(key)).nil?
54
+
55
+ # Convert non duplicable values to String
56
+ case value
57
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
58
+ value = value.to_s
59
+ end
60
+
61
+ # Convert attributes
62
+ case key
63
+ when :start, :end
64
+ item_parameters[key] = {text: value.respond_to?(:iso8601) ? value.iso8601 : value}
65
+ when :body
66
+ item_parameters[key] = {body_type: self.body_type || 'Text', text: value.to_s}
67
+ else
68
+ item_parameters[key] = value
69
+ end
70
+ end
71
+ end
72
+
73
+ item_parameters
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -14,7 +14,7 @@ module Viewpoint::EWS
14
14
  end
15
15
 
16
16
  def has_attachments?
17
- !(file_attachments.empty? && item_attachments.empty?)
17
+ !(file_attachments.empty? && item_attachments.empty? && inline_attachments.empty?)
18
18
  end
19
19
 
20
20
 
@@ -25,12 +25,15 @@ module Viewpoint::EWS
25
25
  self.subject ||= nil
26
26
  self.body ||= nil
27
27
  self.body_type ||= 'Text'
28
+ self.importance ||= 'Normal'
28
29
  self.draft ||= false
29
30
  self.to_recipients ||= []
30
31
  self.cc_recipients ||= []
31
32
  self.bcc_recipients ||= []
32
33
  self.file_attachments ||= []
33
34
  self.item_attachments ||= []
35
+ self.inline_attachments ||= []
36
+ self.extended_properties ||= []
34
37
  end
35
38
 
36
39
  def to_ews_basic
@@ -49,6 +52,8 @@ module Viewpoint::EWS
49
52
  msg[:subject] = subject if subject
50
53
  msg[:body] = {text: body, body_type: body_type} if body
51
54
 
55
+ msg[:importance] = importance if importance
56
+
52
57
  to_r = to_recipients.collect{|r| {mailbox: {email_address: r}}}
53
58
  msg[:to_recipients] = to_r unless to_r.empty?
54
59
 
@@ -58,6 +63,8 @@ module Viewpoint::EWS
58
63
  bcc_r = bcc_recipients.collect{|r| {mailbox: {email_address: r}}}
59
64
  msg[:bcc_recipients] = bcc_r unless bcc_r.empty?
60
65
 
66
+ msg[:extended_properties] = extended_properties unless extended_properties.empty?
67
+
61
68
  [ews_opts, msg]
62
69
  end
63
70
 
@@ -0,0 +1,74 @@
1
+ module Viewpoint::EWS
2
+ module Template
3
+ # Template for creating Tasks
4
+ # @see http://msdn.microsoft.com/en-us/library/exchange/aa564765.aspx
5
+ class Task < OpenStruct
6
+
7
+ # Available parameters with the required ordering
8
+ PARAMETERS = %w{mime_content item_id parent_folder_id item_class subject sensitivity body attachments
9
+ date_time_received size categories in_reply_to is_submitted is_draft is_from_me is_resend
10
+ is_unmodified internet_message_headers date_time_sent date_time_created response_objects
11
+ reminder_due_by reminder_is_set reminder_minutes_before_start display_cc display_to
12
+ has_attachments extended_property culture actual_work assigned_time billing_information
13
+ change_count companies complete_date contacts delegation_state delegator due_date
14
+ is_assignment_editable is_complete is_recurring is_team_task mileage owner percent_complete
15
+ recurrence start_date status status_description total_work effective_rights last_modified_name
16
+ last_modified_time is_associated web_client_read_form_query_string
17
+ web_client_edit_form_query_string conversation_id unique_body
18
+ }.map(&:to_sym).freeze
19
+
20
+ # Returns a new Task template
21
+ def initialize(opts = {})
22
+ super opts.dup
23
+ end
24
+
25
+ # EWS CreateItem container
26
+ # @return [Hash]
27
+ def to_ews_create
28
+ structure = {}
29
+
30
+ if self.saved_item_folder_id
31
+ if self.saved_item_folder_id.kind_of?(Hash)
32
+ structure[:saved_item_folder_id] = saved_item_folder_id
33
+ else
34
+ structure[:saved_item_folder_id] = {id: saved_item_folder_id}
35
+ end
36
+ end
37
+
38
+ structure[:items] = [{task: to_ews_item}]
39
+ structure
40
+ end
41
+
42
+ # EWS Item hash
43
+ #
44
+ # Puts all known parameters in the required ordering and structure
45
+ # @return [Hash]
46
+ def to_ews_item
47
+ item_parameters = {}
48
+ PARAMETERS.each do |key|
49
+ if !(value = self.send(key)).nil?
50
+
51
+ # Convert non duplicable values to String
52
+ case value
53
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
54
+ value = value.to_s
55
+ end
56
+
57
+ # Convert attributes
58
+ case key
59
+ when :start_date, :due_date
60
+ item_parameters[key] = {text: value.respond_to?(:iso8601) ? value.iso8601 : value}
61
+ when :body
62
+ item_parameters[key] = {body_type: self.body_type || 'Text', text: value.to_s}
63
+ else
64
+ item_parameters[key] = value
65
+ end
66
+ end
67
+ end
68
+
69
+ item_parameters
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -1,5 +1,6 @@
1
1
  module Viewpoint::EWS
2
2
  module Types
3
+ include Viewpoint::StringUtils
3
4
 
4
5
  KEY_PATHS = {
5
6
  extended_properties: [:extended_property],
@@ -16,7 +17,8 @@ module Viewpoint::EWS
16
17
  def initialize(ews, ews_item)
17
18
  @ews = ews
18
19
  @ews_item = ews_item
19
- @shallow = true
20
+ @shallow = true
21
+ @frozen = false
20
22
  end
21
23
 
22
24
  def method_missing(method_sym, *arguments, &block)
@@ -31,6 +33,19 @@ module Viewpoint::EWS
31
33
  "#{self.class.name}: EWS METHODS: #{self.ews_methods.sort.join(', ')}"
32
34
  end
33
35
 
36
+ def frozen?
37
+ @frozen
38
+ end
39
+
40
+ # @param ronly [Boolean] true to freeze
41
+ def freeze!
42
+ @frozen = true
43
+ end
44
+
45
+ def unfreeze!
46
+ @frozen = false
47
+ end
48
+
34
49
  def shallow?
35
50
  @shallow
36
51
  end
@@ -91,18 +106,21 @@ module Viewpoint::EWS
91
106
 
92
107
  def class_by_name(cname)
93
108
  if(cname.instance_of? Symbol)
94
- cname = cname.to_s.camel_case
109
+ cname = camel_case(cname)
95
110
  end
96
111
  Viewpoint::EWS::Types.const_get(cname)
97
112
  end
98
113
 
99
114
  def type_convert(key,str)
100
- return nil if str.nil?
101
- key = key_alias[key] || key
102
- if key_types[key]
103
- key_types[key].is_a?(Symbol) ? method(key_types[key]).call(str) : key_types[key].call(str)
104
- else
105
- str
115
+ begin
116
+ key = key_alias[key] || key
117
+ if key_types[key]
118
+ key_types[key].is_a?(Symbol) ? method(key_types[key]).call(str) : key_types[key].call(str)
119
+ else
120
+ str
121
+ end
122
+ rescue
123
+ nil
106
124
  end
107
125
  end
108
126
 
@@ -111,11 +129,20 @@ module Viewpoint::EWS
111
129
  resolve_key_path(@ews_item, method_path(method_sym))
112
130
  rescue
113
131
  if shallow?
114
- if auto_deepen?
132
+ if frozen?
133
+ raise EwsFrozenObjectError, "Could not resolve :#{method_sym} on frozen object."
134
+ elsif auto_deepen?
115
135
  enlighten!
116
136
  retry
117
137
  else
118
- raise EwsMinimalObjectError, "Could not resolve :#{method_sym}. #auto_deepen set to false"
138
+ if !auto_deepen?
139
+ if ews.no_auto_deepen_behavior == :raise
140
+ raise EwsMinimalObjectError, "Could not resolve :#{method_sym}. #auto_deepen set to false"
141
+ else
142
+ nil
143
+ end
144
+ else
145
+ end
119
146
  end
120
147
  else
121
148
  nil
@@ -139,7 +166,28 @@ module Viewpoint::EWS
139
166
  end
140
167
 
141
168
  def build_extended_properties(eprops)
142
- eprops.collect.collect {|e| e[:elems]}
169
+ h = {}
170
+ # todo
171
+ # the return pattern seems broken in some cases,
172
+ # probably needs fixing via a dedicated response parser
173
+ eprops.each do |e|
174
+ if e.size == 1
175
+ e[:elems].each_cons(2) do |k,v|
176
+ key = k[:extended_field_u_r_i][:attribs][:property_name].downcase.to_sym
177
+ val = v[:value][:text]
178
+ h.store(key,val)
179
+ end
180
+ elsif e.size == 2
181
+ e[1].each_cons(2) do |k,v|
182
+ key = k[:extended_field_u_r_i][:attribs][:property_name].downcase.to_sym
183
+ val = v[:value][:text]
184
+ h.store(key,val)
185
+ end
186
+ else
187
+ raise EwsMinimalObjectError, "Not prepared to deal with elements greater than 2"
188
+ end
189
+ end
190
+ h
143
191
  end
144
192
 
145
193
  end