pages_core 3.15.3 → 3.15.5

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.
Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +1 -1
  4. data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
  5. data/app/assets/builds/pages_core/admin.css +378 -253
  6. data/app/assets/builds/pages_core/mailer.css +41 -6
  7. data/app/assets/builds/pages_core_fonts/121b837e.woff2 +0 -0
  8. data/app/assets/builds/pages_core_fonts/216e5c23.woff2 +0 -0
  9. data/app/assets/builds/pages_core_fonts/3017b52f.woff2 +0 -0
  10. data/app/assets/builds/pages_core_fonts/489746b9.woff2 +0 -0
  11. data/app/assets/builds/pages_core_fonts/49775483.woff2 +0 -0
  12. data/app/assets/builds/pages_core_fonts/49c9e472.woff2 +0 -0
  13. data/app/assets/builds/pages_core_fonts/4a119645.woff2 +0 -0
  14. data/app/assets/builds/pages_core_fonts/5d56d7a8.woff2 +0 -0
  15. data/app/assets/builds/pages_core_fonts/61ea75a6.woff2 +0 -0
  16. data/app/assets/builds/pages_core_fonts/62cbb778.woff2 +0 -0
  17. data/app/assets/builds/pages_core_fonts/647d26c.woff2 +0 -0
  18. data/app/assets/builds/pages_core_fonts/67764053.woff2 +0 -0
  19. data/app/assets/builds/pages_core_fonts/6bb0fd00.woff2 +0 -0
  20. data/app/assets/builds/pages_core_fonts/6c0194a2.woff2 +0 -0
  21. data/app/assets/builds/pages_core_fonts/71423409.woff2 +0 -0
  22. data/app/assets/builds/pages_core_fonts/7584e61d.woff2 +0 -0
  23. data/app/assets/builds/pages_core_fonts/77bcfa1c.woff2 +0 -0
  24. data/app/assets/builds/pages_core_fonts/7aca0cc5.woff2 +0 -0
  25. data/app/assets/builds/pages_core_fonts/9a09533f.woff2 +0 -0
  26. data/app/assets/builds/pages_core_fonts/a51f5bc8.woff2 +0 -0
  27. data/app/assets/builds/pages_core_fonts/a80b2975.woff2 +0 -0
  28. data/app/assets/builds/pages_core_fonts/a891f617.woff2 +0 -0
  29. data/app/assets/builds/pages_core_fonts/ad6083f3.woff2 +0 -0
  30. data/app/assets/builds/pages_core_fonts/b29a61ff.woff2 +0 -0
  31. data/app/assets/builds/{fonts/6569749d.ttf → pages_core_fonts/b30b0656.ttf} +0 -0
  32. data/app/assets/builds/pages_core_fonts/b3a5f48c.woff2 +0 -0
  33. data/app/assets/builds/pages_core_fonts/bc73ee06.woff2 +0 -0
  34. data/app/assets/builds/pages_core_fonts/c38c6d45.woff2 +0 -0
  35. data/app/assets/builds/pages_core_fonts/c5ce0b1f.woff2 +0 -0
  36. data/app/assets/builds/pages_core_fonts/c8d53904.woff2 +0 -0
  37. data/app/assets/builds/pages_core_fonts/ce13c169.woff2 +0 -0
  38. data/app/assets/builds/pages_core_fonts/d43bd0d5.woff2 +0 -0
  39. data/app/assets/builds/pages_core_fonts/e1c7d368.woff2 +0 -0
  40. data/app/assets/builds/pages_core_fonts/e1e8175d.woff2 +0 -0
  41. data/app/assets/builds/pages_core_fonts/e318f796.woff2 +0 -0
  42. data/app/assets/builds/{fonts/ee32bc60.ttf → pages_core_fonts/e7acb7d9.ttf} +0 -0
  43. data/app/assets/builds/pages_core_fonts/ee5514c6.woff2 +0 -0
  44. data/app/assets/builds/pages_core_fonts/f4e495e2.woff2 +0 -0
  45. data/app/assets/builds/pages_core_fonts/f736ec65.woff2 +0 -0
  46. data/app/assets/builds/pages_core_fonts/f741c7ba.woff2 +0 -0
  47. data/app/assets/builds/pages_core_fonts/f7767345.woff2 +0 -0
  48. data/app/assets/builds/pages_core_fonts/fe9eb751.woff2 +0 -0
  49. data/app/assets/stylesheets/pages_core/admin/components/forms.css +2 -2
  50. data/app/assets/stylesheets/pages_core/admin/components/header.css +1 -1
  51. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +1 -1
  52. data/app/assets/stylesheets/pages_core/admin/global/fonts.css +38 -38
  53. data/app/controllers/{pages_core → admin}/admin_controller.rb +1 -3
  54. data/app/controllers/attachments_controller.rb +40 -0
  55. data/app/controllers/concerns/pages_core/document_title_controller.rb +16 -0
  56. data/app/controllers/concerns/pages_core/page_parameters.rb +1 -1
  57. data/app/controllers/concerns/pages_core/pages/preview_controller.rb +49 -0
  58. data/app/controllers/concerns/pages_core/pages/rss_controller.rb +43 -0
  59. data/app/controllers/errors_controller.rb +2 -0
  60. data/app/controllers/images_controller.rb +13 -0
  61. data/app/controllers/pages_core/frontend/pages_controller.rb +3 -4
  62. data/app/controllers/pages_core/frontend_controller.rb +6 -1
  63. data/app/controllers/pages_core/sitemaps_controller.rb +21 -52
  64. data/app/helpers/pages_core/admin/image_uploads_helper.rb +1 -1
  65. data/app/helpers/pages_core/application_helper.rb +0 -3
  66. data/app/helpers/pages_core/attachments_helper.rb +0 -10
  67. data/app/helpers/pages_core/feed_tags_helper.rb +31 -0
  68. data/app/helpers/pages_core/frontend_helper.rb +3 -0
  69. data/app/helpers/pages_core/head_tags_helper.rb +80 -70
  70. data/app/helpers/pages_core/page_path_helper.rb +1 -12
  71. data/app/javascript/components/Attachments/Attachment.tsx +3 -3
  72. data/app/javascript/components/Attachments/AttachmentEditor.tsx +5 -5
  73. data/app/javascript/components/Attachments/Deleted.tsx +28 -0
  74. data/app/javascript/components/Attachments/List.tsx +11 -24
  75. data/app/javascript/components/Attachments/Placeholder.tsx +0 -2
  76. data/app/javascript/components/Attachments.tsx +2 -3
  77. data/app/javascript/components/DateRangeSelect.tsx +13 -10
  78. data/app/javascript/components/DateTimeSelect.tsx +11 -11
  79. data/app/javascript/components/EditableImage.tsx +3 -3
  80. data/app/javascript/components/FileUploadButton.tsx +3 -3
  81. data/app/javascript/components/ImageCropper/FocalPoint.tsx +10 -14
  82. data/app/javascript/components/ImageCropper/Image.tsx +19 -25
  83. data/app/javascript/components/ImageCropper/Toolbar.tsx +27 -26
  84. data/app/javascript/components/ImageCropper/useContainerSize.ts +25 -0
  85. data/app/javascript/components/ImageCropper/useCrop.ts +28 -13
  86. data/app/javascript/components/ImageCropper/useImageCropperContext.ts +13 -0
  87. data/app/javascript/components/ImageCropper.tsx +24 -83
  88. data/app/javascript/components/ImageEditor/Form.tsx +25 -28
  89. data/app/javascript/components/ImageEditor/useImageEditor.ts +63 -0
  90. data/app/javascript/components/ImageEditor/useImageEditorContext.ts +14 -0
  91. data/app/javascript/components/ImageEditor.tsx +28 -42
  92. data/app/javascript/components/ImageGrid/Deleted.tsx +28 -0
  93. data/app/javascript/components/ImageGrid/DragElement.tsx +5 -5
  94. data/app/javascript/components/ImageGrid/FilePlaceholder.tsx +0 -2
  95. data/app/javascript/components/ImageGrid/Grid.tsx +15 -24
  96. data/app/javascript/components/ImageGrid/GridImage.tsx +4 -4
  97. data/app/javascript/components/ImageGrid/Placeholder.tsx +2 -4
  98. data/app/javascript/components/ImageGrid.tsx +2 -4
  99. data/app/javascript/components/ImageUploader.tsx +5 -5
  100. data/app/javascript/components/LabelledField.tsx +6 -6
  101. data/app/javascript/components/Modal.tsx +16 -13
  102. data/app/javascript/components/PageForm/Block.tsx +3 -3
  103. data/app/javascript/components/PageForm/Content.tsx +11 -15
  104. data/app/javascript/components/PageForm/Dates.tsx +3 -11
  105. data/app/javascript/components/PageForm/Files.tsx +2 -4
  106. data/app/javascript/components/PageForm/Form.tsx +3 -9
  107. data/app/javascript/components/PageForm/Images.tsx +2 -4
  108. data/app/javascript/components/PageForm/LocaleLinks.tsx +4 -11
  109. data/app/javascript/components/PageForm/Metadata.tsx +8 -13
  110. data/app/javascript/components/PageForm/Options.tsx +28 -11
  111. data/app/javascript/components/PageForm/PageDescription.tsx +7 -14
  112. data/app/javascript/components/PageForm/PathSegment.tsx +5 -10
  113. data/app/javascript/components/PageForm/TabPanel.tsx +3 -6
  114. data/app/javascript/components/PageForm/Tabs.tsx +2 -4
  115. data/app/javascript/components/PageForm/UnconfiguredContent.tsx +7 -12
  116. data/app/javascript/components/PageForm/pageParams.ts +3 -2
  117. data/app/javascript/components/PageForm/usePage.ts +1 -46
  118. data/app/javascript/components/PageForm/usePageFormContext.ts +8 -0
  119. data/app/javascript/components/PageForm/useTabs.ts +1 -1
  120. data/app/javascript/components/PageForm/utils.ts +49 -0
  121. data/app/javascript/components/PageForm.tsx +52 -48
  122. data/app/javascript/components/PageImages.tsx +1 -3
  123. data/app/javascript/components/PageTree/Button.tsx +25 -0
  124. data/app/javascript/components/PageTree/CollapseArrow.tsx +34 -0
  125. data/app/javascript/components/PageTree/CollapsedLabel.tsx +21 -0
  126. data/app/javascript/components/PageTree/EditPageName.tsx +68 -0
  127. data/app/javascript/components/PageTree/Node.tsx +143 -413
  128. data/app/javascript/components/PageTree/PageName.tsx +6 -4
  129. data/app/javascript/components/PageTree/StatusLabel.tsx +10 -0
  130. data/app/javascript/components/PageTree/tree.ts +268 -0
  131. data/app/javascript/components/PageTree/usePageTree.ts +268 -0
  132. data/app/javascript/components/PageTree/usePageTreeContext.ts +13 -0
  133. data/app/javascript/components/PageTree.tsx +194 -214
  134. data/app/javascript/components/{RichTextToolbarButton.tsx → RichTextArea/ToolbarButton.tsx} +3 -5
  135. data/app/javascript/components/RichTextArea/actions.ts +106 -0
  136. data/app/javascript/components/RichTextArea/useMaybeControlledValue.ts +14 -0
  137. data/app/javascript/components/RichTextArea.tsx +91 -209
  138. data/app/javascript/components/TagEditor/AddTagForm.tsx +2 -2
  139. data/app/javascript/components/TagEditor/Editor.tsx +3 -5
  140. data/app/javascript/components/TagEditor/Tag.tsx +3 -5
  141. data/app/javascript/components/TagEditor/useTags.ts +7 -4
  142. data/app/javascript/components/TagEditor.tsx +2 -4
  143. data/app/javascript/components/Toast.tsx +5 -5
  144. data/app/javascript/components/drag/draggedOrder.ts +6 -6
  145. data/app/javascript/components/drag/useDragCollection.ts +21 -25
  146. data/app/javascript/components/drag/useDragUploader.ts +20 -18
  147. data/app/javascript/components/drag/useDraggable.ts +3 -3
  148. data/app/javascript/features/RichText.tsx +0 -1
  149. data/app/javascript/features/contentTabs.ts +2 -2
  150. data/app/javascript/stores/useModalStore.ts +1 -1
  151. data/app/javascript/stores/useToastStore.ts +2 -2
  152. data/app/javascript/types/Attachments.ts +11 -11
  153. data/app/javascript/types/Crop.ts +16 -12
  154. data/app/javascript/types/Drag.ts +21 -23
  155. data/app/javascript/types/Images.ts +8 -8
  156. data/app/javascript/types/PageEditor.ts +11 -4
  157. data/app/javascript/types/Pages.ts +22 -27
  158. data/app/javascript/types/Tags.ts +5 -6
  159. data/app/javascript/types/Template.ts +4 -4
  160. data/app/javascript/types.ts +2 -2
  161. data/app/models/attachment.rb +5 -9
  162. data/app/models/autopublisher.rb +1 -1
  163. data/app/models/concerns/pages_core/page_model/redirectable.rb +1 -2
  164. data/app/models/concerns/pages_core/page_model/searchable.rb +1 -1
  165. data/app/models/concerns/pages_core/page_model/status.rb +2 -4
  166. data/app/models/concerns/pages_core/searchable_document.rb +2 -4
  167. data/app/models/image.rb +0 -15
  168. data/app/models/page_builder.rb +4 -6
  169. data/app/resources/admin/page_resource.rb +2 -2
  170. data/app/resources/export/page_resource.rb +1 -1
  171. data/app/services/pages_core/invite_service.rb +1 -2
  172. data/app/views/layouts/admin.html.erb +1 -0
  173. data/app/views/pages_core/sitemaps/index.xml.builder +10 -0
  174. data/config/routes.rb +4 -3
  175. data/db/migrate/20240917142300_add_skip_index_to_pages.rb +7 -0
  176. data/lib/pages_core/engine.rb +15 -17
  177. data/lib/pages_core/sitemap.rb +58 -0
  178. data/lib/pages_core/templates/configuration_proxy.rb +3 -3
  179. data/lib/pages_core.rb +7 -4
  180. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +2 -2
  181. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +13 -5
  182. data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +2 -6
  183. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +3 -1
  184. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +2 -3
  185. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +1 -1
  186. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +6 -5
  187. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +1 -1
  188. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +9 -6
  189. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +42 -26
  190. data/lib/rails/generators/pages_core/install/templates/application_controller.rb +0 -6
  191. data/lib/rails/generators/pages_core/rspec/templates/rails_helper.rb +1 -1
  192. metadata +81 -49
  193. data/app/assets/builds/fonts/7b7db107.woff2 +0 -0
  194. data/app/assets/builds/fonts/921961e9.woff2 +0 -0
  195. data/app/controller_dummies/admin/admin_controller.rb +0 -6
  196. data/app/controller_dummies/application_controller.rb +0 -6
  197. data/app/controller_dummies/attachments_controller.rb +0 -4
  198. data/app/controller_dummies/frontend_controller.rb +0 -4
  199. data/app/controller_dummies/images_controller.rb +0 -4
  200. data/app/controller_dummies/page_files_controller.rb +0 -4
  201. data/app/controller_dummies/pages_controller.rb +0 -4
  202. data/app/controller_dummies/sitemaps_controller.rb +0 -4
  203. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +0 -47
  204. data/app/controllers/concerns/pages_core/rss_controller.rb +0 -41
  205. data/app/controllers/pages_core/attachments_controller.rb +0 -42
  206. data/app/controllers/pages_core/frontend/page_files_controller.rb +0 -25
  207. data/app/controllers/pages_core/images_controller.rb +0 -15
  208. data/app/helpers/pages_core/meta_tags_helper.rb +0 -96
  209. data/app/helpers/pages_core/open_graph_tags_helper.rb +0 -49
  210. data/app/javascript/components/PageTree/Draggable.tsx +0 -338
  211. data/app/javascript/lib/Tree.ts +0 -305
  212. data/app/javascript/types/Trees.ts +0 -19
  213. data/app/views/sitemaps/show.xml.builder +0 -11
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PagesCore
4
+ module Pages
5
+ module RssController
6
+ extend ActiveSupport::Concern
7
+
8
+ private
9
+
10
+ def all_feed_items
11
+ feeds = Page.enabled_feeds(locale, include_hidden: true)
12
+ Page.where(parent_page_id: feeds)
13
+ .order("published_at DESC")
14
+ .published
15
+ .localized(locale)
16
+ end
17
+
18
+ def per_page_rss_param(default = 20, max = 1000)
19
+ return default unless params[:per_page].is_a?(String)
20
+
21
+ params[:per_page].to_i.clamp(1, max)
22
+ end
23
+
24
+ def render_page_rss(page, pagination_page = 1)
25
+ if page.feed_enabled?
26
+ render_rss(page.pages.paginate(per_page: per_page_rss_param,
27
+ page: pagination_page)
28
+ .includes(:image, :author),
29
+ title: page.name)
30
+ else
31
+ render_error 404
32
+ end
33
+ end
34
+
35
+ def render_rss(items, title: nil)
36
+ @title = PagesCore.config.site_name
37
+ @title += ": #{title}" if title
38
+ @items = items
39
+ render template: "feeds/pages", layout: false
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ErrorsController < ApplicationController
4
+ include PagesCore::DocumentTitleController
5
+
4
6
  layout "errors"
