pageflow 15.6.1 → 15.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pageflow might be problematic. Click here for more details.

Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +236 -11
  3. data/README.md +1 -2
  4. data/admins/pageflow/entry.rb +28 -57
  5. data/admins/pageflow/user.rb +1 -3
  6. data/app/assets/images/pageflow/admin/icons/buttons/editor.svg +2 -0
  7. data/app/assets/images/pageflow/admin/icons/buttons/preview.svg +2 -0
  8. data/app/assets/images/pageflow/admin/icons/buttons/show_public.svg +2 -0
  9. data/app/assets/images/pageflow/admin/icons/delete.svg +9 -0
  10. data/app/assets/images/pageflow/admin/icons/done.svg +6 -0
  11. data/app/assets/images/pageflow/admin/icons/edit.svg +7 -0
  12. data/app/assets/images/pageflow/admin/icons/editor.svg +2 -0
  13. data/app/assets/images/pageflow/admin/icons/folder.svg +8 -0
  14. data/app/assets/images/pageflow/admin/icons/folder_active.svg +8 -0
  15. data/app/assets/images/pageflow/admin/icons/info.svg +3 -0
  16. data/app/assets/images/pageflow/admin/icons/new.svg +5 -0
  17. data/app/assets/images/pageflow/admin/icons/preview.svg +2 -0
  18. data/app/assets/images/pageflow/admin/icons/published.svg +2 -0
  19. data/app/assets/images/pageflow/admin/icons/published_with_password.svg +2 -0
  20. data/app/assets/images/pageflow/admin/icons/show_public.svg +2 -0
  21. data/app/assets/images/pageflow/editor/blank_entry/logo.png +0 -0
  22. data/app/assets/images/pageflow/themes/default/embed_opt_in.svg +3 -0
  23. data/app/assets/images/pageflow/themes/default/embed_opt_out_info.svg +16 -0
  24. data/app/assets/javascripts/pageflow/admin/entries.js +9 -57
  25. data/app/assets/javascripts/pageflow/dist/ui.js +127 -10
  26. data/app/assets/stylesheets/pageflow/admin/active_admin_patches.scss +18 -16
  27. data/app/assets/stylesheets/pageflow/admin/badge_list.scss +25 -35
  28. data/app/assets/stylesheets/pageflow/admin/columns.scss +13 -11
  29. data/app/assets/stylesheets/pageflow/admin/embed_code.scss +1 -4
  30. data/app/assets/stylesheets/pageflow/admin/entries/folders.scss +44 -19
  31. data/app/assets/stylesheets/pageflow/admin/entries.scss +15 -31
  32. data/app/assets/stylesheets/pageflow/admin/features.scss +3 -1
  33. data/app/assets/stylesheets/pageflow/admin/forms.scss +14 -12
  34. data/app/assets/stylesheets/pageflow/admin/hint.scss +8 -14
  35. data/app/assets/stylesheets/pageflow/admin/icon_button.scss +25 -14
  36. data/app/assets/stylesheets/pageflow/admin/icon_link.scss +31 -15
  37. data/app/assets/stylesheets/pageflow/admin/publication_state_indicator.scss +14 -7
  38. data/app/assets/stylesheets/pageflow/admin/status_tags.scss +5 -4
  39. data/app/assets/stylesheets/pageflow/admin/tabs_view.scss +36 -34
  40. data/app/assets/stylesheets/pageflow/admin/tooltip_bubble.scss +15 -11
  41. data/app/assets/stylesheets/pageflow/admin.scss +12 -0
  42. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +15 -61
  43. data/app/assets/stylesheets/pageflow/editor/base.scss +28 -13
  44. data/app/assets/stylesheets/pageflow/editor/blank_entry.scss +6 -6
  45. data/app/assets/stylesheets/pageflow/editor/change_theme.scss +28 -71
  46. data/app/assets/stylesheets/pageflow/editor/composables.scss +2 -2
  47. data/app/assets/stylesheets/pageflow/editor/confirm_encoding.scss +18 -36
  48. data/app/assets/stylesheets/pageflow/editor/confirm_upload.scss +12 -39
  49. data/app/assets/stylesheets/pageflow/editor/dialogs.scss +39 -12
  50. data/app/assets/stylesheets/pageflow/editor/disabled_atmo_indicator.scss +3 -3
  51. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +7 -10
  52. data/app/assets/stylesheets/pageflow/editor/emulation_mode_button.scss +3 -8
  53. data/app/assets/stylesheets/pageflow/editor/failures.scss +3 -3
  54. data/app/assets/stylesheets/pageflow/editor/file_import.scss +22 -38
  55. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +3 -7
  56. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +4 -24
  57. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +10 -11
  58. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +4 -8
  59. data/app/assets/stylesheets/pageflow/editor/files.scss +7 -6
  60. data/app/assets/stylesheets/pageflow/editor/files_explorer.scss +13 -30
  61. data/app/assets/stylesheets/pageflow/editor/files_gallery.scss +15 -11
  62. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +6 -7
  63. data/app/assets/stylesheets/pageflow/editor/help.scss +19 -14
  64. data/app/assets/stylesheets/pageflow/editor/help_image.scss +1 -1
  65. data/app/assets/stylesheets/pageflow/editor/info_box.scss +19 -0
  66. data/app/assets/stylesheets/pageflow/editor/inputs/file_input.scss +7 -11
  67. data/app/assets/stylesheets/pageflow/editor/inputs/file_processing_state_display.scss +0 -2
  68. data/app/assets/stylesheets/pageflow/editor/inputs/reference.scss +7 -11
  69. data/app/assets/stylesheets/pageflow/editor/list.scss +13 -10
  70. data/app/assets/stylesheets/pageflow/editor/loading.scss +1 -1
  71. data/app/assets/stylesheets/pageflow/editor/locked.scss +9 -5
  72. data/app/assets/stylesheets/pageflow/editor/menu.scss +5 -5
  73. data/app/assets/stylesheets/pageflow/editor/notifications.scss +15 -14
  74. data/app/assets/stylesheets/pageflow/editor/other_entry_item.scss +7 -3
  75. data/app/assets/stylesheets/pageflow/editor/outline.scss +57 -19
  76. data/app/assets/stylesheets/pageflow/editor/page_links.scss +10 -8
  77. data/app/assets/stylesheets/pageflow/editor/page_selection.scss +2 -29
  78. data/app/assets/stylesheets/pageflow/editor/publish_entry.scss +5 -6
  79. data/app/assets/stylesheets/pageflow/editor/quotas.scss +2 -3
  80. data/app/assets/stylesheets/pageflow/editor/select_button.scss +12 -6
  81. data/app/assets/stylesheets/pageflow/editor/sidebar_footer.scss +1 -5
  82. data/app/assets/stylesheets/pageflow/editor/storyline_picker.scss +6 -2
  83. data/app/assets/stylesheets/pageflow/editor/text_tracks.scss +6 -22
  84. data/app/assets/stylesheets/pageflow/editor/widgets.scss +2 -2
  85. data/app/assets/stylesheets/pageflow/editor/wysihtml5.scss +35 -29
  86. data/app/assets/stylesheets/pageflow/mixins/background_icons.scss +3 -3
  87. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +50 -68
  88. data/app/assets/stylesheets/pageflow/themes/default/base.scss +2 -0
  89. data/app/assets/stylesheets/pageflow/themes/default/consent/bar.scss +156 -0
  90. data/app/assets/stylesheets/pageflow/themes/default/consent/vendor_list.scss +62 -0
  91. data/app/assets/stylesheets/pageflow/themes/default/consent.scss +2 -0
  92. data/app/assets/stylesheets/pageflow/themes/default/logo/alignment.scss +7 -0
  93. data/app/assets/stylesheets/pageflow/themes/default/page/shadow.scss +44 -0
  94. data/app/assets/stylesheets/pageflow/themes/default/third_party_embed_consent.scss +103 -0
  95. data/app/assets/stylesheets/pageflow/ui/forms.scss +79 -84
  96. data/app/assets/stylesheets/pageflow/ui/functions.scss +56 -0
  97. data/app/assets/stylesheets/pageflow/ui/input/check_box_group_input.scss +2 -3
  98. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +6 -6
  99. data/app/assets/stylesheets/pageflow/ui/input/extended_select_input.scss +63 -50
  100. data/app/assets/stylesheets/pageflow/ui/normalize/forms.scss +153 -0
  101. data/app/assets/stylesheets/pageflow/ui/normalize.scss +278 -0
  102. data/app/assets/stylesheets/pageflow/ui/properties.scss +44 -0
  103. data/app/assets/stylesheets/pageflow/ui/table_cells/delete_row_table_cell.scss +1 -1
  104. data/app/assets/stylesheets/pageflow/ui/table_view.scss +14 -18
  105. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -11
  106. data/app/assets/stylesheets/pageflow/ui/tooltip.scss +6 -8
  107. data/app/assets/stylesheets/pageflow/ui/validation_error_messages.scss +6 -0
  108. data/app/assets/stylesheets/pageflow/ui.scss +4 -0
  109. data/app/controllers/pageflow/edit_locks_controller.rb +3 -1
  110. data/app/helpers/pageflow/admin/entries_helper.rb +2 -12
  111. data/app/helpers/pageflow/embed_code_helper.rb +1 -1
  112. data/app/helpers/pageflow/entries_helper.rb +41 -2
  113. data/app/helpers/pageflow/file_background_images_helper.rb +1 -1
  114. data/app/helpers/pageflow/page_types_helper.rb +1 -1
  115. data/app/helpers/pageflow/themes_helper.rb +1 -1
  116. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -1
  117. data/app/jobs/pageflow/poll_zencoder_job.rb +1 -9
  118. data/app/models/pageflow/audio_file.rb +17 -0
  119. data/app/models/pageflow/audio_file_url_templates.rb +4 -1
  120. data/app/models/pageflow/customized_theme.rb +37 -0
  121. data/app/models/pageflow/draft_entry.rb +6 -59
  122. data/app/models/pageflow/entry_at_revision.rb +68 -0
  123. data/app/models/pageflow/entry_role_query.rb +44 -24
  124. data/app/models/pageflow/image_file_css_background_image_urls.rb +8 -2
  125. data/app/models/pageflow/published_entry.rb +9 -56
  126. data/app/models/pageflow/theme_customization.rb +46 -0
  127. data/app/models/pageflow/theme_customization_file.rb +58 -0
  128. data/app/models/pageflow/video_file.rb +11 -1
  129. data/app/models/pageflow/video_file_url_templates.rb +1 -0
  130. data/app/policies/pageflow/entry_policy.rb +4 -0
  131. data/app/state_machines/pageflow/media_encoding_state_machine.rb +23 -4
  132. data/app/views/admin/entries/_form.html.erb +50 -0
  133. data/app/views/admin/entries/entry_type_name_input.html.erb +5 -0
  134. data/app/views/admin/features/_form.html.erb +1 -1
  135. data/app/views/admin/memberships/_form.html.erb +2 -3
  136. data/app/views/admin/users/_form.html.erb +1 -1
  137. data/app/views/admin/users/invitation.html.erb +2 -2
  138. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +10 -9
  139. data/app/views/components/pageflow/admin/members_tab.rb +5 -1
  140. data/app/views/components/pageflow/admin/revisions_tab.rb +13 -3
  141. data/app/views/components/pageflow/admin/timestamp.rb +20 -0
  142. data/app/views/components/pageflow/admin/user_account_badge_list.rb +5 -6
  143. data/app/views/pageflow/admin/entries/_cannot_add_user.html.erb +9 -5
  144. data/app/views/pageflow/admin/entries/_entry_type_name_input.html.erb +7 -0
  145. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +13 -0
  146. data/app/views/pageflow/editor/encoding_confirmations/_intro.html.erb +0 -0
  147. data/app/views/pageflow/editor/encoding_confirmations/check.json.jbuilder +14 -2
  148. data/config/initializers/paperclip.rb +16 -1
  149. data/config/locales/de.yml +24 -28
  150. data/config/locales/en.yml +19 -10
  151. data/db/migrate/20210528073122_create_pageflow_theme_customizations.rb +11 -0
  152. data/db/migrate/20210531090654_create_pageflow_theme_customization_files.rb +15 -0
  153. data/db/migrate/20210531102228_add_selected_file_ids_to_theme_customizations.rb +5 -0
  154. data/db/migrate/20211020085902_add_canonical_entry_url_prefix_to_themings.rb +5 -0
  155. data/db/migrate/20220503150010_add_peak_data_to_audio_files.rb +5 -0
  156. data/db/migrate/20220705084830_add_trailing_slash_in_canonical_urls_to_themings.rb +5 -0
  157. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +256 -124
  158. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +566 -167
  159. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +5 -5
  160. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +4 -4
  161. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +38 -0
  162. data/entry_types/paged/app/views/pageflow_paged/third_party_embed_consent/_opt_in.html.erb +12 -0
  163. data/entry_types/paged/app/views/pageflow_paged/third_party_embed_consent/_opt_out_info.html.erb +10 -0
  164. data/entry_types/paged/config/initializers/features.rb +2 -0
  165. data/entry_types/paged/config/locales/new/video_contain.de.yml +7 -0
  166. data/entry_types/paged/config/locales/new/video_contain.en.yml +7 -0
  167. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +3 -0
  168. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +1 -0
  169. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +58 -0
  170. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +37 -3
  171. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +92 -6
  172. data/entry_types/scrolled/app/helpers/pageflow_scrolled/webpack_public_path_helper.rb +20 -0
  173. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +7 -0
  174. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +5 -2
  175. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_seed.json.jbuilder +5 -1
  176. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +5 -4
  177. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +16 -0
  178. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +13 -2
  179. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_widget.json.jbuilder +2 -0
  180. data/entry_types/scrolled/config/locales/de.yml +1 -8
  181. data/entry_types/scrolled/config/locales/en.yml +1 -9
  182. data/entry_types/scrolled/config/locales/new/before_after_slider.de.yml +8 -0
  183. data/entry_types/scrolled/config/locales/new/before_after_slider.en.yml +8 -0
  184. data/entry_types/scrolled/config/locales/new/center_ragged.de.yml +8 -0
  185. data/entry_types/scrolled/config/locales/new/center_ragged.en.yml +9 -0
  186. data/entry_types/scrolled/config/locales/new/consent.de.yml +25 -0
  187. data/entry_types/scrolled/config/locales/new/consent.en.yml +24 -0
  188. data/entry_types/scrolled/config/locales/new/content_element_categories.de.yml +39 -0
  189. data/entry_types/scrolled/config/locales/new/content_element_categories.en.yml +39 -0
  190. data/entry_types/scrolled/config/locales/new/default_transition.de.yml +14 -0
  191. data/entry_types/scrolled/config/locales/new/default_transition.en.yml +14 -0
  192. data/entry_types/scrolled/config/locales/new/header_line_breaks.de.yml +28 -0
  193. data/entry_types/scrolled/config/locales/new/header_line_breaks.en.yml +27 -0
  194. data/entry_types/scrolled/config/locales/new/header_size.de.yml +17 -0
  195. data/entry_types/scrolled/config/locales/new/header_size.en.yml +17 -0
  196. data/entry_types/scrolled/config/locales/new/iframe_embed.de.yml +39 -0
  197. data/entry_types/scrolled/config/locales/new/iframe_embed.en.yml +39 -0
  198. data/entry_types/scrolled/config/locales/new/inline_loops.de.yml +26 -0
  199. data/entry_types/scrolled/config/locales/new/inline_loops.en.yml +26 -0
  200. data/entry_types/scrolled/config/locales/new/portrait_inline_image.de.yml +9 -0
  201. data/entry_types/scrolled/config/locales/new/portrait_inline_image.en.yml +9 -0
  202. data/entry_types/scrolled/config/locales/new/section_width.de.yml +10 -0
  203. data/entry_types/scrolled/config/locales/new/section_width.en.yml +10 -0
  204. data/entry_types/scrolled/config/locales/new/typography_variants.de.yml +7 -0
  205. data/entry_types/scrolled/config/locales/new/typography_variants.en.yml +7 -0
  206. data/entry_types/scrolled/config/locales/new/video_embed_poster.de.yml +8 -0
  207. data/entry_types/scrolled/config/locales/new/video_embed_poster.en.yml +8 -0
  208. data/entry_types/scrolled/config/locales/new/waveform_styles.de.yml +11 -0
  209. data/entry_types/scrolled/config/locales/new/waveform_styles.en.yml +12 -0
  210. data/entry_types/scrolled/config/locales/new/widgets.de.yml +6 -0
  211. data/entry_types/scrolled/config/locales/new/widgets.en.yml +6 -0
  212. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +29 -5
  213. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +8 -6
  214. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +37 -0
  215. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +57 -0
  216. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +49 -0
  217. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +8 -0
  218. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +42 -0
  219. data/entry_types/scrolled/lib/pageflow_scrolled.rb +17 -1
  220. data/entry_types/scrolled/package/contentElements-editor.js +323 -89
  221. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  222. data/entry_types/scrolled/package/contentElements-frontend.js +719 -529
  223. data/entry_types/scrolled/package/editor.js +459 -261
  224. data/entry_types/scrolled/package/frontend/{EditableText-7093fd0e.js → EditableInlineText.module-b9923660.js} +284 -362
  225. data/entry_types/scrolled/package/frontend/{i18n-4dc6c377.js → PhonePlatformContext-9fb97827.js} +199 -106
  226. data/entry_types/scrolled/package/frontend/{Viewer-e49e7807.js → Viewer-e2290ea0.js} +196 -79
  227. data/entry_types/scrolled/package/frontend/{Wavesurfer-0adf5667.js → Wavesurfer-7d9cf1b7.js} +16 -58
  228. data/entry_types/scrolled/package/frontend/{components-6a6793ca.js → components-6ab26015.js} +664 -671
  229. data/entry_types/scrolled/package/frontend/{getPrototypeOf-63c7c8e8.js → createSuper-d0f30da3.js} +34 -5
  230. data/entry_types/scrolled/package/frontend/index.css +1 -9
  231. data/entry_types/scrolled/package/frontend/index.js +1919 -2386
  232. data/entry_types/scrolled/package/frontend/{useBrowserFeature-91a4c29d.js → usePhonePlatform-2857c22b.js} +9 -8
  233. data/entry_types/scrolled/package/frontend-server.js +3 -6
  234. data/entry_types/scrolled/package/package.json +16 -8
  235. data/entry_types/scrolled/package/testHelpers.js +456 -0
  236. data/entry_types/scrolled/package/values/breakpoints.module.css +9 -0
  237. data/entry_types/scrolled/package/values/colors.module.css +5 -0
  238. data/entry_types/scrolled/package/widgets/defaultNavigation.css +9 -0
  239. data/entry_types/scrolled/package/widgets/defaultNavigation.js +612 -0
  240. data/entry_types/scrolled/spec/fixtures/image.svg +1 -0
  241. data/lib/pageflow/ability_mixin.rb +4 -0
  242. data/lib/pageflow/built_in_widget_type.rb +4 -0
  243. data/lib/pageflow/built_in_widget_types_plugin.rb +7 -0
  244. data/lib/pageflow/configuration.rb +34 -1
  245. data/lib/pageflow/entry_type.rb +11 -2
  246. data/lib/pageflow/entry_type_configuration.rb +2 -0
  247. data/lib/pageflow/file_type.rb +24 -0
  248. data/lib/pageflow/paperclip_processors/audio_waveform.rb +42 -0
  249. data/lib/pageflow/paperclip_processors/noop.rb +10 -0
  250. data/lib/pageflow/theme_customizations.rb +61 -0
  251. data/lib/pageflow/version.rb +1 -1
  252. data/lib/pageflow.rb +9 -0
  253. data/package/config/jest/index.js +2 -1
  254. data/package/config/postcss/scaleFunctions.js +71 -0
  255. data/package/editor.js +95 -85
  256. data/package/frontend.js +521 -161
  257. data/package/package.json +5 -3
  258. data/package/testHelpers.js +26 -5
  259. data/package/ui.js +124 -11
  260. data/spec/factories/draft_entries.rb +19 -1
  261. data/spec/factories/entries.rb +4 -0
  262. data/spec/factories/published_entries.rb +6 -0
  263. data/spec/fixtures/audio.ogg +0 -0
  264. metadata +118 -14
  265. data/app/views/admin/entries/_not_allowed_to_see_entry_types.json.jbuilder +0 -2
  266. data/app/views/admin/entries/entry_types.json.jbuilder +0 -4
  267. data/package/config/jest/transformers/upwardBabel.js +0 -5
