From e7e6969f9ad03a68edc96695db69695c18cce063 Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Tue, 5 Apr 2011 09:07:47 -0500 Subject: [PATCH 01/12] add base support for extended properties --- lib/model/generic_folder.rb | 12 ++++++++- .../handsoap/builders/ews_build_helpers.rb | 26 +++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 569d3f6d..6ed49468 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -217,7 +217,17 @@ def get_events def find_items(opts = {}) opts = opts.clone # clone the passed in object so we don't modify it in case it's being used in a loop item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'} - item_shape[:additional_properties] = {:field_uRI => ['item:ParentFolderId']} + if item_shape.has_key?(:additional_properties) + aprops = item_shape[:additional_properties] + if aprops.has_key?(:field_uRI) + raise EwsBadArgumentError, ":field_uRI val should be an Array instead of #{aprops[:field_uRI].class.name}" unless aprops[:field_uRI].is_a?(Array) + aprops[:field_uRI] << ['item:ParentFolderId'] + else + aprops[:field_uRI] = ['item:ParentFolderId'] + end + else + item_shape[:additional_properties] = {:field_uRI => ['item:ParentFolderId']} + end resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts) if(resp.status == 'Success') parms = resp.items.shift diff --git a/lib/soap/handsoap/builders/ews_build_helpers.rb b/lib/soap/handsoap/builders/ews_build_helpers.rb index 810232d5..bf9e1efb 100644 --- a/lib/soap/handsoap/builders/ews_build_helpers.rb +++ b/lib/soap/handsoap/builders/ews_build_helpers.rb @@ -148,6 +148,11 @@ def folder_shape!(node, folder_shape) end end + # This isn't exactly pretty for AdditionalProperties, but it works. The incoming Hash should be formulated like so: + # @example + # :additional_properties => { + # :extended_field_uRI => [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + # } # @todo Finish AdditionalProperties implementation def item_shape!(node, item_shape) node.add("#{NS_EWS_MESSAGES}:ItemShape") do |is| @@ -157,11 +162,22 @@ def item_shape!(node, item_shape) is.add("#{NS_EWS_TYPES}:FilterHtmlContent", item_shape[:filter_html_content]) if item_shape.has_key?(:filter_html_content) is.add("#{NS_EWS_TYPES}:ConvertHtmlCodePageToUTF8", item_shape[:convert_html_code_page_to_utf8]) if item_shape.has_key?(:convert_html_code_page_to_utf8) unless( item_shape[:additional_properties].nil? ) - unless( item_shape[:additional_properties][:field_uRI].nil? ) - is.add("#{NS_EWS_TYPES}:AdditionalProperties") do |addprops| - item_shape[:additional_properties][:field_uRI].each do |uri| - addprops.add("#{NS_EWS_TYPES}:FieldURI") { |furi| furi.set_attr('FieldURI', uri) } - end + is.add("#{NS_EWS_TYPES}:AdditionalProperties") do |addprops| + item_shape[:additional_properties].each_pair do |prop_t,prop_v| + case prop_t + when :field_uRI + prop_v.each do |uri| + addprops.add("#{NS_EWS_TYPES}:FieldURI") { |furi| furi.set_attr('FieldURI', uri) } + end + when :extended_field_uRI + prop_v.each do |uri| + addprops.add("#{NS_EWS_TYPES}:ExtendedFieldURI") do |furi| + uri.each_pair do |attr,val| + furi.set_attr(attr.to_s.camel_case, val) + end + end + end + end #when end end end From f9c31526e5b61da403435e6423398c8a3608676e Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Tue, 5 Apr 2011 12:53:07 -0500 Subject: [PATCH 02/12] add #add_tag and #find_items_with_tag --- lib/model/generic_folder.rb | 10 ++++++++++ lib/model/item.rb | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 6ed49468..b8bb8022 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -242,6 +242,16 @@ def find_items(opts = {}) end end + # Find Items with a specific tag + def find_items_with_tag(tag) + restrict = { :restriction => { + :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + :field_uRI_or_constant => {:constant => {:value=>tag}} ] + } } + + find_items(restrict) + end + # Fetch only items from today (since midnight) def todays_items(opts = {}) #opts = {:query_string => ["Received:today"]} diff --git a/lib/model/item.rb b/lib/model/item.rb index ce3e9846..ccfbc056 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -278,6 +278,24 @@ def parent_folder GenericFolder.get_folder @parent_folder_id end + # Use ExtendedProperties to create tags + # @param [String] tag a tag to add to this item + def add_tag!(tag) + i_type = self.class.name.split(/::/).last.ruby_case.to_sym + + vtag = {:preformatted => []} + vtag[:preformatted] << {:set_item_field => [ + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + {i_type => [ + {:extended_property => [ + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + {:values => [ {:value => {:text => tag} }]} + ]} + ]} + ]} + + self.update_attribs!(vtag) + end private From bc3301d07b31cc78c4a5c9cba2aae2b1873b4e11 Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Tue, 5 Apr 2011 13:43:42 -0500 Subject: [PATCH 03/12] add tags to default find_items. add @tags to Item and parse_tags --- lib/model/generic_folder.rb | 10 +++++++++- lib/model/item.rb | 29 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index b8bb8022..31f3905f 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -225,8 +225,16 @@ def find_items(opts = {}) else aprops[:field_uRI] = ['item:ParentFolderId'] end + if aprops.has_key?(:extended_field_uRI) + raise EwsBadArgumentError, ":extended_field_uRI val should be an Array instead of #{aprops[:extended_field_uRI].class.name}" unless aprops[:extended_field_uRI].is_a?(Array) + aprops[:extended_field_uRI] << [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + else + aprops[:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + end else - item_shape[:additional_properties] = {:field_uRI => ['item:ParentFolderId']} + item_shape[:additional_properties] = {} + item_shape[:additional_properties][:field_uRI] = ['item:ParentFolderId'] + item_shape[:additional_properties][:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] end resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts) if(resp.status == 'Success') diff --git a/lib/model/item.rb b/lib/model/item.rb index ccfbc056..70b67749 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -62,7 +62,7 @@ def self.add_attachments(parent_id, attachments) {:id => resp.items.first[:attachment_id][:root_item_id], :change_key => resp.items.first[:attachment_id][:root_item_change_key]} end - attr_reader :item_id, :change_key + attr_reader :item_id, :change_key, :tags alias :id :item_id # Initialize an Exchange Web Services item @@ -76,6 +76,7 @@ def initialize(ews_item, shallow = true) @change_key = ews_item[:item_id][:change_key] @text_only = false @updates = {} + @tags = parse_tags init_methods end @@ -283,13 +284,19 @@ def parent_folder def add_tag!(tag) i_type = self.class.name.split(/::/).last.ruby_case.to_sym + @tags |= [tag] + tag_vals = [] + @tags.each do |t| + tag_vals << {:value => {:text => t}} + end + vtag = {:preformatted => []} vtag[:preformatted] << {:set_item_field => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, {i_type => [ {:extended_property => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, - {:values => [ {:value => {:text => tag} }]} + {:values => tag_vals} ]} ]} ]} @@ -328,6 +335,24 @@ def method_missing(m, *args, &block) end end + def parse_tags + return [] unless(@ews_item.has_key?(:extended_property) && + @ews_item[:extended_property].has_key?(:extended_field_u_r_i) && + @ews_item[:extended_property][:extended_field_u_r_i].has_key?(:property_name) && + @ews_item[:extended_property][:extended_field_u_r_i][:property_name] == 'viewpoint_tags') + + tags = [] + vals = @ews_item[:extended_property][:values][:value] + if vals.is_a?(Array) + vals.each do |v| + tags << v[:text] + end + else + tags << vals[:text] + end + tags + end + end # Item end # EWS end # Viewpoint From ac5643d4ba26e5a5b7c07c1e1caa8c7db5fc0d1b Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Tue, 5 Apr 2011 14:01:47 -0500 Subject: [PATCH 04/12] add #remove_tag! #clear_all_tags! --- lib/model/item.rb | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/model/item.rb b/lib/model/item.rb index 70b67749..bffa06bc 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -282,11 +282,35 @@ def parent_folder # Use ExtendedProperties to create tags # @param [String] tag a tag to add to this item def add_tag!(tag) + @tags |= [tag] + set_tags!(@tags) + end + + # @param [String] tag a tag to delete from this item + def remove_tag!(tag) + @tags -= [tag] + if(@tags.blank?) + clear_all_tags! + else + set_tags!(@tags) + end + end + + def clear_all_tags! + vtag = {:preformatted => []} + vtag[:preformatted] << {:delete_item_field => + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}} + } + + self.update_attribs!(vtag) + end + + # @param [Array] tags viewpoint_tags to set on this item + def set_tags!(tags) i_type = self.class.name.split(/::/).last.ruby_case.to_sym - @tags |= [tag] tag_vals = [] - @tags.each do |t| + tags.each do |t| tag_vals << {:value => {:text => t}} end @@ -304,7 +328,6 @@ def add_tag!(tag) self.update_attribs!(vtag) end - private # @todo Handle: From 4adbeea8dba9339143db56f754ffe77979fd6ff1 Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Tue, 5 Apr 2011 14:08:23 -0500 Subject: [PATCH 05/12] normalize tags to lowercase --- lib/model/item.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/model/item.rb b/lib/model/item.rb index bffa06bc..7a02a772 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -282,13 +282,13 @@ def parent_folder # Use ExtendedProperties to create tags # @param [String] tag a tag to add to this item def add_tag!(tag) - @tags |= [tag] + @tags |= [tag.downcase] set_tags!(@tags) end # @param [String] tag a tag to delete from this item def remove_tag!(tag) - @tags -= [tag] + @tags -= [tag.downcase] if(@tags.blank?) clear_all_tags! else @@ -302,7 +302,12 @@ def clear_all_tags! {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}} } - self.update_attribs!(vtag) + if(self.update_attribs!(vtag)) + @tags = [] + true + else + false + end end # @param [Array] tags viewpoint_tags to set on this item From 89642d4610af7caa78bef86bfaa2300ddaa86a99 Mon Sep 17 00:00:00 2001 From: friflaj Date: Thu, 7 Apr 2011 15:29:52 +0200 Subject: [PATCH 06/12] Allow namespaces for tags --- lib/model/generic_folder.rb | 13 ++++++++----- lib/model/item.rb | 18 +++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 31f3905f..e764535a 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -216,6 +216,7 @@ def get_events # Find Items def find_items(opts = {}) opts = opts.clone # clone the passed in object so we don't modify it in case it's being used in a loop + namespace = opts.delete(:namespace) || 'viewpoint_tags' item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'} if item_shape.has_key?(:additional_properties) aprops = item_shape[:additional_properties] @@ -227,14 +228,14 @@ def find_items(opts = {}) end if aprops.has_key?(:extended_field_uRI) raise EwsBadArgumentError, ":extended_field_uRI val should be an Array instead of #{aprops[:extended_field_uRI].class.name}" unless aprops[:extended_field_uRI].is_a?(Array) - aprops[:extended_field_uRI] << [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + aprops[:extended_field_uRI] << [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] else - aprops[:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + aprops[:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] end else item_shape[:additional_properties] = {} item_shape[:additional_properties][:field_uRI] = ['item:ParentFolderId'] - item_shape[:additional_properties][:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}] + item_shape[:additional_properties][:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] end resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts) if(resp.status == 'Success') @@ -251,9 +252,11 @@ def find_items(opts = {}) end # Find Items with a specific tag - def find_items_with_tag(tag) + def find_items_with_tag(tag, opts) + namespace = opts.delete(:namespace) || 'viewpoint_tags' + restrict = { :restriction => { - :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, :field_uRI_or_constant => {:constant => {:value=>tag}} ] } } diff --git a/lib/model/item.rb b/lib/model/item.rb index 7a02a772..e98a62e5 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -296,10 +296,11 @@ def remove_tag!(tag) end end - def clear_all_tags! + def clear_all_tags!(options) + namespace = options[:namespace] || 'viewpoint_tags' vtag = {:preformatted => []} vtag[:preformatted] << {:delete_item_field => - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}} + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}} } if(self.update_attribs!(vtag)) @@ -311,7 +312,8 @@ def clear_all_tags! end # @param [Array] tags viewpoint_tags to set on this item - def set_tags!(tags) + def set_tags!(tags, options) + namespace = options[:namespace] || 'viewpoint_tags' i_type = self.class.name.split(/::/).last.ruby_case.to_sym tag_vals = [] @@ -321,10 +323,10 @@ def set_tags!(tags) vtag = {:preformatted => []} vtag[:preformatted] << {:set_item_field => [ - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, {i_type => [ {:extended_property => [ - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>"viewpoint_tags", :property_type=>"StringArray"}}, + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, {:values => tag_vals} ]} ]} @@ -363,11 +365,13 @@ def method_missing(m, *args, &block) end end - def parse_tags + def parse_tags(options) + namespace = options[:namespace] || 'viewpoint_tags' + return [] unless(@ews_item.has_key?(:extended_property) && @ews_item[:extended_property].has_key?(:extended_field_u_r_i) && @ews_item[:extended_property][:extended_field_u_r_i].has_key?(:property_name) && - @ews_item[:extended_property][:extended_field_u_r_i][:property_name] == 'viewpoint_tags') + @ews_item[:extended_property][:extended_field_u_r_i][:property_name] == namespace) tags = [] vals = @ews_item[:extended_property][:values][:value] From 633a33fd875d19b0ff378f4ca78873a2996375f0 Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Thu, 7 Apr 2011 09:46:33 -0500 Subject: [PATCH 07/12] rename namespace => tagspace and make opts optional arguments --- lib/model/generic_folder.rb | 14 ++++++------ lib/model/item.rb | 44 ++++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index e764535a..32aebf03 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -216,8 +216,8 @@ def get_events # Find Items def find_items(opts = {}) opts = opts.clone # clone the passed in object so we don't modify it in case it's being used in a loop - namespace = opts.delete(:namespace) || 'viewpoint_tags' item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'} + tagspace = opts.delete(:tagspace) || 'viewpoint_tags' if item_shape.has_key?(:additional_properties) aprops = item_shape[:additional_properties] if aprops.has_key?(:field_uRI) @@ -228,14 +228,14 @@ def find_items(opts = {}) end if aprops.has_key?(:extended_field_uRI) raise EwsBadArgumentError, ":extended_field_uRI val should be an Array instead of #{aprops[:extended_field_uRI].class.name}" unless aprops[:extended_field_uRI].is_a?(Array) - aprops[:extended_field_uRI] << [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] + aprops[:extended_field_uRI] << [{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}] else - aprops[:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] + aprops[:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}] end else item_shape[:additional_properties] = {} item_shape[:additional_properties][:field_uRI] = ['item:ParentFolderId'] - item_shape[:additional_properties][:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}] + item_shape[:additional_properties][:extended_field_uRI] = [{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}] end resp = (Viewpoint::EWS::EWS.instance).ews.find_item([@folder_id], 'Shallow', item_shape, opts) if(resp.status == 'Success') @@ -252,11 +252,11 @@ def find_items(opts = {}) end # Find Items with a specific tag - def find_items_with_tag(tag, opts) - namespace = opts.delete(:namespace) || 'viewpoint_tags' + def find_items_with_tag(tag, opts = {}) + tagspace = opts.delete(:tagspace) || 'viewpoint_tags' restrict = { :restriction => { - :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, + :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, :field_uRI_or_constant => {:constant => {:value=>tag}} ] } } diff --git a/lib/model/item.rb b/lib/model/item.rb index e98a62e5..803583a8 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -57,7 +57,7 @@ def self.add_attachments(parent_id, attachments) attachments.each do |a| b64attach << {:name => {:text =>(File.basename a.path)}, :content => {:text => Base64.encode64(a.read)}} end - resp = conn.ews.create_attachment(parent_id, b64attach) + resp = conn.ews.create_attachment(parent_id, b64attach) (resp.status == 'Success') || (raise EwsError, "Could not create attachments. #{resp.code}: #{resp.message}") {:id => resp.items.first[:attachment_id][:root_item_id], :change_key => resp.items.first[:attachment_id][:root_item_change_key]} end @@ -183,7 +183,7 @@ def deepen! return true unless @shallow conn = Viewpoint::EWS::EWS.instance shape = {:base_shape => 'AllProperties', :body_type => (@text_only ? 'Text' : 'Best')} - resp = conn.ews.get_item([@item_id], shape) + resp = conn.ews.get_item([@item_id], shape) resp = resp.items.shift @ews_item = resp[resp.keys.first] @shallow = false @@ -250,7 +250,7 @@ def attachments end # Delete this item - # @param [Boolean] soft Whether or not to do a soft delete. By default EWS will do a + # @param [Boolean] soft Whether or not to do a soft delete. By default EWS will do a # hard delete of this item. See the MSDN docs for more info: # http://msdn.microsoft.com/en-us/library/aa562961.aspx # @return [Boolean] Whether or not the item was deleted @@ -281,26 +281,32 @@ def parent_folder # Use ExtendedProperties to create tags # @param [String] tag a tag to add to this item - def add_tag!(tag) + # @param [Hash] opts options to pass to add_tag! + # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') + def add_tag!(tag, opts={}) @tags |= [tag.downcase] - set_tags!(@tags) + set_tags!(@tags, opts) end # @param [String] tag a tag to delete from this item - def remove_tag!(tag) + # @param [Hash] opts options to pass to remove_tag! + # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') + def remove_tag!(tag, opts={}) @tags -= [tag.downcase] if(@tags.blank?) clear_all_tags! else - set_tags!(@tags) + set_tags!(@tags, opts) end end - def clear_all_tags!(options) - namespace = options[:namespace] || 'viewpoint_tags' + # @param [Hash] opts options to pass to clear_all_tags! + # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') + def clear_all_tags!(opts={}) + tagspace = opts[:tagspace] || 'viewpoint_tags' vtag = {:preformatted => []} - vtag[:preformatted] << {:delete_item_field => - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}} + vtag[:preformatted] << {:delete_item_field => + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}} } if(self.update_attribs!(vtag)) @@ -312,8 +318,10 @@ def clear_all_tags!(options) end # @param [Array] tags viewpoint_tags to set on this item - def set_tags!(tags, options) - namespace = options[:namespace] || 'viewpoint_tags' + # @param [Hash] opts options to pass to set_tags! + # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') + def set_tags!(tags, opts={}) + tagspace = opts[:tagspace] || 'viewpoint_tags' i_type = self.class.name.split(/::/).last.ruby_case.to_sym tag_vals = [] @@ -323,10 +331,10 @@ def set_tags!(tags, options) vtag = {:preformatted => []} vtag[:preformatted] << {:set_item_field => [ - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, {i_type => [ {:extended_property => [ - {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>namespace, :property_type=>"StringArray"}}, + {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, {:values => tag_vals} ]} ]} @@ -365,13 +373,13 @@ def method_missing(m, *args, &block) end end - def parse_tags(options) - namespace = options[:namespace] || 'viewpoint_tags' + def parse_tags(opts={}) + tagspace = opts[:tagspace] || 'viewpoint_tags' return [] unless(@ews_item.has_key?(:extended_property) && @ews_item[:extended_property].has_key?(:extended_field_u_r_i) && @ews_item[:extended_property][:extended_field_u_r_i].has_key?(:property_name) && - @ews_item[:extended_property][:extended_field_u_r_i][:property_name] == namespace) + @ews_item[:extended_property][:extended_field_u_r_i][:property_name] == tagspace) tags = [] vals = @ews_item[:extended_property][:values][:value] From 11120cc9f77a32c9f2ce16a09b57bf669311ebac Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Thu, 7 Apr 2011 10:47:05 -0500 Subject: [PATCH 08/12] remove EOL spaces --- lib/model/generic_folder.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 32aebf03..594e6c27 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -69,7 +69,7 @@ def self.find_folders(root = :msgfolderroot, traversal = 'Shallow', shape = 'Def if( folder_type.nil? ) resp = (Viewpoint::EWS::EWS.instance).ews.find_folder( [normalize_id(root)], traversal, {:base_shape => shape} ) else - restr = {:restriction => + restr = {:restriction => {:is_equal_to => [{:field_uRI => {:field_uRI=>'folder:FolderClass'}}, {:field_uRI_or_constant=>{:constant => {:value => folder_type}}}]} } resp = (Viewpoint::EWS::EWS.instance).ews.find_folder( [normalize_id(root)], traversal, {:base_shape => shape}, restr) @@ -109,7 +109,7 @@ def self.get_folder_by_name(name, shape = 'Default') # For now the :field_uRI and :field_uRI_or_constant must be in an Array for Ruby 1.8.7 because Hashes # are not positional at insertion until 1.9 restr = {:restriction => - {:is_equal_to => + {:is_equal_to => [{:field_uRI => {:field_uRI=>'folder:DisplayName'}}, {:field_uRI_or_constant =>{:constant => {:value=>name}}}]}} resp = (Viewpoint::EWS::EWS.instance).ews.find_folder([:msgfolderroot], 'Deep', {:base_shape => shape}, restr) if(resp.status == 'Success') @@ -255,7 +255,7 @@ def find_items(opts = {}) def find_items_with_tag(tag, opts = {}) tagspace = opts.delete(:tagspace) || 'viewpoint_tags' - restrict = { :restriction => { + restrict = { :restriction => { :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, :field_uRI_or_constant => {:constant => {:value=>tag}} ] } } @@ -274,7 +274,7 @@ def todays_items(opts = {}) # @param [DateTime] date_time the time to fetch Items since. def items_since(date_time, opts = {}) restr = {:restriction => - {:is_greater_than_or_equal_to => + {:is_greater_than_or_equal_to => [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}}, {:field_uRI_or_constant =>{:constant => {:value=>date_time}}}] }} @@ -286,7 +286,7 @@ def items_since(date_time, opts = {}) # @param [DateTime] end_date the time to stop fetching Items from def items_between(start_date, end_date, opts={}) restr = {:restriction => {:and => [ - {:is_greater_than_or_equal_to => + {:is_greater_than_or_equal_to => [{:field_uRI => {:field_uRI=>'item:DateTimeReceived'}}, {:field_uRI_or_constant=>{:constant => {:value =>start_date}}}]}, {:is_less_than_or_equal_to => From 85052c681047e75bb0609064c98b6891f6d1f0cb Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Fri, 8 Apr 2011 08:19:35 -0500 Subject: [PATCH 09/12] fix hash syntax error in array --- lib/model/generic_folder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 594e6c27..03fffd6e 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -257,7 +257,7 @@ def find_items_with_tag(tag, opts = {}) restrict = { :restriction => { :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, - :field_uRI_or_constant => {:constant => {:value=>tag}} ] + {:field_uRI_or_constant => {:constant => {:value=>tag}}} ] } } find_items(restrict) From d68e63bcbabd42eb5ece1147edd668f6c0e5fb0c Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Fri, 8 Apr 2011 08:57:10 -0500 Subject: [PATCH 10/12] pass tagspace to find_items so shape is built correctly --- lib/model/generic_folder.rb | 4 ++-- lib/model/item.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 03fffd6e..2b6f23ed 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -253,14 +253,14 @@ def find_items(opts = {}) # Find Items with a specific tag def find_items_with_tag(tag, opts = {}) - tagspace = opts.delete(:tagspace) || 'viewpoint_tags' + tagspace = opts[:tagspace] || 'viewpoint_tags' restrict = { :restriction => { :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, {:field_uRI_or_constant => {:constant => {:value=>tag}}} ] } } - find_items(restrict) + find_items(opts.merge(restrict)) end # Fetch only items from today (since midnight) diff --git a/lib/model/item.rb b/lib/model/item.rb index 803583a8..601f7645 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -294,7 +294,7 @@ def add_tag!(tag, opts={}) def remove_tag!(tag, opts={}) @tags -= [tag.downcase] if(@tags.blank?) - clear_all_tags! + clear_all_tags!(opts) else set_tags!(@tags, opts) end From 02024aca0706ef9a56459acc722583f30e9fd017 Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Mon, 11 Apr 2011 12:38:21 -0500 Subject: [PATCH 11/12] add ability to set tagspace at the global level --- lib/model/generic_folder.rb | 27 ++++++++++++++------------- lib/model/item.rb | 19 ++++++++++--------- lib/viewpoint.rb | 10 +++++++++- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/lib/model/generic_folder.rb b/lib/model/generic_folder.rb index 2b6f23ed..2138129b 100644 --- a/lib/model/generic_folder.rb +++ b/lib/model/generic_folder.rb @@ -127,12 +127,12 @@ def self.get_folder_by_name(name, shape = 'Default') def initialize(ews_item) super() # Calls initialize in Model (creates @ews_methods Array) - @ews_item = ews_item - @folder_id = ews_item[:folder_id][:id] - @ews_methods << :folder_id - @ews_methods << :id - @change_key = ews_item[:folder_id][:change_key] - @ews_methods << :change_key + @ews_item = ews_item + @folder_id = ews_item[:folder_id][:id] + @ews_methods << :folder_id + @ews_methods << :id + @change_key = ews_item[:folder_id][:change_key] + @ews_methods << :change_key unless ews_item[:parent_folder_id].nil? @parent_id = ews_item[:parent_folder_id] @ews_methods << :parent_id @@ -142,11 +142,12 @@ def initialize(ews_item) # @todo Handle: # , , , - @sync_state = nil # Base-64 encoded sync data - @synced = false # Whether or not the synchronization process is complete - @subscription_id = nil - @watermark = nil - @shallow = true + @tagspace = (Viewpoint::EWS::EWS.instance).tagspace + @sync_state = nil # Base-64 encoded sync data + @synced = false # Whether or not the synchronization process is complete + @subscription_id = nil + @watermark = nil + @shallow = true end # Subscribe this folder to events. This method initiates an Exchange pull @@ -217,7 +218,7 @@ def get_events def find_items(opts = {}) opts = opts.clone # clone the passed in object so we don't modify it in case it's being used in a loop item_shape = opts.has_key?(:item_shape) ? opts.delete(:item_shape) : {:base_shape => 'Default'} - tagspace = opts.delete(:tagspace) || 'viewpoint_tags' + tagspace = opts.delete(:tagspace) || @tagspace if item_shape.has_key?(:additional_properties) aprops = item_shape[:additional_properties] if aprops.has_key?(:field_uRI) @@ -253,7 +254,7 @@ def find_items(opts = {}) # Find Items with a specific tag def find_items_with_tag(tag, opts = {}) - tagspace = opts[:tagspace] || 'viewpoint_tags' + tagspace = opts[:tagspace] || @tagspace restrict = { :restriction => { :is_equal_to => [ {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}}, diff --git a/lib/model/item.rb b/lib/model/item.rb index 601f7645..2af23194 100644 --- a/lib/model/item.rb +++ b/lib/model/item.rb @@ -70,13 +70,14 @@ def self.add_attachments(parent_id, attachments) # @param [Boolean] shallow Whether or not we have retrieved all the elements for this object def initialize(ews_item, shallow = true) super() # Calls initialize in Model (creates @ews_methods Array) - @ews_item = ews_item - @shallow = shallow - @item_id = ews_item[:item_id][:id] + @ews_item = ews_item + @shallow = shallow + @item_id = ews_item[:item_id][:id] @change_key = ews_item[:item_id][:change_key] - @text_only = false - @updates = {} - @tags = parse_tags + @text_only = false + @updates = {} + @tags = parse_tags + @tagspace = (Viewpoint::EWS::EWS.instance).tagspace init_methods end @@ -303,7 +304,7 @@ def remove_tag!(tag, opts={}) # @param [Hash] opts options to pass to clear_all_tags! # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') def clear_all_tags!(opts={}) - tagspace = opts[:tagspace] || 'viewpoint_tags' + tagspace = opts[:tagspace] || @tagspace vtag = {:preformatted => []} vtag[:preformatted] << {:delete_item_field => {:extended_field_uRI=>{:distinguished_property_set_id=>"PublicStrings", :property_name=>tagspace, :property_type=>"StringArray"}} @@ -321,7 +322,7 @@ def clear_all_tags!(opts={}) # @param [Hash] opts options to pass to set_tags! # @option opts [String] :tagspace the namespace to add the tag to. (default: 'viewpoint_tags') def set_tags!(tags, opts={}) - tagspace = opts[:tagspace] || 'viewpoint_tags' + tagspace = opts[:tagspace] || @tagspace i_type = self.class.name.split(/::/).last.ruby_case.to_sym tag_vals = [] @@ -374,7 +375,7 @@ def method_missing(m, *args, &block) end def parse_tags(opts={}) - tagspace = opts[:tagspace] || 'viewpoint_tags' + tagspace = opts[:tagspace] || @tagspace return [] unless(@ews_item.has_key?(:extended_property) && @ews_item[:extended_property].has_key?(:extended_field_u_r_i) && diff --git a/lib/viewpoint.rb b/lib/viewpoint.rb index 00d55f28..137a457d 100644 --- a/lib/viewpoint.rb +++ b/lib/viewpoint.rb @@ -81,7 +81,7 @@ class EWS include Singleton include Viewpoint - attr_reader :ews + attr_reader :ews, :tagspace # Set the endpoint for Exchange Web Services. # @param [String] endpoint The URL of the endpoint. This should end in @@ -122,6 +122,7 @@ def self.set_trust_ca(ca_path) end def initialize + @tagspace ||= 'viewpoint_tags' @ews = SOAP::ExchangeWebService.new end @@ -130,6 +131,13 @@ def me MailboxUser.find_user((@@user.include?('@') ? @@user : "#{@@user}@")) end + # Set the tagspace used for creating tags on the Model objects. Under the covers this uses + # a StringArray and Exchange extended properties. + # @param [String] tagspace the name of the tagspace to collect tags in (default: viewpoint_tags) + def set_tagspace(tagspace) + @tagspace = tagspace.to_s + end + end # class EWS end # EWS end From dbe140620db6dd31956888e82a7c3b74afacdfcb Mon Sep 17 00:00:00 2001 From: Dan Wanek Date: Mon, 11 Apr 2011 12:47:11 -0500 Subject: [PATCH 12/12] change tagspace to an accessor instead of set_tagspace --- lib/viewpoint.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/viewpoint.rb b/lib/viewpoint.rb index 137a457d..878ff77c 100644 --- a/lib/viewpoint.rb +++ b/lib/viewpoint.rb @@ -77,11 +77,15 @@ module EWS # @attr_reader [Viewpoint::EWS::SOAP::ExchangeWebService] :ews The EWS object used # to make SOAP calls. You typically don't need to use this, but if you want to # play around with the SOAP back-end it's available. + # @attr_accessor [String] :tagspace the name of the tagspace to collect tags in (default: viewpoint_tags) + # Set the tagspace used for creating tags on the Model objects. Under the covers this uses an Exchange + # StringArray and Exchange extended properties. class EWS include Singleton include Viewpoint - attr_reader :ews, :tagspace + attr_reader :ews + attr_accessor :tagspace # Set the endpoint for Exchange Web Services. # @param [String] endpoint The URL of the endpoint. This should end in @@ -131,13 +135,6 @@ def me MailboxUser.find_user((@@user.include?('@') ? @@user : "#{@@user}@")) end - # Set the tagspace used for creating tags on the Model objects. Under the covers this uses - # a StringArray and Exchange extended properties. - # @param [String] tagspace the name of the tagspace to collect tags in (default: viewpoint_tags) - def set_tagspace(tagspace) - @tagspace = tagspace.to_s - end - end # class EWS end # EWS end