5
7
 
6
8
  def show
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ImagesController < ApplicationController
4
+ include DynamicImage::Controller
5
+
6
+ static_cache :show, :uncropped, :original, permanent: true
7
+
8
+ private
9
+
10
+ def model
11
+ Image
12
+ end
13
+ end
@@ -5,10 +5,9 @@ module PagesCore
5
5
  class PagesController < ::FrontendController
6
6
  include PagesCore::FrontendHelper
7
7
  include PagesCore::Templates::ControllerActions
8
- include PagesCore::HeadTagsHelper
9
8
 
10
- include PagesCore::PreviewPagesController
11
- include PagesCore::RssController
9
+ include PagesCore::Pages::PreviewController
10
+ include PagesCore::Pages::RssController
12
11
 
13
12
  before_action :load_root_pages
14
13
  before_action :find_page_by_path, only: [:show]
@@ -75,7 +74,7 @@ module PagesCore
75
74
  def render_page
76
75
  return if redirect_page(@page)
77
76
 
78
- unless document_title?
77
+ if document_title.blank?
79
78
  document_title(@page.meta_title? ? @page.meta_title : @page.name)
80
79
  end
81
80
 
@@ -3,6 +3,7 @@
3
3
  # Abstract controller for all frontend controllers.
4
4
  module PagesCore