@@ -5,8 +5,47 @@ module Pageflow
5
5
  end
6
6
 
7
7
  def pretty_entry_url(entry, options = {})
8
- params = options.reverse_merge(Pageflow.config.theming_url_options(entry.theming) || {})
9
- pageflow.short_entry_url(entry.to_model, params)
8
+ PrettyUrl.build(pageflow, entry, options)
9
+ end
10
+
11
+ # @api private
12
+ module PrettyUrl
13
+ extend self
14
+
15
+ def build(routes, entry, options)
16
+ with_custom_canonical_url_prefix(entry) ||
17
+ default(routes, entry, options)
18
+ end
19
+
20
+ private
21
+
22
+ def with_custom_canonical_url_prefix(entry)
23
+ return if entry.theming.canonical_entry_url_prefix.blank?
24
+ entry = ensure_entry_with_revision(entry)
25
+
26
+ [
27
+ entry.theming.canonical_entry_url_prefix.gsub(':locale', entry.locale),
28
+ entry.to_param,
29
+ entry.theming.trailing_slash_in_canonical_urls ? '/' : ''
30
+ ].join
31
+ end
32
+
33
+ def default(routes, entry, options)
34
+ params =
35
+ options
36
+ .reverse_merge(trailing_slash: entry.theming.trailing_slash_in_canonical_urls)
37
+ .reverse_merge(Pageflow.config.theming_url_options(entry.theming) || {})
38
+
39
+ routes.short_entry_url(entry.to_model, params)
40
+ end
41
+
42
+ def ensure_entry_with_revision(entry)
43
+ if entry.is_a?(EntryAtRevision)
44
+ entry
45
+ else
46
+ PublishedEntry.new(entry, entry.published_revision || entry.draft)
47
+ end
48
+ end
10
49
  end
