viewpoint 0.1.9 → 0.1.10

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.
@@ -0,0 +1,197 @@
1
+ FolderId => 'older:FolderId'
2
+ ParentFolderId => 'folder:ParentFolderId'
3
+ DisplayName => 'folder:DisplayName'
4
+ UnreadCount => 'folder:UnreadCount'
5
+ TotalCount => 'folder:TotalCount'
6
+ ChildFolderCount => 'folder:ChildFolderCount'
7
+ FolderClass => 'folder:FolderClass'
8
+ SearchParameters => 'folder:SearchParameters'
9
+ ManagedFolderInformation => 'folder:ManagedFolderInformation'
10
+ PermissionSet => 'folder:PermissionSet'
11
+ EffectiveRights => 'folder:EffectiveRights'
12
+ SharingEffectiveRights => 'folder:SharingEffectiveRights'
13
+ ItemId => 'item:ItemId'
14
+ ParentFolderId => 'item:ParentFolderId'
15
+ ItemClass => 'item:ItemClass'
16
+ MimeContent => 'item:MimeContent'
17
+ Attachments => 'item:Attachments'
18
+ Subject => 'item:Subject'
19
+ DateTimeReceived => 'item:DateTimeReceived'
20
+ Size => 'item:Size'
21
+ Categories => 'item:Categories'
22
+ HasAttachments => 'item:HasAttachments'
23
+ Importance => 'item:Importance'
24
+ InReplyTo => 'item:InReplyTo'
25
+ InternetMessageHeaders => 'item:InternetMessageHeaders'
26
+ IsAssociated => 'item:IsAssociated'
27
+ IsDraft => 'item:IsDraft'
28
+ IsFromMe => 'item:IsFromMe'
29
+ IsResend => 'item:IsResend'
30
+ IsSubmitted => 'item:IsSubmitted'
31
+ IsUnmodified => 'item:IsUnmodified'
32
+ DateTimeSent => 'item:DateTimeSent'
33
+ DateTimeCreated => 'item:DateTimeCreated'
34
+ Body => 'item:Body'
35
+ ResponseObjects => 'item:ResponseObjects'
36
+ Sensitivity => 'item:Sensitivity'
37
+ ReminderDueBy => 'item:ReminderDueBy'
38
+ ReminderIsSet => 'item:ReminderIsSet'
39
+ ReminderMinutesBeforeStart => 'item:ReminderMinutesBeforeStart'
40
+ DisplayTo => 'item:DisplayTo'
41
+ DisplayCc => 'item:DisplayCc'
42
+ Culture => 'item:Culture'
43
+ EffectiveRights => 'item:EffectiveRights'
44
+ LastModifiedName => 'item:LastModifiedName'
45
+ LastModifiedTime => 'item:LastModifiedTime'
46
+ ConversationId => 'item:ConversationId'
47
+ UniqueBody => 'item:UniqueBody'
48
+ WebClientReadFormQueryString => 'item:WebClientReadFormQueryString'
49
+ WebClientEditFormQueryString => 'item:WebClientEditFormQueryString'
50
+ ConversationIndex => 'message:ConversationIndex'
51
+ ConversationTopic => 'message:ConversationTopic'
52
+ InternetMessageId => 'message:InternetMessageId'
53
+ IsRead => 'message:IsRead'
54
+ IsResponseRequested => 'message:IsResponseRequested'
55
+ IsReadReceiptRequested => 'message:IsReadReceiptRequested'
56
+ IsDeliveryReceiptRequested => 'message:IsDeliveryReceiptRequested'
57
+ References => 'message:References'
58
+ ReplyTo => 'message:ReplyTo'
59
+ From => 'message:From'
60
+ Sender => 'message:Sender'
61
+ ToRecipients => 'message:ToRecipients'
62
+ CcRecipients => 'message:CcRecipients'
63
+ BccRecipients => 'message:BccRecipients'
64
+ AssociatedCalendarItemId => 'meeting:AssociatedCalendarItemId'
65
+ IsDelegated => 'meeting:IsDelegated'
66
+ IsOutOfDate => 'meeting:IsOutOfDate'
67
+ HasBeenProcessed => 'meeting:HasBeenProcessed'
68
+ ResponseType => 'meeting:ResponseType'
69
+ MeetingRequestType => 'meetingRequest:MeetingRequestType'
70
+ IntendedFreeBusyStatus => 'meetingRequest:IntendedFreeBusyStatus'
71
+ Start => 'calendar:Start'
72
+ End => 'calendar:End'
73
+ OriginalStart => 'calendar:OriginalStart'
74
+ IsAllDayEvent => 'calendar:IsAllDayEvent'
75
+ LegacyFreeBusyStatus => 'calendar:LegacyFreeBusyStatus'
76
+ Location => 'calendar:Location'
77
+ When => 'calendar:When'
78
+ IsMeeting => 'calendar:IsMeeting'
79
+ IsCancelled => 'calendar:IsCancelled'
80
+ IsRecurring => 'calendar:IsRecurring'
81
+ MeetingRequestWasSent => 'calendar:MeetingRequestWasSent'
82
+ IsResponseRequested => 'calendar:IsResponseRequested'
83
+ CalendarItemType => 'calendar:CalendarItemType'
84
+ MyResponseType => 'calendar:MyResponseType'
85
+ Organizer => 'calendar:Organizer'
86
+ RequiredAttendees => 'calendar:RequiredAttendees'
87
+ OptionalAttendees => 'calendar:OptionalAttendees'
88
+ Resources => 'calendar:Resources'
89
+ ConflictingMeetingCount => 'calendar:ConflictingMeetingCount'
90
+ AdjacentMeetingCount => 'calendar:AdjacentMeetingCount'
91
+ ConflictingMeetings => 'calendar:ConflictingMeetings'
92
+ AdjacentMeetings => 'calendar:AdjacentMeetings'
93
+ Duration => 'calendar:Duration'
94
+ TimeZone => 'calendar:TimeZone'
95
+ AppointmentReplyTime => 'calendar:AppointmentReplyTime'
96
+ AppointmentSequenceNumber => 'calendar:AppointmentSequenceNumber'
97
+ AppointmentState => 'calendar:AppointmentState'
98
+ Recurrence => 'calendar:Recurrence'
99
+ FirstOccurrence => 'calendar:FirstOccurrence'
100
+ LastOccurrence => 'calendar:LastOccurrence'
101
+ ModifiedOccurrences => 'calendar:ModifiedOccurrences'
102
+ DeletedOccurrences => 'calendar:DeletedOccurrences'
103
+ MeetingTimeZone => 'calendar:MeetingTimeZone'
104
+ ConferenceType => 'calendar:ConferenceType'
105
+ AllowNewTimeProposal => 'calendar:AllowNewTimeProposal'
106
+ IsOnlineMeeting => 'calendar:IsOnlineMeeting'
107
+ MeetingWorkspaceUrl => 'calendar:MeetingWorkspaceUrl'
108
+ NetShowUrl => 'calendar:NetShowUrl'
109
+ UID => 'calendar:UID'
110
+ RecurrenceId => 'calendar:RecurrenceId'
111
+ DateTimeStamp => 'calendar:DateTimeStamp'
112
+ StartTimeZone => 'calendar:StartTimeZone'
113
+ EndTimeZone => 'calendar:EndTimeZone'
114
+ ActualWork => 'task:ActualWork'
115
+ AssignedTime => 'task:AssignedTime'
116
+ BillingInformation => 'task:BillingInformation'
117
+ ChangeCount => 'task:ChangeCount'
118
+ Companies => 'task:Companies'
119
+ CompleteDate => 'task:CompleteDate'
120
+ Contacts => 'task:Contacts'
121
+ DelegationState => 'task:DelegationState'
122
+ Delegator => 'task:Delegator'
123
+ DueDate => 'task:DueDate'
124
+ IsAssignmentEditable => 'task:IsAssignmentEditable'
125
+ IsComplete => 'task:IsComplete'
126
+ IsRecurring => 'task:IsRecurring'
127
+ IsTeamTask => 'task:IsTeamTask'
128
+ Mileage => 'task:Mileage'
129
+ Owner => 'task:Owner'
130
+ PercentComplete => 'task:PercentComplete'
131
+ Recurrence => 'task:Recurrence'
132
+ StartDate => 'task:StartDate'
133
+ Status => 'task:Status'
134
+ StatusDescription => 'task:StatusDescription'
135
+ TotalWork => 'task:TotalWork'
136
+ AssistantName => 'contacts:AssistantName'
137
+ Birthday => 'contacts:Birthday'
138
+ BusinessHomePage => 'contacts:BusinessHomePage'
139
+ Children => 'contacts:Children'
140
+ Companies => 'contacts:Companies'
141
+ CompanyName => 'contacts:CompanyName'
142
+ CompleteName => 'contacts:CompleteName'
143
+ ContactSource => 'contacts:ContactSource'
144
+ Culture => 'contacts:Culture'
145
+ Department => 'contacts:Department'
146
+ DisplayName => 'contacts:DisplayName'
147
+ EmailAddresses => 'contacts:EmailAddresses'
148
+ FileAs => 'contacts:FileAs'
149
+ FileAsMapping => 'contacts:FileAsMapping'
150
+ Generation => 'contacts:Generation'
151
+ GivenName => 'contacts:GivenName'
152
+ HasPicture => 'contacts:HasPicture'
153
+ ImAddresses => 'contacts:ImAddresses'
154
+ Initials => 'contacts:Initials'
155
+ JobTitle => 'contacts:JobTitle'
156
+ Manager => 'contacts:Manager'
157
+ MiddleName => 'contacts:MiddleName'
158
+ Mileage => 'contacts:Mileage'
159
+ Nickname => 'contacts:Nickname'
160
+ OfficeLocation => 'contacts:OfficeLocation'
161
+ PhoneNumbers => 'contacts:PhoneNumbers'
162
+ PhysicalAddresses => 'contacts:PhysicalAddresses'
163
+ PostalAddressIndex => 'contacts:PostalAddressIndex'
164
+ Profession => 'contacts:Profession'
165
+ SpouseName => 'contacts:SpouseName'
166
+ Surname => 'contacts:Surname'
167
+ WeddingAnniversary => 'contacts:WeddingAnniversary'
168
+ Members => 'distributionlist:Members'
169
+ PostedTime => 'postitem:PostedTime'
170
+ ConversationId => 'conversation:ConversationId'
171
+ ConversationTopic => 'conversation:ConversationTopic'
172
+ UniqueRecipients => 'conversation:UniqueRecipients'
173
+ GlobalUniqueRecipients => 'conversation:GlobalUniqueRecipients'
174
+ UniqueUnreadSenders => 'conversation:UniqueUnreadSenders'
175
+ GlobalUniqueUnreadSenders => 'conversation:GlobalUniqueUnreadSenders'
176
+ UniqueSenders => 'conversation:UniqueSenders'
177
+ GlobalUniqueSenders => 'conversation:GlobalUniqueSenders'
178
+ LastDeliveryTime => 'conversation:LastDeliveryTime'
179
+ GlobalLastDeliveryTime => 'conversation:GlobalLastDeliveryTime'
180
+ Categories => 'conversation:Categories'
181
+ GlobalCategories => 'conversation:GlobalCategories'
182
+ FlagStatus => 'conversation:FlagStatus'
183
+ GlobalFlagStatus => 'conversation:GlobalFlagStatus'
184
+ HasAttachments => 'conversation:HasAttachments'
185
+ GlobalHasAttachments => 'conversation:GlobalHasAttachments'
186
+ MessageCount => 'conversation:MessageCount'
187
+ GlobalMessageCount => 'conversation:GlobalMessageCount'
188
+ UnreadCount => 'conversation:UnreadCount'
189
+ GlobalUnreadCount => 'conversation:GlobalUnreadCount'
190
+ Size => 'conversation:Size'
191
+ GlobalSize => 'conversation:GlobalSize'
192
+ ItemClasses => 'conversation:ItemClasses'
193
+ GlobalItemClasses => 'conversation:GlobalItemClasses'
194
+ Importance => 'conversation:Importance'
195
+ GlobalImportance => 'conversation:GlobalImportance'
196
+ ItemIds => 'conversation:ItemIds'
197
+ GlobalItemIds => 'conversation:GlobalItemIds'
@@ -0,0 +1 @@
1
+ :folder_id => 'older:FolderId', :parent_folder_id => 'folder:ParentFolderId', :display_name => 'folder:DisplayName', :unread_count => 'folder:UnreadCount', :total_count => 'folder:TotalCount', :child_folder_count => 'folder:ChildFolderCount', :folder_class => 'folder:FolderClass', :search_parameters => 'folder:SearchParameters', :managed_folder_information => 'folder:ManagedFolderInformation', :permission_set => 'folder:PermissionSet', :effective_rights => 'folder:EffectiveRights', :sharing_effective_rights => 'folder:SharingEffectiveRights', :item_id => 'item:ItemId', :parent_folder_id => 'item:ParentFolderId', :item_class => 'item:ItemClass', :mime_content => 'item:MimeContent', :attachments => 'item:Attachments', :subject => 'item:Subject', :date_time_received => 'item:DateTimeReceived', :size => 'item:Size', :categories => 'item:Categories', :has_attachments => 'item:HasAttachments', :importance => 'item:Importance', :in_reply_to => 'item:InReplyTo', :internet_message_headers => 'item:InternetMessageHeaders', :is_associated => 'item:IsAssociated', :is_draft => 'item:IsDraft', :is_from_me => 'item:IsFromMe', :is_resend => 'item:IsResend', :is_submitted => 'item:IsSubmitted', :is_unmodified => 'item:IsUnmodified', :date_time_sent => 'item:DateTimeSent', :date_time_created => 'item:DateTimeCreated', :body => 'item:Body', :response_objects => 'item:ResponseObjects', :sensitivity => 'item:Sensitivity', :reminder_due_by => 'item:ReminderDueBy', :reminder_is_set => 'item:ReminderIsSet', :reminder_minutes_before_start => 'item:ReminderMinutesBeforeStart', :display_to => 'item:DisplayTo', :display_cc => 'item:DisplayCc', :culture => 'item:Culture', :effective_rights => 'item:EffectiveRights', :last_modified_name => 'item:LastModifiedName', :last_modified_time => 'item:LastModifiedTime', :conversation_id => 'item:ConversationId', :unique_body => 'item:UniqueBody', :web_client_read_form_query_string => 'item:WebClientReadFormQueryString', :web_client_edit_form_query_string => 'item:WebClientEditFormQueryString', :conversation_index => 'message:ConversationIndex', :conversation_topic => 'message:ConversationTopic', :internet_message_id => 'message:InternetMessageId', :is_read => 'message:IsRead', :is_response_requested => 'message:IsResponseRequested', :is_read_receipt_requested => 'message:IsReadReceiptRequested', :is_delivery_receipt_requested => 'message:IsDeliveryReceiptRequested', :references => 'message:References', :reply_to => 'message:ReplyTo', :from => 'message:From', :sender => 'message:Sender', :to_recipients => 'message:ToRecipients', :cc_recipients => 'message:CcRecipients', :bcc_recipients => 'message:BccRecipients', :associated_calendar_item_id => 'meeting:AssociatedCalendarItemId', :is_delegated => 'meeting:IsDelegated', :is_out_of_date => 'meeting:IsOutOfDate', :has_been_processed => 'meeting:HasBeenProcessed', :response_type => 'meeting:ResponseType', :meeting_request_type => 'meetingRequest:MeetingRequestType', :intended_free_busy_status => 'meetingRequest:IntendedFreeBusyStatus', :start => 'calendar:Start', :end => 'calendar:End', :original_start => 'calendar:OriginalStart', :is_all_day_event => 'calendar:IsAllDayEvent', :legacy_free_busy_status => 'calendar:LegacyFreeBusyStatus', :location => 'calendar:Location', :when => 'calendar:When', :is_meeting => 'calendar:IsMeeting', :is_cancelled => 'calendar:IsCancelled', :is_recurring => 'calendar:IsRecurring', :meeting_request_was_sent => 'calendar:MeetingRequestWasSent', :is_response_requested => 'calendar:IsResponseRequested', :calendar_item_type => 'calendar:CalendarItemType', :my_response_type => 'calendar:MyResponseType', :organizer => 'calendar:Organizer', :required_attendees => 'calendar:RequiredAttendees', :optional_attendees => 'calendar:OptionalAttendees', :resources => 'calendar:Resources', :conflicting_meeting_count => 'calendar:ConflictingMeetingCount', :adjacent_meeting_count => 'calendar:AdjacentMeetingCount', :conflicting_meetings => 'calendar:ConflictingMeetings', :adjacent_meetings => 'calendar:AdjacentMeetings', :duration => 'calendar:Duration', :time_zone => 'calendar:TimeZone', :appointment_reply_time => 'calendar:AppointmentReplyTime', :appointment_sequence_number => 'calendar:AppointmentSequenceNumber', :appointment_state => 'calendar:AppointmentState', :recurrence => 'calendar:Recurrence', :first_occurrence => 'calendar:FirstOccurrence', :last_occurrence => 'calendar:LastOccurrence', :modified_occurrences => 'calendar:ModifiedOccurrences', :deleted_occurrences => 'calendar:DeletedOccurrences', :meeting_time_zone => 'calendar:MeetingTimeZone', :conference_type => 'calendar:ConferenceType', :allow_new_time_proposal => 'calendar:AllowNewTimeProposal', :is_online_meeting => 'calendar:IsOnlineMeeting', :meeting_workspace_url => 'calendar:MeetingWorkspaceUrl', :net_show_url => 'calendar:NetShowUrl', :u_i_d => 'calendar:UID', :recurrence_id => 'calendar:RecurrenceId', :date_time_stamp => 'calendar:DateTimeStamp', :start_time_zone => 'calendar:StartTimeZone', :end_time_zone => 'calendar:EndTimeZone', :actual_work => 'task:ActualWork', :assigned_time => 'task:AssignedTime', :billing_information => 'task:BillingInformation', :change_count => 'task:ChangeCount', :companies => 'task:Companies', :complete_date => 'task:CompleteDate', :contacts => 'task:Contacts', :delegation_state => 'task:DelegationState', :delegator => 'task:Delegator', :due_date => 'task:DueDate', :is_assignment_editable => 'task:IsAssignmentEditable', :is_complete => 'task:IsComplete', :is_recurring => 'task:IsRecurring', :is_team_task => 'task:IsTeamTask', :mileage => 'task:Mileage', :owner => 'task:Owner', :percent_complete => 'task:PercentComplete', :recurrence => 'task:Recurrence', :start_date => 'task:StartDate', :status => 'task:Status', :status_description => 'task:StatusDescription', :total_work => 'task:TotalWork', :assistant_name => 'contacts:AssistantName', :birthday => 'contacts:Birthday', :business_home_page => 'contacts:BusinessHomePage', :children => 'contacts:Children', :companies => 'contacts:Companies', :company_name => 'contacts:CompanyName', :complete_name => 'contacts:CompleteName', :contact_source => 'contacts:ContactSource', :culture => 'contacts:Culture', :department => 'contacts:Department', :display_name => 'contacts:DisplayName', :email_addresses => 'contacts:EmailAddresses', :file_as => 'contacts:FileAs', :file_as_mapping => 'contacts:FileAsMapping', :generation => 'contacts:Generation', :given_name => 'contacts:GivenName', :has_picture => 'contacts:HasPicture', :im_addresses => 'contacts:ImAddresses', :initials => 'contacts:Initials', :job_title => 'contacts:JobTitle', :manager => 'contacts:Manager', :middle_name => 'contacts:MiddleName', :mileage => 'contacts:Mileage', :nickname => 'contacts:Nickname', :office_location => 'contacts:OfficeLocation', :phone_numbers => 'contacts:PhoneNumbers', :physical_addresses => 'contacts:PhysicalAddresses', :postal_address_index => 'contacts:PostalAddressIndex', :profession => 'contacts:Profession', :spouse_name => 'contacts:SpouseName', :surname => 'contacts:Surname', :wedding_anniversary => 'contacts:WeddingAnniversary', :members => 'distributionlist:Members', :posted_time => 'postitem:PostedTime', :conversation_id => 'conversation:ConversationId', :conversation_topic => 'conversation:ConversationTopic', :unique_recipients => 'conversation:UniqueRecipients', :global_unique_recipients => 'conversation:GlobalUniqueRecipients', :unique_unread_senders => 'conversation:UniqueUnreadSenders', :global_unique_unread_senders => 'conversation:GlobalUniqueUnreadSenders', :unique_senders => 'conversation:UniqueSenders', :global_unique_senders => 'conversation:GlobalUniqueSenders', :last_delivery_time => 'conversation:LastDeliveryTime', :global_last_delivery_time => 'conversation:GlobalLastDeliveryTime', :categories => 'conversation:Categories', :global_categories => 'conversation:GlobalCategories', :flag_status => 'conversation:FlagStatus', :global_flag_status => 'conversation:GlobalFlagStatus', :has_attachments => 'conversation:HasAttachments', :global_has_attachments => 'conversation:GlobalHasAttachments', :message_count => 'conversation:MessageCount', :global_message_count => 'conversation:GlobalMessageCount', :unread_count => 'conversation:UnreadCount', :global_unread_count => 'conversation:GlobalUnreadCount', :size => 'conversation:Size', :global_size => 'conversation:GlobalSize', :item_classes => 'conversation:ItemClasses', :global_item_classes => 'conversation:GlobalItemClasses', :importance => 'conversation:Importance', :global_importance => 'conversation:GlobalImportance', :item_ids => 'conversation:ItemIds', :global_item_ids => 'conversation:GlobalItemIds'
@@ -43,27 +43,25 @@ module Viewpoint
43
43
  # <SharingEffectiveRights/>
