pageflow 17.0.5 → 17.1.0

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 (488) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +362 -35
  3. data/README.md +0 -1
  4. data/admins/pageflow/accounts.rb +16 -14
  5. data/admins/pageflow/entry.rb +68 -20
  6. data/admins/pageflow/entry_templates.rb +10 -9
  7. data/admins/pageflow/folder.rb +1 -1
  8. data/admins/pageflow/membership.rb +4 -4
  9. data/admins/pageflow/revisions.rb +5 -5
  10. data/admins/pageflow/site_root_entry.rb +64 -0
  11. data/admins/pageflow/sites.rb +12 -3
  12. data/admins/pageflow/translations.rb +75 -0
  13. data/admins/pageflow/user.rb +21 -35
  14. data/app/assets/javascripts/pageflow/admin/entries.js +30 -5
  15. data/app/assets/javascripts/pageflow/dist/ui.js +2865 -257
  16. data/app/assets/stylesheets/pageflow/admin/entries/index_table.scss +2 -1
  17. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +2 -1
  18. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +6 -0
  19. data/app/assets/stylesheets/pageflow/editor/base.scss +2 -0
  20. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +7 -1
  21. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +5 -1
  22. data/app/assets/stylesheets/pageflow/editor/files.scss +28 -16
  23. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +24 -0
  24. data/app/assets/stylesheets/pageflow/editor/info_box.scss +11 -3
  25. data/app/assets/stylesheets/pageflow/editor/list.scss +6 -4
  26. data/app/assets/stylesheets/pageflow/editor/list_search_field.scss +52 -0
  27. data/app/assets/stylesheets/pageflow/editor/outline.scss +0 -13
  28. data/app/assets/stylesheets/pageflow/editor/select_button.scss +1 -1
  29. data/app/assets/stylesheets/pageflow/editor/sortable.scss +12 -0
  30. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +4 -4
  31. data/app/assets/stylesheets/pageflow/ui/forms.scss +4 -0
  32. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +8 -0
  33. data/app/assets/stylesheets/pageflow/ui/input/file_name_input.scss +37 -0
  34. data/app/assets/stylesheets/pageflow/ui/properties.scss +5 -0
  35. data/app/assets/stylesheets/pageflow/ui.scss +1 -0
  36. data/app/controllers/concerns/pageflow/controller_delegation.rb +1 -1
  37. data/app/controllers/concerns/pageflow/edit_locking.rb +17 -10
  38. data/app/controllers/concerns/pageflow/entry_password_protection.rb +1 -0
  39. data/app/controllers/concerns/pageflow/public_https_mode.rb +2 -1
  40. data/app/controllers/concerns/pageflow/quota_verification.rb +5 -4
  41. data/app/controllers/pageflow/admin/initial_passwords_controller.rb +1 -0
  42. data/app/controllers/pageflow/application_controller.rb +19 -19
  43. data/app/controllers/pageflow/chapters_controller.rb +3 -2
  44. data/app/controllers/pageflow/edit_locks_controller.rb +3 -2
  45. data/app/controllers/pageflow/editor/encoding_confirmations_controller.rb +1 -0
  46. data/app/controllers/pageflow/editor/entries_controller.rb +2 -1
  47. data/app/controllers/pageflow/editor/entry_publications_controller.rb +1 -0
  48. data/app/controllers/pageflow/editor/file_import_controller.rb +3 -3
  49. data/app/controllers/pageflow/editor/files_controller.rb +7 -6
  50. data/app/controllers/pageflow/editor/widgets_controller.rb +1 -0
  51. data/app/controllers/pageflow/entries_controller.rb +77 -17
  52. data/app/controllers/pageflow/feeds_controller.rb +1 -1
  53. data/app/controllers/pageflow/files_controller.rb +1 -0
  54. data/app/controllers/pageflow/pages_controller.rb +4 -2
  55. data/app/controllers/pageflow/revisions_controller.rb +2 -1
  56. data/app/controllers/pageflow/sitemaps_controller.rb +1 -1
  57. data/app/controllers/pageflow/storylines_controller.rb +1 -0
  58. data/app/helpers/pageflow/admin/cutoff_modes_helper.rb +12 -0
  59. data/app/helpers/pageflow/admin/entries_helper.rb +1 -1
  60. data/app/helpers/pageflow/admin/entry_translations_helper.rb +18 -0
  61. data/app/helpers/pageflow/admin/features_helper.rb +1 -1
  62. data/app/helpers/pageflow/admin/form_helper.rb +3 -3
  63. data/app/helpers/pageflow/admin/locales_helper.rb +10 -2
  64. data/app/helpers/pageflow/admin/memberships_helper.rb +32 -32
  65. data/app/helpers/pageflow/admin/revisions_helper.rb +1 -1
  66. data/app/helpers/pageflow/admin/sites_helper.rb +22 -0
  67. data/app/helpers/pageflow/admin/users_helper.rb +7 -5
  68. data/app/helpers/pageflow/admin/widgets_helper.rb +3 -3
  69. data/app/helpers/pageflow/asset_urls_helper.rb +1 -1
  70. data/app/helpers/pageflow/audio_files_helper.rb +5 -2
  71. data/app/helpers/pageflow/background_image_helper.rb +7 -6
  72. data/app/helpers/pageflow/common_entry_seed_helper.rb +2 -4
  73. data/app/helpers/pageflow/editor/config_helper.rb +1 -1
  74. data/app/helpers/pageflow/editor/files_helper.rb +1 -1
  75. data/app/helpers/pageflow/embed_code_helper.rb +3 -3
  76. data/app/helpers/pageflow/entries_helper.rb +58 -34
  77. data/app/helpers/pageflow/entry_json_seed_helper.rb +3 -2
  78. data/app/helpers/pageflow/feeds_helper.rb +2 -2
  79. data/app/helpers/pageflow/file_background_images_helper.rb +2 -2
  80. data/app/helpers/pageflow/file_thumbnails_helper.rb +4 -3
  81. data/app/helpers/pageflow/files_helper.rb +5 -4
  82. data/app/helpers/pageflow/folders_helper.rb +8 -5
  83. data/app/helpers/pageflow/help_entries_helper.rb +2 -2
  84. data/app/helpers/pageflow/hreflang_links_helper.rb +37 -0
  85. data/app/helpers/pageflow/info_box_helper.rb +7 -11
  86. data/app/helpers/pageflow/media_query_helper.rb +3 -3
  87. data/app/helpers/pageflow/navigation_bar_helper.rb +1 -1
  88. data/app/helpers/pageflow/overview_helper.rb +1 -1
  89. data/app/helpers/pageflow/page_types_helper.rb +5 -5
  90. data/app/helpers/pageflow/pages_helper.rb +29 -15
  91. data/app/helpers/pageflow/public_i18n_helper.rb +1 -1
  92. data/app/helpers/pageflow/quota_helper.rb +4 -4
  93. data/app/helpers/pageflow/render_json_helper.rb +6 -3
  94. data/app/helpers/pageflow/revision_file_helper.rb +2 -1
  95. data/app/helpers/pageflow/sites_helper.rb +3 -3
  96. data/app/helpers/pageflow/social_share_helper.rb +24 -10
  97. data/app/helpers/pageflow/social_share_links_helper.rb +12 -9
  98. data/app/helpers/pageflow/structured_data_helper.rb +1 -1
  99. data/app/helpers/pageflow/stub_page_configuration.rb +7 -3
  100. data/app/helpers/pageflow/text_direction_helper.rb +1 -1
  101. data/app/helpers/pageflow/themes_helper.rb +1 -1
  102. data/app/helpers/pageflow/video_files_helper.rb +29 -13
  103. data/app/helpers/pageflow/widgets_helper.rb +7 -6
  104. data/app/inputs/pageflow_permalink_input.rb +3 -2
  105. data/app/jobs/pageflow/application_job.rb +1 -0
  106. data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +1 -1
  107. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -0
  108. data/app/jobs/pageflow/poll_zencoder_job.rb +3 -2
  109. data/app/jobs/pageflow/process_image_or_text_track_job.rb +1 -0
  110. data/app/jobs/pageflow/prune_auto_snapshots_job.rb +1 -0
  111. data/app/jobs/pageflow/request_meta_data_from_zencoder_job.rb +1 -0
  112. data/app/jobs/pageflow/submit_file_to_zencoder_job.rb +1 -0
  113. data/app/mailers/pageflow/user_mailer.rb +1 -0
  114. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +13 -1
  115. data/app/models/concerns/pageflow/entry_publication_states.rb +7 -3
  116. data/app/models/concerns/pageflow/feature_target.rb +8 -4
  117. data/app/models/concerns/pageflow/output_source.rb +3 -3
  118. data/app/models/concerns/pageflow/permalinkable.rb +2 -1
  119. data/app/models/concerns/pageflow/reusable_file.rb +23 -23
  120. data/app/models/concerns/pageflow/serialization_blacklist.rb +1 -1
  121. data/app/models/concerns/pageflow/suspendable.rb +5 -3
  122. data/app/models/concerns/pageflow/theme_referencer.rb +1 -1
  123. data/app/models/concerns/pageflow/translatable.rb +62 -0
  124. data/app/models/concerns/pageflow/uploadable_file.rb +6 -6
  125. data/app/models/pageflow/account.rb +6 -5
  126. data/app/models/pageflow/account_member_query.rb +1 -1
  127. data/app/models/pageflow/account_role_query.rb +1 -1
  128. data/app/models/pageflow/application_query.rb +1 -1
  129. data/app/models/pageflow/application_record.rb +1 -1
  130. data/app/models/pageflow/audio_file.rb +4 -4
  131. data/app/models/pageflow/audio_file_url_templates.rb +1 -1
  132. data/app/models/pageflow/authentication_token.rb +2 -2
  133. data/app/models/pageflow/chapter.rb +6 -1
  134. data/app/models/pageflow/chapter_scaffold.rb +2 -4
  135. data/app/models/pageflow/cname_site_request_scope.rb +1 -1
  136. data/app/models/pageflow/customized_theme.rb +6 -6
  137. data/app/models/pageflow/draft_entry.rb +37 -9
  138. data/app/models/pageflow/edit_lock.rb +19 -21
  139. data/app/models/pageflow/encoding_confirmation.rb +2 -2
  140. data/app/models/pageflow/entries_feed.rb +2 -2
  141. data/app/models/pageflow/entry.rb +44 -30
  142. data/app/models/pageflow/entry_at_revision.rb +5 -1
  143. data/app/models/pageflow/entry_duplicate.rb +8 -5
  144. data/app/models/pageflow/entry_publication.rb +2 -2
  145. data/app/models/pageflow/entry_role_query.rb +4 -4
  146. data/app/models/pageflow/entry_template.rb +4 -4
  147. data/app/models/pageflow/entry_title_or_account_name_query.rb +2 -2
  148. data/app/models/pageflow/entry_translation_group.rb +42 -0
  149. data/app/models/pageflow/file_reuse.rb +2 -2
  150. data/app/models/pageflow/file_usage.rb +10 -3
  151. data/app/models/pageflow/folder.rb +2 -2
  152. data/app/models/pageflow/home_button.rb +7 -7
  153. data/app/models/pageflow/image_file.rb +20 -5
  154. data/app/models/pageflow/image_file_url_templates.rb +1 -1
  155. data/app/models/pageflow/invitation_form.rb +2 -1
  156. data/app/models/pageflow/managed_user_query.rb +1 -1
  157. data/app/models/pageflow/membership.rb +5 -5
  158. data/app/models/pageflow/nested_revision_component_copy.rb +263 -0
  159. data/app/models/pageflow/null_user.rb +1 -1
  160. data/app/models/pageflow/overview_button.rb +1 -1
  161. data/app/models/pageflow/page.rb +7 -3
  162. data/app/models/pageflow/permalink.rb +23 -2
  163. data/app/models/pageflow/permalink_directory.rb +7 -0
  164. data/app/models/pageflow/permalink_redirect.rb +7 -0
  165. data/app/models/pageflow/positioned_file.rb +5 -5
  166. data/app/models/pageflow/potential_entry_translations.rb +55 -0
  167. data/app/models/pageflow/potential_memberships.rb +5 -4
  168. data/app/models/pageflow/published_entry.rb +62 -11
  169. data/app/models/pageflow/revision.rb +24 -15
  170. data/app/models/pageflow/roles.rb +14 -18
  171. data/app/models/pageflow/site.rb +36 -3
  172. data/app/models/pageflow/site_root_entry_form.rb +27 -0
  173. data/app/models/pageflow/sitemaps.rb +10 -1
  174. data/app/models/pageflow/storyline.rb +5 -2
  175. data/app/models/pageflow/storyline_scaffold.rb +1 -1
  176. data/app/models/pageflow/text_track_file.rb +1 -1
  177. data/app/models/pageflow/text_track_file_url_templates.rb +1 -1
  178. data/app/models/pageflow/theme_customization_file.rb +3 -2
  179. data/app/models/pageflow/thumbnail_file_resolver.rb +1 -1
  180. data/app/models/pageflow/url_template.rb +1 -1
  181. data/app/models/pageflow/used_file.rb +7 -3
  182. data/app/models/pageflow/user_name_query.rb +2 -2
  183. data/app/models/pageflow/video_file.rb +22 -15
  184. data/app/models/pageflow/video_file_url_templates.rb +6 -4
  185. data/app/models/pageflow/widget.rb +4 -3
  186. data/app/models/pageflow/with_file_usage_extension.rb +1 -1
  187. data/app/models/pageflow/zencoder_attachment.rb +8 -8
  188. data/app/policies/pageflow/account_policy.rb +3 -1
  189. data/app/policies/pageflow/admin/admin_only_tab_policy.rb +1 -0
  190. data/app/policies/pageflow/admin/entry_tab_policy.rb +1 -0
  191. data/app/policies/pageflow/application_policy.rb +2 -0
  192. data/app/policies/pageflow/entry_policy.rb +7 -1
  193. data/app/policies/pageflow/entry_template_policy.rb +2 -1
  194. data/app/policies/pageflow/file_policy.rb +4 -7
  195. data/app/policies/pageflow/folder_policy.rb +18 -8
  196. data/app/policies/pageflow/membership_policy.rb +6 -4
  197. data/app/policies/pageflow/site_policy.rb +10 -3
  198. data/app/policies/pageflow/user_policy.rb +6 -4
  199. data/app/state_machines/pageflow/image_and_text_track_processing_state_machine.rb +3 -2
  200. data/app/state_machines/pageflow/media_encoding_state_machine.rb +5 -4
  201. data/app/views/admin/accounts/_entry_template_details.html.arb +2 -2
  202. data/app/views/admin/entries/_attributes_table.html.arb +6 -5
  203. data/app/views/admin/entries/_form.html.erb +2 -1
  204. data/app/views/admin/entries/_permalink_inputs.html.erb +9 -2
  205. data/app/views/admin/entry_templates/_form.html.erb +1 -3
  206. data/app/views/admin/site_root_entry/choose.html.erb +19 -0
  207. data/app/views/admin/sites/_fields.html.erb +14 -1
  208. data/app/views/admin/translations/_form.html.erb +31 -0
  209. data/app/views/admin/users/_attributes_table.html.arb +13 -0
  210. data/app/views/components/pageflow/admin/add_membership_button.rb +13 -12
  211. data/app/views/components/pageflow/admin/custom_scopes_renderer.rb +1 -0
  212. data/app/views/components/pageflow/admin/embed_code_field.rb +1 -0
  213. data/app/views/components/pageflow/admin/embedded_index_table.rb +9 -10
  214. data/app/views/components/pageflow/admin/entries_tab.rb +1 -0
  215. data/app/views/components/pageflow/admin/entry_publication_state_indicator.rb +6 -5
  216. data/app/views/components/pageflow/admin/entry_templates_tab.rb +2 -1
  217. data/app/views/components/pageflow/admin/entry_translations_tab.rb +102 -0
  218. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +1 -0
  219. data/app/views/components/pageflow/admin/extensible_attributes_table.rb +9 -8
  220. data/app/views/components/pageflow/admin/features_tab.rb +1 -0
  221. data/app/views/components/pageflow/admin/grouped_folder_list.rb +1 -0
  222. data/app/views/components/pageflow/admin/icon_link_to.rb +1 -0
  223. data/app/views/components/pageflow/admin/members_tab.rb +4 -3
  224. data/app/views/components/pageflow/admin/membership_role_with_tooltip.rb +3 -2
  225. data/app/views/components/pageflow/admin/revisions_tab.rb +14 -10
  226. data/app/views/components/pageflow/admin/sites_tab.rb +1 -0
  227. data/app/views/components/pageflow/admin/tabs_view.rb +1 -0
  228. data/app/views/components/pageflow/admin/timestamp.rb +6 -5
  229. data/app/views/components/pageflow/admin/user_account_badge_list.rb +2 -3
  230. data/app/views/components/pageflow/admin/user_accounts_tab.rb +1 -0
  231. data/app/views/components/pageflow/admin/user_entries_tab.rb +1 -0
  232. data/app/views/components/pageflow/admin/users_tab.rb +1 -0
  233. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +1 -1
  234. data/app/views/pageflow/editor/entries/index.json.jbuilder +1 -1
  235. data/app/views/pageflow/editor/entries/show.json.jbuilder +1 -1
  236. data/app/views/pageflow/editor/entry_publications/check.json.jbuilder +2 -2
  237. data/app/views/pageflow/editor/files/_file.json.jbuilder +5 -13
  238. data/app/views/pageflow/editor/files/create.json.jbuilder +1 -1
  239. data/app/views/pageflow/editor/files/index.json.jbuilder +1 -1
  240. data/app/views/pageflow/editor/sites/_site.json.jbuilder +1 -0
  241. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +1 -3
  242. data/app/views/pageflow/entries/share_menu/_bluesky_link.html.erb +8 -0
  243. data/app/views/pageflow/entries/share_menu/_threads_link.html.erb +8 -0
  244. data/app/views/pageflow/files/_file.json.jbuilder +3 -3
  245. data/app/views/pageflow/sitemaps/index.xml.builder +9 -1
  246. data/app/views/pageflow/social_share/_entry_meta_tags.html.erb +1 -1
  247. data/app/views/pageflow/social_share/_page_meta_tags.html.erb +1 -1
  248. data/config/initializers/active_admin_patches.rb +3 -6
  249. data/config/initializers/admin_resource_tabs.rb +3 -0
  250. data/config/initializers/paperclip.rb +1 -0
  251. data/config/initializers/symmetric_encryption.rb +1 -1
  252. data/config/initializers/zencoder.rb +24 -13
  253. data/config/locales/de.yml +110 -4
  254. data/config/locales/en.yml +109 -7
  255. data/config/routes.rb +15 -13
  256. data/config/spring.rb +2 -1
  257. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  258. data/db/migrate/20190306161431_copy_file_attributes_of_failed_uploads.rb +2 -2
  259. data/db/migrate/20190820152900_drop_accounts_themes.rb +2 -1
  260. data/db/migrate/20191202145757_create_pageflow_scrolled_sections.rb +11 -11
  261. data/db/migrate/20191202150657_create_pageflow_scrolled_chapters.rb +1 -1
  262. data/db/migrate/20191202154723_create_pageflow_scrolled_content_elements.rb +9 -9
  263. data/db/migrate/20191219143450_add_position_to_content_elements.rb +2 -1
  264. data/db/migrate/20200117133200_change_revision_appearance_option_default_and_null.rb +2 -1
  265. data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +25 -25
  266. data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +3 -3
  267. data/db/migrate/20221215120856_associate_entry_templates_with_sites.rb +2 -2
  268. data/db/migrate/20230120092923_create_other_files.rb +2 -1
  269. data/db/migrate/20230419083307_create_pageflow_entry_translation_group.rb +9 -0
  270. data/db/migrate/20240612110434_add_cutoff_mode_name_to_sites.rb +5 -0
  271. data/db/migrate/20240918084059_create_pageflow_permalink_redirects.rb +14 -0
  272. data/db/migrate/20250508172234_ensure_scrolled_entries_have_main_storyline.rb +14 -0
  273. data/db/migrate/20250617090048_add_custom404_entry_to_sites.rb +5 -0
  274. data/db/migrate/20250617100000_add_perma_id_counter_to_entries.rb +28 -0
  275. data/db/migrate/20250722174123_add_perma_id_to_chapters.rb +10 -0
  276. data/db/migrate/20250725080603_add_perma_id_indexes_to_revision_components.rb +13 -0
  277. data/db/migrate/20250726000000_add_display_name_to_file_usages.rb +5 -0
  278. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +3912 -1486
  279. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +468 -1161
  280. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +28125 -22
  281. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +26589 -19
  282. data/entry_types/paged/app/controllers/pageflow_paged/entries_controller.rb +1 -0
  283. data/entry_types/paged/app/helpers/pageflow_paged/page_background_asset_helper.rb +1 -1
  284. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +7 -7
  285. data/entry_types/paged/app/views/pageflow_paged/editor/entries/_seed.json.jbuilder +1 -0
  286. data/entry_types/paged/app/views/pageflow_paged/entries/show.html.erb +1 -0
  287. data/entry_types/paged/lib/pageflow_paged/plugin.rb +4 -0
  288. data/entry_types/paged/lib/pageflow_paged/react/page_type.rb +1 -1
  289. data/entry_types/paged/lib/pageflow_paged/react/widget_type.rb +3 -3
  290. data/entry_types/paged/lib/pageflow_paged.rb +1 -1
  291. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +5 -3
  292. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +2 -2
  293. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -2
  294. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +2 -1
  295. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +2 -2
  296. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +1 -0
  297. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +2 -1
  298. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +60 -6
  299. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +16 -2
  300. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +21 -23
  301. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +16 -1
  302. data/entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb +15 -14
  303. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +12 -0
  304. data/entry_types/scrolled/app/models/pageflow_scrolled/section.rb +12 -1
  305. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +19 -1
  306. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +1 -0
  307. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/sections/_section_with_content_elements.json.jbuilder +2 -2
  308. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +2 -0
  309. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +17 -9
  310. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry_translations.json.jbuilder +14 -0
  311. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +25 -5
  312. data/entry_types/scrolled/app/views/pageflow_scrolled/storylines/_storyline.json.jbuilder +7 -0
  313. data/entry_types/scrolled/config/locales/de.yml +614 -45
  314. data/entry_types/scrolled/config/locales/en.yml +586 -47
  315. data/entry_types/scrolled/config/routes.rb +18 -8
  316. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +11 -11
  317. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantDesktop.svg +56 -0
  318. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantMobile.svg +22 -0
  319. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +26 -11
  320. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +27 -8
  321. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +1 -1
  322. data/entry_types/scrolled/lib/pageflow_scrolled/additional_theme_assets.rb +27 -0
  323. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +16 -4
  324. data/entry_types/scrolled/lib/pageflow_scrolled/content_element_consent_vendors.rb +1 -1
  325. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +24 -4
  326. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +24 -22
  327. data/entry_types/scrolled/lib/pageflow_scrolled.rb +1 -1
  328. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +6 -5
  329. data/entry_types/scrolled/package/config/webpack.js +22 -0
  330. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -0
  331. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1529 -0
  332. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.css +1 -0
  333. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +59 -0
  334. data/entry_types/scrolled/package/contentElements-editor.js +2075 -321
  335. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  336. data/entry_types/scrolled/package/contentElements-frontend.js +1019 -643
  337. data/entry_types/scrolled/package/editor.js +2086 -1091
  338. data/entry_types/scrolled/package/frontend/{EditableInlineText.module-ebd22921.js → FloatingPortalRootProvider-51914be7.js} +1200 -1258
  339. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-4ec6b2de.js → PhonePlatformContext-9f76033e.js} +7 -8
  340. data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +1932 -0
  341. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-788e9cdb.js +39 -0
  342. data/entry_types/scrolled/package/frontend/{Viewer-1bb5597c.js → Viewer-0345ce57.js} +36 -63
  343. data/entry_types/scrolled/package/frontend/{Viewer-1ecf3375.js → Viewer-cdc549cc.js} +15 -22
  344. data/entry_types/scrolled/package/frontend/{Wavesurfer-7d9cf1b7.js → Wavesurfer-1cdc3925.js} +54 -74
  345. data/entry_types/scrolled/package/frontend/{components-024a9893.js → components-96660ffd.js} +1791 -841
  346. data/entry_types/scrolled/package/frontend/{index-11f32f10.js → index-eb670c2f.js} +11 -18
  347. data/entry_types/scrolled/package/frontend/index.css +1 -1
  348. data/entry_types/scrolled/package/frontend/index.js +517 -903
  349. data/entry_types/scrolled/package/frontend/{createSuper-d0f30da3.js → inherits-539844a6.js} +40 -56
  350. data/entry_types/scrolled/package/frontend/{useContentElementEditorState-4f4c3cf6.js → useContentElementEditorState-77fe6c79.js} +7 -8
  351. data/entry_types/scrolled/package/frontend/usePhonePlatform-c2ba875d.js +8 -0
  352. data/entry_types/scrolled/package/frontend-server.js +0 -1
  353. data/entry_types/scrolled/package/package.json +18 -10
  354. data/entry_types/scrolled/package/testHelpers.js +306 -70
  355. data/entry_types/scrolled/package/values/widgets.module.css +18 -0
  356. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  357. data/entry_types/scrolled/package/widgets/consentBar.js +47 -66
  358. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  359. data/entry_types/scrolled/package/widgets/defaultNavigation.js +177 -207
  360. data/entry_types/scrolled/package/widgets/excursionDialog.css +1 -0
  361. data/entry_types/scrolled/package/widgets/excursionDialog.js +109 -0
  362. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -0
  363. data/entry_types/scrolled/package/widgets/excursionSheet.js +262 -0
  364. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  365. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +6 -9
  366. data/entry_types/scrolled/package/widgets/iconScrollIndicator.css +1 -0
  367. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +48 -0
  368. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -0
  369. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +144 -0
  370. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  371. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +26 -9
  372. data/entry_types/scrolled/package/widgets-server.js +1 -0
  373. data/entry_types/scrolled/spec/factories/chapters.rb +14 -1
  374. data/entry_types/scrolled/spec/factories/content_elements.rb +1 -1
  375. data/lib/generators/pageflow/active_admin_initializer/active_admin_initializer_generator.rb +5 -3
  376. data/lib/generators/pageflow/assets/assets_generator.rb +7 -5
  377. data/lib/generators/pageflow/cancan/cancan_generator.rb +2 -1
  378. data/lib/generators/pageflow/cancan/templates/ability.rb +1 -1
  379. data/lib/generators/pageflow/error_pages/error_pages_generator.rb +2 -1
  380. data/lib/generators/pageflow/initializer/initializer_generator.rb +3 -2
  381. data/lib/generators/pageflow/install/install_generator.rb +2 -2
  382. data/lib/generators/pageflow/procfile/procfile_generator.rb +2 -1
  383. data/lib/generators/pageflow/resque/resque_generator.rb +6 -4
  384. data/lib/generators/pageflow/resque/templates/resque.rake +1 -1
  385. data/lib/generators/pageflow/routes/routes_generator.rb +4 -2
  386. data/lib/generators/pageflow/seeds/seeds_generator.rb +3 -2
  387. data/lib/generators/pageflow/seeds/templates/seeds.rb +6 -6
  388. data/lib/generators/pageflow/theme/theme_generator.rb +2 -1
  389. data/lib/generators/pageflow/user/user_generator.rb +2 -1
  390. data/lib/pageflow/ability_mixin.rb +25 -15
  391. data/lib/pageflow/active_admin_patches/views/attributes_table.rb +1 -0
  392. data/lib/pageflow/active_admin_patches/views/pages/base.rb +3 -2
  393. data/lib/pageflow/active_admin_patches/views/table_for.rb +1 -0
  394. data/lib/pageflow/additional_headers.rb +27 -0
  395. data/lib/pageflow/admin/attributes_table_rows.rb +3 -3
  396. data/lib/pageflow/admin/tabs.rb +1 -1
  397. data/lib/pageflow/built_in_file_type.rb +1 -1
  398. data/lib/pageflow/built_in_page_types_plugin.rb +1 -1
  399. data/lib/pageflow/built_in_widget_type.rb +2 -2
  400. data/lib/pageflow/built_in_widget_types_plugin.rb +2 -1
  401. data/lib/pageflow/configuration.rb +141 -18
  402. data/lib/pageflow/cutoff_modes.rb +39 -0
  403. data/lib/pageflow/editor_controller.rb +6 -10
  404. data/lib/pageflow/engine.rb +4 -5
  405. data/lib/pageflow/entry_export_import/attachment_files.rb +1 -1
  406. data/lib/pageflow/entry_export_import/entry_serialization.rb +5 -4
  407. data/lib/pageflow/entry_export_import/file_mappings.rb +1 -0
  408. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +1 -1
  409. data/lib/pageflow/entry_export_import.rb +3 -3
  410. data/lib/pageflow/entry_types.rb +2 -2
  411. data/lib/pageflow/feature.rb +1 -1
  412. data/lib/pageflow/features.rb +4 -6
  413. data/lib/pageflow/file_importers.rb +4 -4
  414. data/lib/pageflow/file_type.rb +3 -3
  415. data/lib/pageflow/file_types.rb +3 -3
  416. data/lib/pageflow/global_config_api.rb +5 -6
  417. data/lib/pageflow/help_entries.rb +7 -7
  418. data/lib/pageflow/help_entry.rb +1 -1
  419. data/lib/pageflow/hooks.rb +1 -1
  420. data/lib/pageflow/nested_revision_component.rb +9 -27
  421. data/lib/pageflow/news_item_api.rb +1 -1
  422. data/lib/pageflow/page_type.rb +2 -3
  423. data/lib/pageflow/page_types.rb +3 -3
  424. data/lib/pageflow/paperclip_interpolations/support.rb +1 -1
  425. data/lib/pageflow/paperclip_processors/vtt.rb +1 -0
  426. data/lib/pageflow/paperclip_processors/webp.rb +2 -2
  427. data/lib/pageflow/partial_editor_fragment_renderer.rb +2 -2
  428. data/lib/pageflow/plugin.rb +1 -2
  429. data/lib/pageflow/quota.rb +9 -5
  430. data/lib/pageflow/quotas.rb +1 -1
  431. data/lib/pageflow/rails_version.rb +1 -0
  432. data/lib/pageflow/react.rb +1 -1
  433. data/lib/pageflow/revision_component.rb +8 -40
  434. data/lib/pageflow/revision_components.rb +16 -6
  435. data/lib/pageflow/seeds.rb +12 -16
  436. data/lib/pageflow/theme.rb +1 -1
  437. data/lib/pageflow/theme_customizations.rb +10 -10
  438. data/lib/pageflow/themes.rb +3 -3
  439. data/lib/pageflow/user_mixin.rb +6 -6
  440. data/lib/pageflow/version.rb +1 -1
  441. data/lib/pageflow/widget_type.rb +4 -4
  442. data/lib/pageflow/widget_types.rb +9 -9
  443. data/lib/pageflow/zencoder_api.rb +32 -42
  444. data/lib/pageflow/zencoder_audio_output_definition.rb +14 -13
  445. data/lib/pageflow/zencoder_meta_data_output_definition.rb +3 -2
  446. data/lib/pageflow/zencoder_output_definition.rb +16 -14
  447. data/lib/pageflow/zencoder_video_output_definition.rb +140 -88
  448. data/lib/pageflow.rb +7 -2
  449. data/lib/tasks/pageflow_tasks.rake +1 -1
  450. data/package/editor.js +954 -868
  451. data/package/frontend.js +113 -387
  452. data/package/package.json +2 -1
  453. data/package/testHelpers.js +10 -56
  454. data/package/ui.js +209 -251
  455. data/spec/factories/accounts.rb +37 -22
  456. data/spec/factories/audio_files.rb +2 -2
  457. data/spec/factories/authentication_tokens.rb +4 -4
  458. data/spec/factories/chapters.rb +4 -4
  459. data/spec/factories/draft_entries.rb +14 -6
  460. data/spec/factories/edit_locks.rb +1 -1
  461. data/spec/factories/entries.rb +28 -19
  462. data/spec/factories/entry_translation_groups.rb +6 -0
  463. data/spec/factories/file_usages.rb +2 -1
  464. data/spec/factories/folders.rb +1 -1
  465. data/spec/factories/image_files.rb +2 -2
  466. data/spec/factories/pages.rb +3 -3
  467. data/spec/factories/published_entries.rb +26 -8
  468. data/spec/factories/revisions.rb +1 -1
  469. data/spec/factories/sites.rb +6 -0
  470. data/spec/factories/test_multi_attachment_files.rb +1 -1
  471. data/spec/factories/text_track_files.rb +1 -1
  472. data/spec/factories/uploadable_files.rb +1 -1
  473. data/spec/factories/users.rb +6 -6
  474. data/spec/factories/video_files.rb +2 -2
  475. data/spec/factories/widgets.rb +1 -1
  476. data/spec/fixtures/7x15_rotated.jpg +0 -0
  477. data/vendor/assets/javascripts/wysihtml-toolbar.js +19288 -0
  478. metadata +98 -45
  479. data/config/initializers/revision_components.rb +0 -5
  480. data/config/locales/twitter_to_x.de.yml +0 -6
  481. data/config/locales/twitter_to_x.en.yml +0 -6
  482. data/entry_types/scrolled/config/locales/twitter_to_x.de.yml +0 -12
  483. data/entry_types/scrolled/config/locales/twitter_to_x.en.yml +0 -12
  484. data/entry_types/scrolled/config/locales/vr_image_projection.de.yml +0 -24
  485. data/entry_types/scrolled/config/locales/vr_image_projection.en.yml +0 -24
  486. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-b79159cc.js +0 -107
  487. data/entry_types/scrolled/package/frontend/arrowRight-62998af9.js +0 -77
  488. data/entry_types/scrolled/package/frontend/i18n-ce13a8bf.js +0 -1129