11
50
 
12
51
  def entry_privacy_link_url(entry)
@@ -37,7 +37,7 @@ module Pageflow
37
37
  end
38
38
 
39
39
  def rules_for_file(file_type, file)
40
- file_type.css_background_image_urls.call(file).map do |name, url|
40
+ file_type.css_background_image_urls_for(file, entry: entry).map do |name, url|
41
41
  {
42
42
  prefix: rule_prefix(file_type, name),
43
43
  file: file,
@@ -4,7 +4,7 @@ module Pageflow
4
4
 
5
5
  def page_type_json_seeds(config)
6
6
  render_json_partial('pageflow/page_types/page_type',
7
- collection: config.page_types,
7
+ collection: config.page_types.to_a,
8
8
  as: :page_type)
9
9
  end
10
10
 
@@ -10,7 +10,7 @@ module Pageflow
10
10
 
11
11
  def theme_json_seeds(config)
12
12
  render_json_partial('pageflow/themes/theme',
13
- collection: config.themes,
13
+ collection: config.themes.to_a,
14
14
  as: :theme)
15
15
  end
16
16
  end
@@ -1,7 +1,7 @@
1
1
  module Pageflow
2
2
  class PollMetaDataFromZencoderJob < PollZencoderJob
3
3
  def perform_with_result(file, _options)
4
- super(file, skip_thumbnail: true)
4
+ super(file, skip_post_processing: true)
5
5
  end
6
6
  end
7
7
  end
@@ -10,7 +10,7 @@ module Pageflow
10
10
  catch(:halt) do
11
11
  poll_zencoder(file, api)
12
12
  fetch_input_details(file, api)
13
- fetch_thumbnail(file) unless options[:skip_thumbnail]
13
+ file.post_process_encoded_files unless options[:skip_post_processing]
14
14
 
15
15
  :ok
16
16
  end
@@ -39,14 +39,6 @@ module Pageflow
39
39
  raise
40
40
  end
41
41
 
42
- def fetch_thumbnail(file)
43
- return unless file.respond_to?(:thumbnail)
44
- file.thumbnail = URI.parse(file.zencoder_thumbnail.url(default_protocol: 'https'))
45
- file.poster = URI.parse(file.zencoder_poster.url(default_protocol: 'https'))
46
- rescue OpenURI::HTTPError
47
- throw(:halt, :pending)
48
- end
49
-
50
42
  def fetch_input_details(file, api)
51
43
  file.meta_data_attributes = api.get_details(file.job_id)
52
44
  rescue ZencoderApi::RecoverableError => e
@@ -5,6 +5,17 @@ module Pageflow
5
5
 
6
6
  belongs_to :confirmed_by, class_name: 'User', optional: true
7
7
 
8
+ has_attached_file(:peak_data,
9
+ Pageflow.config.paperclip_s3_default_options
10
+ .merge(styles: {
11
+ original: {
12
+ processors: [:pageflow_audio_waveform],
13
+ format: 'json'
14
+ }
15
+ }))
16
+
17
+ do_not_validate_attachment_file_type :peak_data
18
+
8
19
  def attachment_s3_url
9
20
  "s3://#{File.join(attachment.bucket_name, attachment.path)}"
10
21
  end
@@ -28,5 +39,11 @@ module Pageflow
28
39
  def meta_data_attributes=(attributes)
29
40
  self.attributes = attributes.symbolize_keys.slice(:format, :duration_in_ms)
30
41
  end
42
+
43
+ def post_process_encoded_files
44
+ self.peak_data = URI.parse(ogg.url(default_protocol: 'https'))
45
+ rescue OpenURI::HTTPError
46
+ throw(:halt, :pending)
47
+ end
31
48
  end
32
49
  end
@@ -5,6 +5,7 @@ module Pageflow
5
5
  m4a: url_template(:m4a),
6
6
  mp3: url_template(:mp3),
7
7
  ogg: url_template(:ogg),
8
+ peak_data: url_template(:peak_data)
8
9
  }