44
44
  end
45
45
 
46
- # Find Items
47
- # @see GenericFolder#find_items
48
- def find_items(start_date = nil, end_date = nil)
49
- opts ||= {}
50
- if(!start_date.nil? && !end_date.nil?)
51
- opts = {:calendar_view => {:max_entries_returned => 256, :start_date => start_date, :end_date => end_date}}
52
- end
53
- super(opts)
54
- end
55
- alias :items_between :find_items
56
-
57
46
  # Fetch only items from today (since midnight)
58
- def todays_items
47
+ def todays_items(opts = {})
59
48
  #This is a bit convoluted for pre-1.9.x ruby versions that don't support to_datetime
60
- find_items(DateTime.parse(Date.today.to_s), DateTime.parse((Date.today + 1).to_s))
49
+ items_between(DateTime.parse(Date.today.to_s), DateTime.parse((Date.today + 1).to_s), opts)
61
50
  end
62
51
 
63
52
  # Fetch items since a give DateTime
64
53
  # @param [DateTime] start_date_time the time to fetch Items since.
65
- def items_since(start_date_time)
66
- find_items(start_date_time, DateTime.now)
54
+ def items_since(start_date_time, opts = {})
55
+ items_between(start_date_time, DateTime.now, opts)
56
+ end
57
+
58
+ # Find Calendar Items between two dates
59
+ # @param [DateTime] start_date the date to start the search from
60
+ # @param [DateTime] end_date the date to end the search at
61
+ # @param [Hash] opts misc opts like restrictions, etc
62
+ def items_between(start_date, end_date, opts = {})
63
+ opts[:calendar_view] = {:max_entries_returned => 256, :start_date => start_date, :end_date => end_date}
64
+ find_items(opts)
67
65
  end