5
5
  class FrontendController < ::ApplicationController
6
+ include PagesCore::DocumentTitleController
6
7
  include ApplicationHelper
7
8
 
8
9
  before_action :set_i18n_locale
@@ -23,7 +24,11 @@ module PagesCore
23
24
  private
24
25
 
25
26
  def page_param
26
- params[:page].is_a?(String) ? params[:page] : 1
27
+ if params[:page].is_a?(String)
28
+ [Integer(params[:page], exception: false), 1].compact.max
29
+ else
30
+ 1
31
+ end
27
32
  end
28
33
 
29
34
  def set_i18n_locale
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PagesCore
4
- class SitemapsController < ::ApplicationController
4
+ class SitemapsController < ApplicationController
5
5
  include PagesCore::PagePathHelper
6
- static_cache :show
6
+ static_cache :index, :pages
7
7
 
8
- def show
9
- @entries = formatted_entries
8
+ def index
9
+ @sitemaps = PagesCore::Sitemap.sitemaps.flat_map do |entry|
10
+ if entry.is_a?(Proc)
11
+ locales.map { |l| instance_exec(l, &entry) }
12
+ else
13
+ entry
14
+ end
15
+ end.compact_blank.uniq
10
16
  end
11
17
 
12
- private
18
+ def pages
19
+ render_sitemap do |map|
20
+ Page.published.where.not(skip_index: true)
21
+ .localized(content_locale).find_each do |page|
22
+ next if page.redirects?
13
23
 
