feedtools 0.2.22 → 0.2.23

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,7 +31,7 @@ module FeedTools
31
31
  # The default caching mechanism for the FeedTools module
32
32
  class DatabaseFeedCache < ActiveRecord::Base
33
33
  # Overrides the default table name to use the "feeds" table.
34
- def self.table_name() "feeds" end
34
+ set_table_name("cached_feeds")
35
35
 
36
36
  # If ActiveRecord is not already connected, attempts to find a configuration file and use
37
37
  # it to open a connection for ActiveRecord.
@@ -53,7 +53,8 @@ module FeedTools
53
53
  "./config/database.yml",
54
54
  "../config/database.yml",
55
55
  "./database.yml",
56
- "../database.yml"
56
+ "../database.yml",
57
+ "../../database.yml"
57
58
  ]
58
59
  database_config_file = nil
59
60
  for file in possible_config_files
@@ -108,7 +109,7 @@ module FeedTools
108
109
  # True if the appropriate database table already exists
109
110
  def DatabaseFeedCache.table_exists?
110
111
  begin
111
- ActiveRecord::Base.connection.execute "select id, url, title, " +
112
+ ActiveRecord::Base.connection.execute "select id, href, title, " +
112
113
  "link, feed_data, feed_data_type, http_headers, last_retrieved " +
113
114
  "from #{self.table_name()} limit 1"
114
115
  rescue ActiveRecord::StatementInvalid
@@ -21,124 +21,27 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
+ require 'rexml/document'
25
+ require 'feed_tools/feed_item'
26
+ require 'feed_tools/feed_structures'
24
27
  require 'feed_tools/helpers/generic_helper'
28
+ require 'feed_tools/helpers/xml_helper'
29
+ require 'feed_tools/helpers/html_helper'
25
30
 
26
31
  module FeedTools
27
32
  # The <tt>FeedTools::Feed</tt> class represents a web feed's structure.
28
33
  class Feed
29
- # :stopdoc:
30
- include REXML
31
- class << self
32
- include FeedTools::GenericHelper
33
- private :validate_options
34
- end
35
- include FeedTools::GenericHelper
36
- private :validate_options
37
- # :startdoc:
38
-
39
- # Represents a feed/feed item's category
40
- class Category
41
-
42
- # The category term value
43
- attr_accessor :term
44
- # The categorization scheme
45
- attr_accessor :scheme
46
- # A human-readable description of the category
47
- attr_accessor :label
48
-
49
- alias_method :value, :term
50
- alias_method :category, :term
51
- alias_method :domain, :scheme
52
- end
53
-
54
- # Represents a feed/feed item's author
55
- class Author
56
-
57
- # The author's real name
58
- attr_accessor :name
59
- # The author's email address
60
- attr_accessor :email
61
- # The url of the author's homepage
62
- attr_accessor :url
63
- # The raw value of the author tag if present
64
- attr_accessor :raw
65
- end
66
-
67
- # Represents a feed's image
68
- class Image
69
-
70
- # The image's title
71
- attr_accessor :title
72
- # The image's description
73
- attr_accessor :description
74
- # The image's url
75
- attr_accessor :url
76
- # The url to link the image to
77
- attr_accessor :link
78
- # The width of the image
79
- attr_accessor :width
80
- # The height of the image
81
- attr_accessor :height
82
- # The style of the image
83
- # Possible values are "icon", "image", or "image-wide"
84
- attr_accessor :style
85
- end
86
-
87
- # Represents a feed's text input element.
88
- # Be aware that this will be ignored for feed generation. It's a
89
- # pointless element that aggregators usually ignore and it doesn't have an
90
- # equivalent in all feeds types.
91
- class TextInput
92
-
93
- # The label of the Submit button in the text input area.
94
- attr_accessor :title
95
- # The description explains the text input area.
96
- attr_accessor :description
97
- # The URL of the CGI script that processes text input requests.
98
- attr_accessor :link
99
- # The name of the text object in the text input area.
100
- attr_accessor :name
101
- end
102
-
103
- # Represents a feed's cloud.
104
- # Be aware that this will be ignored for feed generation.
105
- class Cloud
106
-
107
- # The domain of the cloud.
108
- attr_accessor :domain
109
- # The path for the cloud.
110
- attr_accessor :path
111
- # The port the cloud is listening on.
112
- attr_accessor :port
113
- # The web services protocol the cloud uses.
114
- # Possible values are either "xml-rpc" or "soap".
115
- attr_accessor :protocol
116
- # The procedure to use to request notification.
117
- attr_accessor :register_procedure
118
- end
119
-
120
- # Represents a simple hyperlink
121
- class Link
122
-
123
- # The url that is being linked to
124
- attr_accessor :url
125
- # The content of the hyperlink
126
- attr_accessor :value
127
-
128
- alias_method :href, :url
129
- end
130
-
131
34
  # Initialize the feed object
132
35
  def initialize
133
36
  super
134
37
  @cache_object = nil
135
38
  @http_headers = nil
136
- @xml_doc = nil
39
+ @xml_document = nil
137
40
  @feed_data = nil
138
41
  @feed_data_type = :xml
139
42
  @root_node = nil
140
43
  @channel_node = nil
141
- @url = nil
44
+ @href = nil
142
45
  @id = nil
143
46
  @title = nil
144
47
  @description = nil
@@ -147,6 +50,7 @@ module FeedTools
147
50
  @time_to_live = nil
148
51
  @entries = nil
149
52
  @live = false
53
+ @encoding = nil
150
54
  end
151
55
 
152
56
  # Loads the feed specified by the url, pulling the data from the
@@ -155,7 +59,7 @@ module FeedTools
155
59
  # * <tt>:cache_only</tt> - If set to true, the feed will only be
156
60
  # pulled from the cache.
157
61
  def Feed.open(url, options={})