68
66
 
69
67
  end # CalendarFolder
@@ -87,6 +87,89 @@ module Viewpoint
87
87
 
88
88
  def init_methods
89
89
  super()
90
+
91
+ define_str_var :file_as, :file_as_mapping, :display_name, :job_title, :given_name, :surname
92
+ define_attr_str_var :complete_name, :first_name, :middle_name, :last_name, :initials, :full_name
93
+ define_inet_addresses :email_addresses, :im_addresses
94
+ define_phone_numbers :phone_numbers
95
+ define_physical_addresses :physical_addresses
96
+ end
97
+
98
+ # Define email_addresses or im_addresses for a Contact
99
+ def define_inet_addresses(*inet_addresses)
100
+ inet_addresses.each do |itype|
101
+ eval "@#{itype} ||= {}"
102
+ return unless self.instance_variable_get("@#{itype}").empty?
103
+ if(@ews_item.has_key?(itype))
104
+ @ews_methods << itype
105
+ if(@ews_item[itype][:entry].is_a?(Array))
106
+ @ews_item[itype][:entry].each do |entry|
107
+ next if entry.keys.length == 1
108
+ eval "@#{itype}[entry[:key].ruby_case.to_sym] = (entry.has_key?(:text) ? entry[:text] : '')"
109
+ end
110
+ else
111
+ entry = @ews_item[itype][:entry]
112
+ next if entry.keys.length == 1
113
+ eval "@#{itype}[entry[:key].ruby_case.to_sym] = (entry.has_key?(:text) ? entry[:text] : '')"
114
+ end
115
+ self.instance_eval <<-EOF
116
+ def #{itype}
117
+ self.instance_variable_get "@#{itype}"
118
+ end
119
+ EOF
120
+ else
121
+ @ews_methods_undef << itype
122
+ end
123
+ end
124
+ end
125
+
126
+ def define_phone_numbers(phone_numbers)
127
+ @phone_numbers ||= {}
128
+ return unless @phone_numbers.empty?
129
+ if(@ews_item.has_key?(phone_numbers))
130
+ if(@ews_item[phone_numbers][:entry].is_a?(Array))
131
+ @ews_item[phone_numbers][:entry].each do |entry|
132
+ next if entry.keys.length == 1
133
+ @phone_numbers[entry[:key].ruby_case.to_sym] = (entry.has_key?(:text) ? entry[:text] : "")
134
+ end
135
+ else # it is a Hash then
136
+ entry = @ews_item[phone_numbers][:entry]
137
+ return if entry.keys.length == 1
138
+ @phone_numbers[entry[:key].ruby_case.to_sym] = (entry.has_key?(:text) ? entry[:text] : "")
139
+ end
140
+ self.instance_eval <<-EOF
141
+ def #{phone_numbers}
142
+ @phone_numbers
143
+ end
144
+ EOF
145
+ @ews_methods << phone_numbers
146
+ else
147
+ @ews_methods_undef << itype
148
+ end
149
+ end
150
+
151
+ # Define physical_addresses for a Contact
152
+ def define_physical_addresses(physical_addresses)
153
+ @physical_addresses ||= {}
154
+ return unless @physical_addresses.empty?
155
+ if(@ews_item.has_key?(physical_addresses))
156
+ @ews_methods << physical_addresses
157
+ @ews_item[physical_addresses][:entry].each do |entry|
158
+ next if entry.keys.length == 1
159
+ key = entry.delete(:key)
160
+ @physical_addresses[key.ruby_case.to_sym] = {}
161
+ entry.each_pair do |ekey,ev|
162
+ @physical_addresses[key.ruby_case.to_sym][ekey] = ev[:text]
163
+ end
164
+ end
165
+ self.instance_eval <<-EOF
166
+ def #{physical_addresses}
167
+ @physical_addresses
168
+ end
169
+ EOF
170
+ else
171
+ @ews_methods_undef << itype
172
+ end
90
173
  end