14
- def format_time(timestamp)
15
- if timestamp.is_a?(Date)
16
- timestamp.strftime("%Y-%m-%d")
17
- else
18
- timestamp.strftime("%Y-%m-%dT%H:%M:%S#{timestamp.formatted_offset}")
24
+ map.add(page_url(page.locale, page), lastmod: page.updated_at)
25
+ end
19
26
  end
20
27
  end
21
28
 
22
- def format_record(record)
23
- { loc: record_url(record),
24
- lastmod: format_time(record.updated_at) }
25
- end
26
-
27
- def formatted_entries
28
- records.map { |r| format_record(r) }
29
- end
29
+ private
30
30
 
31
31
  def locales
32
32
  if PagesCore.config.locales
@@ -36,41 +36,10 @@ module PagesCore
36
36
  end
37
37
  end
38
38
 
39
- def localized?(record)
40
- record.is_a?(LocalizableModel::InstanceMethods)
41
- end
42
-
43
- def pages
44
- ([Page.root.try(:localize, I18n.default_locale)] +
45
- locales.flat_map do |locale|
46
- Page.published.localized(locale).includes(:parent)
47
- end).compact
48
- end
49
-
50
- def page_record_url(record)
51
- if record == root_page && record.locale == I18n.default_locale
52
- root_url
53
- else
54
- page_url(record.locale, record)
55
- end
56
- end
57
-
58
- def record_url(record)
59
- if record.is_a?(Page)
60
- page_record_url(record)
61
- elsif localized?(record)
62
- polymorphic_url(record, locale: record.locale)
63
- else
64
- polymorphic_url(record)
39
+ def render_sitemap(&)
40
+ respond_to do |format|
41
+ format.xml { render xml: PagesCore::Sitemap.new(&).to_xml }
65
42
  end