158
- validate_options([ :cache_only ],
62
+ FeedTools::GenericHelper.validate_options([ :cache_only ],
159
63
  options.keys)
160
64
  options = { :cache_only => false }.merge(options)
161
65
 
@@ -165,17 +69,17 @@ module FeedTools
165
69
  end
166
70
 
167
71
  # clean up the url
168
- url = FeedTools.normalize_url(url)
72
+ url = FeedTools::UriHelper.normalize_url(url)
169
73
 
170
74
  # create and load the new feed
171
75
  feed = FeedTools::Feed.new
172
- feed.url = url
76
+ feed.href = url
173
77
  feed.update! unless options[:cache_only]
174
78
  return feed
175
79
  end
176
80
 
177
- # Loads the feed from the remote url if the feed has expired from the cache or cannot be
178
- # retrieved from the cache for some reason.
81
+ # Loads the feed from the remote url if the feed has expired from the
82
+ # cache or cannot be retrieved from the cache for some reason.
179
83
  def update!
180
84
  if !FeedTools.feed_cache.nil? &&
181
85
  !FeedTools.feed_cache.set_up_correctly?
@@ -193,6 +97,32 @@ module FeedTools
193
97
  @live = false
194
98
  else
195
99
  load_remote_feed!
100
+
101
+ # Handle autodiscovery
102
+ if self.http_headers['content-type'] =~ /text\/html/ ||
103
+ self.http_headers['content-type'] =~ /application\/xhtml\+xml/
104
+
105
+ autodiscovered_url = nil
106
+ autodiscovered_url =
107
+ FeedTools::HtmlHelper.extract_link_by_mime_type(self.feed_data,
108
+ "application/atom+xml")
109
+ if autodiscovered_url.nil?
110
+ autodiscovered_url =
111
+ FeedTools::HtmlHelper.extract_link_by_mime_type(self.feed_data,
112
+ "application/rss+xml")
113
+ end
114
+ if autodiscovered_url.nil?
115
+ autodiscovered_url =
116
+ FeedTools::HtmlHelper.extract_link_by_mime_type(self.feed_data,
117
+ "application/rdf+xml")
118
+ end
119
+ unless autodiscovered_url.nil?
120
+ self.feed_data = nil
121
+ self.href = autodiscovered_url
122
+ self.expire! unless self.cache_object.nil?
123
+ self.update!
124
+ end
125
+ end
196
126
  end
197
127
  end
198
128
 
@@ -207,20 +137,20 @@ module FeedTools
207
137
  @http_headers = YAML.load(self.cache_object.http_headers)
208
138
  end
209
139
 
210
- if (self.url =~ /^feed:/) == 0
140
+ if (self.href =~ /^feed:/) == 0
211
141
  # Woah, Nelly, how'd that happen? You should've already been
212
142
  # corrected. So let's fix that url. And please,
213
143
  # just use less crappy browsers instead of badly defined
214
144
  # pseudo-protocol hacks.
215
- self.url = FeedTools.normalize_url(self.url)
145
+ self.href = FeedTools::UriHelper.normalize_url(self.href)
216
146
  end
217
147
 
218
148
  # Find out what method we're going to be using to obtain this feed.
219
149
  begin
220
- uri = URI.parse(self.url)
150
+ uri = URI.parse(self.href)
221
151
  rescue URI::InvalidURIError
222
152
  raise FeedAccessError,
223
- "Cannot retrieve feed using invalid URL: " + self.url.to_s
153
+ "Cannot retrieve feed using invalid URL: " + self.href.to_s
224
154
  end
225
155
  retrieval_method = "http"
226
156
  case uri.scheme
@@ -262,12 +192,15 @@ module FeedTools
262
192
  feed_uri = URI.parse(feed_url)
263
193
  rescue URI::InvalidURIError
264
194
  # Uh, maybe try to fix it?
265
- feed_uri = URI.parse(FeedTools.normalize_url(feed_url))
195
+ feed_uri = URI.parse(FeedTools::UriHelper.normalize_url(feed_url))
266
196
  end
267
197
 
268
198
  begin
269
- # TODO: Proxy host and proxy port would go here if implemented
270
- http = Net::HTTP.new(feed_uri.host, (feed_uri.port or 80))
199
+ proxy_address = (FeedTools.configurations[:proxy_address] || nil)
200
+ proxy_port = (FeedTools.configurations[:proxy_port].to_i || nil)
201
+
202
+ http = Net::HTTP::Proxy(proxy_address, proxy_port).new(
203
+ feed_uri.host, (feed_uri.port or 80))
271
204
  http.start do
272
205
  final_uri = feed_uri.path
273
206
  final_uri += ('?' + feed_uri.query) if feed_uri.query
@@ -282,7 +215,7 @@ module FeedTools
282
215
  if redirected_response.last.code.to_i == 301
283
216
  # Reset the cache object or we may get duplicate entries
284
217
  self.cache_object = nil
285
- self.url = redirected_response.last['location']
218
+ self.href = redirected_response.last['location']
286
219
  else
287
220
  # Jump out as soon as we hit anything that isn't a
288
221
  # permanently moved redirection.
@@ -316,7 +249,7 @@ module FeedTools
316
249
  if !cached_feed.expired? &&
317
250
  !cached_feed.http_headers.blank?
318
251
  # Copy the cached state
319
- self.url = cached_feed.url
252
+ self.href = cached_feed.href
320
253
 
321
254
  @feed_data = cached_feed.feed_data
322
255
  @feed_data_type = cached_feed.feed_data_type
@@ -372,7 +305,7 @@ module FeedTools
372
305
 
373
306
  begin
374
307
  begin
375
- @http_response = http_fetch.call(self.url, headers, 10, [], false)
308
+ @http_response = http_fetch.call(self.href, headers, 10, [], false)
376
309
  rescue => error
377
310
  if error.respond_to?(:response)
378
311
  # You might not believe this, but...
@@ -385,8 +318,8 @@ module FeedTools
385
318
  # we get to blame other people's bad software and/or bad
386
319
  # configuration files.
387
320
  if error.response.code.to_i == 404 &&
388
- FeedTools.user_agent != nil
389
- @http_response = http_fetch.call(self.url, {}, 10, [], true)
321
+ FeedTools.configurations[:user_agent] != nil
322
+ @http_response = http_fetch.call(self.href, {}, 10, [], true)
390
323
  if @http_response != nil && @http_response.code.to_i == 200
391
324
  warn("The server appears to be blocking based on the " +
392
325
  "User-Agent header. This is stupid, and you should " +
@@ -464,14 +397,18 @@ module FeedTools
464
397
  # Not supported... yet
465
398
  elsif retrieval_method == "ftp"
466
399
  # Not supported... yet
467
- # Technically, CDF feeds are supposed to be able to be accessed directly
468
- # from an ftp server. This is silly, but we'll humor Microsoft.
400
+ # Technically, CDF feeds are supposed to be able to be accessed
401
+ # directly from an ftp server. This is silly, but we'll humor
402
+ # Microsoft.
469
403
  #
470
- # Eventually.
404
+ # Eventually. If they're lucky. And someone demands it.
471
405
  elsif retrieval_method == "file"
472
406
  # Now that we've gone to all that trouble to ensure the url begins
473
407
  # with 'file://', strip the 'file://' off the front of the url.
474
- file_name = self.url.gsub(/^file:\/\//, "")
408
+ file_name = self.href.gsub(/^file:\/\//, "")
409
+ if RUBY_PLATFORM =~ /mswin/
410
+ file_name = file_name[1..-1] if file_name[1..1] == "/"
411
+ end
475
412
  begin
476
413
  open(file_name) do |file|
477
414
  @http_response = nil
@@ -520,7 +457,7 @@ module FeedTools
520
457
  unless self.http_headers.blank?
521
458
  @encoding = "utf-8"
522
459
  else
523
- @encoding = self.encoding_from_xml_data
460
+ @encoding = self.encoding_from_feed_data
524
461
  end
525
462
  end
526
463
  return @encoding
@@ -528,8 +465,8 @@ module FeedTools
528
465
 
529
466
  # Returns the encoding of feed calculated only from the xml data.
530
467
  # I.e., the encoding we would come up with if we ignore RFC 3023.
531
- def encoding_from_xml_data
532
- if @encoding_from_xml_data.nil?
468
+ def encoding_from_feed_data
469
+ if @encoding_from_feed_data.nil?
533
470
  raw_data = self.feed_data
534
471
  encoding_from_xml_instruct =
535
472
  raw_data.scan(
@@ -539,7 +476,7 @@ module FeedTools
539
476
  encoding_from_xml_instruct.downcase!
540
477
  end
541
478
  if encoding_from_xml_instruct.blank?
542
- doc = Document.new(raw_data)
479
+ doc = REXML::Document.new(raw_data)
543
480
  encoding_from_xml_instruct = doc.encoding.downcase
544
481
  if encoding_from_xml_instruct == "utf-8"
545
482
  # REXML has a tendency to report utf-8 overzealously, take with
@@ -547,7 +484,7 @@ module FeedTools
547
484
  encoding_from_xml_instruct = nil
548
485
  end
549
486
  else
550
- @encoding_from_xml_data = encoding_from_xml_instruct
487
+ @encoding_from_feed_data = encoding_from_xml_instruct
551
488
  end
552
489
  if encoding_from_xml_instruct.blank?
553
490
  sniff_table = {
@@ -556,17 +493,17 @@ module FeedTools
556
493
  }
557
494
  sniff = self.feed_data[0..3]
558
495
  if sniff_table[sniff] != nil
559
- @encoding_from_xml_data = sniff_table[sniff].downcase
496
+ @encoding_from_feed_data = sniff_table[sniff].downcase
560
497
  end
561
498
  else
562
- @encoding_from_xml_data = encoding_from_xml_instruct
499
+ @encoding_from_feed_data = encoding_from_xml_instruct
563
500
  end
564
- if @encoding_from_xml_data.blank?
501
+ if @encoding_from_feed_data.blank?
565
502
  # Safest assumption
566
- @encoding_from_xml_data = "utf-8"
503
+ @encoding_from_feed_data = "utf-8"
567
504
  end
568
505
  end
569
- return @encoding_from_xml_data
506
+ return @encoding_from_feed_data
570
507
  end
571
508
 
572
509
  # Returns the feed's raw data.
@@ -581,11 +518,10 @@ module FeedTools
581
518
 
582
519
  # Sets the feed's data.
583
520
  def feed_data=(new_feed_data)
521
+ for var in self.instance_variables
522
+ self.instance_variable_set(var, nil)
523
+ end
584
524
  @http_headers = {}
585
- @cache_object = nil
586
- @url = nil
587
- @id = nil
588
- @encoding = nil
589
525
  @feed_data = new_feed_data
590
526
  unless self.cache_object.nil?
591
527
  self.cache_object.feed_data = new_feed_data
@@ -637,25 +573,25 @@ module FeedTools
637
573
  end
638
574
 
639
575
  # Returns a REXML Document of the feed_data
640
- def xml
576
+ def xml_document
641
577
  if self.feed_data_type != :xml
642
- @xml_doc = nil
578
+ @xml_document = nil
643
579
  else
644
- if @xml_doc.nil?
580
+ if @xml_document.nil?
645
581
  begin
646
582
  begin
647
- @xml_doc = Document.new(self.feed_data_utf_8,
648
- :ignore_whitespace_nodes => :all)
583
+ @xml_document = REXML::Document.new(self.feed_data_utf_8)
649
584
  rescue Object
650
585
  # Something failed, attempt to repair the xml with htree.
651
- @xml_doc = HTree.parse(self.feed_data_utf_8).to_rexml
586
+ @xml_document = HTree.parse(self.feed_data_utf_8).to_rexml
652
587
  end
653
588
  rescue Object
654
- @xml_doc = nil
589
+ @xml_document = nil
590
+ raise
655
591
  end
656
592
  end
657
593
  end
658
- return @xml_doc
594
+ return @xml_document
659
595
  end
660
596
 
661
597
  # Returns the first node within the channel_node that matches the xpath
@@ -664,7 +600,7 @@ module FeedTools
664
600
  if self.feed_data_type != :xml
665
601
  raise "The feed data type is not xml."
666
602
  end
667
- return try_xpaths(self.channel_node, [xpath],
603
+ return FeedTools::XmlHelper.try_xpaths(self.channel_node, [xpath],
668
604
  :select_result_value => select_result_value)
669
605
  end
670
606
 
@@ -673,7 +609,7 @@ module FeedTools
673
609
  if self.feed_data_type != :xml
674
610
  raise "The feed data type is not xml."
675
611
  end
676
- return try_xpaths_all(self.channel_node, [xpath],
612
+ return FeedTools::XmlHelper.try_xpaths_all(self.channel_node, [xpath],
677
613
  :select_result_value => select_result_value)
678
614
  end
679
615
 
@@ -685,10 +621,10 @@ module FeedTools
685
621
  # E.g.: http://smogzer.tripod.com/smog.rdf
686
622
  # ===================================================================
687
623
  begin
688
- if xml.nil?
624
+ if self.xml_document.nil?
689
625
  return nil
690
626
  else
691
- @root_node = xml.root
627
+ @root_node = self.xml_document.root
692
628
  end
693
629
  rescue
694
630
  return nil
@@ -699,14 +635,14 @@ module FeedTools
699
635
 
700
636
  # Returns the channel node of the feed.
701
637
  def channel_node
702
- if @channel_node.nil? && root_node != nil
703
- @channel_node = try_xpaths(root_node, [
638
+ if @channel_node.nil? && self.root_node != nil
639
+ @channel_node = FeedTools::XmlHelper.try_xpaths(self.root_node, [
704
640
  "channel",
705
641
  "CHANNEL",
706
642
  "feedinfo"
707
643
  ])
708
644
  if @channel_node == nil
709
- @channel_node = root_node
645
+ @channel_node = self.root_node
710
646
  end
711
647
  end
712
648
  return @channel_node
@@ -714,14 +650,19 @@ module FeedTools
714
650
 
715
651
  # The cache object that handles the feed persistence.
716
652
  def cache_object
717
- if !@url.nil? && @url =~ /^file:\/\//
653
+ if !@href.nil? && @href =~ /^file:\/\//
718
654
  return nil
719
655
  end
720
656
  unless FeedTools.feed_cache.nil?
721
657
  if @cache_object.nil?
722
658
  begin
723
- if @url != nil
724
- @cache_object = FeedTools.feed_cache.find_by_url(@url)
659
+ if @href != nil
660
+ begin
661
+ @cache_object = FeedTools.feed_cache.find_by_href(@href)
662
+ rescue
663
+ warn("The feed cache seems to be having trouble with the " +
664
+ "find_by_href method. This may cause unexpected results.")
665
+ end
725
666
  end
726
667
  if @cache_object.nil?
727
668
  @cache_object = FeedTools.feed_cache.new
@@ -736,8 +677,8 @@ module FeedTools
736
677
  # Sets the cache object for this feed.
737
678
  #
738
679
  # This can be any object, but it must accept the following messages:
739
- # url
740
- # url=
680
+ # href
681
+ # href=
741
682
  # title
742
683
  # title=
743
684
  # link
@@ -773,7 +714,11 @@ module FeedTools
773
714
  when "rss"
774
715
  @feed_type = "rss"
775
716
  when "channel"
776
- @feed_type = "cdf"
717
+ if self.root_node.namespace == FEED_TOOLS_NAMESPACES['rss11']
718
+ @feed_type = "rss"
719
+ else
720
+ @feed_type = "cdf"
721
+ end
777
722
  end
778
723
  end
779
724
  return @feed_type
@@ -794,26 +739,33 @@ module FeedTools
794
739
  end
795
740
  version = nil
796
741
  begin
797
- version = XPath.first(root_node, "@version").to_s.strip.to_f
742
+ version_string = FeedTools::XmlHelper.try_xpaths(self.root_node, [
743
+ "@version"
744
+ ], :select_result_value => true)
745
+ unless version_string.nil?
746
+ version = version_string.to_f
747
+ end
798
748
  rescue
799
749
  end
800
750
  version = nil if version == 0.0
801
- default_namespace = XPath.first(root_node, "@xmlns").to_s.strip
751
+ default_namespace = FeedTools::XmlHelper.try_xpaths(self.root_node, [
752
+ "@xmlns"
753
+ ], :select_result_value => true)
802
754
  case self.feed_type
803
755
  when "atom"
804
- if default_namespace == "http://www.w3.org/2005/Atom"
756
+ if default_namespace == FEED_TOOLS_NAMESPACES['atom10']
805
757
  @feed_version = 1.0
806
758
  elsif version != nil
807
759
  @feed_version = version
808
- elsif default_namespace == "http://purl.org/atom/ns#"
760
+ elsif default_namespace == FEED_TOOLS_NAMESPACES['atom03']
809
761
  @feed_version = 0.3
810
762
  end
811
763
  when "rss"
812
- if default_namespace == "http://my.netscape.com/rdf/simple/0.9/"
764
+ if default_namespace == FEED_TOOLS_NAMESPACES['rss09']
813
765
  @feed_version = 0.9
814
- elsif default_namespace == "http://purl.org/rss/1.0/"
766
+ elsif default_namespace == FEED_TOOLS_NAMESPACES['rss10']
815
767
  @feed_version = 1.0
816
- elsif default_namespace == "http://purl.org/net/rss1.1#"
768
+ elsif default_namespace == FEED_TOOLS_NAMESPACES['rss11']
817
769
  @feed_version = 1.1
818
770
  elsif version != nil
819
771
  case version
@@ -828,7 +780,7 @@ module FeedTools
828
780
  when "cdf"
829
781
  @feed_version = 0.4
830
782
  when "!okay/news"
831
- @feed_version = nil
783
+ @feed_version = 1.0
832
784
  end
833
785
  end
834
786
  return @feed_version
@@ -842,15 +794,15 @@ module FeedTools
842
794
  # Returns the feed's unique id
843
795
  def id
844
796
  if @id.nil?
845
- @id = select_not_blank([
846
- try_xpaths(self.channel_node, [
797
+ @id = FeedTools::XmlHelper.select_not_blank([
798
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
847
799
  "atom10:id/text()",
848
800
  "atom03:id/text()",
849
801
  "atom:id/text()",
850
802
  "id/text()",
851
803
  "guid/text()"
852
804
  ], :select_result_value => true),
853
- try_xpaths(self.root_node, [
805
+ FeedTools::XmlHelper.try_xpaths(self.root_node, [
854
806
  "atom10:id/text()",
855
807
  "atom03:id/text()",
856
808
  "atom:id/text()",
@@ -868,106 +820,114 @@ module FeedTools
868
820
  end
869
821
 
870
822
  # Returns the feed url.
871
- def url
872
- original_url = @url
873
- override_url = lambda do |result|
874
- begin
875
- if result.nil? && self.feed_data != nil
876
- true
877
- elsif result != nil &&
878
- !(["http", "https"].include?(URI.parse(result.to_s).scheme))
879
- if self.feed_data != nil
823
+ def href
824
+ if @href_overridden != true || @href.nil?
825
+ original_href = @href
826
+
827
+ override_href = lambda do |current_href|
828
+ begin
829
+ if current_href.nil? && self.feed_data != nil
830
+ # The current url is nil and we have feed data to go on
880
831
  true
832
+ elsif current_href != nil && !(["http", "https"].include?(
833
+ URI.parse(current_href.to_s).scheme))
834
+ if self.feed_data != nil
835
+ # The current url is set, but isn't a http/https url and
836
+ # we have feed data to use to replace the current url with
837
+ true
838
+ else
839
+ # The current url is set, but isn't a http/https url but
840
+ # we don't have feed data to use to replace the current url
841
+ # with so we'll have to wait until we do
842
+ false
843
+ end
881
844
  else
845
+ # The current url is set to an http/https url and there's
846
+ # no compelling reason to override it
882
847
  false
883
848
  end
884
- else
885
- false
849
+ rescue
850
+ # Something went wrong, so we should err on the side of caution
851
+ # and attempt to override the url
852
+ true
886
853
  end
887
- rescue
888
- true
889
- end
890
- end
891
- if override_url.call(@url)
892
- # rdf:about is ordered last because a lot of people accidentally
893
- # put the link in that field instead of the url to the feed.
894
- # Ordering it last gives them as many chances as humanly possible
895
- # for them to redeem themselves. If the link turns out to be the
896
- @url = try_xpaths(self.channel_node, [
897
- "link[@rel='self']/@href",
898
- "atom10:link[@rel='self']/@href",
899
- "atom03:link[@rel='self']/@href",
900
- "atom:link[@rel='self']/@href",
901
- "admin:feed/@rdf:resource",
902
- "admin:feed/@resource",
903
- "feed/@rdf:resource",
904
- "feed/@resource",
905
- "@rdf:about",
906
- "@about"
907
- ], :select_result_value => true) do |result|
908
- override_url.call(FeedTools.normalize_url(result))
909
- end
910
- @url = FeedTools.normalize_url(@url)
911
- if @url == nil
912
- @url = original_url
913
- end
914
- if @url == self.link
915
- @url = original_url
916
- end
917
- end
918
- return @url
854
+ end
855
+ if override_href.call(@href) && self.feed_data != nil
856
+ # rdf:about is ordered last because a lot of people put the url to
857
+ # the feed inside it instead of a link to their blog.
858
+ # Ordering it last gives them as many chances as humanly possible
859
+ # for them to redeem themselves. If the link turns out to be the
860
+ # same as the blog link, it will be reset to the original value.
861
+ for link_object in self.links
862
+ if link_object.rel == 'self'
863
+ if link_object.href != self.link
864
+ @href = link_object.href
865
+ @href_overridden = true
866
+ return @href
867
+ end
868
+ end
869
+ end
870
+ @href = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
871
+ "admin:feed/@rdf:resource",
872
+ "admin:feed/@resource",
873
+ "feed/@rdf:resource",
874
+ "feed/@resource",
875
+ "@rdf:about",
876
+ "@about"
877
+ ], :select_result_value => true) do |result|
878
+ override_href.call(FeedTools::UriHelper.normalize_url(result))
879
+ end
880
+ begin
881
+ if !(@href =~ /^file:/) &&
882
+ !FeedTools::UriHelper.is_uri?(@href)
883
+ @href = FeedTools::UriHelper.resolve_relative_uri(
884
+ @href, [self.base_uri])
885
+ end
886
+ rescue
887
+ end
888
+ if FeedTools.configurations[:url_normalization_enabled]
889
+ @href = FeedTools::UriHelper.normalize_url(@href)
890
+ end
891
+ @href.strip! unless @href.nil?
892
+ @href = nil if @href.blank?
893
+ @href_overridden = true
894
+ if @href == nil
895
+ @href = original_href
896
+ @href_overridden = false
897
+ end
898
+ if @href == self.link
899
+ @href = original_href
900
+ @href_overridden = false
901
+ end
902
+ end
903
+ end
904
+ return @href
919
905
  end
920
906
 
921
907
  # Sets the feed url and prepares the cache_object if necessary.
922
- def url=(new_url)
923
- @url = FeedTools.normalize_url(new_url)
924
- self.cache_object.url = new_url unless self.cache_object.nil?
908
+ def href=(new_href)
909
+ @href = FeedTools::UriHelper.normalize_url(new_href)
910
+ self.cache_object.href = new_href unless self.cache_object.nil?
925
911
  end
926
912
 
927
913
  # Returns the feed title
928
914
  def title
929
915
  if @title.nil?
930
916
  repair_entities = false
931
- title_node = try_xpaths(self.channel_node, [
917
+ title_node = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
932
918
  "atom10:title",
933
919
  "atom03:title",
934
920
  "atom:title",
935
921
  "title",
936
- "dc:title"
922
+ "dc:title",
923
+ "channelTitle"
937
924
  ])
938
- if title_node.nil?
939
- return nil
940
- end
941
- title_type = try_xpaths(title_node, "@type",
942
- :select_result_value => true)
943
- title_mode = try_xpaths(title_node, "@mode",
944
- :select_result_value => true)
945
- title_encoding = try_xpaths(title_node, "@encoding",
946
- :select_result_value => true)
947
-
948
- # Note that we're checking for misuse of type, mode and encoding here
949
- if title_type == "base64" || title_mode == "base64" ||
950
- title_encoding == "base64"
951
- @title = Base64.decode64(title_node.inner_xml.strip)
952
- elsif title_type == "xhtml" || title_mode == "xhtml" ||
953
- title_type == "xml" || title_mode == "xml" ||
954
- title_type == "application/xhtml+xml"
955
- @title = title_node.inner_xml
956
- elsif title_type == "escaped" || title_mode == "escaped"
957
- @title = FeedTools.unescape_entities(
958
- title_node.inner_xml)
959
- else
960
- @title = title_node.inner_xml
961
- repair_entities = true
925
+ @title = FeedTools::HtmlHelper.process_text_construct(title_node,
926
+ self.feed_type, self.feed_version)
927
+ if self.feed_type == "atom" ||
928
+ FeedTools.configurations[:always_strip_wrapper_elements]
929
+ @title = FeedTools::HtmlHelper.strip_wrapper_element(@title)
962
930
  end
963
- unless @title.nil?
964
- @title = FeedTools.sanitize_html(@title, :strip)
965
- @title = FeedTools.unescape_entities(@title) if repair_entities
966
- @title = FeedTools.tidy_html(@title) unless repair_entities
967
- end
968
- @title.gsub!(/>\n</, "><")
969
- @title.gsub!(/\n/, " ")
970
- @title.strip!
971
931
  @title = nil if @title.blank?
972
932
  self.cache_object.title = @title unless self.cache_object.nil?
973
933
  end
@@ -984,7 +944,7 @@ module FeedTools
984
944
  def subtitle
985
945
  if @subtitle.nil?
986
946
  repair_entities = false
987
- subtitle_node = try_xpaths(self.channel_node, [
947
+ subtitle_node = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
988
948
  "atom10:subtitle",
989
949
  "subtitle",
990
950
  "atom03:tagline",
@@ -992,44 +952,24 @@ module FeedTools
992
952
  "description",
993
953
  "summary",
994
954
  "abstract",
995
- "ABSTRACT",
996
955
  "content:encoded",
997
956
  "encoded",
998
957
  "content",
999
958
  "xhtml:body",
1000
959
  "body",
960
+ "xhtml:div",
961
+ "div",
962
+ "p:payload",
963
+ "payload",
964
+ "channelDescription",
1001
965
  "blurb",
1002
966
  "info"
1003
967
  ])
1004
- if subtitle_node.nil?
1005
- return nil
1006
- end
1007
- subtitle_type = try_xpaths(subtitle_node, "@type",
1008
- :select_result_value => true)
1009
- subtitle_mode = try_xpaths(subtitle_node, "@mode",
1010
- :select_result_value => true)
1011
- subtitle_encoding = try_xpaths(subtitle_node, "@encoding",
1012
- :select_result_value => true)
1013
-
1014
- # Note that we're checking for misuse of type, mode and encoding here
1015
- if !subtitle_encoding.blank?
1016
- @subtitle =
1017
- "[Embedded data objects are not currently supported.]"
1018
- elsif subtitle_node.cdatas.size > 0
1019
- @subtitle = subtitle_node.cdatas.first.value
1020
- elsif subtitle_type == "base64" || subtitle_mode == "base64" ||
1021
- subtitle_encoding == "base64"
1022
- @subtitle = Base64.decode64(subtitle_node.inner_xml.strip)
1023
- elsif subtitle_type == "xhtml" || subtitle_mode == "xhtml" ||
1024
- subtitle_type == "xml" || subtitle_mode == "xml" ||
1025
- subtitle_type == "application/xhtml+xml"
1026
- @subtitle = subtitle_node.inner_xml
1027
- elsif subtitle_type == "escaped" || subtitle_mode == "escaped"
1028
- @subtitle = FeedTools.unescape_entities(
1029
- subtitle_node.inner_xml)
1030
- else
1031
- @subtitle = subtitle_node.inner_xml
1032
- repair_entities = true
968
+ @subtitle = FeedTools::HtmlHelper.process_text_construct(
969
+ subtitle_node, self.feed_type, self.feed_version)
970
+ if self.feed_type == "atom" ||
971
+ FeedTools.configurations[:always_strip_wrapper_elements]
972
+ @subtitle = FeedTools::HtmlHelper.strip_wrapper_element(@subtitle)
1033
973
  end
1034
974
  if @subtitle.blank?
1035
975
  @subtitle = self.itunes_summary
@@ -1037,15 +977,6 @@ module FeedTools
1037
977
  if @subtitle.blank?
1038
978
  @subtitle = self.itunes_subtitle
1039
979
  end
1040
-
1041
- unless @subtitle.blank?
1042
- @subtitle = FeedTools.sanitize_html(@subtitle, :strip)
1043
- @subtitle = FeedTools.unescape_entities(@subtitle) if repair_entities
1044
- @subtitle = FeedTools.tidy_html(@subtitle)
1045
- end
1046
-
1047
- @subtitle = @subtitle.strip unless @subtitle.nil?
1048
- @subtitle = nil if @subtitle.blank?
1049
980
  end
1050
981
  return @subtitle
1051
982
  end
@@ -1058,17 +989,20 @@ module FeedTools
1058
989
  # Returns the contents of the itunes:summary element
1059
990
  def itunes_summary
1060
991
  if @itunes_summary.nil?
1061
- @itunes_summary = select_not_blank([
1062
- try_xpaths(self.channel_node, [
992
+ @itunes_summary = FeedTools::XmlHelper.select_not_blank([
993
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1063
994
  "itunes:summary/text()"
1064
- ]),
1065
- try_xpaths(self.root_node, [
995
+ ], :select_result_value => true),
996
+ FeedTools::XmlHelper.try_xpaths(self.root_node, [
1066
997
  "itunes:summary/text()"
1067
- ])
998
+ ], :select_result_value => true)
1068
999
  ])
1069
1000
  unless @itunes_summary.blank?
1070
- @itunes_summary = FeedTools.unescape_entities(@itunes_summary)
1071
- @itunes_summary = FeedTools.sanitize_html(@itunes_summary)
1001
+ @itunes_summary =
1002
+ FeedTools::HtmlHelper.unescape_entities(@itunes_summary)
1003
+ @itunes_summary =
1004
+ FeedTools::HtmlHelper.sanitize_html(@itunes_summary)
1005
+ @itunes_summary.strip!
1072
1006
  else
1073
1007
  @itunes_summary = nil
1074
1008
  end
@@ -1084,17 +1018,20 @@ module FeedTools
1084
1018
  # Returns the contents of the itunes:subtitle element
1085
1019
  def itunes_subtitle
1086
1020
  if @itunes_subtitle.nil?
1087
- @itunes_subtitle = select_not_blank([
1088
- try_xpaths(self.channel_node, [
1021
+ @itunes_subtitle = FeedTools::XmlHelper.select_not_blank([
1022
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1089
1023
  "itunes:subtitle/text()"
1090
- ]),
1091
- try_xpaths(self.root_node, [
1024
+ ], :select_result_value => true),
1025
+ FeedTools::XmlHelper.try_xpaths(self.root_node, [
1092
1026
  "itunes:subtitle/text()"
1093
- ])
1027
+ ], :select_result_value => true)
1094
1028
  ])
1095
1029
  unless @itunes_subtitle.blank?
1096
- @itunes_subtitle = FeedTools.unescape_entities(@itunes_subtitle)
1097
- @itunes_subtitle = FeedTools.sanitize_html(@itunes_subtitle)
1030
+ @itunes_subtitle =
1031
+ FeedTools::HtmlHelper.unescape_entities(@itunes_subtitle)
1032
+ @itunes_subtitle =
1033
+ FeedTools::HtmlHelper.sanitize_html(@itunes_subtitle)
1034
+ @itunes_subtitle.strip!
1098
1035
  else
1099
1036
  @itunes_subtitle = nil
1100
1037
  end
@@ -1107,84 +1044,89 @@ module FeedTools
1107
1044
  @itunes_subtitle = new_itunes_subtitle
1108
1045
  end
1109
1046
 
1047
+ # Returns the contents of the media:text element
1048
+ def media_text
1049
+ if @media_text.nil?
1050
+ @media_text = FeedTools::XmlHelper.select_not_blank([
1051
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1052
+ "media:text/text()"
1053
+ ], :select_result_value => true),
1054
+ FeedTools::XmlHelper.try_xpaths(self.root_node, [
1055
+ "media:text/text()"
1056
+ ], :select_result_value => true)
1057
+ ])
1058
+ unless @media_text.blank?
1059
+ @media_text = FeedTools::HtmlHelper.unescape_entities(@media_text)
1060
+ @media_text = FeedTools::HtmlHelper.sanitize_html(@media_text)
1061
+ @media_text.strip!
1062
+ else
1063
+ @media_text = nil
1064
+ end
1065
+ end
1066
+ return @media_text
1067
+ end
1068
+
1069
+ # Sets the contents of the media:text element
1070
+ def media_text=(new_media_text)
1071
+ @media_text = new_media_text
1072
+ end
1073
+
1110
1074
  # Returns the feed link
1111
1075
  def link
1112
1076
  if @link.nil?
1113
- @link = try_xpaths(self.channel_node, [
1114
- "atom10:link[@type='application/xhtml+xml']/@href",
1115
- "atom10:link[@type='text/html']/@href",
1116
- "atom10:link[@rel='alternate']/@href",
1117
- "atom03:link[@type='application/xhtml+xml']/@href",
1118
- "atom03:link[@type='text/html']/@href",
1119
- "atom03:link[@rel='alternate']/@href",
1120
- "atom:link[@type='application/xhtml+xml']/@href",
1121
- "atom:link[@type='text/html']/@href",
1122
- "atom:link[@rel='alternate']/@href",
1123
- "link[@type='application/xhtml+xml']/@href",
1124
- "link[@type='text/html']/@href",
1125
- "link[@rel='alternate']/@href",
1126
- "link/text()",
1127
- "@href",
1128
- "a/@href"
1129
- ], :select_result_value => true)
1130
- if @link.blank?
1131
- if FeedTools.is_uri?(self.guid) &&
1132
- !(self.guid =~ /^urn:uuid:/) &&
1133
- !(self.guid =~ /^tag:/)
1134
- @link = self.guid
1077
+ max_score = 0
1078
+ for link_object in self.links.reverse
1079
+ score = 0
1080
+ if FeedTools::HtmlHelper.html_type?(link_object.type)
1081
+ score = score + 2
1082
+ elsif link_object.type != nil
1083
+ score = score - 1
1084
+ end
1085
+ if FeedTools::HtmlHelper.xml_type?(link_object.type)
1086
+ score = score + 1
1135
1087
  end
1088
+ if link_object.rel == "alternate"
1089
+ score = score + 1
1090
+ end
1091
+ if link_object.rel == "self"
1092
+ score = score - 1
1093
+ end
1094
+ if score >= max_score
1095
+ max_score = score
1096
+ @link = link_object.href
1097
+ end
1098
+ end
1099
+ if @link.blank?
1100
+ @link = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1101
+ "@href",
1102
+ "@rdf:about",
1103
+ "@about"
1104
+ ], :select_result_value => true)
1136
1105
  end
1137
- if @link.blank? && channel_node != nil
1138
- # Technically, we shouldn't use the base attribute for this, but
1139
- # if the href attribute is missing, it's already a given that we're
1140
- # looking at a messed up CDF file. We can always pray it's correct.
1141
- @link = XPath.first(channel_node, "@base").to_s
1106
+ if @link.blank?
1107
+ if FeedTools::UriHelper.is_uri?(self.id) &&
1108
+ (self.id =~ /^http/)
1109
+ @link = self.id
1110
+ end
1142
1111
  end
1143
1112
  if !@link.blank?
1144
- @link = FeedTools.unescape_entities(@link)
1113
+ @link = FeedTools::HtmlHelper.unescape_entities(@link)
1145
1114
  end
1146
- if @link.blank?
1147
- link_node = try_xpaths(self.channel_node, [
1148
- "atom10:link",
1149
- "atom03:link",
1150
- "atom:link",
1151
- "link"
1152
- ])
1153
- if link_node != nil
1154
- if link_node.attributes['type'].to_s =~ /^image/ ||
1155
- link_node.attributes['type'].to_s =~ /^application/ ||
1156
- link_node.attributes['type'].to_s =~ /xml/ ||
1157
- link_node.attributes['rel'].to_s =~ /self/
1158
- for child in self.channel_node
1159
- if child.class == REXML::Element
1160
- if child.name.downcase == "link"
1161
- if child.attributes['type'].to_s =~ /^image/ ||
1162
- child.attributes['type'].to_s =~ /^application/ ||
1163
- child.attributes['type'].to_s =~ /xml/ ||
1164
- child.attributes['rel'].to_s =~ /self/
1165
- @link = nil
1166
- next
1167
- else
1168
- @link = child.attributes['href'].to_s
1169
- if @link.blank?
1170
- @link = child.inner_xml
1171
- end
1172
- if @link.blank?
1173
- next
1174
- end
1175
- break
1176
- end
1177
- end
1178
- end
1179
- end
1180
- else
1181
- @link = link_node.attributes['href'].to_s
1115
+ @link = nil if @link.blank?
1116
+ begin
1117
+ if !(@link =~ /^file:/) &&
1118
+ !FeedTools::UriHelper.is_uri?(@link)
1119
+ channel_base_uri = nil
1120
+ unless self.channel_node.nil?
1121
+ channel_base_uri = self.channel_node.base_uri
1182
1122
  end
1123
+ @link = FeedTools::UriHelper.resolve_relative_uri(
1124
+ @link, [channel_base_uri, self.base_uri])
1183
1125
  end
1126
+ rescue
1184
1127
  end
1185
- @link = nil if @link.blank?
1186
1128
  if FeedTools.configurations[:url_normalization_enabled]
1187
- @link = FeedTools.normalize_url(@link)
1129
+ @link = FeedTools::UriHelper.normalize_url(@link)
1188
1130
  end
1189
1131
  unless self.cache_object.nil?
1190
1132
  self.cache_object.link = @link
@@ -1200,11 +1142,143 @@ module FeedTools
1200
1142
  self.cache_object.link = new_link
1201
1143
  end
1202
1144
  end
1145
+
1146
+ # Returns the links collection
1147
+ def links
1148
+ if @links.blank?
1149
+ @links = []
1150
+ link_nodes =
1151
+ FeedTools::XmlHelper.combine_xpaths_all(self.channel_node, [
1152
+ "atom10:link",
1153
+ "atom03:link",
1154
+ "atom:link",
1155
+ "link",
1156
+ "channelLink",
1157
+ "a",
1158
+ "url",
1159
+ "href"
1160
+ ])
1161
+ for link_node in link_nodes
1162
+ link_object = FeedTools::Link.new
1163
+ link_object.href = FeedTools::XmlHelper.try_xpaths(link_node, [
1164
+ "@atom10:href",
1165
+ "@atom03:href",
1166
+ "@atom:href",
1167
+ "@href",
1168
+ "text()"
1169
+ ], :select_result_value => true)
1170
+ if link_object.href.nil? && link_node.base_uri != nil
1171
+ link_object.href = ""
1172
+ end
1173
+ begin
1174
+ if !(link_object.href =~ /^file:/) &&
1175
+ !FeedTools::UriHelper.is_uri?(link_object.href)
1176
+ link_object.href = FeedTools::UriHelper.resolve_relative_uri(
1177
+ link_object.href,
1178
+ [link_node.base_uri, self.base_uri])
1179
+ end
1180
+ rescue
1181
+ end
1182
+ if FeedTools.configurations[:url_normalization_enabled]
1183
+ link_object.href =
1184
+ FeedTools::UriHelper.normalize_url(link_object.href)
1185
+ end
1186
+ link_object.href.strip! unless link_object.href.nil?
1187
+ next if link_object.href.blank?
1188
+ link_object.hreflang = FeedTools::XmlHelper.try_xpaths(link_node, [
1189
+ "@atom10:hreflang",
1190
+ "@atom03:hreflang",
1191
+ "@atom:hreflang",
1192
+ "@hreflang"
1193
+ ], :select_result_value => true)
1194
+ unless link_object.hreflang.nil?
1195
+ link_object.hreflang = link_object.hreflang.downcase
1196
+ end
1197
+ link_object.rel = FeedTools::XmlHelper.try_xpaths(link_node, [
1198
+ "@atom10:rel",
1199
+ "@atom03:rel",
1200
+ "@atom:rel",
1201
+ "@rel"
1202
+ ], :select_result_value => true)
1203
+ unless link_object.rel.nil?
1204
+ link_object.rel = link_object.rel.downcase
1205
+ end
1206
+ link_object.type = FeedTools::XmlHelper.try_xpaths(link_node, [
1207
+ "@atom10:type",
1208
+ "@atom03:type",
1209
+ "@atom:type",
1210
+ "@type"
1211
+ ], :select_result_value => true)
1212
+ unless link_object.type.nil?
1213
+ link_object.type = link_object.type.downcase
1214
+ end
1215
+ link_object.title = FeedTools::XmlHelper.try_xpaths(link_node, [
1216
+ "@atom10:title",
1217
+ "@atom03:title",
1218
+ "@atom:title",
1219
+ "@title",
1220
+ "text()"
1221
+ ], :select_result_value => true)
1222
+ # This catches the ambiguities between atom, rss, and cdf
1223
+ if link_object.title == link_object.href
1224
+ link_object.title = nil
1225
+ end
1226
+ link_object.length = FeedTools::XmlHelper.try_xpaths(link_node, [
1227
+ "@atom10:length",
1228
+ "@atom03:length",
1229
+ "@atom:length",
1230
+ "@length"
1231
+ ], :select_result_value => true)
1232
+ if !link_object.length.nil?
1233
+ link_object.length = link_object.length.to_i
1234
+ else
1235
+ if !link_object.type.nil? && link_object.type[0..4] != "text" &&
1236
+ link_object.type[-3..-1] != "xml" &&
1237
+ link_object.href =~ /^http:\/\//
1238
+ # Retrieve the length with an http HEAD request
1239
+ else
1240
+ link_object.length = nil
1241
+ end
1242
+ end
1243
+ @links << link_object
1244
+ end
1245
+ end
1246
+ return @links
1247
+ end
1248
+
1249
+ # Sets the links collection
1250
+ def links=(new_links)
1251
+ @links = new_links
1252
+ end
1253
+
1254
+ # Returns the base uri for the feed, used for resolving relative paths
1255
+ def base_uri
1256
+ if @base_uri.nil?
1257
+ @base_uri = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1258
+ "@base"
1259
+ ], :select_result_value => true)
1260
+ if @base_uri.blank?
1261
+ @base_uri =
1262
+ FeedTools::GenericHelper.recursion_trap(:feed_base_uri) do
1263
+ self.href
1264
+ end
1265
+ end
1266
+ if !@base_uri.blank?
1267
+ @base_uri = FeedTools::UriHelper.normalize_url(@base_uri)
1268
+ end
1269
+ end
1270
+ return @base_uri
1271
+ end
1272
+
1273
+ # Sets the base uri for the feed
1274
+ def base_uri=(new_base_uri)
1275
+ @base_uri = new_base_uri
1276
+ end
1203
1277
 
1204
1278
  # Returns the url to the icon file for this feed.
1205
1279
  def icon
1206
1280
  if @icon.nil?
1207
- icon_node = try_xpaths(self.channel_node, [
1281
+ icon_node = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1208
1282
  "link[@rel='icon']",
1209
1283
  "link[@rel='shortcut icon']",
1210
1284
  "link[@type='image/x-icon']",
@@ -1213,15 +1287,26 @@ module FeedTools
1213
1287
  "LOGO[@STYLE='ICON']"
1214
1288
  ])
1215
1289
  unless icon_node.nil?
1216
- @icon = FeedTools.unescape_entities(
1217
- XPath.first(icon_node, "@href").to_s)
1218
- if @icon.blank?
1219
- @icon = FeedTools.unescape_entities(
1220
- XPath.first(icon_node, "text()").to_s)
1221
- unless FeedTools.is_uri? @icon
1222
- @icon = nil
1290
+ @icon = FeedTools::XmlHelper.try_xpaths(icon_node, [
1291
+ "@atom10:href",
1292
+ "@atom03:href",
1293
+ "@atom:href",
1294
+ "@href",
1295
+ "text()"
1296
+ ], :select_result_value => true)
1297
+ begin
1298
+ if !(@icon =~ /^file:/) &&
1299
+ !FeedTools::UriHelper.is_uri?(@icon)
1300
+ channel_base_uri = nil
1301
+ unless self.channel_node.nil?
1302
+ channel_base_uri = self.channel_node.base_uri
1303
+ end
1304
+ @icon = FeedTools::UriHelper.resolve_relative_uri(
1305
+ @icon, [channel_base_uri, self.base_uri])
1223
1306
  end
1307
+ rescue
1224
1308
  end
1309
+ @icon = nil unless FeedTools::UriHelper.is_uri?(@icon)
1225
1310
  @icon = nil if @icon.blank?
1226
1311
  end
1227
1312
  end
@@ -1236,7 +1321,8 @@ module FeedTools
1236
1321
  if @favicon.nil?
1237
1322
  if !self.link.blank?
1238
1323
  begin
1239
- link_uri = URI.parse(FeedTools.normalize_url(self.link))
1324
+ link_uri = URI.parse(
1325
+ FeedTools::UriHelper.normalize_url(self.link))
1240
1326
  if link_uri.scheme == "http"
1241
1327
  @favicon =
1242
1328
  "http://" + link_uri.host + "/favicon.ico"
@@ -1244,9 +1330,10 @@ module FeedTools
1244
1330
  rescue
1245
1331
  @favicon = nil
1246
1332
  end
1247
- if @favicon.nil? && !self.url.blank?
1333
+ if @favicon.nil? && !self.href.blank?
1248
1334
  begin
1249
- feed_uri = URI.parse(FeedTools.normalize_url(self.url))
1335
+ feed_uri = URI.parse(
1336
+ FeedTools::UriHelper.normalize_url(self.href))
1250
1337
  if feed_uri.scheme == "http"
1251
1338
  @favicon =
1252
1339
  "http://" + feed_uri.host + "/favicon.ico"
@@ -1265,8 +1352,8 @@ module FeedTools
1265
1352
  # Returns the feed author
1266
1353
  def author
1267
1354
  if @author.nil?
1268
- @author = FeedTools::Feed::Author.new
1269
- author_node = try_xpaths(self.channel_node, [
1355
+ @author = FeedTools::Author.new
1356
+ author_node = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1270
1357
  "atom10:author",
1271
1358
  "atom03:author",
1272
1359
  "atom:author",
@@ -1276,16 +1363,18 @@ module FeedTools
1276
1363
  "dc:creator"
1277
1364
  ])
1278
1365
  unless author_node.nil?
1279
- @author.raw = FeedTools.unescape_entities(
1280
- XPath.first(author_node, "text()").to_s).strip
1281
- @author.raw = nil if @author.raw.blank?
1366
+ @author.raw = FeedTools::XmlHelper.try_xpaths(
1367
+ author_node, ["text()"], :select_result_value => true)
1368
+ @author.raw = FeedTools::HtmlHelper.unescape_entities(@author.raw)
1282
1369
  unless @author.raw.nil?
1283
1370
  raw_scan = @author.raw.scan(
1284
1371
  /(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
1285
1372
  if raw_scan.nil? || raw_scan.size == 0
1286
1373
  raw_scan = @author.raw.scan(
1287
1374
  /(\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\s*\((.*)\)/i)
1288
- author_raw_pair = raw_scan.first.reverse unless raw_scan.size == 0
1375
+ unless raw_scan.size == 0
1376
+ author_raw_pair = raw_scan.first.reverse
1377
+ end
1289
1378
  else
1290
1379
  author_raw_pair = raw_scan.first
1291
1380
  end
@@ -1302,16 +1391,16 @@ module FeedTools
1302
1391
  else
1303
1392
  unless @author.raw.include?("@")
1304
1393
  # We can be reasonably sure we are looking at something
1305
- # that the creator didn't intend to contain an email address if
1306
- # it got through the preceeding regexes and it doesn't
1394
+ # that the creator didn't intend to contain an email address
1395
+ # if it got through the preceeding regexes and it doesn't
1307
1396
  # contain the tell-tale '@' symbol.
1308
1397
  @author.name = @author.raw
1309
1398
  end
1310
1399
  end
1311
1400
  end
1312
1401
  if @author.name.blank?
1313
- @author.name = FeedTools.unescape_entities(
1314
- try_xpaths(author_node, [
1402
+ @author.name = FeedTools::HtmlHelper.unescape_entities(
1403
+ FeedTools::XmlHelper.try_xpaths(author_node, [
1315
1404
  "atom10:name/text()",
1316
1405
  "atom03:name/text()",
1317
1406
  "atom:name/text()",
@@ -1321,8 +1410,8 @@ module FeedTools
1321
1410
  )
1322
1411
  end
1323
1412
  if @author.email.blank?
1324
- @author.email = FeedTools.unescape_entities(
1325
- try_xpaths(author_node, [
1413
+ @author.email = FeedTools::HtmlHelper.unescape_entities(
1414
+ FeedTools::XmlHelper.try_xpaths(author_node, [
1326
1415
  "atom10:email/text()",
1327
1416
  "atom03:email/text()",
1328
1417
  "atom:email/text()",
@@ -1332,8 +1421,8 @@ module FeedTools
1332
1421
  )
1333
1422
  end
1334
1423
  if @author.url.blank?
1335
- @author.url = FeedTools.unescape_entities(
1336
- try_xpaths(author_node, [
1424
+ @author.url = FeedTools::HtmlHelper.unescape_entities(
1425
+ FeedTools::XmlHelper.try_xpaths(author_node, [
1337
1426
  "atom10:url/text()",
1338
1427
  "atom03:url/text()",
1339
1428
  "atom:url/text()",
@@ -1342,7 +1431,7 @@ module FeedTools
1342
1431
  "atom03:uri/text()",
1343
1432
  "atom:uri/text()",
1344
1433
  "uri/text()",
1345
- "@url",
1434
+ "@href",
1346
1435
  "@uri",
1347
1436
  "@href"
1348
1437
  ], :select_result_value => true)
@@ -1352,6 +1441,16 @@ module FeedTools
1352
1441
  @author.raw = nil if @author.raw.blank?
1353
1442
  @author.email = nil if @author.email.blank?
1354
1443
  @author.url = nil if @author.url.blank?
1444
+ if @author.url != nil
1445
+ begin
1446
+ if !(@author.url =~ /^file:/) &&
1447
+ !FeedTools::UriHelper.is_uri?(@author.url)
1448
+ @author.url = FeedTools::UriHelper.resolve_relative_uri(
1449
+ @author.url, [author_node.base_uri, self.base_uri])
1450
+ end
1451
+ rescue
1452
+ end
1453
+ end
1355
1454
  end
1356
1455
  # Fallback on the itunes module if we didn't find an author name
1357
1456
  begin
@@ -1374,7 +1473,7 @@ module FeedTools
1374
1473
  # We're not looking at an author object, this is probably a string,
1375
1474
  # default to setting the author's name.
1376
1475
  if @author.nil?
1377
- @author = FeedTools::Feed::Author.new
1476
+ @author = FeedTools::Author.new
1378
1477
  end
1379
1478
  @author.name = new_author
1380
1479
  end
@@ -1383,14 +1482,13 @@ module FeedTools
1383
1482
  # Returns the feed publisher
1384
1483
  def publisher
1385
1484
  if @publisher.nil?
1386
- @publisher = FeedTools::Feed::Author.new
1387
- publisher_node = try_xpaths(self.channel_node, [
1388
- "webMaster/text()",
1389
- "dc:publisher/text()"
1390
- ])
1485
+ @publisher = FeedTools::Author.new
1486
+ @publisher.raw = FeedTools::HtmlHelper.unescape_entities(
1487
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1488
+ "webMaster/text()",
1489
+ "dc:publisher/text()"
1490
+ ], :select_result_value => true))
1391
1491
 
1392
- # Set the author name
1393
- @publisher.raw = FeedTools.unescape_entities(publisher_node.to_s)
1394
1492
  unless @publisher.raw.blank?
1395
1493
  raw_scan = @publisher.raw.scan(
1396
1494
  /(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
@@ -1428,6 +1526,20 @@ module FeedTools
1428
1526
  @publisher.raw = nil if @publisher.raw.blank?
1429
1527
  @publisher.email = nil if @publisher.email.blank?
1430
1528
  @publisher.url = nil if @publisher.url.blank?
1529
+ if @publisher.url != nil
1530
+ begin
1531
+ if !(@publisher.url =~ /^file:/) &&
1532
+ !FeedTools::UriHelper.is_uri?(@publisher.url)
1533
+ channel_base_uri = nil
1534
+ unless self.channel_node.nil?
1535
+ channel_base_uri = self.channel_node.base_uri
1536
+ end
1537
+ @publisher.url = FeedTools::UriHelper.resolve_relative_uri(
1538
+ @publisher.url, [channel_base_uri, self.base_uri])
1539
+ end
1540
+ rescue
1541
+ end
1542
+ end
1431
1543
  end
1432
1544
  return @publisher
1433
1545
  end
@@ -1443,7 +1555,7 @@ module FeedTools
1443
1555
  # We're not looking at an Author object, this is probably a string,
1444
1556
  # default to setting the publisher's name.
1445
1557
  if @publisher.nil?
1446
- @publisher = FeedTools::Feed::Author.new
1558
+ @publisher = FeedTools::Author.new
1447
1559
  end
1448
1560
  @publisher.name = new_publisher
1449
1561
  end
@@ -1457,8 +1569,8 @@ module FeedTools
1457
1569
  # attribute.
1458
1570
  def itunes_author
1459
1571
  if @itunes_author.nil?
1460
- @itunes_author = FeedTools.unescape_entities(
1461
- try_xpaths(self.channel_node, [
1572
+ @itunes_author = FeedTools::HtmlHelper.unescape_entities(
1573
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1462
1574
  "itunes:author/text()"
1463
1575
  ], :select_result_value => true)
1464
1576
  )
@@ -1470,7 +1582,7 @@ module FeedTools
1470
1582
  # Returns the feed time
1471
1583
  def time
1472
1584
  if @time.nil?
1473
- time_string = try_xpaths(self.channel_node, [
1585
+ time_string = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1474
1586
  "atom10:updated/text()",
1475
1587
  "atom03:updated/text()",
1476
1588
  "atom:updated/text()",
@@ -1480,6 +1592,7 @@ module FeedTools
1480
1592
  "atom:modified/text()",
1481
1593
  "modified/text()",
1482
1594
  "time/text()",
1595
+ "lastBuildDate/text()",
1483
1596
  "atom10:issued/text()",
1484
1597
  "atom03:issued/text()",
1485
1598
  "atom:issued/text()",
@@ -1488,8 +1601,8 @@ module FeedTools
1488
1601
  "atom03:published/text()",
1489
1602
  "atom:published/text()",
1490
1603
  "published/text()",
1491
- "pubDate/text()",
1492
1604
  "dc:date/text()",
1605
+ "pubDate/text()",
1493
1606
  "date/text()"
1494
1607
  ], :select_result_value => true)
1495
1608
  begin
@@ -1509,15 +1622,15 @@ module FeedTools
1509
1622
  return @time
1510
1623
  end
1511
1624
 
1512
- # Sets the feed item time
1625
+ # Sets the feed time
1513
1626
  def time=(new_time)
1514
1627
  @time = new_time
1515
1628
  end
1516
1629
 
1517
- # Returns the feed item updated time
1630
+ # Returns the feed updated time
1518
1631
  def updated
1519
1632
  if @updated.nil?
1520
- updated_string = try_xpaths(self.channel_node, [
1633
+ updated_string = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1521
1634
  "atom10:updated/text()",
1522
1635
  "atom03:updated/text()",
1523
1636
  "atom:updated/text()",
@@ -1525,7 +1638,8 @@ module FeedTools
1525
1638
  "atom10:modified/text()",
1526
1639
  "atom03:modified/text()",
1527
1640
  "atom:modified/text()",
1528
- "modified/text()"
1641
+ "modified/text()",
1642
+ "lastBuildDate/text()"
1529
1643
  ], :select_result_value => true)
1530
1644
  unless updated_string.blank?
1531
1645
  @updated = Time.parse(updated_string).gmtime rescue nil
@@ -1536,26 +1650,27 @@ module FeedTools
1536
1650
  return @updated
1537
1651
  end
1538
1652
 
1539
- # Sets the feed item updated time
1653
+ # Sets the feed updated time
1540
1654
  def updated=(new_updated)
1541
1655
  @updated = new_updated
1542
1656
  end
1543
1657
 
1544
- # Returns the feed item published time
1658
+ # Returns the feed published time
1545
1659
  def published
1546
1660
  if @published.nil?
1547
- published_string = try_xpaths(self.channel_node, [
1548
- "atom10:published/text()",
1549
- "atom03:published/text()",
1550
- "atom:published/text()",
1551
- "published/text()",
1552
- "pubDate/text()",
1553
- "atom10:issued/text()",
1554
- "atom03:issued/text()",
1555
- "atom:issued/text()",
1556
- "issued/text()",
1557
- "dc:date/text()"
1558
- ], :select_result_value => true)
1661
+ published_string =
1662
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1663
+ "atom10:published/text()",
1664
+ "atom03:published/text()",
1665
+ "atom:published/text()",
1666
+ "published/text()",
1667
+ "dc:date/text()",
1668
+ "pubDate/text()",
1669
+ "atom10:issued/text()",
1670
+ "atom03:issued/text()",
1671
+ "atom:issued/text()",
1672
+ "issued/text()"
1673
+ ], :select_result_value => true)
1559
1674
  unless published_string.blank?
1560
1675
  @published = Time.parse(published_string).gmtime rescue nil
1561
1676
  else
@@ -1565,7 +1680,7 @@ module FeedTools
1565
1680
  return @published
1566
1681
  end
1567
1682
 
1568
- # Sets the feed item published time
1683
+ # Sets the feed published time
1569
1684
  def published=(new_published)
1570
1685
  @published = new_published
1571
1686
  end
@@ -1574,22 +1689,24 @@ module FeedTools
1574
1689
  def categories
1575
1690
  if @categories.nil?
1576
1691
  @categories = []
1577
- category_nodes = try_xpaths_all(self.channel_node, [
1578
- "category",
1579
- "dc:subject"
1580
- ])
1692
+ category_nodes =
1693
+ FeedTools::XmlHelper.try_xpaths_all(self.channel_node, [
1694
+ "category",
1695
+ "dc:subject"
1696
+ ])
1581
1697
  unless category_nodes.nil?
1582
1698
  for category_node in category_nodes
1583
- category = FeedTools::Feed::Category.new
1584
- category.term = try_xpaths(category_node, [
1699
+ category = FeedTools::Category.new
1700
+ category.term = FeedTools::XmlHelper.try_xpaths(category_node, [
1585
1701
  "@term",
1586
1702
  "text()"
1587
1703
  ], :select_result_value => true)
1588
1704
  category.term.strip! unless category.term.blank?
1589
- category.label = try_xpaths(category_node, ["@label"],
1705
+ category.label = FeedTools::XmlHelper.try_xpaths(
1706
+ category_node, ["@label"],
1590
1707
  :select_result_value => true)
1591
1708
  category.label.strip! unless category.label.blank?
1592
- category.scheme = try_xpaths(category_node, [
1709
+ category.scheme = FeedTools::XmlHelper.try_xpaths(category_node, [
1593
1710
  "@scheme",
1594
1711
  "@domain"
1595
1712
  ], :select_result_value => true)
@@ -1605,63 +1722,69 @@ module FeedTools
1605
1722
  def images
1606
1723
  if @images.nil?
1607
1724
  @images = []
1608
- image_nodes = try_xpaths_all(self.channel_node, [
1609
- "image",
1610
- "logo",
1611
- "apple-wallpapers:image",
1612
- "atom10:link",
1613
- "atom03:link",
1614
- "atom:link",
1615
- "link"
1616
- ])
1725
+ image_nodes = FeedTools::XmlHelper.combine_xpaths_all(
1726
+ self.channel_node, [
1727
+ "image",
1728
+ "logo",
1729
+ "apple-wallpapers:image",
1730
+ "imageUrl"
1731
+ ]
1732
+ )
1617
1733
  unless image_nodes.blank?
1618
1734
  for image_node in image_nodes
1619
- image = FeedTools::Feed::Image.new
1620
- image.url = try_xpaths(image_node, [
1735
+ image = FeedTools::Image.new
1736
+ image.href = FeedTools::XmlHelper.try_xpaths(image_node, [
1621
1737
  "url/text()",
1622
1738
  "@rdf:resource",
1739
+ "@href",
1623
1740
  "text()"
1624
1741
  ], :select_result_value => true)
1625
- if image.url.blank? && (image_node.name == "logo" ||
1626
- (image_node.attributes['type'].to_s =~ /^image/) == 0)
1627
- image.url = try_xpaths(image_node, [
1628
- "@atom10:href",
1629
- "@atom03:href",
1630
- "@atom:href",
1631
- "@href"
1632
- ], :select_result_value => true)
1633
- if image.url == self.link && image.url != nil
1634
- image.url = nil
1635
- end
1742
+ if image.href.nil? && image_node.base_uri != nil
1743
+ image.href = ""
1636
1744
  end
1637
- if image.url.blank? && image_node.name == "LOGO"
1638
- image.url = try_xpaths(image_node, [
1639
- "@href"
1640
- ], :select_result_value => true)
1745
+ begin
1746
+ if !(image.href =~ /^file:/) &&
1747
+ !FeedTools::UriHelper.is_uri?(image.href)
1748
+ image.href = FeedTools::UriHelper.resolve_relative_uri(
1749
+ image.href, [image_node.base_uri, self.base_uri])
1750
+ end
1751
+ rescue
1641
1752
  end
1642
- image.url.strip! unless image.url.nil?
1643
- image.title = try_xpaths(image_node,
1753
+ if FeedTools.configurations[:url_normalization_enabled]
1754
+ image.href = FeedTools::UriHelper.normalize_url(image.href)
1755
+ end
1756
+ image.href.strip! unless image.href.nil?
1757
+ next if image.href.blank?
1758
+ image.title = FeedTools::XmlHelper.try_xpaths(image_node,
1644
1759
  ["title/text()"], :select_result_value => true)
1645
1760
  image.title.strip! unless image.title.nil?
1646
- image.description = try_xpaths(image_node,
1761
+ image.description = FeedTools::XmlHelper.try_xpaths(image_node,
1647
1762
  ["description/text()"], :select_result_value => true)
1648
1763
  image.description.strip! unless image.description.nil?
1649
- image.link = try_xpaths(image_node,
1764
+ image.link = FeedTools::XmlHelper.try_xpaths(image_node,
1650
1765
  ["link/text()"], :select_result_value => true)
1651
1766
  image.link.strip! unless image.link.nil?
1652
- image.height = try_xpaths(image_node,
1767
+ image.height = FeedTools::XmlHelper.try_xpaths(image_node,
1653
1768
  ["height/text()"], :select_result_value => true).to_i
1654
1769
  image.height = nil if image.height <= 0
1655
- image.width = try_xpaths(image_node,
1770
+ image.width = FeedTools::XmlHelper.try_xpaths(image_node,
1656
1771
  ["width/text()"], :select_result_value => true).to_i
1657
1772
  image.width = nil if image.width <= 0
1658
- image.style = try_xpaths(image_node, [
1773
+ image.style = FeedTools::XmlHelper.try_xpaths(image_node, [
1659
1774
  "style/text()",
1660
1775
  "@style"
1661
1776
  ], :select_result_value => true)
1662
1777
  image.style.strip! unless image.style.nil?
1663
1778
  image.style.downcase! unless image.style.nil?
1664
- @images << image unless image.url.nil?
1779
+ @images << image unless image.href.nil?
1780
+ end
1781
+ end
1782
+ for link_object in self.links
1783
+ if link_object.type != nil && link_object.type =~ /^image/
1784
+ image = FeedTools::Image.new
1785
+ image.href = link_object.href
1786
+ image.title = link_object.title
1787
+ @images << image unless image.href.nil?
1665
1788
  end
1666
1789
  end
1667
1790
  end
@@ -1671,20 +1794,25 @@ module FeedTools
1671
1794
  # Returns the feed's text input field
1672
1795
  def text_input
1673
1796
  if @text_input.nil?
1674
- @text_input = FeedTools::Feed::TextInput.new
1675
- text_input_node = try_xpaths(self.channel_node, ["textInput"])
1797
+ @text_input = FeedTools::TextInput.new
1798
+ text_input_node =
1799
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, ["textInput"])
1676
1800
  unless text_input_node.nil?
1677
1801
  @text_input.title =
1678
- try_xpaths(text_input_node, ["title/text()"],
1802
+ FeedTools::XmlHelper.try_xpaths(text_input_node,
1803
+ ["title/text()"],
1679
1804
  :select_result_value => true)
1680
1805
  @text_input.description =
1681
- try_xpaths(text_input_node, ["description/text()"],
1806
+ FeedTools::XmlHelper.try_xpaths(text_input_node,
1807
+ ["description/text()"],
1682
1808
  :select_result_value => true)
1683
1809
  @text_input.link =
1684
- try_xpaths(text_input_node, ["link/text()"],
1810
+ FeedTools::XmlHelper.try_xpaths(text_input_node,
1811
+ ["link/text()"],
1685
1812
  :select_result_value => true)
1686
1813
  @text_input.name =
1687
- try_xpaths(text_input_node, ["name/text()"],
1814
+ FeedTools::XmlHelper.try_xpaths(text_input_node,
1815
+ ["name/text()"],
1688
1816
  :select_result_value => true)
1689
1817
  end
1690
1818
  end
@@ -1692,10 +1820,10 @@ module FeedTools
1692
1820
  end
1693
1821
 
1694
1822
  # Returns the feed's copyright information
1695
- def copyright
1696
- if @copyright.nil?
1823
+ def rights
1824
+ if @rights.nil?
1697
1825
  repair_entities = false
1698
- copyright_node = try_xpaths(self.channel_node, [
1826
+ rights_node = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1699
1827
  "atom10:copyright",
1700
1828
  "atom03:copyright",
1701
1829
  "atom:copyright",
@@ -1704,63 +1832,40 @@ module FeedTools
1704
1832
  "dc:rights",
1705
1833
  "rights"
1706
1834
  ])
1707
- if copyright_node.nil?
1708
- return nil
1835
+ @rights = FeedTools::HtmlHelper.process_text_construct(rights_node,
1836
+ self.feed_type, self.feed_version)
1837
+ if self.feed_type == "atom" ||
1838
+ FeedTools.configurations[:always_strip_wrapper_elements]
1839
+ @rights = FeedTools::HtmlHelper.strip_wrapper_element(@rights)
1709
1840
  end
1710
- copyright_type = try_xpaths(copyright_node, "@type",
1711
- :select_result_value => true)
1712
- copyright_mode = try_xpaths(copyright_node, "@mode",
1713
- :select_result_value => true)
1714
- copyright_encoding = try_xpaths(copyright_node, "@encoding",
1715
- :select_result_value => true)
1716
-
1717
- # Note that we're checking for misuse of type, mode and encoding here
1718
- if !copyright_encoding.blank?
1719
- @copyright =
1720
- "[Embedded data objects are not currently supported.]"
1721
- elsif copyright_node.cdatas.size > 0
1722
- @copyright = copyright_node.cdatas.first.value
1723
- elsif copyright_type == "base64" || copyright_mode == "base64" ||
1724
- copyright_encoding == "base64"
1725
- @copyright = Base64.decode64(copyright_node.inner_xml.strip)
1726
- elsif copyright_type == "xhtml" || copyright_mode == "xhtml" ||
1727
- copyright_type == "xml" || copyright_mode == "xml" ||
1728
- copyright_type == "application/xhtml+xml"
1729
- @copyright = copyright_node.inner_xml
1730
- elsif copyright_type == "escaped" || copyright_mode == "escaped"
1731
- @copyright = FeedTools.unescape_entities(
1732
- copyright_node.inner_xml)
1733
- else
1734
- @copyright = copyright_node.inner_xml
1735
- repair_entities = true
1736
- end
1737
-
1738
- unless @copyright.nil?
1739
- @copyright = FeedTools.sanitize_html(@copyright, :strip)
1740
- @copyright = FeedTools.unescape_entities(@copyright) if repair_entities
1741
- @copyright = FeedTools.tidy_html(@copyright)
1742
- end
1743
-
1744
- @copyright = @copyright.strip unless @copyright.nil?
1745
- @copyright = nil if @copyright.blank?
1746
1841
  end
1747
- return @copyright
1842
+ return @rights
1748
1843
  end
1749
1844
 
1750
- # Sets the feed's copyright information
1751
- def copyright=(new_copyright)
1752
- @copyright = new_copyright
1845
+ # Sets the feed's rights information
1846
+ def rights=(new_rights)
1847
+ @rights = new_rights
1753
1848
  end
1754
1849
 
1850
+ def license #:nodoc:
1851
+ raise "Not implemented yet."
1852
+ end
1853
+
1854
+ def license=(new_license) #:nodoc:
1855
+ raise "Not implemented yet."
1856
+ end
1857
+
1755
1858
  # Returns the number of seconds before the feed should expire
1756
1859
  def time_to_live
1757
1860
  if @time_to_live.nil?
1758
1861
  unless channel_node.nil?
1759
1862
  # get the feed time to live from the xml document
1760
- update_frequency = try_xpaths(self.channel_node,
1863
+ update_frequency = FeedTools::XmlHelper.try_xpaths(
1864
+ self.channel_node,
1761
1865
  ["syn:updateFrequency/text()"], :select_result_value => true)
1762
1866
  if !update_frequency.blank?
1763
- update_period = try_xpaths(self.channel_node,
1867
+ update_period = FeedTools::XmlHelper.try_xpaths(
1868
+ self.channel_node,
1764
1869
  ["syn:updatePeriod/text()"], :select_result_value => true)
1765
1870
  if update_period == "daily"
1766
1871
  @time_to_live = update_frequency.to_i.day
@@ -1777,10 +1882,12 @@ module FeedTools
1777
1882
  end
1778
1883
  if @time_to_live.nil?
1779
1884
  # usually expressed in minutes
1780
- update_frequency = try_xpaths(self.channel_node, ["ttl/text()"],
1885
+ update_frequency = FeedTools::XmlHelper.try_xpaths(
1886
+ self.channel_node, ["ttl/text()"],
1781
1887
  :select_result_value => true)
1782
1888
  if !update_frequency.blank?
1783
- update_span = try_xpaths(self.channel_node, ["ttl/@span"],
1889
+ update_span = FeedTools::XmlHelper.try_xpaths(
1890
+ self.channel_node, ["ttl/@span"],
1784
1891
  :select_result_value => true)
1785
1892
  if update_span == "seconds"
1786
1893
  @time_to_live = update_frequency.to_i
@@ -1804,24 +1911,28 @@ module FeedTools
1804
1911
  if @time_to_live.nil?
1805
1912
  @time_to_live = 0
1806
1913
  update_frequency_days =
1807
- XPath.first(channel_node, "SCHEDULE/INTERVALTIME/@DAY").to_s
1914
+ FeedTools::XmlHelper.try_xpaths(self.channel_node,
1915
+ ["schedule/intervaltime/@day"], :select_result_value => true)
1808
1916
  update_frequency_hours =
1809
- XPath.first(channel_node, "schedule/intervaltime/@hour").to_s
1917
+ FeedTools::XmlHelper.try_xpaths(self.channel_node,
1918
+ ["schedule/intervaltime/@hour"], :select_result_value => true)
1810
1919
  update_frequency_minutes =
1811
- XPath.first(channel_node, "schedule/intervaltime/@min").to_s
1920
+ FeedTools::XmlHelper.try_xpaths(self.channel_node,
1921
+ ["schedule/intervaltime/@min"], :select_result_value => true)
1812
1922
  update_frequency_seconds =
1813
- XPath.first(channel_node, "schedule/intervaltime/@sec").to_s
1814
- if update_frequency_days != ""
1923
+ FeedTools::XmlHelper.try_xpaths(self.channel_node,
1924
+ ["schedule/intervaltime/@sec"], :select_result_value => true)
1925
+ if !update_frequency_days.blank?
1815
1926
  @time_to_live = @time_to_live + update_frequency_days.to_i.day
1816
1927
  end
1817
- if update_frequency_hours != ""
1928
+ if !update_frequency_hours.blank?
1818
1929
  @time_to_live = @time_to_live + update_frequency_hours.to_i.hour
1819
1930
  end
1820
- if update_frequency_minutes != ""
1931
+ if !update_frequency_minutes.blank?
1821
1932
  @time_to_live = @time_to_live +
1822
1933
  update_frequency_minutes.to_i.minute
1823
1934
  end
1824
- if update_frequency_seconds != ""
1935
+ if !update_frequency_seconds.blank?
1825
1936
  @time_to_live = @time_to_live + update_frequency_seconds.to_i
1826
1937
  end
1827
1938
  if @time_to_live == 0
@@ -1851,18 +1962,23 @@ module FeedTools
1851
1962
  # Returns the feed's cloud
1852
1963
  def cloud
1853
1964
  if @cloud.nil?
1854
- @cloud = FeedTools::Feed::Cloud.new
1855
- @cloud.domain = try_xpaths(self.channel_node, ["cloud/@domain"],
1965
+ @cloud = FeedTools::Cloud.new
1966
+ @cloud.domain = FeedTools::XmlHelper.try_xpaths(
1967
+ self.channel_node, ["cloud/@domain"],
1856
1968
  :select_result_value => true)
1857
- @cloud.port = try_xpaths(self.channel_node, ["cloud/@port"],
1969
+ @cloud.port = FeedTools::XmlHelper.try_xpaths(
1970
+ self.channel_node, ["cloud/@port"],
1858
1971
  :select_result_value => true)
1859
- @cloud.path = try_xpaths(self.channel_node, ["cloud/@path"],
1972
+ @cloud.path = FeedTools::XmlHelper.try_xpaths(
1973
+ self.channel_node, ["cloud/@path"],
1860
1974
  :select_result_value => true)
1861
1975
  @cloud.register_procedure =
1862
- try_xpaths(self.channel_node, ["cloud/@registerProcedure"],
1976
+ FeedTools::XmlHelper.try_xpaths(
1977
+ self.channel_node, ["cloud/@registerProcedure"],
1863
1978
  :select_result_value => true)
1864
1979
  @cloud.protocol =
1865
- try_xpaths(self.channel_node, ["cloud/@protocol"],
1980
+ FeedTools::XmlHelper.try_xpaths(
1981
+ self.channel_node, ["cloud/@protocol"],
1866
1982
  :select_result_value => true)
1867
1983
  @cloud.protocol.downcase unless @cloud.protocol.nil?
1868
1984
  @cloud.port = @cloud.port.to_s.to_i
@@ -1879,14 +1995,23 @@ module FeedTools
1879
1995
  # Returns the feed generator
1880
1996
  def generator
1881
1997
  if @generator.nil?
1882
- @generator = try_xpaths(self.channel_node, ["generator/text()"],
1998
+ @generator = FeedTools::XmlHelper.try_xpaths(
1999
+ self.channel_node, ["generator/text()"],
1883
2000
  :select_result_value => true)
1884
- @generator = FeedTools.strip_html(@generator) unless @generator.nil?
2001
+ unless @generator.nil?
2002
+ @generator =
2003
+ FeedTools::HtmlHelper.convert_html_to_plain_text(@generator)
2004
+ end
1885
2005
  end
1886
2006
  return @generator
1887
2007
  end
1888
2008
 
1889
2009
  # Sets the feed generator
2010
+ #
2011
+ # Note: Setting this variable will NOT cause this to appear in any
2012
+ # generated output. The generator string is created from the
2013
+ # <tt>:generator_name</tt> and <tt>:generator_href</tt> configuration
2014
+ # variables.
1890
2015
  def generator=(new_generator)
1891
2016
  @generator = new_generator
1892
2017
  end
@@ -1894,9 +2019,24 @@ module FeedTools
1894
2019
  # Returns the feed docs
1895
2020
  def docs
1896
2021
  if @docs.nil?
1897
- @docs = try_xpaths(self.channel_node, ["docs/text()"],
2022
+ @docs = FeedTools::XmlHelper.try_xpaths(
2023
+ self.channel_node, ["docs/text()"],
1898
2024
  :select_result_value => true)
1899
- @docs = FeedTools.strip_html(@docs) unless @docs.nil?
2025
+ begin
2026
+ if !(@docs =~ /^file:/) &&
2027
+ !FeedTools::UriHelper.is_uri?(@docs)
2028
+ channel_base_uri = nil
2029
+ unless self.channel_node.nil?
2030
+ channel_base_uri = self.channel_node.base_uri
2031
+ end
2032
+ @docs = FeedTools::UriHelper.resolve_relative_uri(
2033
+ @docs, [channel_base_uri, self.base_uri])
2034
+ end
2035
+ rescue
2036
+ end
2037
+ if FeedTools.configurations[:url_normalization_enabled]
2038
+ @docs = FeedTools::UriHelper.normalize_url(@docs)
2039
+ end
1900
2040
  end
1901
2041
  return @docs
1902
2042
  end
@@ -1909,15 +2049,15 @@ module FeedTools
1909
2049
  # Returns the feed language
1910
2050
  def language
1911
2051
  if @language.nil?
1912
- @language = select_not_blank([
1913
- try_xpaths(self.channel_node, [
2052
+ @language = FeedTools::XmlHelper.select_not_blank([
2053
+ FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1914
2054
  "language/text()",
1915
2055
  "dc:language/text()",
1916
2056
  "@dc:language",
1917
2057
  "@xml:lang",
1918
2058
  "xml:lang/text()"
1919
2059
  ], :select_result_value => true),
1920
- try_xpaths(self.root_node, [
2060
+ FeedTools::XmlHelper.try_xpaths(self.root_node, [
1921
2061
  "@xml:lang",
1922
2062
  "xml:lang/text()"
1923
2063
  ], :select_result_value => true)
@@ -1938,7 +2078,7 @@ module FeedTools
1938
2078
  # Returns true if this feed contains explicit material.
1939
2079
  def explicit?
1940
2080
  if @explicit.nil?
1941
- explicit_string = try_xpaths(self.channel_node, [
2081
+ explicit_string = FeedTools::XmlHelper.try_xpaths(self.channel_node, [
1942
2082
  "media:adult/text()",
1943
2083
  "itunes:explicit/text()"
1944
2084
  ], :select_result_value => true)
@@ -1958,24 +2098,32 @@ module FeedTools
1958
2098
 
1959
2099
  # Returns the feed entries
1960
2100
  def entries
1961
- if @entries.blank?
1962
- raw_entries = select_not_blank([
1963
- try_xpaths_all(self.channel_node, [
2101
+ if @entries.nil?
2102
+ raw_entries = FeedTools::XmlHelper.select_not_blank([
2103
+ FeedTools::XmlHelper.try_xpaths_all(self.channel_node, [
1964
2104
  "atom10:entry",
1965
2105
  "atom03:entry",
1966
2106
  "atom:entry",
1967
2107
  "entry"
1968
2108
  ]),
1969
- try_xpaths_all(self.root_node, [
2109
+ FeedTools::XmlHelper.try_xpaths_all(self.root_node, [
1970
2110
  "rss10:item",
2111
+ "rss11:items/rss11:item",
2112
+ "rss11:items/item",
2113
+ "items/rss11:item",
2114
+ "items/item",
1971
2115
  "item",
1972
2116
  "atom10:entry",
1973
2117
  "atom03:entry",
1974
2118
  "atom:entry",
1975
2119
  "entry"
1976
2120
  ]),
1977
- try_xpaths_all(self.channel_node, [
2121
+ FeedTools::XmlHelper.try_xpaths_all(self.channel_node, [
1978
2122
  "rss10:item",
2123
+ "rss11:items/rss11:item",
2124
+ "rss11:items/item",
2125
+ "items/rss11:item",
2126
+ "items/item",
1979
2127
  "item"
1980
2128
  ])
1981
2129
  ])
@@ -1987,14 +2135,27 @@ module FeedTools
1987
2135
  new_entry = FeedItem.new
1988
2136
  new_entry.feed_data = entry_node.to_s
1989
2137
  new_entry.feed_data_type = self.feed_data_type
2138
+ new_entry.root_node = entry_node
2139
+ if new_entry.root_node.namespace.blank?
2140
+ new_entry.root_node.add_namespace(self.root_node.namespace)
2141
+ end
1990
2142
  @entries << new_entry
1991
2143
  end
1992
2144
  end
1993
2145
  end
1994
2146
 
1995
2147
  # Sort the items
1996
- @entries = @entries.sort do |a, b|
1997
- (b.time or Time.utc(1970)) <=> (a.time or Time.utc(1970))
2148
+ if FeedTools.configurations[:entry_sorting_property] == "time"
2149
+ @entries = @entries.sort do |a, b|
2150
+ (b.time or Time.utc(1970)) <=> (a.time or Time.utc(1970))
2151
+ end
2152
+ elsif FeedTools.configurations[:entry_sorting_property] != nil
2153
+ sorting_property = FeedTools.configurations[:entry_sorting_property]
2154
+ @entries = @entries.sort do |a, b|
2155
+ eval("a.#{sorting_property}") <=> eval("b.#{sorting_property}")
2156
+ end
2157
+ else
2158
+ @entries = @entries.reverse
1998
2159
  end
1999
2160
  return @entries
2000
2161
  end
@@ -2090,58 +2251,73 @@ module FeedTools
2090
2251
  end
2091
2252
 
2092
2253
  # Generates xml based on the content of the feed
2093
- def build_xml(feed_type=(self.feed_type or "atom"), version=nil,
2254
+ def build_xml(feed_type=(self.feed_type or "atom"), feed_version=nil,
2094
2255
  xml_builder=Builder::XmlMarkup.new(
2095
2256
  :indent => 2, :escape_attrs => false))
2096
2257
  xml_builder.instruct! :xml, :version => "1.0",
2097
2258
  :encoding => (FeedTools.configurations[:output_encoding] or "utf-8")
2098
- if feed_type == "rss" && (version == nil || version <= 0.0)
2099
- version = 1.0
2100
- elsif feed_type == "atom" && (version == nil || version <= 0.0)
2101
- version = 1.0
2259
+ if feed_type.nil?
2260
+ feed_type = self.feed_type
2261
+ end
2262
+ if feed_version.nil?
2263
+ feed_version = self.feed_version
2102
2264
  end
2103
- if feed_type == "rss" && (version == 0.9 || version == 1.0 ||
2104
- version == 1.1)
2265
+ if feed_type == "rss" &&
2266
+ (feed_version == nil || feed_version <= 0.0)
2267
+ feed_version = 1.0
2268
+ elsif feed_type == "atom" &&
2269
+ (feed_version == nil || feed_version <= 0.0)
2270
+ feed_version = 1.0
2271
+ end
2272
+ if feed_type == "rss" &&
2273
+ (feed_version == 0.9 || feed_version == 1.0 || feed_version == 1.1)
2105
2274
  # RDF-based rss format
2106
2275
  return xml_builder.tag!("rdf:RDF",
2107
2276
  "xmlns" => FEED_TOOLS_NAMESPACES['rss10'],
2277
+ "xmlns:content" => FEED_TOOLS_NAMESPACES['content'],
2108
2278
  "xmlns:rdf" => FEED_TOOLS_NAMESPACES['rdf'],
2109
2279
  "xmlns:dc" => FEED_TOOLS_NAMESPACES['dc'],
2110
2280
  "xmlns:syn" => FEED_TOOLS_NAMESPACES['syn'],
2281
+ "xmlns:admin" => FEED_TOOLS_NAMESPACES['admin'],
2111
2282
  "xmlns:taxo" => FEED_TOOLS_NAMESPACES['taxo'],
2112
2283
  "xmlns:itunes" => FEED_TOOLS_NAMESPACES['itunes'],
2113
2284
  "xmlns:media" => FEED_TOOLS_NAMESPACES['media']) do
2114
2285
  channel_attributes = {}
2115
2286
  unless self.link.nil?
2116
2287
  channel_attributes["rdf:about"] =
2117
- FeedTools.escape_entities(self.link)
2288
+ FeedTools::HtmlHelper.escape_entities(self.link)
2118
2289
  end
2119
2290
  xml_builder.channel(channel_attributes) do
2120
- unless title.nil? || title == ""
2121
- xml_builder.title(title)
2291
+ unless self.title.blank?
2292
+ xml_builder.title(
2293
+ FeedTools::HtmlHelper.strip_html_tags(self.title))
2122
2294
  else
2123
2295
  xml_builder.title
2124
2296
  end
2125
- unless link.nil? || link == ""
2126
- xml_builder.link(link)
2297
+ unless self.link.blank?
2298
+ xml_builder.link(self.link)
2127
2299
  else
2128
2300
  xml_builder.link
2129
2301
  end
2130
- unless images.nil? || images.empty?
2131
- xml_builder.image("rdf:resource" => FeedTools.escape_entities(
2132
- images.first.url))
2302
+ unless images.blank?
2303
+ xml_builder.image("rdf:resource" =>
2304
+ FeedTools::HtmlHelper.escape_entities(
2305
+ images.first.url))
2133
2306
  end
2134
2307
  unless description.nil? || description == ""
2135
2308
  xml_builder.description(description)
2136
2309
  else
2137
2310
  xml_builder.description
2138
2311
  end
2139
- unless language.nil? || language == ""
2140
- xml_builder.tag!("dc:language", language)
2312
+ unless self.language.blank?
2313
+ xml_builder.tag!("dc:language", self.language)
2314
+ end
2315
+ unless self.rights.blank?
2316
+ xml_builder.tag!("dc:rights", self.rights)
2141
2317
  end
2142
2318
  xml_builder.tag!("syn:updatePeriod", "hourly")
2143
2319
  xml_builder.tag!("syn:updateFrequency",
2144
- (time_to_live / 1.hour).to_s)
2320
+ (self.time_to_live / 1.hour).to_s)
2145
2321
  xml_builder.tag!("syn:updateBase", Time.mktime(1970).iso8601)
2146
2322
  xml_builder.items do
2147
2323
  xml_builder.tag!("rdf:Seq") do
@@ -2152,14 +2328,17 @@ module FeedTools
2152
2328
  "item link field."
2153
2329
  end
2154
2330
  xml_builder.tag!("rdf:li", "rdf:resource" =>
2155
- FeedTools.escape_entities(item.link))
2331
+ FeedTools::HtmlHelper.escape_entities(item.link))
2156
2332
  end
2157
2333
  end
2158
2334
  end
2159
2335
  end
2160
- build_xml_hook(feed_type, version, xml_builder)
2336
+ xml_builder.tag!(
2337
+ "admin:generatorAgent",
2338
+ "rdf:resource" => FeedTools.configurations[:generator_href])
2339
+ build_xml_hook(feed_type, feed_version, xml_builder)
2161
2340
  end
2162
- unless images.nil? || images.empty?
2341
+ unless self.images.blank?
2163
2342
  best_image = nil
2164
2343
  for image in self.images
2165
2344
  if image.link != nil
@@ -2167,9 +2346,9 @@ module FeedTools
2167
2346
  break
2168
2347
  end
2169
2348
  end
2170
- best_image = images.first if best_image.nil?
2171
- xml_builder.image(
2172
- "rdf:about" => FeedTools.escape_entities(best_image.url)) do
2349
+ best_image = self.images.first if best_image.nil?
2350
+ xml_builder.image("rdf:about" =>
2351
+ FeedTools::HtmlHelper.escape_entities(best_image.url)) do
2173
2352
  if !best_image.title.blank?
2174
2353
  xml_builder.title(best_image.title)
2175
2354
  elsif !self.title.blank?
@@ -2191,13 +2370,14 @@ module FeedTools
2191
2370
  end
2192
2371
  unless items.nil?
2193
2372
  for item in items
2194
- item.build_xml(feed_type, version, xml_builder)
2373
+ item.build_xml(feed_type, feed_version, xml_builder)
2195
2374
  end
2196
2375
  end
2197
2376
  end
2198
2377
  elsif feed_type == "rss"
2199
2378
  # normal rss format
2200
2379
  return xml_builder.rss("version" => "2.0",
2380
+ "xmlns:content" => FEED_TOOLS_NAMESPACES['content'],
2201
2381
  "xmlns:rdf" => FEED_TOOLS_NAMESPACES['rdf'],
2202
2382
  "xmlns:dc" => FEED_TOOLS_NAMESPACES['dc'],
2203
2383
  "xmlns:taxo" => FEED_TOOLS_NAMESPACES['taxo'],
@@ -2205,29 +2385,41 @@ module FeedTools
2205
2385
  "xmlns:itunes" => FEED_TOOLS_NAMESPACES['itunes'],
2206
2386
  "xmlns:media" => FEED_TOOLS_NAMESPACES['media']) do
2207
2387
  xml_builder.channel do
2208
- unless title.blank?
2209
- xml_builder.title(title)
2388
+ unless self.title.blank?
2389
+ xml_builder.title(
2390
+ FeedTools::HtmlHelper.strip_html_tags(self.title))
2210
2391
  end
2211
- unless link.blank?
2392
+ unless self.link.blank?
2212
2393
  xml_builder.link(link)
2213
2394
  end
2214
- unless description.blank?
2395
+ unless self.description.blank?
2215
2396
  xml_builder.description(description)
2397
+ else
2398
+ xml_builder.description
2399
+ end
2400
+ unless self.published.blank?
2401
+ xml_builder.pubDate(self.published.rfc822)
2402
+ end
2403
+ unless self.updated.blank?
2404
+ xml_builder.lastBuildDate(self.updated.rfc822)
2405
+ end
2406
+ unless self.copyright.blank?
2407
+ xml_builder.copyright(self.copyright)
2216
2408
  end
2217
2409
  xml_builder.ttl((time_to_live / 1.minute).to_s)
2218
2410
  xml_builder.generator(
2219
2411
  FeedTools.configurations[:generator_href])
2220
- build_xml_hook(feed_type, version, xml_builder)
2412
+ build_xml_hook(feed_type, feed_version, xml_builder)
2221
2413
  unless items.nil?
2222
2414
  for item in items
2223
- item.build_xml(feed_type, version, xml_builder)
2415
+ item.build_xml(feed_type, feed_version, xml_builder)
2224
2416
  end
2225
2417
  end
2226
2418
  end
2227
2419
  end
2228
- elsif feed_type == "atom" && version == 0.3
2420
+ elsif feed_type == "atom" && feed_version == 0.3
2229
2421
  raise "Atom 0.3 is obsolete."
2230
- elsif feed_type == "atom" && version == 1.0
2422
+ elsif feed_type == "atom" && feed_version == 1.0
2231
2423
  # normal atom format
2232
2424
  return xml_builder.feed("xmlns" => FEED_TOOLS_NAMESPACES['atom10'],
2233
2425
  "xml:lang" => language) do
@@ -2248,18 +2440,18 @@ module FeedTools
2248
2440
  xml_builder.uri(self.author.url)
2249
2441
  end
2250
2442
  end
2251
- unless self.url.blank?
2252
- xml_builder.link("href" => self.url,
2443
+ unless self.href.blank?
2444
+ xml_builder.link("href" => self.href,
2253
2445
  "rel" => "self",
2254
2446
  "type" => "application/atom+xml")
2255
2447
  end
2256
2448
  unless self.link.blank?
2257
- xml_builder.link("href" => FeedTools.escape_entities(self.link),
2258
- "rel" => "alternate",
2259
- "type" => "text/html",
2260
- "title" => FeedTools.escape_entities(self.title))
2449
+ xml_builder.link(
2450
+ "href" =>
2451
+ FeedTools::HtmlHelper.escape_entities(self.link),
2452
+ "rel" => "alternate")
2261
2453
  end
2262
- unless description.blank?
2454
+ unless self.subtitle.blank?
2263
2455
  xml_builder.subtitle(self.subtitle,
2264
2456
  "type" => "html")
2265
2457
  end
@@ -2272,12 +2464,15 @@ module FeedTools
2272
2464
  else
2273
2465
  xml_builder.updated(Time.now.gmtime.iso8601)
2274
2466
  end
2467
+ unless self.rights.blank?
2468
+ xml_builder.rights(self.rights)
2469
+ end
2275
2470
  xml_builder.generator(FeedTools.configurations[:generator_name] +
2276
2471
  " - " + FeedTools.configurations[:generator_href])
2277
2472
  if self.id != nil
2278
- unless FeedTools.is_uri? self.id
2473
+ unless FeedTools::UriHelper.is_uri? self.id
2279
2474
  if self.link != nil
2280
- xml_builder.id(FeedTools.build_urn_uri(self.link))
2475
+ xml_builder.id(FeedTools::UriHelper.build_urn_uri(self.link))
2281
2476
  else
2282
2477
  raise "The unique id must be a valid URI."
2283
2478
  end
@@ -2285,14 +2480,14 @@ module FeedTools
2285
2480
  xml_builder.id(self.id)
2286
2481
  end
2287
2482
  elsif self.link != nil
2288
- xml_builder.id(FeedTools.build_urn_uri(self.link))
2483
+ xml_builder.id(FeedTools::UriHelper.build_urn_uri(self.link))
2289
2484
  else
2290
2485
  raise "Cannot build feed, missing feed unique id."
2291
2486
  end
2292
- build_xml_hook(feed_type, version, xml_builder)
2487
+ build_xml_hook(feed_type, feed_version, xml_builder)
2293
2488
  unless items.nil?
2294
2489
  for item in items
2295
- item.build_xml(feed_type, version, xml_builder)
2490
+ item.build_xml(feed_type, feed_version, xml_builder)
2296
2491
  end
2297
2492
  end
2298
2493
  end
@@ -2303,15 +2498,15 @@ module FeedTools
2303
2498
 
2304
2499
  # Persists the current feed state to the cache.
2305
2500
  def save
2306
- unless self.url =~ /^file:\/\//
2501
+ unless self.href =~ /^file:\/\//
2307
2502
  if FeedTools.feed_cache.nil?
2308
2503
  raise "Caching is currently disabled. Cannot save to cache."
2309
- elsif self.url.nil?
2504
+ elsif self.href.nil?
2310
2505
  raise "The url field must be set to save to the cache."
2311
2506
  elsif self.cache_object.nil?
2312
2507
  raise "The cache_object is currently nil. Cannot save to cache."
2313
2508
  else
2314
- self.cache_object.url = self.url
2509
+ self.cache_object.href = self.href
2315
2510
  unless self.feed_data.nil?
2316
2511
  self.cache_object.title = self.title
2317
2512
  self.cache_object.link = self.link
@@ -2324,15 +2519,17 @@ module FeedTools
2324
2519
  end
2325
2520
  end
2326
2521
  end
2327
-
2522
+
2523
+ alias_method :url, :href
2524
+ alias_method :url=, :href=
2328
2525
  alias_method :tagline, :subtitle
2329
2526
  alias_method :tagline=, :subtitle=
2330
2527
  alias_method :description, :subtitle
2331
2528
  alias_method :description=, :subtitle=
2332
2529
  alias_method :abstract, :subtitle
2333
2530
  alias_method :abstract=, :subtitle=
2334
- alias_method :content, :subtitle
2335
- alias_method :content=, :subtitle=
2531
+ alias_method :copyright, :rights
2532
+ alias_method :copyright=, :rights=
2336
2533
  alias_method :ttl, :time_to_live
2337
2534
  alias_method :ttl=, :time_to_live=
2338
2535
  alias_method :guid, :id
@@ -2362,7 +2559,12 @@ module FeedTools
2362
2559
 
2363
2560
  # Returns a simple representation of the feed object's state.
2364
2561
  def inspect
2365
- return "#<FeedTools::Feed:0x#{self.object_id.to_s(16)} URL:#{self.url}>"
2562
+ return "#<FeedTools::Feed:0x#{self.object_id.to_s(16)} URL:#{self.href}>"
2563
+ end
2564
+
2565
+ # Allows sorting feeds by title
2566
+ def <=>(other_feed)
2567
+ return self.title.to_s <=> other_feed.title.to_s
2366
2568
  end
2367
2569
  end
2368
2570
  end