91
174
 
92
175
  end # Contact
@@ -215,7 +215,7 @@ module Viewpoint
215
215
 
216
216
  # Find Items
217
217
  def find_items(opts = {})
218
- item_shape = {:base_shape => 'Default'}
218
+ item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'}
219
219
  item_shape[:additional_properties] = {:field_uRI => ['item:ParentFolderId']}
220
220
  resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts)
221
221
  if(resp.status == 'Success')
@@ -232,27 +232,27 @@ module Viewpoint
232
232
  end
233
233
 
234
234
  # Fetch only items from today (since midnight)
235
- def todays_items
235
+ def todays_items(opts = {})
236
236
  #opts = {:query_string => ["Received:today"]}
237
237
  #This is a bit convoluted for pre-1.9.x ruby versions that don't support to_datetime
238
- items_since(DateTime.parse(Date.today.to_s))
238
+ items_since(DateTime.parse(Date.today.to_s), opts)
239
239
  end
240
240
 
241
241
  # Fetch items since a give DateTime
242
242
  # @param [DateTime] date_time the time to fetch Items since.
243
- def items_since(date_time)
243
+ def items_since(date_time, opts = {})
244
244
  restr = {:restriction =>
245
245
  {:is_greater_than_or_equal_to =>
246
246
  [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
247
247
  {:field_uRI_or_constant =>{:constant => {:value=>date_time}}}]
248
248
  }}