9
10
  end
10
11
 
@@ -15,7 +16,9 @@ module Pageflow
15
16
  end
16
17
 
17
18
  def example_file
18
- @example_file ||= AudioFile.new(id: 0, file_name: ':basename.mp3')
19
+ @example_file ||= AudioFile.new(id: 0,
20
+ file_name: ':basename.mp3',
21
+ peak_data_file_name: 'audio.json')
19
22
  end
20
23
  end
21
24
  end
@@ -0,0 +1,37 @@
1
+ module Pageflow
2
+ # @api private
3
+ class CustomizedTheme < SimpleDelegator
4
+ def initialize(theme, overrides, files)
5
+ super(theme)
6
+ @options = __getobj__.options.deep_merge(overrides || {})
7
+ @files = files
8
+ end
9
+
10
+ attr_reader :options, :files
11
+
12
+ def self.find(entry:, theme:)
13
+ build(
14
+ entry: entry,
15
+ theme: theme,
16
+ theme_customization: Pageflow.theme_customizations.get(
17
+ account: entry.account,
18
+ entry_type_name: entry.type_name
19
+ )
20
+ )
21
+ end
22
+
23
+ def self.build(entry:, theme:, theme_customization:)
24
+ config = Pageflow.config_for(entry)
25
+
26
+ new(theme,
27
+ config.transform_theme_customization_overrides.call(
28
+ theme_customization.overrides,
29
+ entry
30
+ ),
31
+ config.transform_theme_customization_files.call(
32
+ theme_customization.selected_files.transform_values(&:urls),
33
+ entry
34
+ ))
35
+ end
36
+ end
37
+ end
@@ -1,42 +1,13 @@
1
1
  module Pageflow