@@ -0,0 +1,263 @@
1
+ module Pageflow
2
+ # Service object for copying nested revision components between
3
+ # records using bulk INSERT ... SELECT statements.
4
+ #
5
+ # When `reset_perma_ids` is false we reuse the perma_id values from the
6
+ # source revision. Parents and their copies are joined via perma_id so that
7
+ # foreign keys of the newly inserted records point at the duplicated parents.
8
+ #
9
+ # If `reset_perma_ids` is true we compute an offset for each
10
+ # association. The offset ensures that the minimum perma_id of the
11
+ # records being copied becomes `entry.perma_id_counter + 1`. This
12
+ # yields a range of new perma_ids above the current maximum perma_id
13
+ # of the entry while preserving the relative order of ids within the
14
+ # copied set.
15
+ #
16
+ # Example for a simple one level association:
17
+ #
18
+ # INSERT INTO test_nested_revision_components (parent_id, perma_id, text)
19
+ # SELECT NEW_ROOT_ID, old_items.perma_id + OFFSET, old_items.text
20
+ # FROM test_nested_revision_components AS old_items
21
+ # WHERE old_items.parent_id = OLD_ROOT_ID;
22
+ #
23
+ # Example for a deeply nested association where nested items have parents of
24
+ # the same type:
25
+ #
26
+ # INSERT INTO test_deeply_nested_revision_components (parent_id, perma_id)
27
+ # SELECT new_children.id,
28
+ # old_deep_items.perma_id + OFFSET,
29
+ # old_deep_items.text
30
+ # FROM test_deeply_nested_revision_components AS old_deep_items
31
+ # JOIN test_nested_revision_components AS old_children
32
+ # ON old_children.id = old_deep_items.parent_id
33
+ # JOIN test_nested_revision_components AS new_children
34
+ # ON new_children.parent_id = NEW_ROOT_ID
35
+ # AND new_children.perma_id = old_children.perma_id + CHILD_OFFSET
36
+ # WHERE old_children.parent_id = OLD_ROOT_ID;
37
+ class NestedRevisionComponentCopy
38
+ def initialize(from:, to:, reset_perma_ids: false, connection: ActiveRecord::Base.connection)
39
+ @root_class = from.class
40
+ @old_root_id = from.id
41
+ @new_root_id = to.id
42
+
43
+ @reset_perma_ids = reset_perma_ids
44
+ @connection = connection
45
+
46
+ @offsets = Hash.new(0)
47
+ @entry = to.entry_for_auto_generated_perma_id
48
+ end
49
+
50
+ def perform_for(revision_components:)
51
+ perform(Model.for(revision_components:))
52
+ end
53
+
54
+ def perform_for_nested_revision_components
55
+ perform(Model.for_nested_revision_components(@root_class))
56
+ end
57
+
58
+ private
59
+
60
+ def perform(models)
61
+ lock_entry_if_resetting_perma_ids do
62
+ @entry_counter = @entry.perma_id_counter
63
+ copy_all(models)
64
+
65
+ update_perma_id_counter_if_changed
66
+ end
67
+ end
68
+
69
+ def lock_entry_if_resetting_perma_ids(&block)
70
+ if @reset_perma_ids
71
+ @entry.with_lock(&block)
72
+ else
73
+ block.call
74
+ end
75
+ end
76
+
77
+ def update_perma_id_counter_if_changed
78
+ return unless @entry_counter != @entry.perma_id_counter
79
+
80
+ @entry.update!(perma_id_counter: @entry_counter)
81
+ end
82
+
83
+ def copy_all(models, chain: [])
84
+ models.each do |model|
85
+ compute_offset(model, chain)
86
+ bulk_insert(model, chain)
87
+
88
+ copy_all(model.nested_models,
89
+ chain: chain + [model])
90
+ end
91
+ end
92
+
93
+ def compute_offset(model, chain)
94
+ return nil unless @reset_perma_ids
95
+
96
+ # When resetting perma_ids, shift old ids so the smallest
97
+ # becomes entry.perma_id_counter + 1. Example: current_max = 10,
98
+ # old perma ids=[3, 5] results in offset 10 - 3 + 1 = 8 and
99
+ # hence new ids [11, 13].
100
+ min_perma_id, max_perma_id = get_min_max_perma_id(model, chain)
101
+
102
+ return nil unless min_perma_id
103
+
104
+ offset = @entry_counter - min_perma_id + 1
105
+
106
+ @entry_counter = max_perma_id + offset
107
+ @offsets[model] = offset
108
+ end
109
+
110
+ def get_min_max_perma_id(model, chain)
111
+ row = @connection.select_one(<<-SQL.squish)
112
+ SELECT MIN(#{model.old_table_alias}.#{model.perma_id_column}) AS min_perma_id,
113
+ MAX(#{model.old_table_alias}.#{model.perma_id_column}) AS max_perma_id
114
+ FROM #{model.table_name} AS #{model.old_table_alias}
115
+ #{join_old_parents(model, chain)}
116
+ WHERE #{where_belongs_to_old_root(model, chain)}
117
+ SQL
118
+
119
+ return nil unless row && row['min_perma_id']
120
+
121
+ [row['min_perma_id'].to_i, row['max_perma_id'].to_i]
122
+ end
123
+
124
+ def bulk_insert(model, chain)
125
+ @connection.execute(<<~SQL.squish)
126
+ INSERT INTO #{model.table_name} (#{model.insert_list})
127
+ SELECT #{select_with_new_foreign_key_value(model, chain)}
128
+ FROM #{model.table_name} AS #{model.old_table_alias}
129
+ #{join_old_parents(model, chain)}
130
+ #{join_new_parents(chain)}
131
+ WHERE #{where_belongs_to_old_root(model, chain)}
132
+ SQL
133
+ end
134
+
135
+ def select_with_new_foreign_key_value(model, chain)
136
+ model.select_list(
137
+ new_foreign_key_value: chain.empty? ? @new_root_id : chain.last.new_id,
138
+ perma_id_offset: @offsets[model]
139
+ )
140
+ end
141
+
142
+ def join_old_parents(model, chain)
143
+ [*chain, model].reverse_each.each_cons(2).map { |child, parent|
144
+ parent.join_as_old_on(id_value: child.old_foreign_key)
145
+ }.join("\n")
146
+ end
147
+
148
+ def join_new_parents(chain)
149
+ return '' if chain.empty?
150
+
151
+ [
152
+ chain.first.join_as_new_on_matching_perma_id(
153
+ foreign_key_value: @new_root_id,
154
+ perma_id_offset: @offsets[chain.first]
155
+ ),
156
+ *chain.each_cons(2).map do |parent, child|
157
+ child.join_as_new_on_matching_perma_id(
158
+ foreign_key_value: parent.new_id,
159
+ perma_id_offset: @offsets[child]
160
+ )
161
+ end
162
+ ].join("\n")
163
+ end
164
+
165
+ def where_belongs_to_old_root(model, chain)
166
+ "#{[*chain, model].first.old_foreign_key} = #{@old_root_id}"
167
+ end
168
+
169
+ Model = Struct.new(:klass, :foreign_key, :level, keyword_init: true) do
170
+ def self.for(revision_components:)
171
+ revision_components.map do |revision_component|
172
+ Model.new(klass: revision_component, foreign_key: 'revision_id', level: 1)
173
+ end
174
+ end
175
+
176
+ def self.for_nested_revision_components(klass, level: 1)
177
+ klass.nested_revision_component_collection_names.map do |collection_name|
178
+ reflection = klass.reflect_on_association(collection_name)
179
+ Model.new(klass: reflection.klass, foreign_key: reflection.foreign_key, level:)
180
+ end
181
+ end
182
+
183
+ def nested_models
184
+ Model.for_nested_revision_components(klass, level: level + 1)
185
+ end
186
+
187
+ def table_name
188
+ klass.table_name
189
+ end
190
+
191
+ def new_table_alias
192
+ "new_#{level}"
193
+ end
194
+
195
+ def old_table_alias
196
+ "old_#{level}"
197
+ end
198
+
199
+ def old_foreign_key
200
+ "#{old_table_alias}.#{foreign_key}"
201
+ end
202
+
203
+ def new_id
204
+ "#{new_table_alias}.id"
205
+ end
206
+
207
+ def perma_id_column
208
+ @perma_id_column ||=
209
+ if klass.column_names.include?('file_perma_id')
210
+ 'file_perma_id'
211
+ else
212
+ 'perma_id'
213
+ end
214
+ end
215
+
216
+ def insert_list
217
+ quoted_list(insert_columns)
218
+ end
219
+
220
+ def select_list(new_foreign_key_value:, perma_id_offset:)
221
+ insert_columns.map { |column|
222
+ case column
223
+ when foreign_key
224
+ new_foreign_key_value
225
+ when 'created_at', 'updated_at'
226
+ 'NOW()'
227
+ when 'perma_id'
228
+ perma_id_expr(perma_id_offset)
229
+ else
230
+ "#{old_table_alias}.#{column}"
231
+ end
232
+ }.join(', ')
233
+ end
234
+
235
+ def join_as_old_on(id_value:)
236
+ "JOIN #{table_name} AS #{old_table_alias} " \
237
+ "ON #{old_table_alias}.id = #{id_value}"
238
+ end
239
+
240
+ def join_as_new_on_matching_perma_id(foreign_key_value:, perma_id_offset:)
241
+ "JOIN #{table_name} AS #{new_table_alias} " \
242
+ "ON #{new_table_alias}.#{foreign_key} = #{foreign_key_value} " \
243
+ "AND #{new_table_alias}.#{perma_id_column} = #{perma_id_expr(perma_id_offset)}"
244
+ end
245
+
246
+ private
247
+
248
+ def insert_columns
249
+ klass.column_names.reject { |c| c == 'id' }
250
+ end
251
+
252
+ def perma_id_expr(offset)
253
+ return "#{old_table_alias}.#{perma_id_column}" if offset.zero?
254
+
255
+ "#{old_table_alias}.#{perma_id_column} + #{offset}"
256
+ end
257
+
258
+ def quoted_list(columns)
259
+ columns.map { |c| ActiveRecord::Base.connection.quote_column_name(c) }.join(', ')
260
+ end
261
+ end
262
+ end
263
+ end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class NullUser
2
+ class NullUser # rubocop:todo Style/Documentation
3
3
  def full_name
4
4
  '-'
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class OverviewButton
2
+ class OverviewButton # rubocop:todo Style/Documentation
3
3
  attr_reader :revision
4
4
 
5
5
  def initialize(revision)
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class Page < ApplicationRecord
2
+ class Page < ApplicationRecord # rubocop:todo Style/Documentation
3
3
  include SerializedConfiguration
4
4
  include NestedRevisionComponent
5
5
  include AutoGeneratedPermaId
@@ -8,9 +8,13 @@ module Pageflow
8
8
 
9
9
  attr_accessor :is_first
10
10
 
11
- validates_inclusion_of :template, :in => ->(_) { Pageflow.config.page_types.names }
11
+ validates_inclusion_of :template, in: ->(_) { Pageflow.config.page_types.names }
12
12
 
13
- scope :displayed_in_navigation, -> { where(:display_in_navigation => true) }
13
+ scope :displayed_in_navigation, -> { where(display_in_navigation: true) }
14
+
15
+ def entry_for_auto_generated_perma_id
16
+ chapter.storyline.revision.entry
17
+ end
14
18
 
15
19
  def title
16
20
  configuration['title'].presence || configuration['additional_title']
@@ -2,6 +2,7 @@ module Pageflow
2
2
  # @api private
3
3
  class Permalink < ApplicationRecord
4
4
  extend FriendlyId
5
+
5
6
  friendly_id :slug_candidates, use: :scoped, scope: :directory
6
7
 
7
8
  before_validation :set_default_slug
@@ -11,15 +12,29 @@ module Pageflow
11
12
  belongs_to :directory, class_name: 'PermalinkDirectory'
12
13
 
13
14
  validates(:slug,
14
- format: /\A[0-9a-zA-Z_-]+\z/,
15
+ format: {with: /\A[0-9a-zA-Z_-]+\z/, allow_blank: true},
15
16
  uniqueness: {scope: :directory})
16
17
 
17
18
  validate :belongs_to_same_site_as_entry
18
19
 
20
+ before_update(:create_redirect,
21
+ if: lambda {
22
+ entry.first_published_at.present? && (slug_changed? || directory_changed?)
23
+ })
24
+
25
+ attr_accessor :allow_root_path
26
+
19
27
  private
20
28
 
21
29
  def set_default_slug
22
- self.slug = entry.default_permalink_slug if slug == ''
30
+ return unless slug == ''
31
+ return if allow_emtpy_slug?
32
+
33
+ self.slug = entry.default_permalink_slug
34
+ end
35
+
36
+ def allow_emtpy_slug?
37
+ allow_root_path && directory&.path&.blank?
23
38
  end
24
39
 
25
40
  def slug_candidates
@@ -35,5 +50,11 @@ module Pageflow
35
50
 
36
51
  errors.add(:directory, :invalid)
37
52
  end
53
+
54
+ def create_redirect
55
+ directory.redirects.where(slug:).delete_all
56
+ entry.permalink_redirects.create!(slug: slug_was,
57
+ directory_id: directory_id_was)
58
+ end
38
59
  end
39
60
  end
@@ -6,5 +6,12 @@ module Pageflow
6
6
  validates(:path,
7
7
  format: %r{\A([0-9a-zA-Z-]+/)*\z},
8
8
  uniqueness: {scope: :site_id})
9
+
10
+ has_many(:permalinks,
11
+ foreign_key: :directory_id)
12
+
13
+ has_many(:redirects,
14
+ class_name: 'PermalinkRedirect',
15
+ foreign_key: :directory_id)
9
16
  end
10
17
  end
@@ -0,0 +1,7 @@
1
+ module Pageflow
2
+ # @api private
3
+ class PermalinkRedirect < ApplicationRecord
4
+ belongs_to :entry
5
+ belongs_to :directory, class_name: 'PermalinkDirectory'
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class PositionedFile
2
+ class PositionedFile # rubocop:todo Style/Documentation
3
3
  attr_reader :file, :position_x, :position_y
4
4
 
5
5
  delegate :thumbnail_url, :perma_id, to: :file
@@ -11,7 +11,7 @@ module Pageflow
11
11
  end
12
12
 
13
13
  def ==(other)
14
- super(other) ||
14
+ super ||
15
15
  other == file ||
16
16
  (other.is_a?(PositionedFile) && other.file == file) ||
17
17
  (other.class == file.class && other.id == file.id)
@@ -29,13 +29,13 @@ module Pageflow
29
29
  Null.new
30
30
  end
31
31
 
32
- class Null < PositionedFile
32
+ class Null < PositionedFile # rubocop:todo Style/Documentation
33
33
  def initialize
34
34
  super(nil)
35
35
  end
36
36
 
37
- def thumbnail_url(*args)
38
- ImageFile.new.attachment.url(*args)
37
+ def thumbnail_url(*)
38
+ ImageFile.new.attachment.url(*)
39
39
  end
40
40
 
41
41
  def blank?
@@ -0,0 +1,55 @@
1
+ module Pageflow
2
+ # @api private
3
+ class PotentialEntryTranslations
4
+ def initialize(entry)
5
+ @entry = entry
6
+ end
7
+
8
+ def self.for(entry)
9
+ new(entry)
10
+ end
11
+
12
+ def resolve
13
+ preload(
14
+ group_by_translation_group(
15
+ exclude_translations(
16
+ other_entries_of_account
17
+ )
18
+ )
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def other_entries_of_account
25
+ @entry.account.entries.where.not(id: @entry.id)
26
+ end
27
+
28
+ def exclude_translations(scope)
29
+ return scope unless @entry.translation_group
30
+
31
+ scope
32
+ .where.not(translation_group_id: @entry.translation_group)
33
+ .or(scope.where(translation_group: nil))
34
+ end
35
+
36
+ def group_by_translation_group(scope)
37
+ scope
38
+ # Use MIN(id) to choose an arbitrary entry to represent its
39
+ # translation group. MIN(translation_group_id) is needed since
40
+ # technically translation_group_id is not part of the GROUP BY
41
+ # clause.
42
+ .select(<<-SQL)
43
+ MIN(id) as id,
44
+ GROUP_CONCAT(title) as title,
45
+ MIN(translation_group_id) as translation_group_id
46
+ SQL
47
+ .group('IFNULL(translation_group_id, id)')
48
+ .order('title ASC')
49
+ end
50
+
51
+ def preload(scope)
52
+ scope.includes(translation_group: :entries)
53
+ end
54
+ end
55
+ end
@@ -23,6 +23,7 @@ module Pageflow
23
23
 
24
24
  def users_for_entry(entry)
25
25
  return User.none unless manages_entry?(entry)
26
+
26
27
  exclude_users_with_membership(entry, entry.account.users)
27
28
  end
28
29
 
@@ -88,8 +89,8 @@ module Pageflow
88
89
  'existing_memberships.user_id = :user_id AND ' \
89
90
  "existing_memberships.entity_id = #{entity_model.table_name}.id AND " \
90
91
  'existing_memberships.entity_type = :entity_type',
91
- user_id: user.id,
92
- entity_type: entity_model.name])
92
+ {user_id: user.id,
93
+ entity_type: entity_model.name}])
93
94
  end