249
- find_items(restr)
249
+ find_items(opts.merge(restr))
250
250
  end
251
251
 
252
252
  # Fetch items between a given time period
253
253
  # @param [DateTime] start_date the time to start fetching Items from
254
254
  # @param [DateTime] end_date the time to stop fetching Items from
255
- def items_between(start_date, end_date)
255
+ def items_between(start_date, end_date, opts={})
256
256
  restr = {:restriction => {:and => [
257
257
  {:is_greater_than_or_equal_to =>
258
258
  [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
@@ -261,7 +261,7 @@ module Viewpoint
261
261
  [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}},
262
262
  {:field_uRI_or_constant=>{:constant => {:value =>end_date}}}]}
263
263
  ]}}
264
- find_items(restr)
264
+ find_items(opts.merge(restr))
265
265
  end
266
266
 
267
267
  # Search on the item subject
@@ -323,8 +323,9 @@ module Viewpoint
323
323
  # an array of ItemIds are returned wich is a Hash in the form:
324
324
  # {:id=>"item id", :change_key=>"change key"}
325
325
  # See: http://msdn.microsoft.com/en-us/library/aa565609.aspx
326
- def sync_items!(sync_amount = 256, sync_all = false)
327
- resp = (Viewpoint::EWS::EWS.instance).ews.sync_folder_items(@folder_id, @sync_state, sync_amount)
326
+ def sync_items!(sync_amount = 256, sync_all = false, opts = {})
327
+ item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'}
328
+ resp = (Viewpoint::EWS::EWS.instance).ews.sync_folder_items(@folder_id, @sync_state, sync_amount, item_shape)
328
329
  parms = resp.items.shift