2
- class DraftEntry
3
- include ActiveModel::Conversion
4
-
2
+ # A merged view of an entry and its draft revision
3
+ class DraftEntry < EntryAtRevision
5
4
  class InvalidForeignKeyCustomAttributeError < StandardError; end
6
5
 
7
- attr_reader :entry, :draft
8
-
9
- delegate(:id, :slug,
10
- :entry_type,
11
- :edit_lock, :account, :theming, :slug,
12
- :enabled_feature_names,
13
- :published_until, :published?,
14
- :password_digest,
15
- :to_model, :to_key, :persisted?, :to_json,
16
- :first_published_at,
17
- :type_name,
18
- :to => :entry)
19
-
20
- delegate(:title, :summary, :credits,
21
- :widgets,
22
- :storylines, :main_storyline_chapters, :chapters, :pages,
23
- :share_url, :share_image_id, :share_image_x, :share_image_y,
24
- :share_providers, :active_share_providers,
25
- :find_files, :find_file, :find_file_by_perma_id,
26
- :image_files, :video_files, :audio_files,
27
- :locale,
28
- :author, :publisher, :keywords,
29
- :theme,
30
- :published_at,
31
- :configuration,
32
- :to => :draft)
33
-
34
6
  def initialize(entry, draft = nil)
35
- @entry = entry
36
- @draft = draft || entry.draft
7
+ super(entry, draft || entry.draft)
37
8
  end