94
95
 
95
96
  def existing_memberships_on(entity)
@@ -97,8 +98,8 @@ module Pageflow
97
98
  'existing_memberships.user_id = users.id AND ' \
98
99
  'existing_memberships.entity_id = :entity_id AND ' \
99
100
  'existing_memberships.entity_type = :entity_type',
100
- entity_id: entity.id,
101
- entity_type: entity.class.name])
101
+ {entity_id: entity.id,
102
+ entity_type: entity.class.name}])
102
103
  end
103
104
 
104
105
  def existing_membership_is_missing
@@ -5,12 +5,14 @@ module Pageflow
5
5
 
6
6
  attr_accessor :share_target
7
7
 
8
- delegate(:authenticate, to: :entry)
8
+ delegate(:authenticate,
9
+ :default_translation?,
10
+ to: :entry)
9
11
 
10
12
  delegate(:password_protected?, to: :revision)
11
13
 
12
14
  def initialize(entry, revision = nil, theme: nil)
13
- super(entry, revision || entry.published_revision, theme: theme)
15
+ super(entry, revision || entry.published_revision, theme:)
14
16
  @custom_revision = revision
15
17
  end
16
18
 
@@ -18,6 +20,24 @@ module Pageflow
18
20
  revision.title.presence || entry.title
19
21
  end
20
22
 
23
+ def translations(scope = -> { self }, include_noindex: false)
24
+ return [] unless entry.translation_group
25
+
26
+ if published_revision?
27
+ self.class.wrap_all(
28
+ published_translation_entries(include_noindex:).instance_exec(&scope)
29
+ )
30
+ else
31
+ self.class.wrap_all_drafts(
32
+ entry.translation_group.entries.instance_exec(&scope)
33
+ )
34
+ end
35
+ end
36
+
37
+ def cutoff_mode_enabled_for?(request)
38
+ Pageflow.config.cutoff_modes.enabled_for?(self, request)
39
+ end
40
+
21
41
  def stylesheet_model
22
42
  custom_revision? ? revision : entry
23
43
  end
@@ -26,8 +46,8 @@ module Pageflow
26
46
  revision.cache_key_with_version
27
47
  end
28
48
 
29
- def thumbnail_url(*args)
30
- thumbnail_file.thumbnail_url(*args)
49
+ def thumbnail_url(*)
50
+ thumbnail_file.thumbnail_url(*)
31
51
  end
32
52
 
33
53
  def thumbnail_file
@@ -38,6 +58,7 @@ module Pageflow
38
58
 
39
59
  def page_thumbnail_file(page)
40
60
  return unless page.present?
61
+
41
62
  ThumbnailFileResolver.new(self, page.page_type.thumbnail_candidates, page.configuration)
42
63
  .find_thumbnail
43
64
  end
@@ -46,19 +67,30 @@ module Pageflow
46
67
  PublishedEntry.new(scope.published.find(id))
47
68
  end
48
69
 
49
- def self.find_by_permalink(directory: nil, slug:, scope:)
70
+ def self.find_by_permalink(slug:, scope:, directory: nil)
50
71
  wrap(
51
72
  scope.published.includes(permalink: :directory).where(
52
73
  pageflow_permalink_directories: {path: directory || ''},
53
- pageflow_permalinks: {slug: slug}
74
+ pageflow_permalinks: {slug:}
54
75
  ).first
55
76
  )
56
77
  end
57
78
 
58
- def self.wrap_all(scope)
59
- scope
60
- .includes(:published_revision)
61
- .map { |entry| new(entry) }
79
+ def self.find_by_permalink_redirect(site:, slug:, directory: nil)
80
+ wrap(
81
+ PermalinkRedirect.joins(:entry, :directory).merge(Entry.published).where(
82
+ slug:,
83
+ pageflow_permalink_directories: {
84
+ path: directory || '',
85
+ site:
86
+ }
87
+ ).first&.entry
88
+ )
89
+ end
90
+
91
+ def self.wrap_all(entries)
92
+ entries = entries.includes(:published_revision) unless entries.loaded?
93
+ entries.map { |entry| new(entry) }
62
94
  end
63
95
 
64
96
  def cache_key
@@ -78,14 +110,33 @@ module Pageflow
78
110
  ].compact.join('-').presence
79
111
  end
80
112
 
113
+ def published_revision?
114
+ !custom_revision?
115
+ end
116
+
81
117
  private
82
118
 
119
+ def published_translation_entries(include_noindex:)
120
+ translation_group = entry.translation_group
121
+
122
+ if password_protected? && include_noindex
123
+ translation_group.published_entries
124
+ elsif password_protected?
125
+ translation_group.entries_published_without_noindex
126
+ elsif include_noindex
127
+ translation_group.entries_published_without_password_protection
128
+ else
129
+ translation_group.publicly_visible_entries
130
+ end
131
+ end
132
+
83
133
  def custom_revision?
84
134
  @custom_revision
85
135
  end
86
136
 
87
137
  def share_image_file
88
- PositionedFile.wrap(find_file_by_perma_id(ImageFile, share_image_id), share_image_x, share_image_y)
138
+ PositionedFile.wrap(find_file_by_perma_id(ImageFile, share_image_id), share_image_x,
139
+ share_image_y)
89
140
  end
90
141
 
91
142
  class << self