329
330
  @sync_state = parms[:sync_state]
330
331
  @synced = parms[:includes_last_item_in_range]
@@ -351,14 +352,14 @@ module Viewpoint
351
352
  # additional items. Calling this method again will clear the sync_state and synchronize
352
353
  # everything again.
353
354
  # @return [Array<Item>] returns an array of Items
354
- def sync_items_since!(datetime)
355
+ def sync_items_since!(datetime, opts={})
355
356
  clear_sync_state!
356
357
 
357
358
  begin
358
359
  items = sync_items!
359
360
  end until items.empty?
360
361
 
361
- items_since(datetime)
362
+ items_since(datetime, opts)
362
363
  end
363
364
 
364
365
  # Clears out the @sync_state so you can freshly synchronize this folder if needed
@@ -26,6 +26,7 @@ module Viewpoint
26
26
  module EWS
27
27
  class Item
28
28
  include Model
29
+ include ItemFieldUriMap
29
30
 
30
31
  # This is a class method that fetches an existing Item from the
31
32
  # Exchange Store.
@@ -69,6 +70,7 @@ module Viewpoint
69
70
  @item_id = ews_item[:item_id][:id]
70
71
  @change_key = ews_item[:item_id][:change_key]
71
72
  @text_only = false
73
+ @updates = {}
72
74
 