38
9
 
39
- alias revision draft
10
+ alias draft revision
40
11
 
41
12
  # So we can always get to the original Entry title.
42
13
  def entry_title
@@ -50,8 +21,8 @@ module Pageflow
50
21
  f.entry = entry
51
22
  end
52
23
 
53
- usage = @draft.file_usages.create_with_lock!(file: file,
54
- configuration: attributes[:configuration])
24
+ usage = revision.file_usages.create_with_lock!(file: file,
25
+ configuration: attributes[:configuration])
55
26
  UsedFile.new(file, usage)
56
27
  end
57
28
 
@@ -106,30 +77,6 @@ module Pageflow
106
77
  draft.cache_key
107
78
  end
108
79
 
109
- def home_button
110
- HomeButton.new(draft, theming)
111
- end
112
-
113
- def overview_button
114
- OverviewButton.new(draft)
115
- end
116
-
117
- def manual_start
118
- revision.configuration['manual_start']
119
- end
120
-
121
- def emphasize_chapter_beginning
122
- revision.configuration['emphasize_chapter_beginning']
123
- end
124
-
125
- def emphasize_new_pages
126
- revision.configuration['emphasize_new_pages']
127
- end
128
-
129
- def resolve_widgets(options = {})
130
- widgets.resolve(Pageflow.config_for(entry), options)
131
- end
132
-
133
80
  private
134
81
 
135
82
  def check_foreign_key_custom_attributes(custom_attributes, attributes)
@@ -0,0 +1,68 @@
1
+ module Pageflow
2
+ # A simplified view of an entry at a given revision. Hides the
3
+ # revision concept and makes revision attributes available along
4
+ # with entry attributes.
5
+ class EntryAtRevision
6
+ include ActiveModel::Conversion
7
+
8
+ attr_reader :entry, :revision
9
+
10
+ def initialize(entry, revision, theme: nil)
11
+ @entry = entry
12
+ @revision = revision
13
+ @theme = theme
14
+ end
15
+
16
+ delegate(:id, :slug,
17
+ :entry_type,
18
+ :account, :theming,
19
+ :feature_state, :enabled_feature_names,
20
+ :edit_lock,
21
+ :password_digest,
22
+ :to_model, :to_key, :to_param, :persisted?, :to_json,
23
+ :first_published_at, :published_until, :published?,
24
+ :type_name,
25
+ to: :entry)
26
+
27
+ delegate(:title, :summary, :credits,
28
+ :widgets,
29
+ :storylines, :main_storyline_chapters, :chapters, :pages,
30
+ :share_url, :share_image_id, :share_image_x, :share_image_y,
31
+ :share_providers, :active_share_providers,
32
+ :find_files, :find_file, :find_file_by_perma_id,
33
+ :image_files, :video_files, :audio_files,
34
+ :locale,
35
+ :author, :publisher, :keywords,
36
+ :published_at,
37
+ :configuration,
38
+ to: :revision)
39
+
40
+ def resolve_widgets(options = {})
41
+ widgets.resolve(Pageflow.config_for(entry), options)
42
+ end
43
+
44
+ def theme
45
+ @theme ||= CustomizedTheme.find(entry: self, theme: revision.theme)
46
+ end
47
+
48
+ def home_button
49
+ HomeButton.new(revision, theming)
50
+ end
51
+
52
+ def overview_button
53
+ OverviewButton.new(revision)
54
+ end
55
+
56
+ def manual_start
57
+ revision.configuration['manual_start']
58
+ end
59
+
60
+ def emphasize_chapter_beginning
61
+ revision.configuration['emphasize_chapter_beginning']
62
+ end
63
+
64
+ def emphasize_new_pages
65
+ revision.configuration['emphasize_new_pages']
66
+ end
67
+ end
68
+ end
@@ -11,33 +11,39 @@ module Pageflow
11
11
 
12
12
  def with_role_at_least(role)
13
13
  scope
14
- .joins(memberships_for_entries_with_at_least_role(role))
15
- .joins(memberships_for_account_of_entries_with_at_least_role(role))
16
- .where(either_membership_is_present)
14
+ .joins(memberships_for_account_of_entries)
15
+ .joins(memberships_for_entries)
16
+ .where(either_membership_has_at_least_role(role))
17
17
  end