66
43
  end
67
-
68
- def records
69
- pages
70
- end
71
-
72
- def root_page
73
- @root_page ||= Page.root
74
- end
75
44
  end
76
45
  end
@@ -31,7 +31,7 @@ module PagesCore
31
31
  return {} unless image
32
32
 
33
33
  { src: dynamic_image_path(image, size: "#{width * 2}x"),
34
- image: ::Admin::ImageResource.new(image).to_hash }
34
+ image: ::Admin::ImageResource.new(image).to_h }
35
35
  end
36
36
 
37
37
  def editable_image_options(image, width: 250, caption: false, locale: nil)
@@ -5,10 +5,7 @@
5
5
  module PagesCore
6
6
  module ApplicationHelper
7
7
  include PagesCore::AttachmentsHelper
8
- include PagesCore::HeadTagsHelper
9
8
  include PagesCore::ImagesHelper
10
- include PagesCore::MetaTagsHelper
11
- include PagesCore::OpenGraphTagsHelper
12
9
  include PagesCore::PagePathHelper
13
10
 
14
11
  def page_link(page, options = {})
@@ -10,16 +10,6 @@ module PagesCore
10
10
  super(*attachment_params(args))
11
11
  end
12
12
 
13
- def page_file_path(*args)
14
- ActiveSupport::Deprecation.warn(
15
- "#page_file_path is deprecated, use #attachment_path"
16
- )
17
- page_file = args.detect { |a| a.is_a?(PageFile) }
18
- return attachment_path(page_file.attachment) if page_file
19
-
20
- super
21
- end
22
-
23
13
  private
24
14
 
25
15
  def attachment_params(args)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PagesCore
4
+ module FeedTagsHelper
5
+ # Generates links for all RSS feeds. Specify
6
+ # :include_hidden to also include hidden pages.
7
+ #
8
+ # feed_tags
9
+ # feed_tags include_hidden: true
10
+ #
11
+ def feed_tags(options = {})
12
+ feeds = Page.enabled_feeds(content_locale, options)
13
+ return unless feeds.any?
14
+
15
+ feed_tags = [
16
+ rss_link_tag(PagesCore.config(:site_name),
17
+ pages_url(content_locale, format: :rss))
18
+ ] + feeds.map do |page|
19
+ rss_link_tag("#{PagesCore.config(:site_name)}: #{page.name}",
20
+ page_url(content_locale, page, format: :rss))
21
+ end
22
+ safe_join(feed_tags, "\n")
23
+ end
24
+
25
+ private
26
+
27
+ def rss_link_tag(title, href)
28
+ tag.link(rel: "alternate", type: "application/rss+xml", title:, href:)
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,9 @@
2
2
 
