4
0

atom_common.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package atom // import "miniflux.app/v2/internal/reader/atom"
  4. import (
  5. "strings"
  6. )
  7. // Specs: https://datatracker.ietf.org/doc/html/rfc4287#section-3.2
  8. type AtomPerson struct {
  9. // The "atom:name" element's content conveys a human-readable name for the author.
  10. // It MAY be the name of a corporation or other entity no individual authors can be named.
  11. // Person constructs MUST contain exactly one "atom:name" element, whose content MUST be a string.
  12. Name string `xml:"name"`
  13. // The "atom:email" element's content conveys an e-mail address associated with the Person construct.
  14. // Person constructs MAY contain an atom:email element, but MUST NOT contain more than one.
  15. // Its content MUST be an e-mail address [RFC2822].
  16. // Ordering of the element children of Person constructs MUST NOT be considered significant.
  17. Email string `xml:"email"`
  18. }
  19. func (a *AtomPerson) PersonName() string {
  20. name := strings.TrimSpace(a.Name)
  21. if name != "" {
  22. return name
  23. }
  24. return strings.TrimSpace(a.Email)
  25. }
  26. type atomPersons []*AtomPerson
  27. func (a atomPersons) personNames() []string {
  28. var names []string
  29. authorNamesMap := make(map[string]bool)
  30. for _, person := range a {
  31. personName := person.PersonName()
  32. if _, ok := authorNamesMap[personName]; !ok {
  33. names = append(names, personName)
  34. authorNamesMap[personName] = true
  35. }
  36. }
  37. return names
  38. }
  39. // Specs: https://datatracker.ietf.org/doc/html/rfc4287#section-4.2.7
  40. type AtomLink struct {
  41. Href string `xml:"href,attr"`
  42. Type string `xml:"type,attr"`
  43. Rel string `xml:"rel,attr"`
  44. Length string `xml:"length,attr"`
  45. Title string `xml:"title,attr"`
  46. }
  47. type atomLinks []*AtomLink
  48. func (a atomLinks) originalLink() string {
  49. for _, link := range a {
  50. if strings.EqualFold(link.Rel, "alternate") {
  51. return strings.TrimSpace(link.Href)
  52. }
  53. if link.Rel == "" && (link.Type == "" || link.Type == "text/html") {
  54. return strings.TrimSpace(link.Href)
  55. }
  56. }
  57. return ""
  58. }
  59. func (a atomLinks) firstLinkWithRelation(relation string) string {
  60. for _, link := range a {
  61. if strings.EqualFold(link.Rel, relation) {
  62. return strings.TrimSpace(link.Href)
  63. }
  64. }
  65. return ""
  66. }
  67. func (a atomLinks) firstLinkWithRelationAndType(relation string, contentTypes ...string) string {
  68. for _, link := range a {
  69. if strings.EqualFold(link.Rel, relation) {
  70. for _, contentType := range contentTypes {
  71. if strings.EqualFold(link.Type, contentType) {
  72. return strings.TrimSpace(link.Href)
  73. }
  74. }
  75. }
  76. }
  77. return ""
  78. }
  79. func (a atomLinks) findAllLinksWithRelation(relation string) []*AtomLink {
  80. var links []*AtomLink
  81. for _, link := range a {
  82. if strings.EqualFold(link.Rel, relation) {
  83. link.Href = strings.TrimSpace(link.Href)
  84. if link.Href != "" {
  85. links = append(links, link)
  86. }
  87. }
  88. }
  89. return links
  90. }
  91. // The "atom:category" element conveys information about a category
  92. // associated with an entry or feed. This specification assigns no
  93. // meaning to the content (if any) of this element.
  94. //
  95. // Specs: https://datatracker.ietf.org/doc/html/rfc4287#section-4.2.2
  96. type atomCategory struct {
  97. // The "term" attribute is a string that identifies the category to
  98. // which the entry or feed belongs. Category elements MUST have a
  99. // "term" attribute.
  100. Term string `xml:"term,attr"`
  101. // The "scheme" attribute is an IRI that identifies a categorization
  102. // scheme. Category elements MAY have a "scheme" attribute.
  103. Scheme string `xml:"scheme,attr"`
  104. // The "label" attribute provides a human-readable label for display in
  105. // end-user applications. The content of the "label" attribute is
  106. // Language-Sensitive. Entities such as "&" and "<" represent
  107. // their corresponding characters ("&" and "<", respectively), not
  108. // markup. Category elements MAY have a "label" attribute.
  109. Label string `xml:"label,attr"`
  110. }
  111. type atomCategories []atomCategory
  112. func (ac atomCategories) CategoryNames() []string {
  113. var categories []string
  114. for _, category := range ac {
  115. label := strings.TrimSpace(category.Label)
  116. if label != "" {
  117. categories = append(categories, label)
  118. } else {
  119. term := strings.TrimSpace(category.Term)
  120. if term != "" {
  121. categories = append(categories, term)
  122. }
  123. }
  124. }
  125. return categories
  126. }