18
18
 
19
19
  def with_account_role_at_least(role)
20
20
  scope
21
- .joins(memberships_for_account_of_entries_with_at_least_role(role))
22
- .where(entry_account_membership_is_present)
21
+ .joins(memberships_for_account_of_entries)
22
+ .where(entry_account_membership_has_at_least_role(role))
23
23
  end
24
24
 
25
25
  private
26
26
 
27
- def memberships_for_entries_with_at_least_role(role)
28
- join_memberships(table_alias: table_alias_for('entry'),
27
+ def memberships_for_account_of_entries
28
+ # Since every user with an entry membership also has a
29
+ # membership for the entry's account, we can use an INNER JOIN
30
+ # to prevent a full scan of the entries table. The database
31
+ # will first look up the account memberships and then get the
32
+ # entries using the index on `account_id`. Those are then
33
+ # filtered further based on entry memberships and roles.
34
+ join_memberships(join_type: 'INNER',
35
+ table_alias: table_alias_for('entry_account'),
29
36
  user_id: user.id,
30
- roles: Roles.at_least(role),
31
- entity_id_column: 'pageflow_entries.id',
32
- entity_type: 'Pageflow::Entry')
37
+ entity_id_column: 'pageflow_entries.account_id',
38
+ entity_type: 'Pageflow::Account')
33
39
  end
34
40
 
35
- def memberships_for_account_of_entries_with_at_least_role(role)
36
- join_memberships(table_alias: table_alias_for('entry_account'),
41
+ def memberships_for_entries
42
+ join_memberships(join_type: 'LEFT OUTER',
43
+ table_alias: table_alias_for('entry'),
37
44
  user_id: user.id,
38
- roles: Roles.at_least(role),
39
- entity_id_column: 'pageflow_entries.account_id',
40
- entity_type: 'Pageflow::Account')
45
+ entity_id_column: 'pageflow_entries.id',
46
+ entity_type: 'Pageflow::Entry')
41
47
  end
42
48
 
43
49
  def join_memberships(options)
@@ -45,25 +51,39 @@ module Pageflow
45
51
  entity_id_column = options[:entity_id_column]
46
52
 
47
53
  sanitize_sql(<<-SQL, options)
48
- LEFT OUTER JOIN pageflow_memberships as #{table_alias} ON
54
+ #{options[:join_type]} JOIN pageflow_memberships as #{table_alias} ON
49
55
  #{table_alias}.user_id = :user_id AND
50
- #{table_alias}.role IN (:roles) AND
51
56
  #{table_alias}.entity_id = #{entity_id_column} AND
52
57
  #{table_alias}.entity_type = :entity_type
53
58
  SQL
54
59
  end
55
60
 
56
- def either_membership_is_present
57
- [entry_membership_is_present,
58
- entry_account_membership_is_present].join(' OR ')
61
+ def either_membership_has_at_least_role(role)
62
+ [
63
+ entry_account_membership_has_at_least_role(role),
64
+ entry_membership_has_at_least_role(role)
65
+ ].join(' OR ')
66
+ end
67
+
68
+ def entry_account_membership_has_at_least_role(role)
69
+ membership_has_at_least_role(
70
+ table_alias: table_alias_for('entry_account'),
71
+ role: role
72
+ )
59
73
  end
60
74
 
61
- def entry_membership_is_present
62
- "#{table_alias_for(:entry)}.entity_id IS NOT NULL"
75
+ def entry_membership_has_at_least_role(role)
76
+ membership_has_at_least_role(
77
+ table_alias: table_alias_for('entry'),
78
+ role: role
79
+ )
63
80
  end
64
81
 
65
- def entry_account_membership_is_present
66
- "#{table_alias_for(:entry_account)}.entity_id IS NOT NULL"
82
+ def membership_has_at_least_role(table_alias:, role:)
83
+ sanitize_sql(
84
+ "#{table_alias}.role IN (:roles)",
85
+ roles: Roles.at_least(role)
86
+ )
67
87
  end
68
88
 
69
89
  def table_alias_for(type)
@@ -1,10 +1,10 @@
1
1
  module Pageflow
2
2
  # @api private
3
3
  class ImageFileCssBackgroundImageUrls
4
- def call(image_file)
4
+ def call(image_file, entry:)
5
5
  {
6
6
  default: {
7
- desktop: image_file.ready? ? image_file.attachment.url(:large) : '',
7
+ desktop: image_file.ready? ? image_file.attachment.url(desktop_style(entry)) : '',
8
8
  mobile: image_file.ready? ? image_file.attachment.url(:medium) : ''
9
9
  },
10
10
  panorama: {
@@ -13,5 +13,11 @@ module Pageflow
13
13
  }
14
14
  }
15
15
  end
16
+
17
+ private
18
+
19
+ def desktop_style(entry)
20
+ entry.feature_state('highdef_background_images') ? :ultra : :large
21
+ end
16
22
  end
17
23
  end
@@ -1,56 +1,21 @@
1
1
  module Pageflow
2
- class PublishedEntry
3
- include ActiveModel::Conversion
2
+ # A merged view of an entry and its published revision.
3
+ class PublishedEntry < EntryAtRevision
4
4
  extend ActiveModel::Naming
5
5
 
6
- attr_reader :entry, :revision
7
6
  attr_accessor :share_target
8
7
 