3
3
  module PagesCore
4
4
  module FrontendHelper
5
+ include PagesCore::FeedTagsHelper
6
+ include PagesCore::HeadTagsHelper
7
+
5
8
  def root_pages
6
9
  @root_pages ||= Page.roots.localized(content_locale).published
7
10
  end
@@ -2,95 +2,105 @@
2
2
 
3
3
  module PagesCore
4
4
  module HeadTagsHelper
5
- # Sets a document title.
6
- #
7
- # document_title "Dashboard"
8
- #
9
- def document_title(*args)
10
- if args.any?
11
- @document_title = args.first
12
- else
13
- safe_join([@document_title, PagesCore.config(:site_name)].compact.uniq,
14
- " - ")
15
- end
5
+ def document_title_tag(separator: " - ")
6
+ parts = [document_title, PagesCore.config.site_name]
7
+ tag.title(parts.compact_blank.uniq.join(separator))
16
8
  end
17
9
 
18
- # Returns true if document title has been set.
19
- def document_title?
20
- @document_title ? true : false
21
- end
10
+ def head_tag(&)
11
+ PagesCore.deprecator.warn(
12
+ "head_tag helper is deprecated and will be removed"
13
+ )
22
14
 
23
- # Generates links for all RSS feeds. Specify
24
- # :include_hidden to also include hidden pages.
25
- #
26
- # feed_tags
27
- # feed_tags include_hidden: true
28
- #
29
- def feed_tags(options = {})
30
- feeds = Page.enabled_feeds(content_locale, options)
31
- return unless feeds.any?
32
-
33
- feed_tags = [
34
- rss_link_tag(PagesCore.config(:site_name),
35
- pages_url(content_locale, format: :rss))
36
- ] + feeds.map do |page|
37
- rss_link_tag("#{PagesCore.config(:site_name)}: #{page.name}",
38
- page_url(content_locale, page, format: :rss))
15
+ tag.head do
16
+ safe_join([document_title_tag,
17
+ pages_meta_tags(instance_variable_get(:@page)),
18
+ ([csrf_meta_tags, csp_meta_tag] unless static_cached?),
19
+ (block_given? ? capture(&) : nil)].flatten.compact_blank,
20
+ "\n")
39
21
  end
40
- safe_join(feed_tags, "\n")
41
22
  end
42
23
 
43
- # Outputs a HTML5 doctype and head tags, with document title
44
- # and relevant meta tags. Takes a block which will be placed
45
- # inside <head>.
46
- #
47
- # <%= head_tag do %>
48
- # <%= stylesheet_link_tag "application" %>
49
- # <%= javascript_include_tag "application" %>
50
- # <%= feed_tags %>
51
- # <% end %>
52
- #
53
- def head_tag(&)
54
- # The block output must be captured first
55
- block_output = block_given? ? capture(&) : nil
24
+ def meta_image_url(image, size: "1200x")
25
+ return if image.blank?
26
+ return image unless image.is_a?(Image)
56
27
 
57
- tag.head { safe_join(head_tag_contents(block_output), "\n") }
28
+ dynamic_image_url(image, size:, only_path: false)
58
29
  end
59
30
 
60
- # Generates a link to an RSS feed.
61
- #
62
- # rss_link_tag "My feed", "feed.rss"
63
- #
64
- def rss_link_tag(title, href)
65
- tag.link(rel: "alternate",
66
- type: "application/rss+xml",
67
- title:,
68
- href:)
31
+ def pages_meta_tags(page = nil)
32
+ safe_join(
33
+ [(tag.meta(name: "robots", content: "noindex") if page&.skip_index?),
34
+ meta_description_tag(meta_description(page)),
35
+ meta_image_tag(meta_image(page)),
36
+ open_graph_tags(page)].compact_blank, "\n"
37
+ )
69
38
  end
70
39
 
71
40
  private
72
41
 
73
- def head_tag_contents(block_output)
74
- [tag.meta(charset: "utf-8"),
75
- tag.title(document_title),
76
- meta_description_tag,
77
- meta_keywords_tag,
78
- (tag.link(rel: "image_src", href: meta_image) if meta_image?),
79
- open_graph_tags,
80
- (csrf_meta_tags unless static_cached?),
81
- block_output].compact_blank
42
+ def meta_description(record = nil)
43
+ description = content_for(:meta_description)
44
+ description ||= record.meta_description if record.try(&:meta_description?)
45
+ description ||= record.excerpt if record.try(&:excerpt?)
46
+ strip_tags(description)&.strip
82
47
  end
83
48
 
84
- def meta_description_tag
85
- return unless meta_description?
49
+ def meta_description_tag(content)
50
+ return if content.blank?
86
51
 
87
- tag.meta(name: "description", content: meta_description&.strip)
52
+ tag.meta(name: "description", content:)
88
53
  end
89
54
 
