feedtools 0.2.18 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +28 -0
- data/lib/feed_tools.rb +328 -63
- data/lib/feed_tools/feed.rb +767 -764
- data/lib/feed_tools/feed_item.rb +684 -625
- data/lib/feed_tools/helpers/debug_helper.rb +37 -0
- data/lib/feed_tools/helpers/feed_tools_helper.rb +45 -41
- data/lib/feed_tools/helpers/generic_helper.rb +164 -0
- data/lib/feed_tools/helpers/retrieval_helper.rb +36 -0
- data/rakefile +298 -2
- data/test/unit/amp_test.rb +70 -69
- data/test/unit/atom_test.rb +91 -9
- data/test/unit/cache_test.rb +30 -11
- data/test/unit/cdf_test.rb +6 -4
- data/test/unit/encoding_test.rb +99 -0
- data/test/unit/generation_test.rb +3 -40
- data/test/unit/helper_test.rb +66 -6
- data/test/unit/interface_test.rb +34 -0
- data/test/unit/itunes_test.rb +19 -0
- data/test/unit/nonstandard_test.rb +22 -4
- data/test/unit/rdf_test.rb +19 -0
- data/test/unit/rss_test.rb +137 -43
- metadata +18 -8
- data/lib/feed_tools/vendor/builder.rb +0 -15
- data/lib/feed_tools/vendor/builder/blankslate.rb +0 -55
- data/lib/feed_tools/vendor/builder/xmlbase.rb +0 -144
- data/lib/feed_tools/vendor/builder/xmlevents.rb +0 -65
- data/lib/feed_tools/vendor/builder/xmlmarkup.rb +0 -299
data/lib/feed_tools/feed_item.rb
CHANGED
@@ -165,14 +165,18 @@ module FeedTools
|
|
165
165
|
# Warning, this method may be slow if you have a
|
166
166
|
# large number of FeedTools::Feed objects. Can't
|
167
167
|
# use a direct reference to the parent because it plays
|
168
|
-
# havoc with the garbage collector.
|
168
|
+
# havoc with the garbage collector. Could've used
|
169
|
+
# a WeakRef object, but really, if there are multiple
|
170
|
+
# parent feeds, something is going to go wrong, and the
|
171
|
+
# programmer needs to be notified. A WeakRef
|
172
|
+
# implementation can't detect this condition.
|
169
173
|
def feed
|
170
174
|
parent_feed = nil
|
171
175
|
ObjectSpace.each_object(FeedTools::Feed) do |feed|
|
172
|
-
if feed.instance_variable_get("@
|
176
|
+
if feed.instance_variable_get("@entries").nil?
|
173
177
|
feed.items
|
174
178
|
end
|
175
|
-
unsorted_items = feed.instance_variable_get("@
|
179
|
+
unsorted_items = feed.instance_variable_get("@entries")
|
176
180
|
for item in unsorted_items
|
177
181
|
if item.object_id == self.object_id
|
178
182
|
if parent_feed.nil?
|
@@ -224,13 +228,21 @@ module FeedTools
|
|
224
228
|
end
|
225
229
|
|
226
230
|
# Returns the first node within the root_node that matches the xpath query.
|
227
|
-
def find_node(xpath)
|
228
|
-
|
231
|
+
def find_node(xpath, select_result_value=false)
|
232
|
+
if feed.feed_data_type != :xml
|
233
|
+
raise "The feed data type is not xml."
|
234
|
+
end
|
235
|
+
return try_xpaths(self.root_node, [xpath],
|
236
|
+
:select_result_value => select_result_value)
|
229
237
|
end
|
230
238
|
|
231
239
|
# Returns all nodes within the root_node that match the xpath query.
|
232
|
-
def find_all_nodes(xpath)
|
233
|
-
|
240
|
+
def find_all_nodes(xpath, select_result_value=false)
|
241
|
+
if feed.feed_data_type != :xml
|
242
|
+
raise "The feed data type is not xml."
|
243
|
+
end
|
244
|
+
return try_xpaths_all(self.root_node, [xpath],
|
245
|
+
:select_result_value => select_result_value)
|
234
246
|
end
|
235
247
|
|
236
248
|
# Returns the root node of the feed item.
|
@@ -247,13 +259,13 @@ module FeedTools
|
|
247
259
|
# Returns the feed items's unique id
|
248
260
|
def id
|
249
261
|
if @id.nil?
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
262
|
+
@id = try_xpaths(self.root_node, [
|
263
|
+
"atom10:id/text()",
|
264
|
+
"atom03:id/text()",
|
265
|
+
"atom:id/text()",
|
266
|
+
"id/text()",
|
267
|
+
"guid/text()"
|
268
|
+
], :select_result_value => true)
|
257
269
|
end
|
258
270
|
return @id
|
259
271
|
end
|
@@ -266,37 +278,23 @@ module FeedTools
|
|
266
278
|
# Returns the feed item title
|
267
279
|
def title
|
268
280
|
if @title.nil?
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
title_node = XPath.first(root_node, "atom03:title",
|
278
|
-
FEED_TOOLS_NAMESPACES)
|
279
|
-
end
|
280
|
-
if title_node.nil?
|
281
|
-
title_node = XPath.first(root_node, "atom:title")
|
282
|
-
end
|
283
|
-
if title_node.nil?
|
284
|
-
title_node = XPath.first(root_node, "dc:title",
|
285
|
-
FEED_TOOLS_NAMESPACES)
|
286
|
-
end
|
287
|
-
if title_node.nil?
|
288
|
-
title_node = XPath.first(root_node, "dc:title")
|
289
|
-
end
|
290
|
-
if title_node.nil?
|
291
|
-
title_node = XPath.first(root_node, "TITLE")
|
292
|
-
end
|
293
|
-
end
|
281
|
+
repair_entities = false
|
282
|
+
title_node = try_xpaths(self.root_node, [
|
283
|
+
"atom10:title",
|
284
|
+
"atom03:title",
|
285
|
+
"atom:title",
|
286
|
+
"title",
|
287
|
+
"dc:title"
|
288
|
+
])
|
294
289
|
if title_node.nil?
|
295
290
|
return nil
|
296
291
|
end
|
297
|
-
title_type =
|
298
|
-
|
299
|
-
|
292
|
+
title_type = try_xpaths(title_node, "@type",
|
293
|
+
:select_result_value => true)
|
294
|
+
title_mode = try_xpaths(title_node, "@mode",
|
295
|
+
:select_result_value => true)
|
296
|
+
title_encoding = try_xpaths(title_node, "@encoding",
|
297
|
+
:select_result_value => true)
|
300
298
|
|
301
299
|
# Note that we're checking for misuse of type, mode and encoding here
|
302
300
|
if title_type == "base64" || title_mode == "base64" ||
|
@@ -318,7 +316,7 @@ module FeedTools
|
|
318
316
|
@title = FeedTools.unescape_entities(@title) if repair_entities
|
319
317
|
@title = FeedTools.tidy_html(@title) unless repair_entities
|
320
318
|
end
|
321
|
-
if
|
319
|
+
if !@title.blank? && FeedTools.configurations[:strip_comment_count]
|
322
320
|
# Some blogging tools include the number of comments in a post
|
323
321
|
# in the title... this is supremely ugly, and breaks any
|
324
322
|
# applications which expect the title to be static, so we're
|
@@ -331,7 +329,7 @@ module FeedTools
|
|
331
329
|
@title.gsub!(/>\n</, "><")
|
332
330
|
@title.gsub!(/\n/, " ")
|
333
331
|
@title.strip!
|
334
|
-
@title = nil if @title
|
332
|
+
@title = nil if @title.blank?
|
335
333
|
end
|
336
334
|
return @title
|
337
335
|
end
|
@@ -341,123 +339,90 @@ module FeedTools
|
|
341
339
|
@title = new_title
|
342
340
|
end
|
343
341
|
|
344
|
-
# Returns the feed item
|
345
|
-
def
|
346
|
-
if @
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
description_node = XPath.first(root_node, "xhtml:body")
|
365
|
-
end
|
366
|
-
if description_node.nil?
|
367
|
-
description_node = XPath.first(root_node, "xhtml:body",
|
368
|
-
FEED_TOOLS_NAMESPACES)
|
369
|
-
end
|
370
|
-
if description_node.nil?
|
371
|
-
description_node = XPath.first(root_node, "body")
|
372
|
-
end
|
373
|
-
if description_node.nil?
|
374
|
-
description_node = XPath.first(root_node, "description")
|
375
|
-
end
|
376
|
-
if description_node.nil?
|
377
|
-
description_node = XPath.first(root_node, "tagline")
|
378
|
-
end
|
379
|
-
if description_node.nil?
|
380
|
-
description_node = XPath.first(root_node, "subtitle")
|
381
|
-
end
|
382
|
-
if description_node.nil?
|
383
|
-
description_node = XPath.first(root_node, "summary")
|
384
|
-
end
|
385
|
-
if description_node.nil?
|
386
|
-
description_node = XPath.first(root_node, "abstract")
|
387
|
-
end
|
388
|
-
if description_node.nil?
|
389
|
-
description_node = XPath.first(root_node, "ABSTRACT")
|
390
|
-
end
|
391
|
-
if description_node.nil?
|
392
|
-
description_node = XPath.first(root_node, "blurb")
|
393
|
-
end
|
394
|
-
if description_node.nil?
|
395
|
-
description_node = XPath.first(root_node, "info")
|
396
|
-
end
|
397
|
-
end
|
398
|
-
if description_node.nil?
|
342
|
+
# Returns the feed item content
|
343
|
+
def content
|
344
|
+
if @content.nil?
|
345
|
+
repair_entities = false
|
346
|
+
content_node = try_xpaths(self.root_node, [
|
347
|
+
"content:encoded",
|
348
|
+
"content",
|
349
|
+
"fullitem",
|
350
|
+
"xhtml:body",
|
351
|
+
"body",
|
352
|
+
"encoded",
|
353
|
+
"description",
|
354
|
+
"tagline",
|
355
|
+
"subtitle",
|
356
|
+
"summary",
|
357
|
+
"abstract",
|
358
|
+
"blurb",
|
359
|
+
"info"
|
360
|
+
])
|
361
|
+
if content_node.nil?
|
399
362
|
return nil
|
400
363
|
end
|
401
|
-
|
402
|
-
|
403
|
-
|
364
|
+
content_type = try_xpaths(content_node, "@type",
|
365
|
+
:select_result_value => true)
|
366
|
+
content_mode = try_xpaths(content_node, "@mode",
|
367
|
+
:select_result_value => true)
|
368
|
+
content_encoding = try_xpaths(content_node, "@encoding",
|
369
|
+
:select_result_value => true)
|
404
370
|
|
405
371
|
# Note that we're checking for misuse of type, mode and encoding here
|
406
|
-
if
|
407
|
-
@
|
372
|
+
if !content_encoding.blank?
|
373
|
+
@content =
|
408
374
|
"[Embedded data objects are not currently supported.]"
|
409
|
-
elsif
|
410
|
-
@
|
411
|
-
elsif
|
412
|
-
|
413
|
-
@
|
414
|
-
elsif
|
415
|
-
|
416
|
-
|
417
|
-
@
|
418
|
-
elsif
|
419
|
-
@
|
420
|
-
|
375
|
+
elsif content_node.cdatas.size > 0
|
376
|
+
@content = content_node.cdatas.first.value
|
377
|
+
elsif content_type == "base64" || content_mode == "base64" ||
|
378
|
+
content_encoding == "base64"
|
379
|
+
@content = Base64.decode64(content_node.inner_xml.strip)
|
380
|
+
elsif content_type == "xhtml" || content_mode == "xhtml" ||
|
381
|
+
content_type == "xml" || content_mode == "xml" ||
|
382
|
+
content_type == "application/xhtml+xml"
|
383
|
+
@content = content_node.inner_xml
|
384
|
+
elsif content_type == "escaped" || content_mode == "escaped"
|
385
|
+
@content = FeedTools.unescape_entities(
|
386
|
+
content_node.inner_xml)
|
421
387
|
else
|
422
|
-
@
|
388
|
+
@content = content_node.inner_xml
|
423
389
|
repair_entities = true
|
424
390
|
end
|
425
|
-
if @
|
426
|
-
@
|
427
|
-
@description = "" if @description.nil?
|
391
|
+
if @content.blank?
|
392
|
+
@content = self.itunes_summary
|
428
393
|
end
|
429
|
-
if @
|
430
|
-
@
|
431
|
-
@description = "" if @description.nil?
|
394
|
+
if @content.blank?
|
395
|
+
@content = self.itunes_subtitle
|
432
396
|
end
|
433
397
|
|
434
|
-
unless @
|
435
|
-
@
|
436
|
-
@
|
437
|
-
@
|
398
|
+
unless @content.blank?
|
399
|
+
@content = FeedTools.sanitize_html(@content, :strip)
|
400
|
+
@content = FeedTools.unescape_entities(@content) if repair_entities
|
401
|
+
@content = FeedTools.tidy_html(@content)
|
438
402
|
end
|
439
403
|
|
440
|
-
@
|
441
|
-
@
|
404
|
+
@content = @content.strip unless @content.nil?
|
405
|
+
@content = nil if @content.blank?
|
442
406
|
end
|
443
|
-
return @
|
407
|
+
return @content
|
444
408
|
end
|
445
409
|
|
446
|
-
# Sets the feed item
|
447
|
-
def
|
448
|
-
@
|
410
|
+
# Sets the feed item content
|
411
|
+
def content=(new_content)
|
412
|
+
@content = new_content
|
449
413
|
end
|
450
414
|
|
451
415
|
# Returns the contents of the itunes:summary element
|
452
416
|
def itunes_summary
|
453
417
|
if @itunes_summary.nil?
|
454
|
-
@itunes_summary =
|
455
|
-
"itunes:summary/text()"
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
unless @itunes_summary.nil?
|
418
|
+
@itunes_summary = try_xpaths(self.root_node, [
|
419
|
+
"itunes:summary/text()"
|
420
|
+
])
|
421
|
+
unless @itunes_summary.blank?
|
422
|
+
@itunes_summary = FeedTools.unescape_entities(@itunes_summary)
|
460
423
|
@itunes_summary = FeedTools.sanitize_html(@itunes_summary)
|
424
|
+
else
|
425
|
+
@itunes_summary = nil
|
461
426
|
end
|
462
427
|
end
|
463
428
|
return @itunes_summary
|
@@ -471,13 +436,14 @@ module FeedTools
|
|
471
436
|
# Returns the contents of the itunes:subtitle element
|
472
437
|
def itunes_subtitle
|
473
438
|
if @itunes_subtitle.nil?
|
474
|
-
@itunes_subtitle =
|
475
|
-
"itunes:subtitle/text()"
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
unless @itunes_subtitle.nil?
|
439
|
+
@itunes_subtitle = try_xpaths(self.root_node, [
|
440
|
+
"itunes:subtitle/text()"
|
441
|
+
])
|
442
|
+
unless @itunes_subtitle.blank?
|
443
|
+
@itunes_subtitle = FeedTools.unescape_entities(@itunes_subtitle)
|
480
444
|
@itunes_subtitle = FeedTools.sanitize_html(@itunes_subtitle)
|
445
|
+
else
|
446
|
+
@itunes_subtitle = nil
|
481
447
|
end
|
482
448
|
end
|
483
449
|
return @itunes_subtitle
|
@@ -511,39 +477,31 @@ module FeedTools
|
|
511
477
|
# Returns the feed item link
|
512
478
|
def link
|
513
479
|
if @link.nil?
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
if @link == ""
|
535
|
-
@link = XPath.first(root_node, "@HREF").to_s
|
536
|
-
end
|
537
|
-
if @link == ""
|
538
|
-
@link = XPath.first(root_node, "A/@HREF").to_s
|
539
|
-
end
|
540
|
-
end
|
541
|
-
if @link == "" || @link.nil?
|
480
|
+
@link = try_xpaths(self.root_node, [
|
481
|
+
"atom10:link[@type='application/xhtml+xml']/@href",
|
482
|
+
"atom10:link[@type='text/html']/@href",
|
483
|
+
"atom10:link[@rel='alternate']/@href",
|
484
|
+
"atom03:link[@type='application/xhtml+xml']/@href",
|
485
|
+
"atom03:link[@type='text/html']/@href",
|
486
|
+
"atom03:link[@rel='alternate']/@href",
|
487
|
+
"atom:link[@type='application/xhtml+xml']/@href",
|
488
|
+
"atom:link[@type='text/html']/@href",
|
489
|
+
"atom:link[@rel='alternate']/@href",
|
490
|
+
"link[@type='application/xhtml+xml']/@href",
|
491
|
+
"link[@type='text/html']/@href",
|
492
|
+
"link[@rel='alternate']/@href",
|
493
|
+
"link/text()",
|
494
|
+
"@rdf:about",
|
495
|
+
"guid[@isPermaLink='true']/text()",
|
496
|
+
"@href",
|
497
|
+
"a/@href"
|
498
|
+
], :select_result_value => true)
|
499
|
+
if @link.blank?
|
542
500
|
if FeedTools.is_uri? self.guid
|
543
501
|
@link = self.guid
|
544
502
|
end
|
545
503
|
end
|
546
|
-
if
|
504
|
+
if !@link.blank?
|
547
505
|
@link = FeedTools.unescape_entities(@link)
|
548
506
|
end
|
549
507
|
# TODO: Actually implement proper relative url resolving instead of this crap
|
@@ -556,7 +514,50 @@ module FeedTools
|
|
556
514
|
# # prepend the base to the link since they seem to have used a relative path
|
557
515
|
# @link = feed.base + @link
|
558
516
|
# end
|
559
|
-
@link
|
517
|
+
if @link.blank?
|
518
|
+
link_node = try_xpaths(self.root_node, [
|
519
|
+
"atom10:link",
|
520
|
+
"atom03:link",
|
521
|
+
"atom:link",
|
522
|
+
"link"
|
523
|
+
])
|
524
|
+
if link_node != nil
|
525
|
+
if link_node.attributes['type'].to_s =~ /^image/ ||
|
526
|
+
link_node.attributes['type'].to_s =~ /^application/ ||
|
527
|
+
link_node.attributes['type'].to_s =~ /xml/ ||
|
528
|
+
link_node.attributes['rel'].to_s =~ /self/
|
529
|
+
for child in self.root_node
|
530
|
+
if child.class == REXML::Element
|
531
|
+
if child.name.downcase == "link"
|
532
|
+
if child.attributes['type'].to_s =~ /^image/ ||
|
533
|
+
child.attributes['type'].to_s =~ /^application/ ||
|
534
|
+
child.attributes['type'].to_s =~ /xml/ ||
|
535
|
+
child.attributes['rel'].to_s =~ /self/
|
536
|
+
@link = nil
|
537
|
+
next
|
538
|
+
else
|
539
|
+
@link = child.attributes['href'].to_s
|
540
|
+
if @link.blank?
|
541
|
+
@link = child.inner_xml
|
542
|
+
end
|
543
|
+
if @link.blank?
|
544
|
+
next
|
545
|
+
end
|
546
|
+
break
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
else
|
552
|
+
@link = link_node.attributes['href'].to_s
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
@link = self.comments if @link.blank?
|
557
|
+
@link = nil if @link.blank?
|
558
|
+
if FeedTools.configurations[:url_normalization_enabled]
|
559
|
+
@link = FeedTools.normalize_url(@link)
|
560
|
+
end
|
560
561
|
end
|
561
562
|
return @link
|
562
563
|
end
|
@@ -570,30 +571,24 @@ module FeedTools
|
|
570
571
|
def categories
|
571
572
|
if @categories.nil?
|
572
573
|
@categories = []
|
573
|
-
category_nodes =
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
category.scheme = XPath.first(category_node, "@domain").to_s
|
592
|
-
end
|
593
|
-
category.scheme.strip! unless category.scheme.nil?
|
594
|
-
category.scheme = nil if category.scheme == ""
|
595
|
-
@categories << category
|
596
|
-
end
|
574
|
+
category_nodes = try_xpaths_all(self.root_node, [
|
575
|
+
"category",
|
576
|
+
"dc:subject"
|
577
|
+
])
|
578
|
+
for category_node in category_nodes
|
579
|
+
category = FeedTools::Feed::Category.new
|
580
|
+
category.term = try_xpaths(category_node, ["@term", "text()"],
|
581
|
+
:select_result_value => true)
|
582
|
+
category.term.strip! unless category.term.nil?
|
583
|
+
category.label = try_xpaths(category_node, ["@label"],
|
584
|
+
:select_result_value => true)
|
585
|
+
category.label.strip! unless category.label.nil?
|
586
|
+
category.scheme = try_xpaths(category_node, [
|
587
|
+
"@scheme",
|
588
|
+
"@domain"
|
589
|
+
], :select_result_value => true)
|
590
|
+
category.scheme.strip! unless category.scheme.nil?
|
591
|
+
@categories << category
|
597
592
|
end
|
598
593
|
end
|
599
594
|
return @categories
|
@@ -603,53 +598,61 @@ module FeedTools
|
|
603
598
|
def images
|
604
599
|
if @images.nil?
|
605
600
|
@images = []
|
606
|
-
image_nodes =
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
end
|
616
|
-
unless image_nodes.nil?
|
601
|
+
image_nodes = try_xpaths_all(self.root_node, [
|
602
|
+
"image",
|
603
|
+
"logo",
|
604
|
+
"atom10:link",
|
605
|
+
"atom03:link",
|
606
|
+
"atom:link",
|
607
|
+
"link"
|
608
|
+
])
|
609
|
+
unless image_nodes.blank?
|
617
610
|
for image_node in image_nodes
|
618
611
|
image = FeedTools::Feed::Image.new
|
619
|
-
image.url =
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
if image.url
|
624
|
-
(image_node.attributes['type'] =~ /^image/) == 0)
|
625
|
-
image.url =
|
612
|
+
image.url = try_xpaths(image_node, [
|
613
|
+
"url/text()",
|
614
|
+
"@rdf:resource"
|
615
|
+
], :select_result_value => true)
|
616
|
+
if image.url.blank? && (image_node.name == "logo" ||
|
617
|
+
(image_node.attributes['type'].to_s =~ /^image/) == 0)
|
618
|
+
image.url = try_xpaths(image_node, [
|
619
|
+
"@atom10:href",
|
620
|
+
"@atom03:href",
|
621
|
+
"@atom:href",
|
622
|
+
"@href"
|
623
|
+
], :select_result_value => true)
|
624
|
+
if image.url == self.link && image.url != nil
|
625
|
+
image.url = nil
|
626
|
+
end
|
626
627
|
end
|
627
|
-
if image.url
|
628
|
-
image.url =
|
628
|
+
if image.url.blank? && image_node.name == "LOGO"
|
629
|
+
image.url = try_xpaths(image_node, [
|
630
|
+
"@href"
|
631
|
+
], :select_result_value => true)
|
629
632
|
end
|
630
633
|
image.url.strip! unless image.url.nil?
|
631
|
-
image.
|
632
|
-
|
634
|
+
image.title = try_xpaths(image_node,
|
635
|
+
["title/text()"], :select_result_value => true)
|
633
636
|
image.title.strip! unless image.title.nil?
|
634
|
-
image.
|
635
|
-
|
636
|
-
XPath.first(image_node, "description/text()").to_s
|
637
|
+
image.description = try_xpaths(image_node,
|
638
|
+
["description/text()"], :select_result_value => true)
|
637
639
|
image.description.strip! unless image.description.nil?
|
638
|
-
image.
|
639
|
-
|
640
|
+
image.link = try_xpaths(image_node,
|
641
|
+
["link/text()"], :select_result_value => true)
|
640
642
|
image.link.strip! unless image.link.nil?
|
641
|
-
image.
|
642
|
-
|
643
|
+
image.height = try_xpaths(image_node,
|
644
|
+
["height/text()"], :select_result_value => true).to_i
|
643
645
|
image.height = nil if image.height <= 0
|
644
|
-
image.width =
|
646
|
+
image.width = try_xpaths(image_node,
|
647
|
+
["width/text()"], :select_result_value => true).to_i
|
645
648
|
image.width = nil if image.width <= 0
|
646
|
-
image.style =
|
647
|
-
|
648
|
-
|
649
|
-
|
649
|
+
image.style = try_xpaths(image_node, [
|
650
|
+
"style/text()",
|
651
|
+
"@style"
|
652
|
+
], :select_result_value => true)
|
650
653
|
image.style.strip! unless image.style.nil?
|
651
|
-
image.style
|
652
|
-
@images << image
|
654
|
+
image.style.downcase! unless image.style.nil?
|
655
|
+
@images << image unless image.url.nil?
|
653
656
|
end
|
654
657
|
end
|
655
658
|
end
|
@@ -657,19 +660,15 @@ module FeedTools
|
|
657
660
|
end
|
658
661
|
|
659
662
|
# Returns the feed item itunes image link
|
660
|
-
#
|
661
|
-
# If it's not present, falls back to the normal image link.
|
662
|
-
# Technically, the itunes spec says that the image needs to be
|
663
|
-
# square and larger than 300x300, but hey, if there's an image
|
664
|
-
# to be had, it's better than none at all.
|
665
663
|
def itunes_image_link
|
666
664
|
if @itunes_image_link.nil?
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
665
|
+
@itunes_image_link = try_xpaths(self.root_node, [
|
666
|
+
"itunes:image/@href",
|
667
|
+
"itunes:link[@rel='image']/@href"
|
668
|
+
], :select_result_value => true)
|
669
|
+
if FeedTools.configurations[:url_normalization_enabled]
|
670
|
+
@itunes_image_link = FeedTools.normalize_url(@itunes_image_link)
|
671
671
|
end
|
672
|
-
@itunes_image_link = FeedTools.normalize_url(@itunes_image_link)
|
673
672
|
end
|
674
673
|
return @itunes_image_link
|
675
674
|
end
|
@@ -680,13 +679,14 @@ module FeedTools
|
|
680
679
|
end
|
681
680
|
|
682
681
|
# Returns the feed item media thumbnail link
|
683
|
-
#
|
684
|
-
# If it's not present, falls back to the normal image link.
|
685
682
|
def media_thumbnail_link
|
686
683
|
if @media_thumbnail_link.nil?
|
687
|
-
|
688
|
-
|
689
|
-
|
684
|
+
@media_thumbnail_link = try_xpaths(self.root_node, [
|
685
|
+
"media:thumbnail/@url"
|
686
|
+
], :select_result_value => true)
|
687
|
+
if FeedTools.configurations[:url_normalization_enabled]
|
688
|
+
@media_thumbnail_link = FeedTools.normalize_url(@media_thumbnail_link)
|
689
|
+
end
|
690
690
|
end
|
691
691
|
return @media_thumbnail_link
|
692
692
|
end
|
@@ -699,44 +699,28 @@ module FeedTools
|
|
699
699
|
# Returns the feed item's copyright information
|
700
700
|
def copyright
|
701
701
|
if @copyright.nil?
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
FEED_TOOLS_NAMESPACES)
|
713
|
-
end
|
714
|
-
if copyright_node.nil?
|
715
|
-
copyright_node = XPath.first(root_node, "copyright",
|
716
|
-
FEED_TOOLS_NAMESPACES)
|
717
|
-
end
|
718
|
-
if copyright_node.nil?
|
719
|
-
copyright_node = XPath.first(root_node, "atom03:copyright",
|
720
|
-
FEED_TOOLS_NAMESPACES)
|
721
|
-
end
|
722
|
-
if copyright_node.nil?
|
723
|
-
copyright_node = XPath.first(root_node, "atom10:copyright",
|
724
|
-
FEED_TOOLS_NAMESPACES)
|
725
|
-
end
|
726
|
-
if copyright_node.nil?
|
727
|
-
copyright_node = XPath.first(root_node, "copyrights",
|
728
|
-
FEED_TOOLS_NAMESPACES)
|
729
|
-
end
|
730
|
-
end
|
702
|
+
repair_entities = false
|
703
|
+
copyright_node = try_xpaths(self.root_node, [
|
704
|
+
"atom10:copyright",
|
705
|
+
"atom03:copyright",
|
706
|
+
"atom:copyright",
|
707
|
+
"copyright",
|
708
|
+
"copyrights",
|
709
|
+
"dc:rights",
|
710
|
+
"rights"
|
711
|
+
])
|
731
712
|
if copyright_node.nil?
|
732
713
|
return nil
|
733
714
|
end
|
734
|
-
copyright_type =
|
735
|
-
|
736
|
-
|
715
|
+
copyright_type = try_xpaths(copyright_node, "@type",
|
716
|
+
:select_result_value => true)
|
717
|
+
copyright_mode = try_xpaths(copyright_node, "@mode",
|
718
|
+
:select_result_value => true)
|
719
|
+
copyright_encoding = try_xpaths(copyright_node, "@encoding",
|
720
|
+
:select_result_value => true)
|
737
721
|
|
738
722
|
# Note that we're checking for misuse of type, mode and encoding here
|
739
|
-
if copyright_encoding
|
723
|
+
if !copyright_encoding.blank?
|
740
724
|
@copyright =
|
741
725
|
"[Embedded data objects are not currently supported.]"
|
742
726
|
elsif copyright_node.cdatas.size > 0
|
@@ -763,7 +747,7 @@ module FeedTools
|
|
763
747
|
end
|
764
748
|
|
765
749
|
@copyright = @copyright.strip unless @copyright.nil?
|
766
|
-
@copyright = nil if @copyright
|
750
|
+
@copyright = nil if @copyright.blank?
|
767
751
|
end
|
768
752
|
return @copyright
|
769
753
|
end
|
@@ -779,16 +763,27 @@ module FeedTools
|
|
779
763
|
@enclosures = []
|
780
764
|
|
781
765
|
# First, load up all the different possible sources of enclosures
|
782
|
-
rss_enclosures =
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
766
|
+
rss_enclosures =
|
767
|
+
try_xpaths_all(self.root_node, ["enclosure"])
|
768
|
+
atom_enclosures =
|
769
|
+
try_xpaths_all(self.root_node, [
|
770
|
+
"atom10:link[@rel='enclosure']",
|
771
|
+
"atom03:link[@rel='enclosure']",
|
772
|
+
"atom:link[@rel='enclosure']",
|
773
|
+
"link[@rel='enclosure']"
|
774
|
+
])
|
775
|
+
media_content_enclosures =
|
776
|
+
try_xpaths_all(self.root_node, ["media:content"])
|
777
|
+
media_group_enclosures =
|
778
|
+
try_xpaths_all(self.root_node, ["media:group"])
|
779
|
+
|
780
|
+
# Parse RSS-type enclosures. Thanks to a few buggy enclosures
|
781
|
+
# implementations, sometimes these also manage to show up in atom
|
782
|
+
# files.
|
789
783
|
for enclosure_node in rss_enclosures
|
790
784
|
enclosure = Enclosure.new
|
791
|
-
enclosure.url = FeedTools.unescape_entities(
|
785
|
+
enclosure.url = FeedTools.unescape_entities(
|
786
|
+
enclosure_node.attributes["url"].to_s)
|
792
787
|
enclosure.type = enclosure_node.attributes["type"].to_s
|
793
788
|
enclosure.file_size = enclosure_node.attributes["length"].to_i
|
794
789
|
enclosure.credits = []
|
@@ -796,10 +791,11 @@ module FeedTools
|
|
796
791
|
@enclosures << enclosure
|
797
792
|
end
|
798
793
|
|
799
|
-
# Parse atom-type enclosures. If there are repeats of the same
|
800
|
-
# we merge the two together.
|
794
|
+
# Parse atom-type enclosures. If there are repeats of the same
|
795
|
+
# enclosure object, we merge the two together.
|
801
796
|
for enclosure_node in atom_enclosures
|
802
|
-
enclosure_url = FeedTools.unescape_entities(
|
797
|
+
enclosure_url = FeedTools.unescape_entities(
|
798
|
+
enclosure_node.attributes["href"].to_s)
|
803
799
|
enclosure = nil
|
804
800
|
new_enclosure = false
|
805
801
|
for existing_enclosure in @enclosures
|
@@ -822,13 +818,15 @@ module FeedTools
|
|
822
818
|
end
|
823
819
|
end
|
824
820
|
|
825
|
-
# Creates an anonymous method to parse content objects from the media
|
826
|
-
# do this to avoid excessive duplication of code since we
|
827
|
-
# processing for content objects within group
|
821
|
+
# Creates an anonymous method to parse content objects from the media
|
822
|
+
# module. We do this to avoid excessive duplication of code since we
|
823
|
+
# have to do identical processing for content objects within group
|
824
|
+
# objects.
|
828
825
|
parse_media_content = lambda do |media_content_nodes|
|
829
826
|
affected_enclosures = []
|
830
827
|
for enclosure_node in media_content_nodes
|
831
|
-
enclosure_url = FeedTools.unescape_entities(
|
828
|
+
enclosure_url = FeedTools.unescape_entities(
|
829
|
+
enclosure_node.attributes["url"].to_s)
|
832
830
|
enclosure = nil
|
833
831
|
new_enclosure = false
|
834
832
|
for existing_enclosure in @enclosures
|
@@ -849,73 +847,84 @@ module FeedTools
|
|
849
847
|
enclosure.width = enclosure_node.attributes["width"].to_i
|
850
848
|
enclosure.bitrate = enclosure_node.attributes["bitrate"].to_i
|
851
849
|
enclosure.framerate = enclosure_node.attributes["framerate"].to_i
|
852
|
-
enclosure.expression =
|
850
|
+
enclosure.expression =
|
851
|
+
enclosure_node.attributes["expression"].to_s
|
853
852
|
enclosure.is_default =
|
854
853
|
(enclosure_node.attributes["isDefault"].to_s.downcase == "true")
|
855
|
-
|
854
|
+
enclosure_thumbnail_url = try_xpaths(enclosure_node,
|
855
|
+
["media:thumbnail/@url"], :select_result_value => true)
|
856
|
+
if !enclosure_thumbnail_url.blank?
|
856
857
|
enclosure.thumbnail = EnclosureThumbnail.new(
|
857
|
-
FeedTools.unescape_entities(
|
858
|
-
FeedTools.unescape_entities(
|
859
|
-
|
858
|
+
FeedTools.unescape_entities(enclosure_thumbnail_url),
|
859
|
+
FeedTools.unescape_entities(
|
860
|
+
try_xpaths(enclosure_node, ["media:thumbnail/@height"],
|
861
|
+
:select_result_value => true)),
|
862
|
+
FeedTools.unescape_entities(
|
863
|
+
try_xpaths(enclosure_node, ["media:thumbnail/@width"],
|
864
|
+
:select_result_value => true))
|
860
865
|
)
|
861
|
-
if enclosure.thumbnail.height == ""
|
862
|
-
enclosure.thumbnail.height = nil
|
863
|
-
end
|
864
|
-
if enclosure.thumbnail.width == ""
|
865
|
-
enclosure.thumbnail.width = nil
|
866
|
-
end
|
867
866
|
end
|
868
867
|
enclosure.categories = []
|
869
|
-
for category in
|
868
|
+
for category in try_xpaths_all(enclosure_node, ["media:category"])
|
870
869
|
enclosure.categories << FeedTools::Feed::Category.new
|
871
870
|
enclosure.categories.last.term =
|
872
|
-
FeedTools.unescape_entities(category.
|
871
|
+
FeedTools.unescape_entities(category.inner_xml)
|
873
872
|
enclosure.categories.last.scheme =
|
874
|
-
FeedTools.unescape_entities(
|
873
|
+
FeedTools.unescape_entities(
|
874
|
+
category.attributes["scheme"].to_s)
|
875
875
|
enclosure.categories.last.label =
|
876
|
-
FeedTools.unescape_entities(
|
877
|
-
|
876
|
+
FeedTools.unescape_entities(
|
877
|
+
category.attributes["label"].to_s)
|
878
|
+
if enclosure.categories.last.scheme.blank?
|
878
879
|
enclosure.categories.last.scheme = nil
|
879
880
|
end
|
880
|
-
if enclosure.categories.last.label
|
881
|
+
if enclosure.categories.last.label.blank?
|
881
882
|
enclosure.categories.last.label = nil
|
882
883
|
end
|
883
884
|
end
|
884
|
-
|
885
|
+
enclosure_media_hash = try_xpaths(enclosure_node,
|
886
|
+
["media:hash/text()"], :select_result_value => true)
|
887
|
+
if !enclosure_media_hash.nil?
|
885
888
|
enclosure.hash = EnclosureHash.new(
|
886
|
-
FeedTools.sanitize_html(FeedTools.unescape_entities(
|
887
|
-
|
889
|
+
FeedTools.sanitize_html(FeedTools.unescape_entities(
|
890
|
+
enclosure_media_hash), :strip),
|
888
891
|
"md5"
|
889
892
|
)
|
890
893
|
end
|
891
|
-
|
894
|
+
enclosure_media_player_url = try_xpaths(enclosure_node,
|
895
|
+
["media:player/@url"], :select_result_value => true)
|
896
|
+
if !enclosure_media_player_url.blank?
|
892
897
|
enclosure.player = EnclosurePlayer.new(
|
893
|
-
FeedTools.unescape_entities(
|
894
|
-
FeedTools.unescape_entities(
|
895
|
-
|
898
|
+
FeedTools.unescape_entities(enclosure_media_player_url),
|
899
|
+
FeedTools.unescape_entities(
|
900
|
+
try_xpaths(enclosure_node,
|
901
|
+
["media:player/@height"], :select_result_value => true)),
|
902
|
+
FeedTools.unescape_entities(
|
903
|
+
try_xpaths(enclosure_node,
|
904
|
+
["media:player/@width"], :select_result_value => true))
|
896
905
|
)
|
897
|
-
if enclosure.player.height == ""
|
898
|
-
enclosure.player.height = nil
|
899
|
-
end
|
900
|
-
if enclosure.player.width == ""
|
901
|
-
enclosure.player.width = nil
|
902
|
-
end
|
903
906
|
end
|
904
907
|
enclosure.credits = []
|
905
|
-
for credit in
|
908
|
+
for credit in try_xpaths_all(enclosure_node, ["media:credit"])
|
906
909
|
enclosure.credits << EnclosureCredit.new(
|
907
|
-
FeedTools.unescape_entities(credit.
|
908
|
-
FeedTools.unescape_entities(
|
910
|
+
FeedTools.unescape_entities(credit.inner_xml.to_s.strip),
|
911
|
+
FeedTools.unescape_entities(
|
912
|
+
credit.attributes["role"].to_s.downcase)
|
909
913
|
)
|
910
|
-
if enclosure.credits.last.
|
914
|
+
if enclosure.credits.last.name.blank?
|
915
|
+
enclosure.credits.last.name = nil
|
916
|
+
end
|
917
|
+
if enclosure.credits.last.role.blank?
|
911
918
|
enclosure.credits.last.role = nil
|
912
919
|
end
|
913
920
|
end
|
914
|
-
enclosure.explicit = (
|
915
|
-
"media:adult/text()").to_s.downcase == "true")
|
916
|
-
|
917
|
-
|
918
|
-
|
921
|
+
enclosure.explicit = (try_xpaths(enclosure_node,
|
922
|
+
["media:adult/text()"]).to_s.downcase == "true")
|
923
|
+
enclosure_media_text =
|
924
|
+
try_xpaths(enclosure_node, ["media:text/text()"])
|
925
|
+
if !enclosure_media_text.blank?
|
926
|
+
enclosure.text = FeedTools.unescape_entities(
|
927
|
+
enclosure_media_text)
|
919
928
|
end
|
920
929
|
affected_enclosures << enclosure
|
921
930
|
if new_enclosure
|
@@ -933,7 +942,7 @@ module FeedTools
|
|
933
942
|
# Parse the group objects.
|
934
943
|
for media_group in media_group_enclosures
|
935
944
|
group_media_content_enclosures =
|
936
|
-
|
945
|
+
try_xpaths_all(media_group, ["media:content"])
|
937
946
|
|
938
947
|
# Parse the content objects within the group objects.
|
939
948
|
affected_enclosures =
|
@@ -942,82 +951,87 @@ module FeedTools
|
|
942
951
|
# Now make sure that content objects inherit certain properties from
|
943
952
|
# the group objects.
|
944
953
|
for enclosure in affected_enclosures
|
945
|
-
|
946
|
-
|
954
|
+
media_group_thumbnail = try_xpaths(media_group,
|
955
|
+
["media:thumbnail/@url"], :select_result_value => true)
|
956
|
+
if enclosure.thumbnail.nil? && !media_group_thumbnail.blank?
|
947
957
|
enclosure.thumbnail = EnclosureThumbnail.new(
|
948
958
|
FeedTools.unescape_entities(
|
949
|
-
|
959
|
+
media_group_thumbnail),
|
950
960
|
FeedTools.unescape_entities(
|
951
|
-
|
961
|
+
try_xpaths(media_group, ["media:thumbnail/@height"],
|
962
|
+
:select_result_value => true)),
|
952
963
|
FeedTools.unescape_entities(
|
953
|
-
|
964
|
+
try_xpaths(media_group, ["media:thumbnail/@width"],
|
965
|
+
:select_result_value => true))
|
954
966
|
)
|
955
|
-
if enclosure.thumbnail.height == ""
|
956
|
-
enclosure.thumbnail.height = nil
|
957
|
-
end
|
958
|
-
if enclosure.thumbnail.width == ""
|
959
|
-
enclosure.thumbnail.width = nil
|
960
|
-
end
|
961
967
|
end
|
962
|
-
if (enclosure.categories.
|
968
|
+
if (enclosure.categories.blank?)
|
963
969
|
enclosure.categories = []
|
964
|
-
for category in
|
970
|
+
for category in try_xpaths_all(media_group, ["media:category"])
|
965
971
|
enclosure.categories << FeedTools::Feed::Category.new
|
966
972
|
enclosure.categories.last.term =
|
967
|
-
FeedTools.unescape_entities(category.
|
973
|
+
FeedTools.unescape_entities(category.inner_xml)
|
968
974
|
enclosure.categories.last.scheme =
|
969
|
-
FeedTools.unescape_entities(
|
975
|
+
FeedTools.unescape_entities(
|
976
|
+
category.attributes["scheme"].to_s)
|
970
977
|
enclosure.categories.last.label =
|
971
|
-
FeedTools.unescape_entities(
|
972
|
-
|
978
|
+
FeedTools.unescape_entities(
|
979
|
+
category.attributes["label"].to_s)
|
980
|
+
if enclosure.categories.last.scheme.blank?
|
973
981
|
enclosure.categories.last.scheme = nil
|
974
982
|
end
|
975
|
-
if enclosure.categories.last.label
|
983
|
+
if enclosure.categories.last.label.blank?
|
976
984
|
enclosure.categories.last.label = nil
|
977
985
|
end
|
978
986
|
end
|
979
987
|
end
|
980
|
-
|
981
|
-
|
988
|
+
enclosure_media_group_hash = try_xpaths(enclosure_node,
|
989
|
+
["media:hash/text()"], :select_result_value => true)
|
990
|
+
if enclosure.hash.nil? && !enclosure_media_group_hash.blank?
|
982
991
|
enclosure.hash = EnclosureHash.new(
|
983
|
-
FeedTools.
|
992
|
+
FeedTools.sanitize_html(FeedTools.unescape_entities(
|
993
|
+
enclosure_media_group_hash), :strip),
|
984
994
|
"md5"
|
985
995
|
)
|
986
996
|
end
|
987
|
-
|
988
|
-
|
997
|
+
enclosure_media_group_url = try_xpaths(media_group,
|
998
|
+
"media:player/@url", :select_result_value => true)
|
999
|
+
if enclosure.player.nil? && !enclosure_media_group_url.blank?
|
989
1000
|
enclosure.player = EnclosurePlayer.new(
|
990
|
-
FeedTools.unescape_entities(
|
991
|
-
FeedTools.unescape_entities(
|
992
|
-
|
1001
|
+
FeedTools.unescape_entities(enclosure_media_group_url),
|
1002
|
+
FeedTools.unescape_entities(
|
1003
|
+
try_xpaths(media_group, ["media:player/@height"],
|
1004
|
+
:select_result_value => true)),
|
1005
|
+
FeedTools.unescape_entities(
|
1006
|
+
try_xpaths(media_group, ["media:player/@width"],
|
1007
|
+
:select_result_value => true))
|
993
1008
|
)
|
994
|
-
if enclosure.player.height == ""
|
995
|
-
enclosure.player.height = nil
|
996
|
-
end
|
997
|
-
if enclosure.player.width == ""
|
998
|
-
enclosure.player.width = nil
|
999
|
-
end
|
1000
1009
|
end
|
1001
1010
|
if enclosure.credits.nil? || enclosure.credits.size == 0
|
1002
1011
|
enclosure.credits = []
|
1003
|
-
for credit in
|
1012
|
+
for credit in try_xpaths_all(media_group, ["media:credit"])
|
1004
1013
|
enclosure.credits << EnclosureCredit.new(
|
1005
|
-
FeedTools.unescape_entities(credit.
|
1006
|
-
FeedTools.unescape_entities(
|
1014
|
+
FeedTools.unescape_entities(credit.inner_xml),
|
1015
|
+
FeedTools.unescape_entities(
|
1016
|
+
credit.attributes["role"].to_s.downcase)
|
1007
1017
|
)
|
1008
|
-
if enclosure.credits.last.role
|
1018
|
+
if enclosure.credits.last.role.blank?
|
1009
1019
|
enclosure.credits.last.role = nil
|
1010
1020
|
end
|
1011
1021
|
end
|
1012
1022
|
end
|
1013
1023
|
if enclosure.explicit?.nil?
|
1014
|
-
enclosure.explicit = (
|
1015
|
-
"media:adult/text()"
|
1024
|
+
enclosure.explicit = ((try_xpaths(media_group, [
|
1025
|
+
"media:adult/text()"
|
1026
|
+
], :select_result_value => true).downcase == "true") ?
|
1027
|
+
true : false)
|
1016
1028
|
end
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1029
|
+
enclosure_media_group_text = try_xpaths(media_group,
|
1030
|
+
["media:text/text()"], :select_result_value => true)
|
1031
|
+
if enclosure.text.nil? && !enclosure_media_group_text.blank?
|
1032
|
+
enclosure.text = FeedTools.sanitize_html(
|
1033
|
+
FeedTools.unescape_entities(
|
1034
|
+
enclosure_media_group_text), :strip)
|
1021
1035
|
end
|
1022
1036
|
end
|
1023
1037
|
|
@@ -1033,10 +1047,14 @@ module FeedTools
|
|
1033
1047
|
end
|
1034
1048
|
|
1035
1049
|
# Add all the itunes categories
|
1036
|
-
|
1050
|
+
itunes_categories =
|
1051
|
+
try_xpaths_all(self.root_node, ["itunes:category"])
|
1052
|
+
for itunes_category in itunes_categories
|
1037
1053
|
genre = "Podcasts"
|
1038
1054
|
category = itunes_category.attributes["text"].to_s
|
1039
|
-
subcategory =
|
1055
|
+
subcategory =
|
1056
|
+
try_xpaths(itunes_category, ["itunes:category/@text"],
|
1057
|
+
:select_result_value => true)
|
1040
1058
|
category_path = genre
|
1041
1059
|
if category != ""
|
1042
1060
|
category_path << "/" + category
|
@@ -1061,7 +1079,7 @@ module FeedTools
|
|
1061
1079
|
for enclosure in @enclosures
|
1062
1080
|
# Clean up any of those attributes that incorrectly have ""
|
1063
1081
|
# or 0 as their values
|
1064
|
-
if enclosure.type
|
1082
|
+
if enclosure.type.blank?
|
1065
1083
|
enclosure.type = nil
|
1066
1084
|
end
|
1067
1085
|
if enclosure.file_size == 0
|
@@ -1082,12 +1100,13 @@ module FeedTools
|
|
1082
1100
|
if enclosure.framerate == 0
|
1083
1101
|
enclosure.framerate = nil
|
1084
1102
|
end
|
1085
|
-
if enclosure.expression
|
1103
|
+
if enclosure.expression.blank?
|
1086
1104
|
enclosure.expression = "full"
|
1087
1105
|
end
|
1088
1106
|
|
1089
|
-
# If an enclosure is missing the text field, fall back on the
|
1090
|
-
|
1107
|
+
# If an enclosure is missing the text field, fall back on the
|
1108
|
+
# itunes:summary field
|
1109
|
+
if enclosure.text.blank?
|
1091
1110
|
enclosure.text = self.itunes_summary
|
1092
1111
|
end
|
1093
1112
|
|
@@ -1128,8 +1147,8 @@ module FeedTools
|
|
1128
1147
|
end
|
1129
1148
|
end
|
1130
1149
|
|
1131
|
-
# If we have a single enclosure, it's safe to inherit the
|
1132
|
-
# if it's missing.
|
1150
|
+
# If we have a single enclosure, it's safe to inherit the
|
1151
|
+
# itunes:duration field if it's missing.
|
1133
1152
|
if @enclosures.size == 1
|
1134
1153
|
if @enclosures.first.duration.nil? || @enclosures.first.duration == 0
|
1135
1154
|
@enclosures.first.duration = self.itunes_duration
|
@@ -1147,48 +1166,29 @@ module FeedTools
|
|
1147
1166
|
def author
|
1148
1167
|
if @author.nil?
|
1149
1168
|
@author = FeedTools::Feed::Author.new
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
if author_node.nil?
|
1161
|
-
author_node = XPath.first(root_node, "author")
|
1162
|
-
end
|
1163
|
-
if author_node.nil?
|
1164
|
-
author_node = XPath.first(root_node, "managingEditor")
|
1165
|
-
end
|
1166
|
-
if author_node.nil?
|
1167
|
-
author_node = XPath.first(root_node, "dc:author",
|
1168
|
-
FEED_TOOLS_NAMESPACES)
|
1169
|
-
end
|
1170
|
-
if author_node.nil?
|
1171
|
-
author_node = XPath.first(root_node, "dc:author")
|
1172
|
-
end
|
1173
|
-
if author_node.nil?
|
1174
|
-
author_node = XPath.first(root_node, "dc:creator",
|
1175
|
-
FEED_TOOLS_NAMESPACES)
|
1176
|
-
end
|
1177
|
-
if author_node.nil?
|
1178
|
-
author_node = XPath.first(root_node, "dc:creator")
|
1179
|
-
end
|
1180
|
-
end
|
1169
|
+
author_node = try_xpaths(self.root_node, [
|
1170
|
+
"atom10:author",
|
1171
|
+
"atom03:author",
|
1172
|
+
"atom:author",
|
1173
|
+
"author",
|
1174
|
+
"managingEditor",
|
1175
|
+
"dc:author",
|
1176
|
+
"dc:creator",
|
1177
|
+
"creator"
|
1178
|
+
])
|
1181
1179
|
unless author_node.nil?
|
1182
1180
|
@author.raw = FeedTools.unescape_entities(
|
1183
|
-
XPath.first(author_node, "text()").to_s)
|
1184
|
-
@author.raw = nil if @author.raw
|
1181
|
+
XPath.first(author_node, "text()").to_s).strip
|
1182
|
+
@author.raw = nil if @author.raw.blank?
|
1185
1183
|
unless @author.raw.nil?
|
1186
1184
|
raw_scan = @author.raw.scan(
|
1187
1185
|
/(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
|
1188
1186
|
if raw_scan.nil? || raw_scan.size == 0
|
1189
1187
|
raw_scan = @author.raw.scan(
|
1190
1188
|
/(\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\s*\((.*)\)/i)
|
1191
|
-
|
1189
|
+
unless raw_scan.size == 0
|
1190
|
+
author_raw_pair = raw_scan.first.reverse
|
1191
|
+
end
|
1192
1192
|
else
|
1193
1193
|
author_raw_pair = raw_scan.first
|
1194
1194
|
end
|
@@ -1205,42 +1205,44 @@ module FeedTools
|
|
1205
1205
|
else
|
1206
1206
|
unless @author.raw.include?("@")
|
1207
1207
|
# We can be reasonably sure we are looking at something
|
1208
|
-
# that the creator didn't intend to contain an email address
|
1209
|
-
# it got through the preceeding regexes and it doesn't
|
1208
|
+
# that the creator didn't intend to contain an email address
|
1209
|
+
# if it got through the preceeding regexes and it doesn't
|
1210
1210
|
# contain the tell-tale '@' symbol.
|
1211
1211
|
@author.name = @author.raw
|
1212
1212
|
end
|
1213
1213
|
end
|
1214
1214
|
end
|
1215
|
-
|
1216
|
-
if @author.name == ""
|
1215
|
+
if @author.name.blank?
|
1217
1216
|
@author.name = FeedTools.unescape_entities(
|
1218
|
-
|
1217
|
+
try_xpaths(author_node, [
|
1218
|
+
"name/text()",
|
1219
|
+
"@name"
|
1220
|
+
], :select_result_value => true)
|
1221
|
+
)
|
1219
1222
|
end
|
1220
|
-
if @author.
|
1221
|
-
@author.name = FeedTools.unescape_entities(
|
1222
|
-
XPath.first(author_node, "@name").to_s)
|
1223
|
-
end
|
1224
|
-
if @author.email == ""
|
1225
|
-
@author.email = FeedTools.unescape_entities(
|
1226
|
-
XPath.first(author_node, "email/text()").to_s)
|
1227
|
-
end
|
1228
|
-
if @author.email == ""
|
1223
|
+
if @author.email.blank?
|
1229
1224
|
@author.email = FeedTools.unescape_entities(
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1225
|
+
try_xpaths(author_node, [
|
1226
|
+
"email/text()",
|
1227
|
+
"@email"
|
1228
|
+
], :select_result_value => true)
|
1229
|
+
)
|
1235
1230
|
end
|
1236
|
-
if @author.url
|
1231
|
+
if @author.url.blank?
|
1237
1232
|
@author.url = FeedTools.unescape_entities(
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1233
|
+
try_xpaths(author_node, [
|
1234
|
+
"url/text()",
|
1235
|
+
"uri/text()",
|
1236
|
+
"@url",
|
1237
|
+
"@uri",
|
1238
|
+
"@href"
|
1239
|
+
], :select_result_value => true)
|
1240
|
+
)
|
1241
|
+
end
|
1242
|
+
@author.name = nil if @author.name.blank?
|
1243
|
+
@author.raw = nil if @author.raw.blank?
|
1244
|
+
@author.email = nil if @author.email.blank?
|
1245
|
+
@author.url = nil if @author.url.blank?
|
1244
1246
|
end
|
1245
1247
|
# Fallback on the itunes module if we didn't find an author name
|
1246
1248
|
begin
|
@@ -1276,12 +1278,11 @@ module FeedTools
|
|
1276
1278
|
|
1277
1279
|
# Set the author name
|
1278
1280
|
@publisher.raw = FeedTools.unescape_entities(
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
unless @publisher.raw == ""
|
1281
|
+
try_xpaths(self.root_node, [
|
1282
|
+
"dc:publisher/text()",
|
1283
|
+
"webMaster/text()"
|
1284
|
+
], :select_result_value => true))
|
1285
|
+
unless @publisher.raw.blank?
|
1285
1286
|
raw_scan = @publisher.raw.scan(
|
1286
1287
|
/(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
|
1287
1288
|
if raw_scan.nil? || raw_scan.size == 0
|
@@ -1314,10 +1315,10 @@ module FeedTools
|
|
1314
1315
|
end
|
1315
1316
|
end
|
1316
1317
|
|
1317
|
-
@publisher.name = nil if @publisher.name
|
1318
|
-
@publisher.raw = nil if @publisher.raw
|
1319
|
-
@publisher.email = nil if @publisher.email
|
1320
|
-
@publisher.url = nil if @publisher.url
|
1318
|
+
@publisher.name = nil if @publisher.name.blank?
|
1319
|
+
@publisher.raw = nil if @publisher.raw.blank?
|
1320
|
+
@publisher.email = nil if @publisher.email.blank?
|
1321
|
+
@publisher.url = nil if @publisher.url.blank?
|
1321
1322
|
end
|
1322
1323
|
return @publisher
|
1323
1324
|
end
|
@@ -1345,10 +1346,10 @@ module FeedTools
|
|
1345
1346
|
# elements. They're actually amazingly common. People don't read specs.
|
1346
1347
|
def itunes_author
|
1347
1348
|
if @itunes_author.nil?
|
1348
|
-
@itunes_author = FeedTools.unescape_entities(
|
1349
|
-
|
1350
|
-
|
1351
|
-
@itunes_author =
|
1349
|
+
@itunes_author = FeedTools.unescape_entities(
|
1350
|
+
try_xpaths(self.root_node,
|
1351
|
+
["itunes:author/text()"], :select_result_value => true))
|
1352
|
+
@itunes_author = feed.itunes_author if @itunes_author.blank?
|
1352
1353
|
end
|
1353
1354
|
return @itunes_author
|
1354
1355
|
end
|
@@ -1361,14 +1362,15 @@ module FeedTools
|
|
1361
1362
|
# Returns the number of seconds that the associated media runs for
|
1362
1363
|
def itunes_duration
|
1363
1364
|
if @itunes_duration.nil?
|
1364
|
-
raw_duration = FeedTools.unescape_entities(
|
1365
|
-
|
1366
|
-
|
1365
|
+
raw_duration = FeedTools.unescape_entities(
|
1366
|
+
try_xpaths(self.root_node,
|
1367
|
+
["itunes:duration/text()"], :select_result_value => true))
|
1368
|
+
if !raw_duration.blank?
|
1367
1369
|
hms = raw_duration.split(":").map { |x| x.to_i }
|
1368
1370
|
if hms.size == 3
|
1369
|
-
@itunes_duration = hms[0].
|
1371
|
+
@itunes_duration = hms[0].hours + hms[1].minutes + hms[2]
|
1370
1372
|
elsif hms.size == 2
|
1371
|
-
@itunes_duration = hms[0].
|
1373
|
+
@itunes_duration = hms[0].minutes + hms[1]
|
1372
1374
|
elsif hms.size == 1
|
1373
1375
|
@itunes_duration = hms[0]
|
1374
1376
|
end
|
@@ -1388,39 +1390,51 @@ module FeedTools
|
|
1388
1390
|
options.keys)
|
1389
1391
|
options = { :estimate_timestamp => true }.merge(options)
|
1390
1392
|
if @time.nil?
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1393
|
+
time_string = try_xpaths(self.root_node, [
|
1394
|
+
"atom10:updated/text()",
|
1395
|
+
"atom03:updated/text()",
|
1396
|
+
"atom:updated/text()",
|
1397
|
+
"updated/text()",
|
1398
|
+
"atom10:modified/text()",
|
1399
|
+
"atom03:modified/text()",
|
1400
|
+
"atom:modified/text()",
|
1401
|
+
"modified/text()",
|
1402
|
+
"time/text()",
|
1403
|
+
"atom10:issued/text()",
|
1404
|
+
"atom03:issued/text()",
|
1405
|
+
"atom:issued/text()",
|
1406
|
+
"issued/text()",
|
1407
|
+
"atom10:published/text()",
|
1408
|
+
"atom03:published/text()",
|
1409
|
+
"atom:published/text()",
|
1410
|
+
"published/text()",
|
1411
|
+
"pubDate/text()",
|
1412
|
+
"dc:date/text()",
|
1413
|
+
"date/text()"
|
1414
|
+
], :select_result_value => true)
|
1406
1415
|
begin
|
1407
|
-
|
1408
|
-
if time_string != ""
|
1416
|
+
if !time_string.blank?
|
1409
1417
|
@time = Time.parse(time_string).gmtime
|
1418
|
+
elsif FeedTools.configurations[:timestamp_estimation_enabled] &&
|
1419
|
+
!self.title.nil? &&
|
1420
|
+
(Time.parse(self.title) - Time.now).abs > 100
|
1421
|
+
@time = Time.parse(self.title).gmtime
|
1410
1422
|
end
|
1411
1423
|
rescue
|
1412
1424
|
end
|
1413
|
-
if
|
1414
|
-
if
|
1415
|
-
|
1416
|
-
|
1425
|
+
if FeedTools.configurations[:timestamp_estimation_enabled]
|
1426
|
+
if options[:estimate_timestamp]
|
1427
|
+
if @time.nil?
|
1428
|
+
begin
|
1429
|
+
@time = succ_time
|
1430
|
+
if @time.nil?
|
1431
|
+
@time = prev_time
|
1432
|
+
end
|
1433
|
+
rescue
|
1434
|
+
end
|
1417
1435
|
if @time.nil?
|
1418
|
-
@time =
|
1436
|
+
@time = Time.now.gmtime
|
1419
1437
|
end
|
1420
|
-
rescue
|
1421
|
-
end
|
1422
|
-
if @time.nil?
|
1423
|
-
@time = Time.now.gmtime
|
1424
1438
|
end
|
1425
1439
|
end
|
1426
1440
|
end
|
@@ -1440,10 +1454,10 @@ module FeedTools
|
|
1440
1454
|
if parent_feed.nil?
|
1441
1455
|
return nil
|
1442
1456
|
end
|
1443
|
-
if parent_feed.instance_variable_get("@
|
1457
|
+
if parent_feed.instance_variable_get("@entries").nil?
|
1444
1458
|
parent_feed.items
|
1445
1459
|
end
|
1446
|
-
unsorted_items = parent_feed.instance_variable_get("@
|
1460
|
+
unsorted_items = parent_feed.instance_variable_get("@entries")
|
1447
1461
|
item_index = unsorted_items.index(self)
|
1448
1462
|
if item_index.nil?
|
1449
1463
|
return nil
|
@@ -1457,7 +1471,7 @@ module FeedTools
|
|
1457
1471
|
return nil
|
1458
1472
|
end
|
1459
1473
|
end
|
1460
|
-
|
1474
|
+
private :succ_time
|
1461
1475
|
|
1462
1476
|
# Returns 1 second before the succeeding item's time.
|
1463
1477
|
def prev_time #:nodoc:
|
@@ -1466,10 +1480,10 @@ module FeedTools
|
|
1466
1480
|
if parent_feed.nil?
|
1467
1481
|
return nil
|
1468
1482
|
end
|
1469
|
-
if parent_feed.instance_variable_get("@
|
1483
|
+
if parent_feed.instance_variable_get("@entries").nil?
|
1470
1484
|
parent_feed.items
|
1471
1485
|
end
|
1472
|
-
unsorted_items = parent_feed.instance_variable_get("@
|
1486
|
+
unsorted_items = parent_feed.instance_variable_get("@entries")
|
1473
1487
|
item_index = unsorted_items.index(self)
|
1474
1488
|
if item_index.nil?
|
1475
1489
|
return nil
|
@@ -1483,18 +1497,22 @@ module FeedTools
|
|
1483
1497
|
return nil
|
1484
1498
|
end
|
1485
1499
|
end
|
1486
|
-
|
1487
|
-
|
1500
|
+
private :prev_time
|
1501
|
+
|
1488
1502
|
# Returns the feed item updated time
|
1489
1503
|
def updated
|
1490
1504
|
if @updated.nil?
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1505
|
+
updated_string = try_xpaths(self.root_node, [
|
1506
|
+
"atom10:updated/text()",
|
1507
|
+
"atom03:updated/text()",
|
1508
|
+
"atom:updated/text()",
|
1509
|
+
"updated/text()",
|
1510
|
+
"atom10:modified/text()",
|
1511
|
+
"atom03:modified/text()",
|
1512
|
+
"atom:modified/text()",
|
1513
|
+
"modified/text()"
|
1514
|
+
], :select_result_value => true)
|
1515
|
+
if !updated_string.blank?
|
1498
1516
|
@updated = Time.parse(updated_string).gmtime rescue nil
|
1499
1517
|
else
|
1500
1518
|
@updated = nil
|
@@ -1508,23 +1526,24 @@ module FeedTools
|
|
1508
1526
|
@updated = new_updated
|
1509
1527
|
end
|
1510
1528
|
|
1511
|
-
# Returns the feed item
|
1512
|
-
def
|
1513
|
-
if @
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1529
|
+
# Returns the feed item published time
|
1530
|
+
def published
|
1531
|
+
if @published.nil?
|
1532
|
+
published_string = try_xpaths(self.root_node, [
|
1533
|
+
"atom10:issued/text()",
|
1534
|
+
"atom03:issued/text()",
|
1535
|
+
"atom:issued/text()",
|
1536
|
+
"issued/text()",
|
1537
|
+
"atom10:published/text()",
|
1538
|
+
"atom03:published/text()",
|
1539
|
+
"atom:published/text()",
|
1540
|
+
"published/text()",
|
1541
|
+
"pubDate/text()",
|
1542
|
+
"dc:date/text()",
|
1543
|
+
"date/text()"
|
1544
|
+
], :select_result_value => true)
|
1545
|
+
if !published_string.blank?
|
1546
|
+
@issued = Time.parse(published_string).gmtime rescue nil
|
1528
1547
|
else
|
1529
1548
|
@issued = nil
|
1530
1549
|
end
|
@@ -1532,17 +1551,19 @@ module FeedTools
|
|
1532
1551
|
return @issued
|
1533
1552
|
end
|
1534
1553
|
|
1535
|
-
# Sets the feed item
|
1536
|
-
def
|
1537
|
-
@
|
1554
|
+
# Sets the feed item published time
|
1555
|
+
def published=(new_published)
|
1556
|
+
@published = new_published
|
1538
1557
|
end
|
1539
1558
|
|
1540
1559
|
# Returns the url for posting comments
|
1541
1560
|
def comments
|
1542
1561
|
if @comments.nil?
|
1543
|
-
@comments =
|
1544
|
-
|
1545
|
-
|
1562
|
+
@comments = try_xpaths(self.root_node, ["comments/text()"],
|
1563
|
+
:select_result_value => true)
|
1564
|
+
if FeedTools.configurations[:url_normalization_enabled]
|
1565
|
+
@comments = FeedTools.normalize_url(@comments)
|
1566
|
+
end
|
1546
1567
|
end
|
1547
1568
|
return @comments
|
1548
1569
|
end
|
@@ -1556,10 +1577,10 @@ module FeedTools
|
|
1556
1577
|
def source
|
1557
1578
|
if @source.nil?
|
1558
1579
|
@source = FeedTools::Feed::Link.new
|
1559
|
-
@source.url =
|
1560
|
-
|
1561
|
-
@source.value =
|
1562
|
-
|
1580
|
+
@source.url = try_xpaths(self.root_node, ["source/@url"],
|
1581
|
+
:select_result_value => true)
|
1582
|
+
@source.value = try_xpaths(self.root_node, ["source/text()"],
|
1583
|
+
:select_result_value => true)
|
1563
1584
|
end
|
1564
1585
|
return @source
|
1565
1586
|
end
|
@@ -1575,26 +1596,26 @@ module FeedTools
|
|
1575
1596
|
end
|
1576
1597
|
if @tags.nil? || @tags.size == 0
|
1577
1598
|
@tags = []
|
1578
|
-
tag_list =
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
end
|
1583
|
-
if tag_list != nil && tag_list.size > 1
|
1599
|
+
tag_list = try_xpaths_all(self.root_node,
|
1600
|
+
["dc:subject/rdf:Bag/rdf:li/text()"],
|
1601
|
+
:select_result_value => true)
|
1602
|
+
if tag_list != nil && tag_list.size > 0
|
1584
1603
|
for tag in tag_list
|
1585
|
-
@tags << tag.
|
1604
|
+
@tags << tag.downcase.strip
|
1586
1605
|
end
|
1587
1606
|
end
|
1588
1607
|
end
|
1589
1608
|
if @tags.nil? || @tags.size == 0
|
1590
1609
|
# messy effort to find ourselves some tags, mainly for del.icio.us
|
1591
1610
|
@tags = []
|
1592
|
-
rdf_bag =
|
1611
|
+
rdf_bag = try_xpaths_all(self.root_node,
|
1612
|
+
["taxo:topics/rdf:Bag/rdf:li"])
|
1593
1613
|
if rdf_bag != nil && rdf_bag.size > 0
|
1594
1614
|
for tag_node in rdf_bag
|
1595
1615
|
begin
|
1596
|
-
tag_url =
|
1597
|
-
|
1616
|
+
tag_url = try_xpaths(tag_node, ["@resource"],
|
1617
|
+
:select_result_value => true)
|
1618
|
+
tag_match = tag_url.scan(/\/(tag|tags)\/(\w+)$/)
|
1598
1619
|
if tag_match.size > 0
|
1599
1620
|
@tags << tag_match.first.last.downcase.strip
|
1600
1621
|
end
|
@@ -1605,21 +1626,36 @@ module FeedTools
|
|
1605
1626
|
end
|
1606
1627
|
if @tags.nil? || @tags.size == 0
|
1607
1628
|
@tags = []
|
1608
|
-
tag_list =
|
1629
|
+
tag_list = try_xpaths_all(self.root_node, ["category/text()"],
|
1630
|
+
:select_result_value => true)
|
1609
1631
|
for tag in tag_list
|
1610
1632
|
@tags << tag.to_s.downcase.strip
|
1611
1633
|
end
|
1612
1634
|
end
|
1613
1635
|
if @tags.nil? || @tags.size == 0
|
1614
1636
|
@tags = []
|
1615
|
-
tag_list =
|
1637
|
+
tag_list = try_xpaths_all(self.root_node, ["dc:subject/text()"],
|
1638
|
+
:select_result_value => true)
|
1616
1639
|
for tag in tag_list
|
1617
1640
|
@tags << tag.to_s.downcase.strip
|
1618
1641
|
end
|
1619
1642
|
end
|
1620
|
-
if @tags.
|
1643
|
+
if @tags.blank?
|
1621
1644
|
begin
|
1622
|
-
|
1645
|
+
itunes_keywords_string = try_xpaths(self.root_node, [
|
1646
|
+
"itunes:keywords/text()"
|
1647
|
+
], :select_result_value => true)
|
1648
|
+
unless itunes_keywords_string.blank?
|
1649
|
+
@tags = itunes_keywords_string.downcase.split(",")
|
1650
|
+
if @tags.size == 1
|
1651
|
+
@tags = itunes_keywords_string.downcase.split(" ")
|
1652
|
+
@tags = @tags.map { |tag| tag.chomp(",") }
|
1653
|
+
end
|
1654
|
+
if @tags.size == 1
|
1655
|
+
@tags = itunes_keywords_string.downcase.split(",")
|
1656
|
+
end
|
1657
|
+
@tags = @tags.map { |tag| tag.strip }
|
1658
|
+
end
|
1623
1659
|
rescue
|
1624
1660
|
@tags = []
|
1625
1661
|
end
|
@@ -1642,12 +1678,11 @@ module FeedTools
|
|
1642
1678
|
# isn't explicitly marked as explicit.
|
1643
1679
|
def explicit?
|
1644
1680
|
if @explicit.nil?
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
"itunes:explicit/text()").to_s.downcase == "true" ||
|
1681
|
+
explicit_string = try_xpaths(self.root_node, [
|
1682
|
+
"media:adult/text()",
|
1683
|
+
"itunes:explicit/text()"
|
1684
|
+
], :select_result_value => true)
|
1685
|
+
if explicit_string == "true" || explicit_string == "yes" ||
|
1651
1686
|
feed.explicit?
|
1652
1687
|
@explicit = true
|
1653
1688
|
else
|
@@ -1669,8 +1704,9 @@ module FeedTools
|
|
1669
1704
|
end
|
1670
1705
|
|
1671
1706
|
# Generates xml based on the content of the feed item
|
1672
|
-
def build_xml(feed_type=(self.feed.feed_type or "
|
1673
|
-
xml_builder=Builder::XmlMarkup.new(
|
1707
|
+
def build_xml(feed_type=(self.feed.feed_type or "atom"), version=nil,
|
1708
|
+
xml_builder=Builder::XmlMarkup.new(
|
1709
|
+
:indent => 2, :escape_attrs => false))
|
1674
1710
|
if feed_type == "rss" && (version == nil || version == 0.0)
|
1675
1711
|
version = 1.0
|
1676
1712
|
elsif feed_type == "atom" && (version == nil || version == 0.0)
|
@@ -1681,7 +1717,8 @@ module FeedTools
|
|
1681
1717
|
if link.nil?
|
1682
1718
|
raise "Cannot generate an rdf-based feed item with a nil link field."
|
1683
1719
|
end
|
1684
|
-
return xml_builder.item("rdf:about" =>
|
1720
|
+
return xml_builder.item("rdf:about" =>
|
1721
|
+
FeedTools.escape_entities(link)) do
|
1685
1722
|
unless title.nil? || title == ""
|
1686
1723
|
xml_builder.title(title)
|
1687
1724
|
else
|
@@ -1708,24 +1745,37 @@ module FeedTools
|
|
1708
1745
|
end
|
1709
1746
|
end
|
1710
1747
|
end
|
1711
|
-
|
1748
|
+
if self.feed.podcast?
|
1749
|
+
xml_builder.tag!("itunes:keywords", tags.join(", "))
|
1750
|
+
end
|
1712
1751
|
end
|
1713
1752
|
build_xml_hook(feed_type, version, xml_builder)
|
1714
1753
|
end
|
1715
1754
|
elsif feed_type == "rss"
|
1716
1755
|
# normal rss format
|
1717
1756
|
return xml_builder.item do
|
1718
|
-
unless title.
|
1719
|
-
xml_builder.title(title)
|
1757
|
+
unless self.title.blank?
|
1758
|
+
xml_builder.title(self.title)
|
1720
1759
|
end
|
1721
|
-
unless link.
|
1760
|
+
unless self.link.blank?
|
1722
1761
|
xml_builder.link(link)
|
1723
1762
|
end
|
1724
|
-
unless description.
|
1725
|
-
xml_builder.description(description)
|
1763
|
+
unless self.description.blank?
|
1764
|
+
xml_builder.description(self.description)
|
1726
1765
|
end
|
1727
|
-
unless time.nil?
|
1728
|
-
xml_builder.pubDate(time.rfc822)
|
1766
|
+
unless self.time.nil?
|
1767
|
+
xml_builder.pubDate(self.time.rfc822)
|
1768
|
+
end
|
1769
|
+
unless self.guid.blank?
|
1770
|
+
if FeedTools.is_uri?(self.guid)
|
1771
|
+
xml_builder.guid(self.guid, "isPermaLink" => "true")
|
1772
|
+
else
|
1773
|
+
xml_builder.guid(self.guid, "isPermaLink" => "false")
|
1774
|
+
end
|
1775
|
+
else
|
1776
|
+
unless self.link.blank?
|
1777
|
+
xml_builder.guid(self.link, "isPermaLink" => "true")
|
1778
|
+
end
|
1729
1779
|
end
|
1730
1780
|
unless tags.nil? || tags.size == 0
|
1731
1781
|
xml_builder.tag!("taxo:topics") do
|
@@ -1735,53 +1785,51 @@ module FeedTools
|
|
1735
1785
|
end
|
1736
1786
|
end
|
1737
1787
|
end
|
1738
|
-
|
1739
|
-
|
1740
|
-
build_xml_hook(feed_type, version, xml_builder)
|
1741
|
-
end
|
1742
|
-
elsif feed_type == "atom" && version == 0.3
|
1743
|
-
# normal atom format
|
1744
|
-
return xml_builder.entry("xmlns" =>
|
1745
|
-
FEED_TOOLS_NAMESPACES['atom03']) do
|
1746
|
-
unless title.nil? || title == ""
|
1747
|
-
xml_builder.title(title,
|
1748
|
-
"mode" => "escaped",
|
1749
|
-
"type" => "text/html")
|
1750
|
-
end
|
1751
|
-
xml_builder.author do
|
1752
|
-
unless self.author.nil? || self.author.name.nil?
|
1753
|
-
xml_builder.name(self.author.name)
|
1754
|
-
else
|
1755
|
-
xml_builder.name("n/a")
|
1756
|
-
end
|
1757
|
-
unless self.author.nil? || self.author.email.nil?
|
1758
|
-
xml_builder.email(self.author.email)
|
1759
|
-
end
|
1760
|
-
unless self.author.nil? || self.author.url.nil?
|
1761
|
-
xml_builder.url(self.author.url)
|
1788
|
+
if self.feed.podcast?
|
1789
|
+
xml_builder.tag!("itunes:keywords", tags.join(", "))
|
1762
1790
|
end
|
1763
1791
|
end
|
1764
|
-
unless
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1792
|
+
unless self.enclosures.blank? || self.enclosures.size == 0
|
1793
|
+
for enclosure in self.enclosures
|
1794
|
+
attribute_hash = {}
|
1795
|
+
next if enclosure.url.blank?
|
1796
|
+
begin
|
1797
|
+
if enclosure.file_size.blank? || enclosure.file_size.to_i == 0
|
1798
|
+
# We can't use this enclosure because it's missing the
|
1799
|
+
# required file size. Check alternate versions for
|
1800
|
+
# file_size.
|
1801
|
+
if !enclosure.versions.blank? && enclosure.versions.size > 0
|
1802
|
+
for alternate in enclosure.versions
|
1803
|
+
if alternate.file_size != nil &&
|
1804
|
+
alternate.file_size.to_i > 0
|
1805
|
+
enclosure = alternate
|
1806
|
+
break
|
1807
|
+
end
|
1808
|
+
end
|
1809
|
+
end
|
1810
|
+
end
|
1811
|
+
rescue
|
1812
|
+
end
|
1813
|
+
attribute_hash["url"] = FeedTools.normalize_url(enclosure.url)
|
1814
|
+
if enclosure.type != nil
|
1815
|
+
attribute_hash["type"] = enclosure.type
|
1816
|
+
end
|
1817
|
+
if enclosure.file_size != nil && enclosure.file_size.to_i > 0
|
1818
|
+
attribute_hash["length"] = enclosure.file_size.to_s
|
1819
|
+
else
|
1820
|
+
# We couldn't find an alternate and the problem is still
|
1821
|
+
# there. Give up and go on.
|
1822
|
+
xml_builder.comment!(
|
1823
|
+
"*** Enclosure failed to include file size. Ignoring. ***")
|
1824
|
+
next
|
1825
|
+
end
|
1826
|
+
xml_builder.enclosure(attribute_hash)
|
1781
1827
|
end
|
1782
1828
|
end
|
1783
1829
|
build_xml_hook(feed_type, version, xml_builder)
|
1784
1830
|
end
|
1831
|
+
elsif feed_type == "atom" && version == 0.3
|
1832
|
+
raise "Atom 0.3 is obsolete."
|
1785
1833
|
elsif feed_type == "atom" && version == 1.0
|
1786
1834
|
# normal atom format
|
1787
1835
|
return xml_builder.entry("xmlns" =>
|
@@ -1800,20 +1848,20 @@ module FeedTools
|
|
1800
1848
|
xml_builder.email(self.author.email)
|
1801
1849
|
end
|
1802
1850
|
unless self.author.nil? || self.author.url.nil?
|
1803
|
-
xml_builder.
|
1851
|
+
xml_builder.uri(self.author.url)
|
1804
1852
|
end
|
1805
1853
|
end
|
1806
1854
|
unless link.nil? || link == ""
|
1807
|
-
xml_builder.link("href" => link,
|
1855
|
+
xml_builder.link("href" => FeedTools.escape_entities(self.link),
|
1808
1856
|
"rel" => "alternate",
|
1809
1857
|
"type" => "text/html",
|
1810
|
-
"title" => title)
|
1858
|
+
"title" => FeedTools.escape_entities(title))
|
1811
1859
|
end
|
1812
|
-
|
1860
|
+
if !description.blank?
|
1813
1861
|
xml_builder.content(description,
|
1814
1862
|
"type" => "html")
|
1815
|
-
|
1816
|
-
xml_builder.content(FeedTools.no_content_string,
|
1863
|
+
elsif !FeedTools.configurations[:no_content_string].blank?
|
1864
|
+
xml_builder.content(FeedTools.configurations[:no_content_string],
|
1817
1865
|
"type" => "html")
|
1818
1866
|
end
|
1819
1867
|
if self.updated != nil
|
@@ -1851,25 +1899,36 @@ module FeedTools
|
|
1851
1899
|
xml_builder.category("term" => tag)
|
1852
1900
|
end
|
1853
1901
|
end
|
1902
|
+
unless self.enclosures.blank? || self.enclosures.size == 0
|
1903
|
+
for enclosure in self.enclosures
|
1904
|
+
attribute_hash = {}
|
1905
|
+
next if enclosure.url.blank?
|
1906
|
+
attribute_hash["rel"] = "enclosure"
|
1907
|
+
attribute_hash["href"] = FeedTools.normalize_url(enclosure.url)
|
1908
|
+
if enclosure.type != nil
|
1909
|
+
attribute_hash["type"] = enclosure.type
|
1910
|
+
end
|
1911
|
+
if enclosure.file_size != nil && enclosure.file_size.to_i > 0
|
1912
|
+
attribute_hash["length"] = enclosure.file_size.to_s
|
1913
|
+
end
|
1914
|
+
xml_builder.link(attribute_hash)
|
1915
|
+
end
|
1916
|
+
end
|
1854
1917
|
build_xml_hook(feed_type, version, xml_builder)
|
1855
1918
|
end
|
1919
|
+
else
|
1920
|
+
raise "Unsupported feed format/version."
|
1856
1921
|
end
|
1857
1922
|
end
|
1858
1923
|
|
1859
|
-
alias_method :
|
1860
|
-
alias_method :
|
1861
|
-
alias_method :
|
1862
|
-
alias_method :
|
1863
|
-
alias_method :
|
1864
|
-
alias_method :
|
1865
|
-
alias_method :abstract, :description
|
1866
|
-
alias_method :abstract=, :description=
|
1867
|
-
alias_method :content, :description
|
1868
|
-
alias_method :content=, :description=
|
1924
|
+
alias_method :summary, :content
|
1925
|
+
alias_method :summary=, :content=
|
1926
|
+
alias_method :abstract, :content
|
1927
|
+
alias_method :abstract=, :content=
|
1928
|
+
alias_method :description, :content
|
1929
|
+
alias_method :description=, :content=
|
1869
1930
|
alias_method :guid, :id
|
1870
1931
|
alias_method :guid=, :id=
|
1871
|
-
alias_method :published, :issued
|
1872
|
-
alias_method :published=, :issued=
|
1873
1932
|
|
1874
1933
|
# Returns a simple representation of the feed item object's state.
|
1875
1934
|
def inspect
|