9
- delegate(:id, :slug,
10
- :entry_type,
11
- :account, :theming,
12
- :enabled_feature_names,
13
- :to_model, :to_key, :persisted?,
14
- :authenticate,
15
- :first_published_at,
16
- :type_name,
17
- :to => :entry)
18
-
19
- delegate(:widgets,
20
- :storylines, :main_storyline_chapters, :chapters, :pages,
21
- :find_files, :find_file, :find_file_by_perma_id,
22
- :image_files, :video_files, :audio_files,
23
- :summary, :credits,
24
- :share_url, :share_image_id, :share_image_x, :share_image_y,
25
- :share_providers, :active_share_providers,
26
- :locale,
27
- :author, :publisher, :keywords,
28
- :theme,
29
- :password_protected?,
30
- :published_at,
31
- :configuration,
32
- :to => :revision)
33
-
34
- def initialize(entry, revision = nil)
35
- @entry = entry
36
- @revision = revision || entry.published_revision
37
- @custom_revision = !!revision
38
- end
8
+ delegate(:authenticate, to: :entry)
39
9
 
40
- def title
41
- revision.title.presence || entry.title
42
- end
10
+ delegate(:password_protected?, to: :revision)
43
11
 
44
- def manual_start
45
- revision.configuration['manual_start']
12
+ def initialize(entry, revision = nil, theme: nil)
13
+ super(entry, revision || entry.published_revision, theme: theme)
14
+ @custom_revision = revision
46
15
  end
47
16
 
48
- def emphasize_chapter_beginning
49
- revision.configuration['emphasize_chapter_beginning']
50
- end
51
-
52
- def emphasize_new_pages
53
- revision.configuration['emphasize_new_pages']
17
+ def title
18
+ revision.title.presence || entry.title
54
19
  end
55
20
 
56
21
  def stylesheet_model
@@ -98,18 +63,6 @@ module Pageflow
98
63
  ].compact.join('-').presence
99
64
  end
100
65
 
101
- def home_button
102
- HomeButton.new(revision, theming)
103
- end
104
-
105
- def overview_button
106
- OverviewButton.new(revision)
107
- end
108
-
109
- def resolve_widgets(options = {})
110
- widgets.resolve(Pageflow.config_for(entry), options)
111
- end
112
-
113
66
  private
114
67
 
115
68
  def custom_revision?
@@ -0,0 +1,46 @@
1
+ module Pageflow
2
+ # Override theme options and upload theme files for an entry type on
3
+ # a per account basis.
4
+ #
5
+ # @since 15.7
6
+ class ThemeCustomization < ApplicationRecord
7
+ belongs_to :account
8
+
9
+ serialize :overrides, JSON
10
+ serialize :selected_file_ids, JSON
11
+
12
+ has_many :uploaded_files, class_name: 'ThemeCustomizationFile'
13
+
14
+ # Options that were passed to [ThemeCustomizations#update].
15
+ #
16
+ # @return [Hash<Symbol, Object>]
17
+ def overrides
18
+ super&.deep_symbolize_keys || {}
19
+ end
20
+
21
+ # Theme customization files that have been assigned a role via
22
+ # [ThemeCustomizations#update].
23
+ #
24
+ # @return [Hash<Symbol, ThemeCustomizationFile>]
25
+ def selected_files
26
+ selected_file_ids.compact.transform_values do |id|
27
+ selected_files_by_id[id]
28
+ end
29
+ end
30
+
31
+ # @api private
32
+ def entry_type
33
+ Pageflow.config.entry_types.find_by_name!(entry_type_name)
34
+ end
35
+
36
+ private
37
+
38
+ def selected_file_ids
39
+ super&.deep_symbolize_keys || {}
40
+ end
41
+
42
+ def selected_files_by_id
43
+ @selected_files_by_id ||= uploaded_files.where(id: selected_file_ids.values).index_by(&:id)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,58 @@
1
+ module Pageflow
2
+ # A file that has been uploaded by the user to customize a theme,
3
+ # e.g., to use a custom logo.
4
+ #
5
+ # @since 15.7
6
+ class ThemeCustomizationFile < ApplicationRecord
7
+ belongs_to :theme_customization
8
+
9
+ has_attached_file(:attachment,
10
+ Pageflow
11
+ .config.paperclip_s3_default_options
12
+ .merge(styles: ->(attachment) { attachment.instance.attachment_styles }))
13
+
14
+ # Paperclip requires a content type validator for security
15
+ # reasons. Sub-classes are fine, though. The original validator
16
+ # does not provide a way to dynamically determine the permitted
17
+ # content types.
18
+ #
19
+ # @api private
20
+ class AttachmentContentTypeValidator < Paperclip::Validators::AttachmentContentTypeValidator
21
+ def validate_whitelist(record, attribute, value)
22
+ return if value =~ record.options_from_entry_type[:content_type]
23
+ record.errors.add(attribute, :invalid)
24
+ end
25
+
26
+ # Override to not require :content_type option.
27
+ def check_validity!
28
+ true
29
+ end
30
+ end
31
+
32
+ validates_with AttachmentContentTypeValidator, attributes: :attachment
33
+
34
+ # The name of the originally uploaded file
35
+ def file_name
36
+ attachment_file_name
37
+ end
38
+
39
+ # A hash of urls based on the styles that were defined when
40
+ # registering the entry type.
41
+ def urls
42
+ attachment_styles.map { |style, _|
43
+ [style, attachment.url(style)]
44
+ }.to_h
45
+ end
46
+
47
+ # @api private
48
+ def options_from_entry_type
49
+ theme_customization.entry_type.theme_files.fetch(type_name.to_sym, {})
50
+ end
51
+
52
+ # @api private
53
+ def attachment_styles
54
+ styles = options_from_entry_type.fetch(:styles, {})
55
+ styles.respond_to?(:call) ? styles.call(self) : styles
56
+ end
57
+ end
58
+ end