90
- def meta_keywords_tag
91
- return unless meta_keywords?
55
+ def meta_image(record = nil)
56
+ meta_image_url(
57
+ content_for(:meta_image) ||
58
+ record.try(:meta_image) || record.try(:image)
59
+ )
60
+ end
61
+
62
+ def meta_image_tag(href)
63
+ return if href.blank?
92
64
 
93
- tag.meta(name: "keywords", content: meta_keywords&.strip)
65
+ tag.link(rel: "image_src", href:)
66
+ end
67
+
68
+ def open_graph_description(record = nil)
69
+ if content_for?(:open_graph_description)
70
+ content_for(:open_graph_description)
71
+ elsif record.try(:open_graph_description?)
72
+ record.open_graph_description
73
+ else
74
+ meta_description(record)
75
+ end
76
+ end
77
+
78
+ def open_graph_properties(record = nil)
79
+ { type: "website",
80
+ site_name: PagesCore.config(:site_name),
81
+ title: open_graph_title(record),
82
+ image: meta_image(record),
83
+ description: open_graph_description(record)&.strip,
84
+ url: request.url }
85
+ end
86
+
87
+ def open_graph_tags(record = nil)
88
+ safe_join(
89
+ open_graph_properties(record)
90
+ .compact
91
+ .map { |name, content| tag.meta(property: "og:#{name}", content:) },
92
+ "\n"
93
+ )
94
+ end
95
+
96
+ def open_graph_title(record = nil)
97
+ if content_for?(:open_graph_title)
98
+ content_for(:open_graph_title)
99
+ elsif record.try(:open_graph_title?)
100
+ record.open_graph_title
101
+ else
102
+ document_title
103
+ end
94
104
  end
95
105
  end
96
106
  end
@@ -13,9 +13,7 @@ module PagesCore
13
13
  end
14
14
  end
15
15
 
16
- def page_url(page_or_locale, page = nil, opts = {})
17
- locale, page = page_url_locale_and_page(page_or_locale, page, opts)
18
-
16
+ def page_url(locale, page, opts = {})
19
17
  page.localize(locale) do |p|
20
18
  if p.redirects? && html_format?(opts)
21
19
  page_redirect_url(locale, p)
@@ -48,15 +46,6 @@ module PagesCore
48
46
  path.split("/").map { |s| CGI.escape(s) }.join("/")
49
47
  end
50
48
 
51
- def page_url_locale_and_page(page_or_locale, page, opts)
52
- return [page_or_locale, page] if page
53
-
54
- ActiveSupport::Deprecation.warn(
55
- "Calling page_url without locale is deprecated"
56
- )
57
- [opts[:locale] || content_locale, page_or_locale]
58
- end
59
-
60
49
  def paginated_section(opts)
61
50
  return "" unless opts[:page]
62
51
 
@@ -1,4 +1,4 @@
1
- import React, { MouseEvent } from "react";
1
+ import { MouseEvent } from "react";
2
2
  import copyToClipboard from "../../lib/copyToClipboard";
3
3
  import AttachmentEditor from "./AttachmentEditor";
4
4
  import useModalStore from "../../stores/useModalStore";
@@ -9,7 +9,7 @@ import { Locale } from "../../types";
9
9
 
10
10
  import { useDraggable } from "../drag";
11
11
 
12
- interface Props {
12
+ type Props = {
13
13
  attributeName: string;
14
14
  placeholder: boolean;
15
15
  draggable: Drag.Draggable<Attachments.Record>;
@@ -23,7 +23,7 @@ interface Props {
23
23
  evt: MouseEvent,
24
24
  draggable: Drag.Draggable<Attachments.Record>
25
25
  ) => void;
26
- }
26
+ };
27
27
 