73
75
  init_methods
74
76
  end
@@ -86,24 +88,60 @@ module Viewpoint
86
88
  @text_only = ( txt == true ? true : false)
87
89
  end
88
90
 
91
+ # Save any pending updates that were manipulated via setter methods.
92
+ def save!
93
+ return true if @updates.empty?
94
+ if update_attribs!(@updates)
95
+ @updates = {}
96
+ true
97
+ else
98
+ false
99
+ end
100
+ end
101
+
89
102
  # Call UpdateItem for this item with the passed updates
90
103
  # @param [Hash] updates a well-formed update hash
91
- # @example {:updates=>{:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>"message:IsRead"}, :message=>{:is_read=>{:text=>"true"}}}}}
104
+ # @example {:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>"message:IsRead"}, :message=>{:is_read=>{:text=>"true"}}}}
92
105
  def update!(updates)
93
106
  conn = Viewpoint::EWS::EWS.instance
94
- resp = conn.ews.update_item([{:id => @item_id, :change_key => @change_key}], updates)
95
- (resp.status == 'Success') || (raise EwsError, "Trouble updating Item. #{resp.code}: #{resp.message}")
107
+ resp = conn.ews.update_item([{:id => @item_id, :change_key => @change_key}], {:updates => updates})
108
+ if resp.status == 'Success'
109
+ @item_id = resp.items.first[resp.items.first.keys.first][:item_id][:id]
110
+ @change_key = resp.items.first[resp.items.first.keys.first][:item_id][:change_key]
111
+ @shallow = true
112
+ deepen!
113
+ else
114
+ raise EwsError, "Trouble updating Item. #{resp.code}: #{resp.message}"
115
+ end
116
+
117
+ end
118
+
119
+ # This takes a hash of attributes with new values and builds the appropriate udpate hash
120
+ # @param [Hash] updates a hash that is formed like so :item_attr => newvalue
121
+ # @example {:sensitivity => {:text => 'Normal'}, :display_name => {:text => 'Test User'}}
122
+ def update_attribs!(updates)
123
+ changes = []
124
+ type = self.class.name.split(/::/).last.ruby_case.to_sym
125
+
126
+ updates.each_pair do |k,v|
127
+ raise EwsError, "Field (#{FIELD_URIS[k][:text]}) not writable by update." unless FIELD_URIS[k][:writable]
128
+ changes << {:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>FIELD_URIS[k][:text]}, type=>{k => v}}}
129
+ end
130
+
131
+ update!(changes)
96
132
  end
97
133
 
98
134
  # Mark this Item as read
99
135
  def mark_read!
100
- update!({:updates=>{:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>"message:IsRead"}, :message=>{:is_read=>{:text=>"true"}}}}})
136
+ field = :is_read
137
+ update!({:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>FIELD_URIS[field][:text]}, :message=>{field=>{:text=>"true"}}}})
101
138
  @is_read = true
102
139
  end
103
140
 
104
141
  # Mark this Item as unread
105
142
  def mark_unread!
106
- update!({:updates=>{:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>"message:IsRead"}, :message=>{:is_read=>{:text=>"false"}}}}})
143
+ field = :is_read
144
+ update!({:set_item_field=>{:field_u_r_i=>{:field_u_r_i=>FIELD_URIS[field][:text]}, :message=>{field=>{:text=>"false"}}}})
107
145
  @is_read = false
108
146
  true
109
147
  end
@@ -209,6 +247,7 @@ module Viewpoint
209
247
  end
210
248
 
211
249
 
250
+
212
251
  private
213
252
 
214
253
  # @todo Handle: