pageflow 17.0.5 → 17.2.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 (636) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +342 -41
  3. data/README.md +3 -5
  4. data/admins/pageflow/accounts.rb +22 -20
  5. data/admins/pageflow/entry.rb +76 -26
  6. data/admins/pageflow/entry_templates.rb +10 -9
  7. data/admins/pageflow/folder.rb +1 -1
  8. data/admins/pageflow/membership.rb +15 -15
  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/images/pageflow/admin/icons/comment.svg +1 -0
  15. data/app/assets/javascripts/pageflow/admin/entries.js +30 -5
  16. data/app/assets/javascripts/pageflow/dist/ui.js +4228 -900
  17. data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
  18. data/app/assets/javascripts/pageflow/ui.js +0 -1
  19. data/app/assets/stylesheets/pageflow/admin/entries/index_table.scss +2 -1
  20. data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
  21. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -0
  22. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  23. data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
  24. data/app/assets/stylesheets/pageflow/animations.scss +1 -0
  25. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +27 -5
  26. data/app/assets/stylesheets/pageflow/editor/base.scss +11 -1
  27. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +20 -2
  28. data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
  29. data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
  30. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +5 -2
  31. data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
  32. data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
  33. data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
  34. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
  35. data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
  36. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
  37. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
  38. data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
  39. data/app/assets/stylesheets/pageflow/editor/files.scss +242 -50
  40. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +161 -9
  41. data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
  42. data/app/assets/stylesheets/pageflow/editor/info_box.scss +23 -3
  43. data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
  44. data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
  45. data/app/assets/stylesheets/pageflow/editor/list.scss +89 -37
  46. data/app/assets/stylesheets/pageflow/editor/list_search_field.scss +52 -0
  47. data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
  48. data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
  49. data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
  50. data/app/assets/stylesheets/pageflow/editor/outline.scss +0 -13
  51. data/app/assets/stylesheets/pageflow/editor/sortable.scss +12 -0
  52. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +4 -4
  53. data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
  54. data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
  55. data/app/assets/stylesheets/pageflow/ui/forms.scss +38 -4
  56. data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
  57. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -8
  58. data/app/assets/stylesheets/pageflow/ui/input/file_name_input.scss +37 -0
  59. data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
  60. data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
  61. data/app/assets/stylesheets/pageflow/ui/properties.scss +34 -0
  62. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
  63. data/app/assets/stylesheets/pageflow/ui.scss +4 -1
  64. data/app/controllers/concerns/pageflow/controller_delegation.rb +1 -1
  65. data/app/controllers/concerns/pageflow/edit_locking.rb +17 -10
  66. data/app/controllers/concerns/pageflow/entry_password_protection.rb +1 -0
  67. data/app/controllers/concerns/pageflow/public_https_mode.rb +2 -1
  68. data/app/controllers/concerns/pageflow/quota_verification.rb +5 -4
  69. data/app/controllers/pageflow/admin/initial_passwords_controller.rb +1 -0
  70. data/app/controllers/pageflow/application_controller.rb +19 -19
  71. data/app/controllers/pageflow/chapters_controller.rb +3 -2
  72. data/app/controllers/pageflow/edit_locks_controller.rb +3 -2
  73. data/app/controllers/pageflow/editor/encoding_confirmations_controller.rb +1 -0
  74. data/app/controllers/pageflow/editor/entries_controller.rb +3 -1
  75. data/app/controllers/pageflow/editor/entry_publications_controller.rb +1 -0
  76. data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
  77. data/app/controllers/pageflow/editor/file_import_controller.rb +4 -3
  78. data/app/controllers/pageflow/editor/files_controller.rb +20 -10
  79. data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
  80. data/app/controllers/pageflow/editor/widgets_controller.rb +1 -0
  81. data/app/controllers/pageflow/entries_controller.rb +85 -21
  82. data/app/controllers/pageflow/feeds_controller.rb +1 -1
  83. data/app/controllers/pageflow/files_controller.rb +1 -0
  84. data/app/controllers/pageflow/pages_controller.rb +4 -2
  85. data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
  86. data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
  87. data/app/controllers/pageflow/review/comments_controller.rb +53 -0
  88. data/app/controllers/pageflow/revisions_controller.rb +5 -1
  89. data/app/controllers/pageflow/sitemaps_controller.rb +1 -1
  90. data/app/controllers/pageflow/storylines_controller.rb +1 -0
  91. data/app/helpers/pageflow/admin/cutoff_modes_helper.rb +12 -0
  92. data/app/helpers/pageflow/admin/entries_helper.rb +41 -1
  93. data/app/helpers/pageflow/admin/entry_translations_helper.rb +18 -0
  94. data/app/helpers/pageflow/admin/features_helper.rb +1 -1
  95. data/app/helpers/pageflow/admin/form_helper.rb +3 -3
  96. data/app/helpers/pageflow/admin/locales_helper.rb +10 -2
  97. data/app/helpers/pageflow/admin/memberships_helper.rb +32 -32
  98. data/app/helpers/pageflow/admin/revisions_helper.rb +1 -1
  99. data/app/helpers/pageflow/admin/sites_helper.rb +22 -0
  100. data/app/helpers/pageflow/admin/users_helper.rb +7 -5
  101. data/app/helpers/pageflow/admin/widgets_helper.rb +5 -5
  102. data/app/helpers/pageflow/asset_urls_helper.rb +1 -1
  103. data/app/helpers/pageflow/audio_files_helper.rb +5 -2
  104. data/app/helpers/pageflow/background_image_helper.rb +7 -6
  105. data/app/helpers/pageflow/common_entry_seed_helper.rb +7 -7
  106. data/app/helpers/pageflow/editor/config_helper.rb +3 -1
  107. data/app/helpers/pageflow/editor/files_helper.rb +1 -1
  108. data/app/helpers/pageflow/embed_code_helper.rb +3 -3
  109. data/app/helpers/pageflow/entries_helper.rb +52 -46
  110. data/app/helpers/pageflow/entry_json_seed_helper.rb +7 -6
  111. data/app/helpers/pageflow/feeds_helper.rb +2 -2
  112. data/app/helpers/pageflow/file_background_images_helper.rb +2 -2
  113. data/app/helpers/pageflow/file_thumbnails_helper.rb +4 -3
  114. data/app/helpers/pageflow/files_helper.rb +5 -4
  115. data/app/helpers/pageflow/folders_helper.rb +8 -5
  116. data/app/helpers/pageflow/help_entries_helper.rb +2 -2
  117. data/app/helpers/pageflow/hreflang_links_helper.rb +37 -0
  118. data/app/helpers/pageflow/info_box_helper.rb +7 -11
  119. data/app/helpers/pageflow/media_query_helper.rb +3 -3
  120. data/app/helpers/pageflow/navigation_bar_helper.rb +1 -1
  121. data/app/helpers/pageflow/overview_helper.rb +1 -1
  122. data/app/helpers/pageflow/page_types_helper.rb +5 -5
  123. data/app/helpers/pageflow/pages_helper.rb +29 -15
  124. data/app/helpers/pageflow/public_i18n_helper.rb +10 -9
  125. data/app/helpers/pageflow/quota_helper.rb +4 -4
  126. data/app/helpers/pageflow/render_json_helper.rb +6 -3
  127. data/app/helpers/pageflow/revision_file_helper.rb +2 -1
  128. data/app/helpers/pageflow/sites_helper.rb +3 -3
  129. data/app/helpers/pageflow/social_share_helper.rb +24 -10
  130. data/app/helpers/pageflow/social_share_links_helper.rb +12 -9
  131. data/app/helpers/pageflow/structured_data_helper.rb +5 -2
  132. data/app/helpers/pageflow/stub_page_configuration.rb +7 -3
  133. data/app/helpers/pageflow/text_direction_helper.rb +1 -1
  134. data/app/helpers/pageflow/themes_helper.rb +2 -4
  135. data/app/helpers/pageflow/video_files_helper.rb +29 -13
  136. data/app/helpers/pageflow/widgets_helper.rb +8 -6
  137. data/app/inputs/pageflow_permalink_input.rb +8 -3
  138. data/app/jobs/pageflow/application_job.rb +1 -0
  139. data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +1 -1
  140. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -0
  141. data/app/jobs/pageflow/poll_zencoder_job.rb +3 -2
  142. data/app/jobs/pageflow/process_image_or_text_track_job.rb +1 -0
  143. data/app/jobs/pageflow/prune_auto_snapshots_job.rb +1 -0
  144. data/app/jobs/pageflow/request_meta_data_from_zencoder_job.rb +1 -0
  145. data/app/jobs/pageflow/submit_file_to_zencoder_job.rb +1 -0
  146. data/app/mailers/pageflow/user_mailer.rb +1 -0
  147. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +20 -1
  148. data/app/models/concerns/pageflow/entry_publication_states.rb +7 -3
  149. data/app/models/concerns/pageflow/feature_target.rb +8 -4
  150. data/app/models/concerns/pageflow/output_source.rb +3 -3
  151. data/app/models/concerns/pageflow/permalinkable.rb +2 -1
  152. data/app/models/concerns/pageflow/reusable_file.rb +24 -24
  153. data/app/models/concerns/pageflow/serialization_blacklist.rb +3 -5
  154. data/app/models/concerns/pageflow/suspendable.rb +5 -3
  155. data/app/models/concerns/pageflow/theme_referencer.rb +1 -1
  156. data/app/models/concerns/pageflow/translatable.rb +62 -0
  157. data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
  158. data/app/models/concerns/pageflow/uploadable_file.rb +6 -6
  159. data/app/models/pageflow/account.rb +6 -5
  160. data/app/models/pageflow/account_member_query.rb +1 -1
  161. data/app/models/pageflow/account_role_query.rb +1 -1
  162. data/app/models/pageflow/application_query.rb +1 -1
  163. data/app/models/pageflow/application_record.rb +1 -1
  164. data/app/models/pageflow/audio_file.rb +4 -4
  165. data/app/models/pageflow/audio_file_url_templates.rb +1 -1
  166. data/app/models/pageflow/authentication_token.rb +2 -2
  167. data/app/models/pageflow/chapter.rb +6 -1
  168. data/app/models/pageflow/chapter_scaffold.rb +2 -4
  169. data/app/models/pageflow/cname_site_request_scope.rb +1 -1
  170. data/app/models/pageflow/comment.rb +30 -0
  171. data/app/models/pageflow/comment_thread.rb +65 -0
  172. data/app/models/pageflow/comment_thread_read.rb +22 -0
  173. data/app/models/pageflow/customized_theme.rb +9 -8
  174. data/app/models/pageflow/draft_entry.rb +49 -9
  175. data/app/models/pageflow/edit_lock.rb +19 -21
  176. data/app/models/pageflow/encoding_confirmation.rb +2 -2
  177. data/app/models/pageflow/entries_feed.rb +2 -2
  178. data/app/models/pageflow/entry.rb +46 -30
  179. data/app/models/pageflow/entry_at_revision.rb +14 -2
  180. data/app/models/pageflow/entry_comment_summary.rb +114 -0
  181. data/app/models/pageflow/entry_duplicate.rb +8 -5
  182. data/app/models/pageflow/entry_publication.rb +2 -2
  183. data/app/models/pageflow/entry_role_query.rb +4 -4
  184. data/app/models/pageflow/entry_template.rb +4 -4
  185. data/app/models/pageflow/entry_title_or_account_name_query.rb +2 -2
  186. data/app/models/pageflow/entry_translation_group.rb +42 -0
  187. data/app/models/pageflow/file_folder.rb +74 -0
  188. data/app/models/pageflow/file_reuse.rb +5 -4
  189. data/app/models/pageflow/file_usage.rb +18 -3
  190. data/app/models/pageflow/folder.rb +2 -2
  191. data/app/models/pageflow/home_button.rb +7 -7
  192. data/app/models/pageflow/image_file.rb +44 -25
  193. data/app/models/pageflow/image_file_url_templates.rb +4 -4
  194. data/app/models/pageflow/invitation_form.rb +2 -1
  195. data/app/models/pageflow/legal_info.rb +38 -0
  196. data/app/models/pageflow/managed_user_query.rb +1 -1
  197. data/app/models/pageflow/membership.rb +5 -5
  198. data/app/models/pageflow/nested_revision_component_copy.rb +263 -0
  199. data/app/models/pageflow/null_user.rb +1 -1
  200. data/app/models/pageflow/overview_button.rb +1 -1
  201. data/app/models/pageflow/page.rb +7 -3
  202. data/app/models/pageflow/permalink.rb +24 -2
  203. data/app/models/pageflow/permalink_directory.rb +7 -0
  204. data/app/models/pageflow/permalink_redirect.rb +7 -0
  205. data/app/models/pageflow/positioned_file.rb +5 -5
  206. data/app/models/pageflow/potential_entry_translations.rb +55 -0
  207. data/app/models/pageflow/potential_memberships.rb +5 -4
  208. data/app/models/pageflow/published_entry.rb +62 -11
  209. data/app/models/pageflow/revision.rb +29 -16
  210. data/app/models/pageflow/roles.rb +14 -18
  211. data/app/models/pageflow/site.rb +46 -3
  212. data/app/models/pageflow/site_root_entry_form.rb +27 -0
  213. data/app/models/pageflow/sitemaps.rb +10 -1
  214. data/app/models/pageflow/storyline.rb +5 -2
  215. data/app/models/pageflow/storyline_scaffold.rb +1 -1
  216. data/app/models/pageflow/text_track_file.rb +1 -1
  217. data/app/models/pageflow/text_track_file_url_templates.rb +1 -1
  218. data/app/models/pageflow/theme_customization_file.rb +3 -2
  219. data/app/models/pageflow/thumbnail_file_resolver.rb +1 -1
  220. data/app/models/pageflow/url_template.rb +1 -1
  221. data/app/models/pageflow/used_file.rb +13 -3
  222. data/app/models/pageflow/user_name_query.rb +2 -2
  223. data/app/models/pageflow/video_file.rb +22 -15
  224. data/app/models/pageflow/video_file_url_templates.rb +6 -4
  225. data/app/models/pageflow/widget.rb +5 -4
  226. data/app/models/pageflow/with_file_usage_extension.rb +1 -1
  227. data/app/models/pageflow/zencoder_attachment.rb +8 -8
  228. data/app/policies/pageflow/account_policy.rb +3 -1
  229. data/app/policies/pageflow/admin/admin_only_tab_policy.rb +1 -0
  230. data/app/policies/pageflow/admin/entry_tab_policy.rb +1 -0
  231. data/app/policies/pageflow/application_policy.rb +2 -0
  232. data/app/policies/pageflow/entry_policy.rb +7 -1
  233. data/app/policies/pageflow/entry_template_policy.rb +2 -1
  234. data/app/policies/pageflow/file_policy.rb +4 -7
  235. data/app/policies/pageflow/folder_policy.rb +18 -8
  236. data/app/policies/pageflow/membership_policy.rb +6 -4
  237. data/app/policies/pageflow/site_policy.rb +10 -3
  238. data/app/policies/pageflow/user_policy.rb +6 -4
  239. data/app/state_machines/pageflow/image_and_text_track_processing_state_machine.rb +3 -2
  240. data/app/state_machines/pageflow/media_encoding_state_machine.rb +5 -4
  241. data/app/views/admin/accounts/_entry_template_details.html.arb +2 -2
  242. data/app/views/admin/entries/_attributes_table.html.arb +6 -5
  243. data/app/views/admin/entries/_form.html.erb +2 -1
  244. data/app/views/admin/entries/_permalink_inputs.html.erb +9 -2
  245. data/app/views/admin/entry_templates/_form.html.erb +1 -3
  246. data/app/views/admin/site_root_entry/choose.html.erb +19 -0
  247. data/app/views/admin/sites/_fields.html.erb +14 -1
  248. data/app/views/admin/translations/_form.html.erb +31 -0
  249. data/app/views/admin/users/_attributes_table.html.arb +13 -0
  250. data/app/views/components/pageflow/admin/add_membership_button.rb +13 -12
  251. data/app/views/components/pageflow/admin/custom_scopes_renderer.rb +1 -0
  252. data/app/views/components/pageflow/admin/embed_code_field.rb +1 -0
  253. data/app/views/components/pageflow/admin/embedded_index_table.rb +9 -10
  254. data/app/views/components/pageflow/admin/entries_tab.rb +1 -0
  255. data/app/views/components/pageflow/admin/entry_publication_state_indicator.rb +6 -5
  256. data/app/views/components/pageflow/admin/entry_templates_tab.rb +2 -1
  257. data/app/views/components/pageflow/admin/entry_translations_tab.rb +102 -0
  258. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +1 -0
  259. data/app/views/components/pageflow/admin/extensible_attributes_table.rb +9 -8
  260. data/app/views/components/pageflow/admin/features_tab.rb +1 -0
  261. data/app/views/components/pageflow/admin/grouped_folder_list.rb +1 -0
  262. data/app/views/components/pageflow/admin/icon_link_to.rb +1 -0
  263. data/app/views/components/pageflow/admin/members_tab.rb +4 -3
  264. data/app/views/components/pageflow/admin/membership_role_with_tooltip.rb +3 -2
  265. data/app/views/components/pageflow/admin/revisions_tab.rb +14 -10
  266. data/app/views/components/pageflow/admin/sites_tab.rb +1 -0
  267. data/app/views/components/pageflow/admin/tabs_view.rb +1 -0
  268. data/app/views/components/pageflow/admin/timestamp.rb +6 -5
  269. data/app/views/components/pageflow/admin/user_account_badge_list.rb +2 -3
  270. data/app/views/components/pageflow/admin/user_accounts_tab.rb +1 -0
  271. data/app/views/components/pageflow/admin/user_entries_tab.rb +1 -0
  272. data/app/views/components/pageflow/admin/users_tab.rb +1 -0
  273. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
  274. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +5 -3
  275. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
  276. data/app/views/pageflow/editor/entries/index.json.jbuilder +1 -1
  277. data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
  278. data/app/views/pageflow/editor/entries/show.json.jbuilder +1 -1
  279. data/app/views/pageflow/editor/entry_publications/check.json.jbuilder +2 -2
  280. data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
  281. data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
  282. data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
  283. data/app/views/pageflow/editor/files/_file.json.jbuilder +7 -14
  284. data/app/views/pageflow/editor/files/create.json.jbuilder +1 -1
  285. data/app/views/pageflow/editor/files/index.json.jbuilder +1 -1
  286. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
  287. data/app/views/pageflow/editor/sites/_site.json.jbuilder +1 -0
  288. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +4 -3
  289. data/app/views/pageflow/entries/share_menu/_bluesky_link.html.erb +8 -0
  290. data/app/views/pageflow/entries/share_menu/_threads_link.html.erb +8 -0
  291. data/app/views/pageflow/files/_file.json.jbuilder +5 -3
  292. data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
  293. data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
  294. data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
  295. data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
  296. data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
  297. data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
  298. data/app/views/pageflow/sitemaps/index.xml.builder +9 -1
  299. data/app/views/pageflow/social_share/_entry_meta_tags.html.erb +1 -1
  300. data/app/views/pageflow/social_share/_page_meta_tags.html.erb +1 -1
  301. data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
  302. data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
  303. data/config/initializers/active_admin_patches.rb +3 -6
  304. data/config/initializers/admin_resource_tabs.rb +3 -0
  305. data/config/initializers/paperclip.rb +1 -0
  306. data/config/initializers/symmetric_encryption.rb +1 -1
  307. data/config/initializers/zencoder.rb +24 -13
  308. data/config/locales/de.yml +256 -12
  309. data/config/locales/en.yml +253 -15
  310. data/config/routes.rb +29 -13
  311. data/config/spring.rb +2 -1
  312. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  313. data/db/migrate/20190306161431_copy_file_attributes_of_failed_uploads.rb +2 -2
  314. data/db/migrate/20190820152900_drop_accounts_themes.rb +2 -1
  315. data/db/migrate/20191202145757_create_pageflow_scrolled_sections.rb +11 -11
  316. data/db/migrate/20191202150657_create_pageflow_scrolled_chapters.rb +1 -1
  317. data/db/migrate/20191202154723_create_pageflow_scrolled_content_elements.rb +9 -9
  318. data/db/migrate/20191219143450_add_position_to_content_elements.rb +2 -1
  319. data/db/migrate/20200117133200_change_revision_appearance_option_default_and_null.rb +2 -1
  320. data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +25 -25
  321. data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +3 -3
  322. data/db/migrate/20221215120856_associate_entry_templates_with_sites.rb +2 -2
  323. data/db/migrate/20230120092923_create_other_files.rb +2 -1
  324. data/db/migrate/20230419083307_create_pageflow_entry_translation_group.rb +9 -0
  325. data/db/migrate/20240612110434_add_cutoff_mode_name_to_sites.rb +5 -0
  326. data/db/migrate/20240918084059_create_pageflow_permalink_redirects.rb +14 -0
  327. data/db/migrate/20250508172234_ensure_scrolled_entries_have_main_storyline.rb +14 -0
  328. data/db/migrate/20250617090048_add_custom404_entry_to_sites.rb +5 -0
  329. data/db/migrate/20250617100000_add_perma_id_counter_to_entries.rb +28 -0
  330. data/db/migrate/20250722174123_add_perma_id_to_chapters.rb +10 -0
  331. data/db/migrate/20250725080603_add_perma_id_indexes_to_revision_components.rb +13 -0
  332. data/db/migrate/20250726000000_add_display_name_to_file_usages.rb +5 -0
  333. data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
  334. data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
  335. data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
  336. data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
  337. data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
  338. data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
  339. data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
  340. data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
  341. data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
  342. data/db/migrate/20260728000000_create_file_folders.rb +17 -0
  343. data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
  344. data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
  345. data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
  346. data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
  347. data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
  348. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +12599 -5398
  349. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +1275 -4560
  350. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20204 -22
  351. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18657 -19
  352. data/entry_types/paged/app/controllers/pageflow_paged/entries_controller.rb +1 -0
  353. data/entry_types/paged/app/helpers/pageflow_paged/page_background_asset_helper.rb +1 -1
  354. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +7 -7
  355. data/entry_types/paged/app/views/pageflow_paged/editor/entries/_seed.json.jbuilder +1 -0
  356. data/entry_types/paged/app/views/pageflow_paged/entries/show.html.erb +1 -0
  357. data/entry_types/paged/lib/pageflow_paged/plugin.rb +4 -0
  358. data/entry_types/paged/lib/pageflow_paged/react/page_type.rb +1 -1
  359. data/entry_types/paged/lib/pageflow_paged/react/widget_type.rb +3 -3
  360. data/entry_types/paged/lib/pageflow_paged.rb +1 -1
  361. data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
  362. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +5 -3
  363. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +35 -5
  364. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +4 -5
  365. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +23 -2
  366. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
  367. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +8 -5
  368. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +7 -3
  369. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +72 -10
  370. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
  371. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +41 -12
  372. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +23 -25
  373. data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
  374. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +189 -3
  375. data/entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb +15 -14
  376. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +61 -7
  377. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
  378. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
  379. data/entry_types/scrolled/app/models/pageflow_scrolled/section.rb +12 -1
  380. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +20 -1
  381. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +1 -0
  382. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/sections/_section_with_content_elements.json.jbuilder +2 -2
  383. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +15 -12
  384. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +45 -17
  385. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry_translations.json.jbuilder +14 -0
  386. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +34 -5
  387. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
  388. data/entry_types/scrolled/app/views/pageflow_scrolled/storylines/_storyline.json.jbuilder +7 -0
  389. data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
  390. data/entry_types/scrolled/config/locales/de.yml +1094 -78
  391. data/entry_types/scrolled/config/locales/en.yml +1083 -78
  392. data/entry_types/scrolled/config/routes.rb +18 -8
  393. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +11 -11
  394. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantDesktop.svg +56 -0
  395. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantMobile.svg +22 -0
  396. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +26 -11
  397. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +27 -8
  398. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
  399. data/entry_types/scrolled/lib/pageflow_scrolled/additional_theme_assets.rb +27 -0
  400. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +67 -10
  401. data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
  402. data/entry_types/scrolled/lib/pageflow_scrolled/content_element_consent_vendors.rb +1 -1
  403. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
  404. data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
  405. data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
  406. data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
  407. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +222 -19
  408. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
  409. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +76 -54
  410. data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
  411. data/entry_types/scrolled/lib/pageflow_scrolled.rb +14 -1
  412. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +24 -5
  413. data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
  414. data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
  415. data/entry_types/scrolled/package/config/webpack.js +94 -0
  416. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -0
  417. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1795 -0
  418. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
  419. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
  420. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
  421. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
  422. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
  423. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
  424. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.css +1 -0
  425. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +109 -0
  426. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
  427. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
  428. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
  429. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
  430. data/entry_types/scrolled/package/contentElements-editor.js +3155 -583
  431. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  432. data/entry_types/scrolled/package/contentElements-frontend.js +3375 -1585
  433. data/entry_types/scrolled/package/editor.css +1 -0
  434. data/entry_types/scrolled/package/editor.js +8452 -2867
  435. data/entry_types/scrolled/package/entryState.js +1600 -0
  436. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
  437. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
  438. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-4ec6b2de.js → PhonePlatformContext-035a99fa.js} +9 -15
  439. data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
  440. data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
  441. data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
  442. data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
  443. data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
  444. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-30fd1124.js +39 -0
  445. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
  446. data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
  447. data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
  448. data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
  449. data/entry_types/scrolled/package/frontend/{Viewer-1bb5597c.js → Viewer-cc8d4072.js} +100 -135
  450. data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
  451. data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
  452. data/entry_types/scrolled/package/frontend/commenting.css +1 -0
  453. data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
  454. data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
  455. data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
  456. data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
  457. data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
  458. data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
  459. data/entry_types/scrolled/package/frontend/{index-11f32f10.js → index-2e5546f7.js} +39 -54
  460. data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
  461. data/entry_types/scrolled/package/frontend/index.css +1 -1
  462. data/entry_types/scrolled/package/frontend/index.js +1976 -1367
  463. data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
  464. data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
  465. data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
  466. data/entry_types/scrolled/package/frontend/usePhonePlatform-4ecb96d4.js +8 -0
  467. data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
  468. data/entry_types/scrolled/package/frontend-server.js +20 -210
  469. data/entry_types/scrolled/package/package.json +22 -12
  470. data/entry_types/scrolled/package/review.css +1 -0
  471. data/entry_types/scrolled/package/review.js +3306 -0
  472. data/entry_types/scrolled/package/testHelpers.js +952 -239
  473. data/entry_types/scrolled/package/values/widgets.module.css +18 -0
  474. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  475. data/entry_types/scrolled/package/widgets/consentBar.js +275 -315
  476. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  477. data/entry_types/scrolled/package/widgets/defaultNavigation.js +961 -432
  478. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -0
  479. data/entry_types/scrolled/package/widgets/excursionSheet.js +225 -0
  480. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  481. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -32
  482. data/entry_types/scrolled/package/widgets/iconScrollIndicator.css +1 -0
  483. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +67 -0
  484. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -0
  485. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +100 -0
  486. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  487. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +48 -26
  488. data/entry_types/scrolled/package/widgets-server.js +2 -0
  489. data/entry_types/scrolled/spec/factories/chapters.rb +14 -1
  490. data/entry_types/scrolled/spec/factories/content_elements.rb +1 -1
  491. data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
  492. data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
  493. data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
  494. data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
  495. data/lib/generators/pageflow/active_admin_initializer/active_admin_initializer_generator.rb +5 -3
  496. data/lib/generators/pageflow/assets/assets_generator.rb +7 -5
  497. data/lib/generators/pageflow/cancan/cancan_generator.rb +2 -1
  498. data/lib/generators/pageflow/cancan/templates/ability.rb +1 -1
  499. data/lib/generators/pageflow/error_pages/error_pages_generator.rb +2 -1
  500. data/lib/generators/pageflow/initializer/initializer_generator.rb +3 -2
  501. data/lib/generators/pageflow/install/install_generator.rb +2 -2
  502. data/lib/generators/pageflow/procfile/procfile_generator.rb +2 -1
  503. data/lib/generators/pageflow/resque/resque_generator.rb +4 -4
  504. data/lib/generators/pageflow/resque/templates/resque.rake +1 -1
  505. data/lib/generators/pageflow/routes/routes_generator.rb +4 -2
  506. data/lib/generators/pageflow/seeds/seeds_generator.rb +3 -2
  507. data/lib/generators/pageflow/seeds/templates/seeds.rb +6 -6
  508. data/lib/generators/pageflow/theme/theme_generator.rb +2 -1
  509. data/lib/generators/pageflow/user/user_generator.rb +2 -1
  510. data/lib/pageflow/ability_mixin.rb +25 -15
  511. data/lib/pageflow/active_admin_patches/views/attributes_table.rb +1 -0
  512. data/lib/pageflow/active_admin_patches/views/pages/base.rb +3 -2
  513. data/lib/pageflow/active_admin_patches/views/table_for.rb +1 -0
  514. data/lib/pageflow/additional_headers.rb +27 -0
  515. data/lib/pageflow/admin/attributes_table_rows.rb +3 -3
  516. data/lib/pageflow/admin/tabs.rb +1 -1
  517. data/lib/pageflow/built_in_file_type.rb +1 -1
  518. data/lib/pageflow/built_in_page_types_plugin.rb +1 -1
  519. data/lib/pageflow/built_in_widget_type.rb +2 -2
  520. data/lib/pageflow/built_in_widget_types_plugin.rb +2 -1
  521. data/lib/pageflow/configuration.rb +183 -18
  522. data/lib/pageflow/cutoff_modes.rb +39 -0
  523. data/lib/pageflow/deprecation.rb +19 -0
  524. data/lib/pageflow/editor_controller.rb +6 -10
  525. data/lib/pageflow/engine.rb +5 -6
  526. data/lib/pageflow/entries_controller_env_helper.rb +32 -2
  527. data/lib/pageflow/entry_export_import/attachment_files.rb +1 -1
  528. data/lib/pageflow/entry_export_import/entry_serialization.rb +9 -8
  529. data/lib/pageflow/entry_export_import/file_mappings.rb +1 -0
  530. data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
  531. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +11 -1
  532. data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
  533. data/lib/pageflow/entry_export_import.rb +3 -3
  534. data/lib/pageflow/entry_structured_data_types.rb +43 -0
  535. data/lib/pageflow/entry_type_configuration.rb +1 -0
  536. data/lib/pageflow/entry_types.rb +2 -2
  537. data/lib/pageflow/feature.rb +1 -1
  538. data/lib/pageflow/features.rb +4 -6
  539. data/lib/pageflow/file_importers.rb +4 -4
  540. data/lib/pageflow/file_type.rb +5 -5
  541. data/lib/pageflow/file_types.rb +4 -4
  542. data/lib/pageflow/global_config_api.rb +5 -6
  543. data/lib/pageflow/help_entries.rb +7 -7
  544. data/lib/pageflow/help_entry.rb +1 -1
  545. data/lib/pageflow/hooks.rb +1 -1
  546. data/lib/pageflow/nested_revision_component.rb +9 -27
  547. data/lib/pageflow/news_item_api.rb +1 -1
  548. data/lib/pageflow/oembed_providers.rb +42 -0
  549. data/lib/pageflow/page_type.rb +2 -3
  550. data/lib/pageflow/page_types.rb +3 -3
  551. data/lib/pageflow/paperclip_interpolations/support.rb +1 -1
  552. data/lib/pageflow/paperclip_processors/vtt.rb +1 -0
  553. data/lib/pageflow/paperclip_processors/webp.rb +2 -2
  554. data/lib/pageflow/partial_editor_fragment_renderer.rb +2 -2
  555. data/lib/pageflow/plugin.rb +1 -2
  556. data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
  557. data/lib/pageflow/quota.rb +9 -5
  558. data/lib/pageflow/quotas.rb +1 -1
  559. data/lib/pageflow/rails_version.rb +14 -2
  560. data/lib/pageflow/react.rb +1 -1
  561. data/lib/pageflow/revision_component.rb +8 -40
  562. data/lib/pageflow/revision_components.rb +16 -6
  563. data/lib/pageflow/seeds.rb +12 -16
  564. data/lib/pageflow/theme.rb +1 -1
  565. data/lib/pageflow/theme_customizations.rb +10 -10
  566. data/lib/pageflow/themes.rb +44 -3
  567. data/lib/pageflow/user_mixin.rb +18 -6
  568. data/lib/pageflow/version.rb +1 -1
  569. data/lib/pageflow/widget_type.rb +4 -4
  570. data/lib/pageflow/widget_types.rb +9 -9
  571. data/lib/pageflow/zencoder_api.rb +32 -42
  572. data/lib/pageflow/zencoder_audio_output_definition.rb +14 -13
  573. data/lib/pageflow/zencoder_meta_data_output_definition.rb +3 -2
  574. data/lib/pageflow/zencoder_output_definition.rb +16 -14
  575. data/lib/pageflow/zencoder_video_output_definition.rb +140 -88
  576. data/lib/pageflow.rb +8 -2
  577. data/lib/tasks/pageflow_tasks.rake +1 -1
  578. data/package/config/eslint-rules/documented-in-toc.js +125 -0
  579. data/package/config/jest/index.js +0 -1
  580. data/package/config/webpack.js +0 -1
  581. data/package/config/webpack5.js +0 -1
  582. data/package/editor.js +4880 -2313
  583. data/package/frontend.js +710 -1223
  584. data/package/package.json +3 -1
  585. data/package/review.js +384 -0
  586. data/package/testHelpers.js +257 -243
  587. data/package/ui.js +1487 -813
  588. data/package/vendor/stubs/wysihtml5.js +2 -0
  589. data/spec/factories/accounts.rb +37 -22
  590. data/spec/factories/audio_files.rb +2 -2
  591. data/spec/factories/authentication_tokens.rb +4 -4
  592. data/spec/factories/chapters.rb +4 -4
  593. data/spec/factories/comment_thread_reads.rb +10 -0
  594. data/spec/factories/comment_threads.rb +10 -0
  595. data/spec/factories/comments.rb +9 -0
  596. data/spec/factories/draft_entries.rb +14 -6
  597. data/spec/factories/edit_locks.rb +1 -1
  598. data/spec/factories/entries.rb +28 -19
  599. data/spec/factories/entry_translation_groups.rb +6 -0
  600. data/spec/factories/file_folders.rb +8 -0
  601. data/spec/factories/file_usages.rb +2 -1
  602. data/spec/factories/folders.rb +1 -1
  603. data/spec/factories/image_files.rb +2 -2
  604. data/spec/factories/pages.rb +3 -3
  605. data/spec/factories/published_entries.rb +28 -8
  606. data/spec/factories/revisions.rb +1 -1
  607. data/spec/factories/sites.rb +6 -0
  608. data/spec/factories/test_multi_attachment_files.rb +1 -1
  609. data/spec/factories/text_track_files.rb +1 -1
  610. data/spec/factories/uploadable_files.rb +1 -1
  611. data/spec/factories/users.rb +6 -6
  612. data/spec/factories/video_files.rb +2 -2
  613. data/spec/factories/widgets.rb +1 -1
  614. data/spec/fixtures/7x15_rotated.jpg +0 -0
  615. data/vendor/assets/javascripts/wysihtml-toolbar.js +19288 -0
  616. metadata +266 -92
  617. data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
  618. data/config/initializers/revision_components.rb +0 -5
  619. data/config/locales/twitter_to_x.de.yml +0 -6
  620. data/config/locales/twitter_to_x.en.yml +0 -6
  621. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
  622. data/entry_types/scrolled/config/locales/twitter_to_x.de.yml +0 -12
  623. data/entry_types/scrolled/config/locales/twitter_to_x.en.yml +0 -12
  624. data/entry_types/scrolled/config/locales/vr_image_projection.de.yml +0 -24
  625. data/entry_types/scrolled/config/locales/vr_image_projection.en.yml +0 -24
  626. data/entry_types/scrolled/package/frontend/EditableInlineText.module-ebd22921.js +0 -5497
  627. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-b79159cc.js +0 -107
  628. data/entry_types/scrolled/package/frontend/Viewer-1ecf3375.js +0 -154
  629. data/entry_types/scrolled/package/frontend/Wavesurfer-7d9cf1b7.js +0 -333
  630. data/entry_types/scrolled/package/frontend/arrowRight-62998af9.js +0 -77
  631. data/entry_types/scrolled/package/frontend/components-024a9893.js +0 -2853
  632. data/entry_types/scrolled/package/frontend/createSuper-d0f30da3.js +0 -115
  633. data/entry_types/scrolled/package/frontend/i18n-ce13a8bf.js +0 -1129
  634. data/entry_types/scrolled/package/frontend/useContentElementEditorState-4f4c3cf6.js +0 -52
  635. data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
  636. data/package/vendor/jquery.minicolors.js +0 -1108
@@ -1,21 +1,22 @@
1
1
  module Pageflow
2
- module ReusableFile
2
+ module ReusableFile # rubocop:todo Style/Documentation
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
6
  belongs_to :uploader, class_name: 'User', optional: true
7
- belongs_to :entry, optional: true
8
- belongs_to :parent_file, polymorphic: true, foreign_type: :parent_file_model_type, optional: true
7
+ belongs_to :entry, class_name: 'Pageflow::Entry', optional: true
8
+ belongs_to :parent_file, polymorphic: true, foreign_type: :parent_file_model_type,
9
+ optional: true
9
10
 
10
- has_many :usages, :as => :file, :class_name => 'Pageflow::FileUsage', :dependent => :destroy
11
+ has_many :usages, as: :file, class_name: 'Pageflow::FileUsage', dependent: :destroy
11
12
  has_one :import,
12
13
  as: :file,
13
14
  class_name: 'Pageflow::FileImport',
14
15
  required: false,
15
16
  dependent: :destroy
16
- has_many :using_revisions, :through => :usages, :source => :revision
17
- has_many :using_entries, :through => :using_revisions, :source => :entry
18
- has_many :using_accounts, :through => :using_entries, :source => :account
17
+ has_many :using_revisions, through: :usages, source: :revision
18
+ has_many :using_entries, through: :using_revisions, source: :entry
19
+ has_many :using_accounts, through: :using_entries, source: :account
19
20
 
20
21
  validate :parent_allows_type_for_nesting, :parent_belongs_to_same_entry
21
22
 
@@ -25,30 +26,29 @@ module Pageflow
25
26
  end
26
27
 
27
28
  def parent_allows_type_for_nesting
28
- if parent_file.present?
29
- parent_class = parent_file.class
30
- file_type_of_parent = Pageflow.config.file_types.find_by_model!(parent_class)
31
- models_of_nested_file_types = file_type_of_parent.nested_file_types.map(&:model)
32
- unless models_of_nested_file_types.include?(self.class)
33
- errors.add(:base, 'File type of provided parent file does not permit nesting files of '\
34
- "type #{self.class.name}")
35
- end
36
- end
29
+ return unless parent_file.present?
30
+
31
+ parent_class = parent_file.class
32
+ file_type_of_parent = Pageflow.config.file_types.find_by_model!(parent_class)
33
+ models_of_nested_file_types = file_type_of_parent.nested_file_types.map(&:model)
34
+ return if models_of_nested_file_types.include?(self.class)
35
+
36
+ errors.add(:base, 'File type of provided parent file does not permit nesting files of ' \
37
+ "type #{self.class.name}")
37
38
  end
38
39
 
39
40
  def parent_belongs_to_same_entry
40
- if parent_file.present?
41
- unless parent_file.using_entries.include?(entry)
42
- errors.add(:base, 'Parent file does not belong to same entry as nested file')
43
- end
44
- end
41
+ return unless parent_file.present?
42
+ return if parent_file.using_entries.include?(entry)
43
+
44
+ errors.add(:base, 'Parent file does not belong to same entry as nested file')
45
45
  end
46
46
 
47
47
  def nested_files(model)
48
48
  model_table_name = model.table_name
49
49
  model
50
50
  .select("#{model_table_name}.*")
51
- .where("#{model_table_name}.parent_file_id = #{id} AND "\
51
+ .where("#{model_table_name}.parent_file_id = #{id} AND " \
52
52
  "#{model_table_name}.parent_file_model_type = '#{self.class.name}'")
53
53
  end
54
54
 
@@ -65,7 +65,6 @@ module Pageflow
65
65
  "#{super}-#{state}"
66
66
  end
67
67
 
68
-
69
68
  # The following are method defaults for file types that do not require processing/encoding.
70
69
  # They are overwritten with default values in UploadableFile for files that are uploaded
71
70
  # through the editor.
@@ -131,7 +130,8 @@ module Pageflow
131
130
  raise 'Not implemented!'
132
131
  end
133
132
 
134
- # Gets called to trigger the `file_uploaded` event in the upload state machine of UploadableFile.
133
+ # Gets called to trigger the `file_uploaded` event in the upload state
134
+ # machine of UploadableFile.
135
135
  # Files that are not uploaded through the editor (and therefore not using the
136
136
  # upload state machine of UploadableFile) can overwrite this method to trigger whatever
137
137
  # the file does (i.e. processing/transcoding), depending on the including class'
@@ -2,12 +2,10 @@ module Pageflow
2
2
  # @api private
3
3
  module SerializationBlacklist
4
4
  def serializable_hash(options = nil)
5
- options ||= {}
5
+ options = (options || {}).dup
6
+ options[:except] = Array(options[:except]) + blacklist_for_serialization
6
7
 
7
- options[:except] = Array(options[:except])
8
- options[:except].concat(blacklist_for_serialization)
9
-
10
- super(options)
8
+ super
11
9
  end
12
10
 
13
11
  private
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- module Suspendable
2
+ module Suspendable # rubocop:todo Style/Documentation
3
3
  def active_for_authentication?
4
4
  super && !suspended?
5
5
  end
@@ -10,14 +10,16 @@ module Pageflow
10
10
 
11
11
  def suspend!
12
12
  return if suspended?
13
+
13
14
  self.suspended_at = Time.zone.now
14
- save(:validate => false)
15
+ save(validate: false)
15
16
  end
16
17
 
17
18
  def unsuspend!
18
19
  return unless suspended?
20
+
19
21
  self.suspended_at = nil
20
- save(:validate => false)
22
+ save(validate: false)
21
23
  end
22
24
  end
23
25
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- module ThemeReferencer
2
+ module ThemeReferencer # rubocop:todo Style/Documentation
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
@@ -0,0 +1,62 @@
1
+ module Pageflow
2
+ # @api private
3
+ module Translatable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to(:translation_group,
8
+ optional: true,
9
+ class_name: 'EntryTranslationGroup')
10
+
11
+ has_many(:translations,
12
+ through: :translation_group,
13
+ source: :entries)
14
+
15
+ after_destroy do
16
+ if translation_group&.single_item_or_empty?
17
+ translation_group.destroy
18
+ elsif default_translation?
19
+ translation_group.update(default_translation: nil)
20
+ end
21
+ end
22
+ end
23
+
24
+ def mark_as_translation_of(entry)
25
+ transaction do
26
+ ensure_translation_group(entry)
27
+
28
+ if !entry.translation_group
29
+ entry.update!(translation_group:)
30
+ elsif entry.translation_group != translation_group
31
+ entry.translation_group.merge_into(translation_group)
32
+ end
33
+ end
34
+ end
35
+
36
+ def remove_from_translation_group
37
+ if translation_group.entries.count <= 2
38
+ translation_group.destroy
39
+ else
40
+ translation_group.update(default_translation: nil) if default_translation?
41
+ update!(translation_group: nil)
42
+ end
43
+ end
44
+
45
+ def mark_as_default_translation
46
+ translation_group.update!(default_translation: self)
47
+ end
48
+
49
+ def default_translation?
50
+ translation_group&.default_translation == self
51
+ end
52
+
53
+ private
54
+
55
+ def ensure_translation_group(other_entry)
56
+ return if translation_group
57
+
58
+ update!(translation_group: other_entry.translation_group ||
59
+ build_translation_group(default_translation: self))
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,101 @@
1
+ module Pageflow
2
+ # Store per locale overrides for a set of attributes in an
3
+ # `attribute_translations` column. The attribute itself keeps holding
4
+ # the value used for locales without translation. Since that value is
5
+ # read via the attribute's reader, overriding the reader also changes
6
+ # the value locales without translation fall back to.
7
+ #
8
+ # Not to be confused with {Translatable}, which groups entries that
9
+ # are translations of each other.
10
+ #
11
+ # @api private
12
+ module TranslatedAttributes
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ class_attribute :translated_attribute_names, default: []
17
+
18
+ serialize :attribute_translations, coder: JSON
19
+
20
+ validate :attribute_translations_are_known_locales_and_attributes
21
+ end
22
+
23
+ module ClassMethods # rubocop:todo Style/Documentation
24
+ # Declare which attributes can be overridden per locale. Defines
25
+ # a `<name>_translations` reader and writer for each of them,
26
+ # mapping locales to values.
27
+ def translated_attributes(*names)
28
+ self.translated_attribute_names = names.map(&:to_s)
29
+
30
+ names.each do |name|
31
+ define_method(:"#{name}_translations") { translations_for(name) }
32
+
33
+ define_method(:"#{name}_translations=") do |translations|
34
+ assign_translations(name, translations)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def attribute_translations
41
+ self[:attribute_translations] || {}
42
+ end
43
+
44
+ # Returns the translation for the given locale, or the value of the
45
+ # attribute itself if there is none. The fallback goes through the
46
+ # attribute's reader, so models can override it to compute a
47
+ # default. Such an override must not call `translated` itself, since
48
+ # that would recurse.
49
+ def translated(attribute, locale:)
50
+ locale_chain(locale)
51
+ .lazy
52
+ .map { |candidate| attribute_translations.dig(candidate, attribute.to_s) }
53
+ .find(&:present?) || public_send(attribute)
54
+ end
55
+
56
+ private
57
+
58
+ def translations_for(attribute)
59
+ attribute_translations.each_with_object({}) do |(locale, values), result|
60
+ value = values[attribute.to_s]
61
+ result[locale] = value if value.present?
62
+ end
63
+ end
64
+
65
+ # Translations of other attributes are preserved, while the given
66
+ # translations replace all translations previously stored for this
67
+ # attribute. Removing a translation in a form therefore does not
68
+ # require sending a separate delete instruction.
69
+ def assign_translations(attribute, translations)
70
+ result = attribute_translations.deep_dup
71
+ result.each_value { |values| values.delete(attribute.to_s) }
72
+
73
+ translations.each do |locale, value|
74
+ next if value.blank?
75
+
76
+ (result[locale.to_s] ||= {})[attribute.to_s] = value
77
+ end
78
+
79
+ self[:attribute_translations] = result.reject { |_locale, values| values.empty? }
80
+ end
81
+
82
+ # Regional locales fall back to their base locale, matching how
83
+ # translations are looked up for the entry's locale elsewhere.
84
+ def locale_chain(locale)
85
+ [locale.to_s, locale.to_s.split('-').first].uniq
86
+ end
87
+
88
+ def attribute_translations_are_known_locales_and_attributes
89
+ available_locales = Pageflow.config.available_public_locales.map(&:to_s)
90
+
91
+ attribute_translations.each do |locale, values|
92
+ unless available_locales.include?(locale)
93
+ errors.add(:attribute_translations, :unknown_locale)
94
+ end
95
+
96
+ unknown = values.keys - translated_attribute_names
97
+ errors.add(:attribute_translations, :not_translatable) if unknown.any?
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- module UploadableFile
2
+ module UploadableFile # rubocop:todo Style/Documentation
3
3
  extend ActiveSupport::Concern
4
4
  include ReusableFile
5
5
 
@@ -20,7 +20,7 @@ module Pageflow
20
20
  ))
21
21
 
22
22
  validates_attachment_presence :attachment_on_s3
23
- validates_attachment_file_name :attachment_on_s3, matches: %r{^[^\/\\]+\.[\w]{3,4}$}
23
+ validates_attachment_file_name :attachment_on_s3, matches: %r{^[^/\\]+\.\w{3,6}$}
24
24
  do_not_validate_attachment_file_type :attachment_on_s3
25
25
 
26
26
  state_machine initial: 'uploading' do
@@ -76,9 +76,9 @@ module Pageflow
76
76
  end
77
77
 
78
78
  def url
79
- if attachment.present?
80
- attachment.url
81
- end
79
+ return unless attachment.present?
80
+
81
+ attachment.url
82
82
  end
83
83
 
84
84
  def basename
@@ -109,7 +109,7 @@ module Pageflow
109
109
  file_uploaded!
110
110
  end
111
111
 
112
- module ClassMethods
112
+ module ClassMethods # rubocop:todo Style/Documentation
113
113
  def processing_state_machine(&block)
114
114
  state_machine do
115
115
  extend StateMachineJob::Macro
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class Account < ApplicationRecord
2
+ class Account < ApplicationRecord # rubocop:todo Style/Documentation
3
3
  include FeatureTarget
4
4
  include SerializationBlacklist
5
5
 
@@ -10,17 +10,18 @@ module Pageflow
10
10
  has_many :entry_memberships, through: :entries, source: :memberships
11
11
 
12
12
  has_many :sites, dependent: :destroy
13
- belongs_to :default_site, :class_name => 'Site'
13
+ belongs_to :default_site, class_name: 'Site'
14
14
 
15
- validates :default_site, :presence => true
15
+ validates :default_site, presence: true
16
16
 
17
- accepts_nested_attributes_for :default_site, :update_only => true
17
+ accepts_nested_attributes_for :default_site, update_only: true
18
18
 
19
- scope :with_landing_page, -> { where.not(:landing_page_name => '') }
19
+ scope :with_landing_page, -> { where.not(landing_page_name: '') }
20
20
 
21
21
  def build_default_site(*args)
22
22
  super.tap do |site|
23
23
  site.account = self
24
+ site.build_root_permalink_directory
24
25
  end
25
26
  end
26
27
 
@@ -1,7 +1,7 @@
1
1
  module Pageflow
2
2
  # Query accounts for members, e.g. based on role
3
3
  class AccountMemberQuery < ApplicationQuery
4
- class Scope < ApplicationQuery::Scope
4
+ class Scope < ApplicationQuery::Scope # rubocop:todo Style/Documentation
5
5
  # Account whose members we scope
6
6
  # @return [Pageflow::Account]
7
7
  attr_reader :account
@@ -59,7 +59,7 @@ module Pageflow
59
59
  @user
60
60
  .memberships
61
61
  .where(role: Roles.at_least(role))
62
- .where('(entity_id = :account_id AND '\
62
+ .where('(entity_id = :account_id AND ' \
63
63
  "entity_type = 'Pageflow::Account')",
64
64
  account_id: @account.id)
65
65
  .any?
@@ -1,7 +1,7 @@
1
1
  module Pageflow
2
2
  # Abstraction layer for Pageflow's query interface
3
3
  class ApplicationQuery
4
- class Scope
4
+ class Scope # rubocop:todo Style/Documentation
5
5
  protected
6
6
 
7
7
  # @api private
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class ApplicationRecord < ActiveRecord::Base
2
+ class ApplicationRecord < ActiveRecord::Base # rubocop:todo Style/Documentation
3
3
  self.abstract_class = true
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class AudioFile < ApplicationRecord
2
+ class AudioFile < ApplicationRecord # rubocop:todo Style/Documentation
3
3
  include UploadableFile
4
4
  include MediaEncodingStateMachine
5
5
 
@@ -21,15 +21,15 @@ module Pageflow
21
21
  end
22
22
 
23
23
  def m4a
24
- ZencoderAttachment.new(self, "audio.m4a")
24
+ ZencoderAttachment.new(self, 'audio.m4a')
25
25
  end
26
26
 
27
27
  def mp3
28
- ZencoderAttachment.new(self, "audio.mp3")
28
+ ZencoderAttachment.new(self, 'audio.mp3')
29
29
  end
30
30
 
31
31
  def ogg
32
- ZencoderAttachment.new(self, "audio.ogg")
32
+ ZencoderAttachment.new(self, 'audio.ogg')
33
33
  end
34
34
 
35
35
  def output_definition
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class AudioFileUrlTemplates
2
+ class AudioFileUrlTemplates # rubocop:todo Style/Documentation
3
3
  def call
4
4
  {
5
5
  original: url_template(:attachment, :original),
@@ -4,7 +4,7 @@ module Pageflow
4
4
  # AuthenticationToken stores the tokens with expiry time against user and provider
5
5
  class AuthenticationToken < ApplicationRecord
6
6
  belongs_to :user, class_name: 'User'
7
- default_scope -> { where(['expiry_time > :expiry', expiry: Time.zone.now]) }
7
+ default_scope -> { where(['expiry_time > :expiry', {expiry: Time.zone.now}]) }
8
8
 
9
9
  def auth_token
10
10
  SymmetricEncryption.decrypt(read_attribute(:auth_token))
@@ -31,7 +31,7 @@ module Pageflow
31
31
 
32
32
  def self.find_auth_token(current_user, provider)
33
33
  where(user_id: current_user.id,
34
- provider: provider)
34
+ provider:)
35
35
  end
36
36
  end
37
37
  end
@@ -1,7 +1,8 @@
1
1
  module Pageflow
2
- class Chapter < ApplicationRecord
2
+ class Chapter < ApplicationRecord # rubocop:todo Style/Documentation
3
3
  include SerializedConfiguration
4
4
  include NestedRevisionComponent
5
+ include AutoGeneratedPermaId
5
6
 
6
7
  belongs_to :storyline, touch: true
7
8
  has_many :pages, -> { order('position ASC') }, dependent: :destroy, inverse_of: :chapter
@@ -15,5 +16,9 @@ module Pageflow
15
16
  def pages
16
17
  super.tap { |p| p.first.is_first = true if is_first && p.present? }
17
18
  end
19
+
20
+ def entry_for_auto_generated_perma_id
21
+ storyline.revision.entry
22
+ end
18
23
  end
19
24
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class ChapterScaffold
2
+ class ChapterScaffold # rubocop:todo Style/Documentation
3
3
  def initialize(chapter, options)
4
4
  @chapter = chapter
5
5
  @options = options
@@ -8,9 +8,7 @@ module Pageflow
8
8
  delegate :save!, to: :@chapter
9
9
 
10
10
  def build
11
- if @options[:depth] == 'page'
12
- @page = @chapter.pages.build(template: 'background_image')
13
- end
11
+ @page = @chapter.pages.build(template: 'background_image') if @options[:depth] == 'page'
14
12
 
15
13
  self
16
14
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class CnameSiteRequestScope
2
+ class CnameSiteRequestScope # rubocop:todo Style/Documentation
3
3
  def call(sites, request)
4
4
  sites.where(<<-SQL, host: request.host)
5
5
  cname = :host OR
@@ -0,0 +1,30 @@
1
+ module Pageflow
2
+ # @api private
3
+ class Comment < ApplicationRecord
4
+ include NestedRevisionComponent
5
+
6
+ # Quotes are recorded by the client, so cut rather than reject: an
7
+ # unexpectedly long selection must not keep a comment from being saved.
8
+ # Kept in sync with the limit quote extraction applies in
9
+ # entry_types/scrolled/package/src/review/subjectQuote.js, so that a
10
+ # recorded quote stays comparable to the text as it reads later on.
11
+ QUOTE_LIMIT = 4_000
12
+
13
+ belongs_to :comment_thread
14
+ belongs_to :creator, class_name: 'User'
15
+
16
+ validates :body, presence: true
17
+
18
+ before_validation :truncate_quote
19
+
20
+ def entry_for_auto_generated_perma_id
21
+ comment_thread.revision.entry
22
+ end
23
+
24
+ private
25
+
26
+ def truncate_quote
27
+ self.quote = quote[0, QUOTE_LIMIT] if quote
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,65 @@
1
+ module Pageflow
2
+ # @api private
3
+ class CommentThread < ApplicationRecord
4
+ include RevisionComponent
5
+
6
+ serialize :subject_range, coder: JSON
7
+
8
+ belongs_to :creator, class_name: 'User'
9
+ belongs_to :resolver, class_name: 'User', foreign_key: :resolved_by_id, optional: true
10
+ # Ordered because clients read position as meaning: the first comment
11
+ # is the topic, the rest are replies.
12
+ has_many :comments, -> { order(:id) }, dependent: :destroy
13
+
14
+ nested_revision_components :comments
15
+
16
+ validates :subject_type, :subject_id, presence: true
17
+
18
+ def resolve(user)
19
+ update!(resolved_at: Time.current, resolver: user) unless resolved_at
20
+ end
21
+
22
+ def unresolve
23
+ update!(resolved_at: nil, resolver: nil)
24
+ end
25
+
26
+ def self.migrate_to_subject(revision:, subject_type:, subject_id:, thread_ids:)
27
+ return if thread_ids.blank?
28
+
29
+ where(revision_id: revision.id,
30
+ subject_type:,
31
+ id: thread_ids).update_all(subject_id:, updated_at: Time.current)
32
+ end
33
+
34
+ def self.update_subject_ranges_for(revision:, subject_type:, subject_id:, ranges:)
35
+ return if ranges.blank?
36
+
37
+ where(revision_id: revision.id,
38
+ subject_type:, subject_id:,
39
+ id: ranges.keys).update_all(
40
+ sanitize_sql_array(
41
+ ["subject_range = #{subject_range_case_sql(ranges)}, updated_at = ?",
42
+ Time.current]
43
+ )
44
+ )
45
+ end
46
+
47
+ def self.update_section_perma_id_for(revision:, subject_type:, subject_id:, section_perma_id:)
48
+ return if subject_id.blank?
49
+
50
+ where(revision_id: revision.id,
51
+ subject_type:,
52
+ subject_id:)
53
+ .where.not(section_perma_id:)
54
+ .update_all(section_perma_id:, updated_at: Time.current)
55
+ end
56
+
57
+ def self.subject_range_case_sql(ranges)
58
+ when_clauses = ranges.map do |id, range|
59
+ sanitize_sql_array(['WHEN ? THEN ?', id.to_i, range.to_json])
60
+ end
61
+ "CASE id #{when_clauses.join(' ')} END"
62
+ end
63
+ private_class_method :subject_range_case_sql
64
+ end
65
+ end
@@ -0,0 +1,22 @@
1
+ module Pageflow
2
+ # Records when a user last read a comment thread. Keyed by perma id
3
+ # so read state survives comment threads being copied to a new
4
+ # revision.
5
+ #
6
+ # @api private
7
+ class CommentThreadRead < ApplicationRecord
8
+ belongs_to :entry
9
+ belongs_to :user
10
+
11
+ def self.read_at_by_perma_id(entry:, user:)
12
+ where(entry:, user:).pluck(:comment_thread_perma_id, :read_at).to_h
13
+ end
14
+
15
+ def self.mark(entry:, user:, comment_thread_perma_ids:, read_at: Time.current)
16
+ comment_thread_perma_ids.each do |perma_id|
17
+ find_or_initialize_by(entry:, user:, comment_thread_perma_id: perma_id)
18
+ .update!(read_at:)
19
+ end
20
+ end
21
+ end
22
+ end