28
28
  export default function Attachment(props: Props) {
29
29
  const { attributeName, draggable, locales, locale } = props;
@@ -1,4 +1,4 @@
1
- import React, { ChangeEvent, MouseEvent, useState } from "react";
1
+ import { ChangeEvent, MouseEvent, useState } from "react";
2
2
  import copyToClipboard, { copySupported } from "../../lib/copyToClipboard";
3
3
  import useModalStore from "../../stores/useModalStore";
4
4
  import useToastStore from "../../stores/useToastStore";
@@ -6,12 +6,12 @@ import { putJson } from "../../lib/request";
6
6
  import * as Attachments from "../../types/Attachments";
7
7
  import { Locale } from "../../types";
8
8
 
9
- interface Props {
9
+ type Props = {
10
10
  attachment: Attachments.Resource;
11
11
  locale: string;
12
12
  locales: { [index: string]: Locale };
13
13
  onUpdate: (attachment: Partial<Attachments.Resource>) => void;
14
- }
14
+ };
15
15
 
16
16
  export default function AttachmentEditor(props: Props) {
17
17
  const { attachment, locales } = props;
@@ -40,13 +40,13 @@ export default function AttachmentEditor(props: Props) {
40
40
  notice("Embed code copied to clipboard");
41
41
  };
42
42
 
43
- const save = (evt: MouseEvent) => {
43
+ const save = async (evt: MouseEvent) => {
44
44
  evt.preventDefault();
45
45
  evt.stopPropagation();
46
46
 
47
47
  const data = { ...localizations };
48
48
 
49
- void putJson(`/admin/attachments/${attachment.id}`, { attachment: data });
49
+ await putJson(`/admin/attachments/${attachment.id}`, { attachment: data });
50
50
 
51
51
  if (props.onUpdate) {
52
52
  props.onUpdate(data);
@@ -0,0 +1,28 @@
1
+ import * as Attachments from "../../types/Attachments";
2
+
3
+ type Props = {
4
+ attributeName: (record: Attachments.Record) => string;
5
+ deleted: Attachments.Record[];
6
+ };
7
+
8
+ export default function Deleted({ attributeName, deleted }: Props) {
9
+ return (
10
+ <div className="deleted">
11
+ {deleted.map((r) => (
12
+ <span className="deleted-attachment" key={r.id}>
13
+ <input name={`${attributeName(r)}[id]`} type="hidden" value={r.id} />
14
+ <input
15
+ name={`${attributeName(r)}[attachment_id]`}
16
+ type="hidden"
17
+ value={(r.attachment && r.attachment.id) || ""}
18
+ />
19
+ <input
20
+ name={`${attributeName(r)}[_destroy]`}
21
+ type="hidden"
22
+ value="true"
23
+ />
24
+ </span>
25
+ ))}
26
+ </div>
27
+ );
28
+ }
@@ -1,5 +1,5 @@
1
- import React from "react";
2
1
  import Attachment from "./Attachment";
2
+ import Deleted from "./Deleted";
3
3
  import Placeholder from "./Placeholder";
4
4
  import FileUploadButton from "../FileUploadButton";
5
5
  import { post } from "../../lib/request";
@@ -8,9 +8,9 @@ import * as Drag from "../../types/Drag";
8
8
 
9
9
  import { createDraggable, draggedOrder, useDragUploader } from "../drag";
10
10
 
11
- interface Props extends Attachments.Options {
11
+ type Props = Attachments.Options & {
12
12
  state: Attachments.State;
13
- }
13
+ };
14
14
 
15
15
  function filenameToName(str: string): string {
16
16
  return str.replace(/\.[\w\d]+$/, "").replace(/_/g, " ");
@@ -24,7 +24,7 @@ export default function List(props: Props) {
24
24
  const name = {};
25
25
  locales.forEach((l) => (name[l] = file.name));
26
26
 
27
- const draggable = createDraggable({
27
+ const draggable = createDraggable<Attachments.Record>({
28
28
  attachment: { filename: file.name, name: name },
29
29
  uploading: true
30
30
  });
@@ -58,7 +58,10 @@ export default function List(props: Props) {
58
58
  });
59
59
  };
60
60
 
61
- const dragEnd = (dragState: Drag.State, files: File[]) => {
61
+ const dragEnd = (
62
+ dragState: Drag.State<Attachments.Record>,
63
+ files: File[]
64
+ ) => {
62
65
  collection.dispatch({
63
66
  type: "reorder",
64
67
  payload: draggedOrder(collection, dragState)
@@ -69,7 +72,7 @@ export default function List(props: Props) {
69
72
  });
70
73
  };
71
74
 
72
- const [dragState, dragStart, listeners] = useDragUploader<Attachments.Record>(
75
+ const [dragState, dragStart, listeners] = useDragUploader(
73
76
  [collection],
74
77
  dragEnd
75
78
  );
@@ -110,7 +113,7 @@ export default function List(props: Props) {
110
113
  }
111
114
  };
112
115
 
113
- const attachment = (draggable: Drag.Item<Attachments.Record>) => {
116
+ const attachment = (draggable: Drag.DraggableOrFiles<Attachments.Record>) => {
114
117
  const { dragging } = dragState;
115
118
 
116
119
  if (draggable === "Files") {
@@ -144,23 +147,7 @@ export default function List(props: Props) {
144
147
  return (
145
148
  <div className={classes.join(" ")} ref={collection.ref} {...listeners}>
146
149
  <div className="files">{dragOrder.map((d) => attachment(d))}</div>
147
- <div className="deleted">
148
- {deleted.map((r) => (
149
- <span className="deleted-attachment" key={r.id}>
150
- <input name={`${attrName(r)}[id]`} type="hidden" value={r.id} />
151
- <input
152
- name={`${attrName(r)}[attachment_id]`}
153
- type="hidden"
154
- value={(r.attachment && r.attachment.id) || ""}
155
- />
156
- <input
157
- name={`${attrName(r)}[_destroy]`}
158
- type="hidden"
159
- value="true"
160
- />
161
- </span>
162
- ))}
163
- </div>
150
+ <Deleted attributeName={attrName} deleted={deleted} />
164
151
  <div className="drop-target">
165
152
  <FileUploadButton
166
153
  multiple={true}
@@ -1,5 +1,3 @@
1
- import React from "react";
2
-
3
1
  export default function Placeholder() {
4
2
  return (
5
3
  <div className="attachment drop-placeholder" key="file-placeholder">