pageflow 17.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +319 -345
- data/README.md +3 -4
- data/admins/pageflow/accounts.rb +6 -6
- data/admins/pageflow/entry.rb +10 -8
- data/admins/pageflow/membership.rb +11 -11
- data/admins/pageflow/translations.rb +5 -5
- data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
- data/app/assets/javascripts/pageflow/dist/ui.js +1350 -630
- data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
- data/app/assets/javascripts/pageflow/ui.js +0 -1
- data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
- data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -1
- data/app/assets/stylesheets/pageflow/admin.scss +1 -0
- data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
- data/app/assets/stylesheets/pageflow/animations.scss +1 -0
- data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +21 -5
- data/app/assets/stylesheets/pageflow/editor/base.scss +9 -1
- data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +13 -1
- data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
- data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
- data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +0 -1
- data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
- data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
- data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
- data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
- data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
- data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
- data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
- data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
- data/app/assets/stylesheets/pageflow/editor/files.scss +230 -50
- data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +142 -14
- data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
- data/app/assets/stylesheets/pageflow/editor/info_box.scss +12 -0
- data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
- data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
- data/app/assets/stylesheets/pageflow/editor/list.scss +88 -38
- data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
- data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
- data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
- data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
- data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
- data/app/assets/stylesheets/pageflow/ui/forms.scss +34 -4
- data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
- data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -16
- data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
- data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
- data/app/assets/stylesheets/pageflow/ui/properties.scss +29 -0
- data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
- data/app/assets/stylesheets/pageflow/ui.scss +4 -2
- data/app/controllers/pageflow/editor/entries_controller.rb +1 -0
- data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
- data/app/controllers/pageflow/editor/file_import_controller.rb +1 -0
- data/app/controllers/pageflow/editor/files_controller.rb +15 -6
- data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
- data/app/controllers/pageflow/entries_controller.rb +17 -13
- data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
- data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
- data/app/controllers/pageflow/review/comments_controller.rb +53 -0
- data/app/controllers/pageflow/revisions_controller.rb +4 -1
- data/app/helpers/pageflow/admin/entries_helper.rb +40 -0
- data/app/helpers/pageflow/admin/widgets_helper.rb +2 -2
- data/app/helpers/pageflow/common_entry_seed_helper.rb +5 -3
- data/app/helpers/pageflow/editor/config_helper.rb +3 -1
- data/app/helpers/pageflow/entries_helper.rb +24 -42
- data/app/helpers/pageflow/entry_json_seed_helper.rb +4 -4
- data/app/helpers/pageflow/public_i18n_helper.rb +9 -8
- data/app/helpers/pageflow/structured_data_helper.rb +4 -1
- data/app/helpers/pageflow/themes_helper.rb +1 -3
- data/app/helpers/pageflow/widgets_helper.rb +2 -1
- data/app/inputs/pageflow_permalink_input.rb +6 -2
- data/app/models/concerns/pageflow/auto_generated_perma_id.rb +11 -4
- data/app/models/concerns/pageflow/reusable_file.rb +1 -1
- data/app/models/concerns/pageflow/serialization_blacklist.rb +2 -4
- data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
- data/app/models/concerns/pageflow/uploadable_file.rb +1 -1
- data/app/models/pageflow/comment.rb +30 -0
- data/app/models/pageflow/comment_thread.rb +65 -0
- data/app/models/pageflow/comment_thread_read.rb +22 -0
- data/app/models/pageflow/customized_theme.rb +3 -2
- data/app/models/pageflow/draft_entry.rb +20 -8
- data/app/models/pageflow/entry.rb +2 -0
- data/app/models/pageflow/entry_at_revision.rb +9 -1
- data/app/models/pageflow/entry_comment_summary.rb +114 -0
- data/app/models/pageflow/file_folder.rb +74 -0
- data/app/models/pageflow/file_reuse.rb +3 -2
- data/app/models/pageflow/file_usage.rb +8 -0
- data/app/models/pageflow/image_file.rb +24 -20
- data/app/models/pageflow/image_file_url_templates.rb +3 -3
- data/app/models/pageflow/legal_info.rb +38 -0
- data/app/models/pageflow/nested_revision_component_copy.rb +1 -1
- data/app/models/pageflow/permalink.rb +1 -0
- data/app/models/pageflow/revision.rb +6 -2
- data/app/models/pageflow/site.rb +10 -0
- data/app/models/pageflow/used_file.rb +8 -2
- data/app/models/pageflow/widget.rb +2 -2
- data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
- data/app/views/pageflow/editor/config/_seeds.json.jbuilder +4 -2
- data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
- data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
- data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
- data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
- data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
- data/app/views/pageflow/editor/files/_file.json.jbuilder +2 -1
- data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
- data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +3 -0
- data/app/views/pageflow/files/_file.json.jbuilder +2 -0
- data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
- data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
- data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
- data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
- data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
- data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
- data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
- data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
- data/config/locales/de.yml +152 -14
- data/config/locales/en.yml +150 -14
- data/config/routes.rb +14 -0
- data/db/migrate/20140418225525_setup_schema.rb +2 -2
- data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
- data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
- data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
- data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
- data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
- data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
- data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
- data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
- data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
- data/db/migrate/20260728000000_create_file_folders.rb +17 -0
- data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
- data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
- data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
- data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
- data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +8665 -3890
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +979 -3571
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20199 -28120
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18652 -26584
- data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +33 -3
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -3
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +21 -1
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +7 -5
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +5 -2
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +13 -5
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +26 -11
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +2 -2
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +174 -3
- data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +49 -7
- data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
- data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
- data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +1 -0
- data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +13 -12
- data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +30 -10
- data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +9 -0
- data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
- data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
- data/entry_types/scrolled/config/locales/de.yml +512 -65
- data/entry_types/scrolled/config/locales/en.yml +518 -52
- data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
- data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +51 -6
- data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
- data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +205 -22
- data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
- data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +56 -36
- data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
- data/entry_types/scrolled/lib/pageflow_scrolled.rb +13 -0
- data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +19 -1
- data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
- data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
- data/entry_types/scrolled/package/config/webpack.js +72 -0
- data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -1
- data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1181 -915
- data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
- data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
- data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
- data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +71 -21
- data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
- data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
- data/entry_types/scrolled/package/contentElements-editor.js +1915 -1097
- data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
- data/entry_types/scrolled/package/contentElements-frontend.js +3093 -1679
- data/entry_types/scrolled/package/editor.css +1 -0
- data/entry_types/scrolled/package/editor.js +8667 -4077
- data/entry_types/scrolled/package/entryState.js +1600 -0
- data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
- data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
- data/entry_types/scrolled/package/frontend/{PhonePlatformContext-9f76033e.js → PhonePlatformContext-035a99fa.js} +7 -12
- data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
- data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
- data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
- data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
- data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
- data/entry_types/scrolled/package/frontend/{ToggleFullscreenCornerButton-788e9cdb.js → ToggleFullscreenCornerButton-30fd1124.js} +6 -6
- data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
- data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
- data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
- data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
- data/entry_types/scrolled/package/frontend/{Viewer-0345ce57.js → Viewer-cc8d4072.js} +84 -92
- data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
- data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
- data/entry_types/scrolled/package/frontend/commenting.css +1 -0
- data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
- data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
- data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
- data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
- data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
- data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
- data/entry_types/scrolled/package/frontend/{index-eb670c2f.js → index-2e5546f7.js} +36 -44
- data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
- data/entry_types/scrolled/package/frontend/index.css +1 -1
- data/entry_types/scrolled/package/frontend/index.js +1870 -875
- data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
- data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
- data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
- data/entry_types/scrolled/package/frontend/{usePhonePlatform-c2ba875d.js → usePhonePlatform-4ecb96d4.js} +1 -1
- data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
- data/entry_types/scrolled/package/frontend-server.js +20 -209
- data/entry_types/scrolled/package/package.json +5 -3
- data/entry_types/scrolled/package/review.css +1 -0
- data/entry_types/scrolled/package/review.js +3306 -0
- data/entry_types/scrolled/package/testHelpers.js +836 -359
- data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
- data/entry_types/scrolled/package/widgets/consentBar.js +269 -290
- data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
- data/entry_types/scrolled/package/widgets/defaultNavigation.js +911 -352
- data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -1
- data/entry_types/scrolled/package/widgets/excursionSheet.js +87 -124
- data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
- data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -29
- data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +32 -13
- data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -1
- data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +56 -100
- data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
- data/entry_types/scrolled/package/widgets/textInlineFileRights.js +33 -28
- data/entry_types/scrolled/package/widgets-server.js +1 -0
- data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
- data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
- data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
- data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
- data/lib/generators/pageflow/resque/resque_generator.rb +0 -2
- data/lib/pageflow/configuration.rb +49 -7
- data/lib/pageflow/deprecation.rb +19 -0
- data/lib/pageflow/engine.rb +1 -1
- data/lib/pageflow/entries_controller_env_helper.rb +32 -2
- data/lib/pageflow/entry_export_import/entry_serialization.rb +4 -4
- data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
- data/lib/pageflow/entry_export_import/revision_serialization/import.rb +10 -0
- data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
- data/lib/pageflow/entry_structured_data_types.rb +43 -0
- data/lib/pageflow/entry_type_configuration.rb +1 -0
- data/lib/pageflow/file_type.rb +2 -2
- data/lib/pageflow/file_types.rb +1 -1
- data/lib/pageflow/oembed_providers.rb +42 -0
- data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
- data/lib/pageflow/rails_version.rb +13 -2
- data/lib/pageflow/themes.rb +41 -0
- data/lib/pageflow/user_mixin.rb +12 -0
- data/lib/pageflow/version.rb +1 -1
- data/lib/pageflow.rb +1 -0
- data/package/config/eslint-rules/documented-in-toc.js +125 -0
- data/package/config/jest/index.js +0 -1
- data/package/config/webpack.js +0 -1
- data/package/config/webpack5.js +0 -1
- data/package/editor.js +4378 -1897
- data/package/frontend.js +647 -886
- data/package/package.json +2 -1
- data/package/review.js +384 -0
- data/package/testHelpers.js +248 -188
- data/package/ui.js +1346 -630
- data/package/vendor/stubs/wysihtml5.js +2 -0
- data/spec/factories/comment_thread_reads.rb +10 -0
- data/spec/factories/comment_threads.rb +10 -0
- data/spec/factories/comments.rb +9 -0
- data/spec/factories/file_folders.rb +8 -0
- data/spec/factories/published_entries.rb +2 -0
- metadata +188 -67
- data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
- data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-51914be7.js +0 -5439
- data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +0 -1932
- data/entry_types/scrolled/package/frontend/Viewer-cdc549cc.js +0 -147
- data/entry_types/scrolled/package/frontend/Wavesurfer-1cdc3925.js +0 -313
- data/entry_types/scrolled/package/frontend/components-96660ffd.js +0 -3803
- data/entry_types/scrolled/package/frontend/inherits-539844a6.js +0 -99
- data/entry_types/scrolled/package/frontend/useContentElementEditorState-77fe6c79.js +0 -51
- data/entry_types/scrolled/package/widgets/excursionDialog.css +0 -1
- data/entry_types/scrolled/package/widgets/excursionDialog.js +0 -109
- data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
- data/package/vendor/jquery.minicolors.js +0 -1108
|
@@ -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
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module Pageflow
|
|
2
2
|
# @api private
|
|
3
3
|
class CustomizedTheme < SimpleDelegator
|
|
4
|
-
def initialize(theme, overrides, files)
|
|
4
|
+
def initialize(theme, transformed_options, overrides, files)
|
|
5
5
|
super(theme)
|
|
6
|
-
@options =
|
|
6
|
+
@options = transformed_options.deep_merge(overrides || {})
|
|
7
7
|
@files = files
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -24,6 +24,7 @@ module Pageflow
|
|
|
24
24
|
config = Pageflow.config_for(entry)
|
|
25
25
|
|
|
26
26
|
new(theme,
|
|
27
|
+
config.themes.apply_default_options(theme.options, entry:),
|
|
27
28
|
config.transform_theme_customization_overrides.call(
|
|
28
29
|
theme_customization.overrides,
|
|
29
30
|
entry:,
|
|
@@ -31,15 +31,18 @@ module Pageflow
|
|
|
31
31
|
def create_file!(file_type, attributes)
|
|
32
32
|
check_foreign_key_custom_attributes(file_type.custom_attributes, attributes)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
file_attributes = attributes.except(:configuration, :display_name, :folder_perma_id)
|
|
35
|
+
|
|
36
|
+
if file_name_attribute?(file_type)
|
|
37
|
+
file_attributes[:file_name] = generate_file_name(attributes[:display_name])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
file = file_type.model.create!(file_attributes) { |f| f.entry = entry }
|
|
39
41
|
|
|
40
42
|
usage = revision.file_usages.create!(file:,
|
|
41
43
|
configuration: attributes[:configuration],
|
|
42
|
-
display_name: attributes[:display_name]
|
|
44
|
+
display_name: attributes[:display_name],
|
|
45
|
+
folder_perma_id: attributes[:folder_perma_id])
|
|
43
46
|
UsedFile.new(file, usage)
|
|
44
47
|
end
|
|
45
48
|
|
|
@@ -59,10 +62,15 @@ module Pageflow
|
|
|
59
62
|
file.destroy if file.usages.reload.empty?
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
def
|
|
65
|
+
def create_folder!(attributes)
|
|
66
|
+
draft.file_folders.create!(attributes)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def use_file(file, folder_perma_id: nil)
|
|
63
70
|
draft.file_usages.create!(file: file.to_model,
|
|
64
71
|
configuration: file.configuration,
|
|
65
|
-
display_name: file.display_name
|
|
72
|
+
display_name: file.display_name,
|
|
73
|
+
folder_perma_id:)
|
|
66
74
|
end
|
|
67
75
|
|
|
68
76
|
def save!
|
|
@@ -115,6 +123,10 @@ module Pageflow
|
|
|
115
123
|
end
|
|
116
124
|
end
|
|
117
125
|
|
|
126
|
+
def file_name_attribute?(file_type)
|
|
127
|
+
file_type.model.new.respond_to?(:file_name=)
|
|
128
|
+
end
|
|
129
|
+
|
|
118
130
|
def generate_file_name(file_name)
|
|
119
131
|
return nil if file_name.blank?
|
|
120
132
|
|
|
@@ -33,6 +33,8 @@ module Pageflow
|
|
|
33
33
|
|
|
34
34
|
has_many :imports, class_name: 'Pageflow::FileImport', dependent: :destroy
|
|
35
35
|
|
|
36
|
+
has_many :comment_thread_reads, dependent: :destroy
|
|
37
|
+
|
|
36
38
|
has_one :draft, -> { editable }, class_name: 'Revision', inverse_of: :entry
|
|
37
39
|
has_one :published_revision, -> { published }, class_name: 'Revision', inverse_of: :entry
|
|
38
40
|
|
|
@@ -26,18 +26,22 @@ module Pageflow
|
|
|
26
26
|
:type_name,
|
|
27
27
|
to: :entry)
|
|
28
28
|
|
|
29
|
-
delegate(:
|
|
29
|
+
delegate(:layout_version,
|
|
30
|
+
:title, :summary, :credits,
|
|
30
31
|
:widgets,
|
|
31
32
|
:storylines, :main_storyline_chapters, :chapters, :pages,
|
|
32
33
|
:share_url, :share_image_id, :share_image_x, :share_image_y,
|
|
33
34
|
:share_providers, :active_share_providers,
|
|
34
35
|
:find_files, :find_file, :find_file_by_perma_id,
|
|
36
|
+
:file_folders,
|
|
35
37
|
:image_files, :video_files, :audio_files,
|
|
36
38
|
:locale,
|
|
37
39
|
:author, :publisher, :keywords,
|
|
38
40
|
:published_at,
|
|
39
41
|
:noindex?,
|
|
42
|
+
:comment_threads,
|
|
40
43
|
:configuration,
|
|
44
|
+
:structured_data_type_name,
|
|
41
45
|
to: :revision)
|
|
42
46
|
|
|
43
47
|
def resolve_widgets(options = {})
|
|
@@ -48,6 +52,10 @@ module Pageflow
|
|
|
48
52
|
@theme ||= CustomizedTheme.find(entry: self, theme: revision.theme)
|
|
49
53
|
end
|
|
50
54
|
|
|
55
|
+
def legal_info
|
|
56
|
+
@legal_info ||= site.legal_info(locale:)
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
def home_button
|
|
52
60
|
HomeButton.new(revision, site)
|
|
53
61
|
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
# Counts of comment topics and of activity the user has not seen, for
|
|
3
|
+
# displaying an indicator next to an entry in lists of entries.
|
|
4
|
+
#
|
|
5
|
+
# Built for a whole page of entries at once: rendering a list must not
|
|
6
|
+
# query per row.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class EntryCommentSummary
|
|
10
|
+
attr_reader :topic_count, :unread_topic_count, :unread_reply_count, :unread_resolution_count
|
|
11
|
+
|
|
12
|
+
def self.for_entries(entries, user:)
|
|
13
|
+
entries = entries.to_a
|
|
14
|
+
return {} if entries.empty?
|
|
15
|
+
|
|
16
|
+
threads = threads_by_entry_id(entries)
|
|
17
|
+
read_at = read_at_by_entry_id(entries, user)
|
|
18
|
+
|
|
19
|
+
entries.to_h do |entry|
|
|
20
|
+
[entry.id, build(threads.fetch(entry.id, []),
|
|
21
|
+
read_at: read_at.fetch(entry.id, {}),
|
|
22
|
+
user:)]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(topic_count:, unread_topic_count:, unread_reply_count:,
|
|
27
|
+
unread_resolution_count: 0)
|
|
28
|
+
@topic_count = topic_count
|
|
29
|
+
@unread_topic_count = unread_topic_count
|
|
30
|
+
@unread_reply_count = unread_reply_count
|
|
31
|
+
@unread_resolution_count = unread_resolution_count
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Unread activity shows even where no topic is left open: the last
|
|
35
|
+
# one being resolved is exactly what the user should not miss.
|
|
36
|
+
def any?
|
|
37
|
+
topic_count.positive? || unread?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def unread?
|
|
41
|
+
unread_topic_count.positive? ||
|
|
42
|
+
unread_reply_count.positive? ||
|
|
43
|
+
unread_resolution_count.positive?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Comment threads live on the draft revision, so entries are reached
|
|
47
|
+
# through their editable revision rather than directly. Resolved ones
|
|
48
|
+
# come along: somebody resolving a thread is activity of its own.
|
|
49
|
+
def self.threads_by_entry_id(entries)
|
|
50
|
+
entry_id_by_revision_id =
|
|
51
|
+
Revision.editable.where(entry_id: entries.map(&:id)).pluck(:id, :entry_id).to_h
|
|
52
|
+
|
|
53
|
+
CommentThread
|
|
54
|
+
.where(revision_id: entry_id_by_revision_id.keys)
|
|
55
|
+
.includes(:comments)
|
|
56
|
+
.group_by { |thread| entry_id_by_revision_id[thread.revision_id] }
|
|
57
|
+
end
|
|
58
|
+
private_class_method :threads_by_entry_id
|
|
59
|
+
|
|
60
|
+
def self.read_at_by_entry_id(entries, user)
|
|
61
|
+
CommentThreadRead
|
|
62
|
+
.where(user:, entry_id: entries.map(&:id))
|
|
63
|
+
.pluck(:entry_id, :comment_thread_perma_id, :read_at)
|
|
64
|
+
.group_by(&:first)
|
|
65
|
+
.transform_values do |rows|
|
|
66
|
+
rows.to_h { |(_entry_id, perma_id, read_at)| [perma_id, read_at] }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
private_class_method :read_at_by_entry_id
|
|
70
|
+
|
|
71
|
+
def self.build(threads, read_at:, user:)
|
|
72
|
+
unread = threads.map { |thread| unread_activity(thread, read_at:, user:) }
|
|
73
|
+
|
|
74
|
+
new(topic_count: threads.count { |thread| thread.resolved_at.nil? },
|
|
75
|
+
unread_topic_count: unread.count { |kinds| kinds.include?(:topic) },
|
|
76
|
+
unread_reply_count: unread.sum { |kinds| kinds.count(:reply) },
|
|
77
|
+
unread_resolution_count: unread.count { |kinds| kinds.include?(:resolution) })
|
|
78
|
+
end
|
|
79
|
+
private_class_method :build
|
|
80
|
+
|
|
81
|
+
# What the user has not seen in a thread, as one symbol per event.
|
|
82
|
+
# The resolution goes by the thread's read mark like the comments do,
|
|
83
|
+
# having none of its own.
|
|
84
|
+
def self.unread_activity(thread, read_at:, user:)
|
|
85
|
+
seen_up_to = [read_at[thread.perma_id], user.unread_comments_since_at].compact.max
|
|
86
|
+
first, *replies = thread.comments.sort_by(&:id)
|
|
87
|
+
|
|
88
|
+
unread_replies = replies.count do |reply|
|
|
89
|
+
unread?(reply.creator_id, reply.created_at, seen_up_to, user)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
kinds = Array.new(unread_replies, :reply)
|
|
93
|
+
kinds << :topic if first && unread?(first.creator_id, first.created_at, seen_up_to, user)
|
|
94
|
+
kinds << :resolution if unread_resolution?(thread, seen_up_to, user)
|
|
95
|
+
kinds
|
|
96
|
+
end
|
|
97
|
+
private_class_method :unread_activity
|
|
98
|
+
|
|
99
|
+
def self.unread_resolution?(thread, seen_up_to, user)
|
|
100
|
+
thread.resolved_at &&
|
|
101
|
+
unread?(thread.resolved_by_id, thread.resolved_at, seen_up_to, user)
|
|
102
|
+
end
|
|
103
|
+
private_class_method :unread_resolution?
|
|
104
|
+
|
|
105
|
+
# Mirrors the unread rule of the review interface: the user's own
|
|
106
|
+
# activity never counts, and neither does anything from before their
|
|
107
|
+
# baseline. Kept in sync with isUnread in
|
|
108
|
+
# entry_types/scrolled/package/src/review/unreadActivity.js.
|
|
109
|
+
def self.unread?(creator_id, created_at, seen_up_to, user)
|
|
110
|
+
creator_id != user.id && (seen_up_to.nil? || created_at > seen_up_to)
|
|
111
|
+
end
|
|
112
|
+
private_class_method :unread?
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
# Groups the file usages of a revision. Folders refer to their parent
|
|
3
|
+
# folder by perma id since row ids would not survive copying a
|
|
4
|
+
# revision.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
class FileFolder < ApplicationRecord
|
|
8
|
+
include RevisionComponent
|
|
9
|
+
|
|
10
|
+
validates :name, presence: true
|
|
11
|
+
validate :parent_belongs_to_revision
|
|
12
|
+
validate :parent_is_not_nested_inside_folder
|
|
13
|
+
|
|
14
|
+
def parent
|
|
15
|
+
return if parent_folder_perma_id.blank?
|
|
16
|
+
|
|
17
|
+
folders_of_revision.find_by(perma_id: parent_folder_perma_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Broken data with a folder nested inside one of its own descendants
|
|
21
|
+
# would make walking up the tree loop forever.
|
|
22
|
+
def ancestors
|
|
23
|
+
result = []
|
|
24
|
+
current = parent
|
|
25
|
+
|
|
26
|
+
while current && result.exclude?(current)
|
|
27
|
+
result.unshift(current)
|
|
28
|
+
current = current.parent
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def children
|
|
35
|
+
folders_of_revision.where(parent_folder_perma_id: perma_id)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def file_usages
|
|
39
|
+
revision.file_usages.where(folder_perma_id: perma_id)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def empty?
|
|
43
|
+
children.none? && file_usages.none?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# A folder from another revision would leave this folder unreachable
|
|
49
|
+
# in any folder tree.
|
|
50
|
+
def parent_belongs_to_revision
|
|
51
|
+
return if parent_folder_perma_id.blank? || revision.blank?
|
|
52
|
+
return if folders_of_revision.exists?(perma_id: parent_folder_perma_id)
|
|
53
|
+
|
|
54
|
+
errors.add(:parent_folder_perma_id, 'unknown folder')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Nesting a folder inside itself or inside one of its own subfolders
|
|
58
|
+
# would detach the whole subtree from the folder tree.
|
|
59
|
+
def parent_is_not_nested_inside_folder
|
|
60
|
+
return if parent_folder_perma_id.blank? || revision.blank?
|
|
61
|
+
|
|
62
|
+
parent_folder = parent
|
|
63
|
+
|
|
64
|
+
return if parent_folder.blank?
|
|
65
|
+
return if [parent_folder, *parent_folder.ancestors].exclude?(self)
|
|
66
|
+
|
|
67
|
+
errors.add(:parent_folder_perma_id, 'nested inside folder')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def folders_of_revision
|
|
71
|
+
FileFolder.all_for_revision(revision)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -2,15 +2,16 @@ module Pageflow
|
|
|
2
2
|
class FileReuse # rubocop:todo Style/Documentation
|
|
3
3
|
attr_accessor :source_entry, :destination_entry, :file_type, :file
|
|
4
4
|
|
|
5
|
-
def initialize(destination_entry, source_entry, file_type, file_id)
|
|
5
|
+
def initialize(destination_entry, source_entry, file_type, file_id, folder_perma_id: nil)
|
|
6
6
|
@source_entry = source_entry
|
|
7
7
|
@destination_entry = destination_entry
|
|
8
8
|
@file_type = file_type
|
|
9
9
|
@file = source_entry.find_file(file_type.model, file_id)
|
|
10
|
+
@folder_perma_id = folder_perma_id
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def save! # rubocop:todo Metrics/AbcSize
|
|
13
|
-
destination_entry.use_file(file)
|
|
14
|
+
destination_entry.use_file(file, folder_perma_id: @folder_perma_id)
|
|
14
15
|
|
|
15
16
|
file_type.nested_file_types.each do |nested_file_type|
|
|
16
17
|
source_entry.find_files(nested_file_type.model).each do |nested_file|
|
|
@@ -8,6 +8,7 @@ module Pageflow
|
|
|
8
8
|
belongs_to :file, polymorphic: true
|
|
9
9
|
|
|
10
10
|
validate :display_name_extension_matches
|
|
11
|
+
validate :folder_belongs_to_revision
|
|
11
12
|
|
|
12
13
|
private
|
|
13
14
|
|
|
@@ -17,5 +18,12 @@ module Pageflow
|
|
|
17
18
|
|
|
18
19
|
errors.add(:display_name, 'extension mismatch')
|
|
19
20
|
end
|
|
21
|
+
|
|
22
|
+
def folder_belongs_to_revision
|
|
23
|
+
return if folder_perma_id.blank? || revision.blank?
|
|
24
|
+
return if FileFolder.all_for_revision(revision).exists?(perma_id: folder_perma_id)
|
|
25
|
+
|
|
26
|
+
errors.add(:folder_perma_id, 'unknown folder')
|
|
27
|
+
end
|
|
20
28
|
end
|
|
21
29
|
end
|
|
@@ -40,26 +40,30 @@ module Pageflow
|
|
|
40
40
|
|
|
41
41
|
Pageflow
|
|
42
42
|
.config.thumbnail_styles.transform_values { |options| options.merge(style_defaults) }
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
.merge(
|
|
44
|
+
print: {geometry: '300x300>',
|
|
45
|
+
**style_defaults,
|
|
46
|
+
convert_options: '-quality 10 -interlace Plane'},
|
|
47
|
+
medium: {geometry: '1024x1024>',
|
|
48
|
+
**style_defaults,
|
|
49
|
+
convert_options: '-quality 70 -interlace Plane'},
|
|
50
|
+
large: {geometry: '1920x1920>',
|
|
51
|
+
**style_defaults,
|
|
52
|
+
convert_options: '-quality 70 -interlace Plane'},
|
|
53
|
+
ultra: {geometry: '3840x3840>',
|
|
54
|
+
**style_defaults,
|
|
55
|
+
convert_options: '-quality 90 -interlace Plane'},
|
|
56
|
+
panorama_medium: {
|
|
57
|
+
geometry: ImageFile.scale_down_to_cover(1024, 1024),
|
|
58
|
+
format: panorama_format,
|
|
59
|
+
convert_options: '-quality 90 -interlace Plane'
|
|
60
|
+
},
|
|
61
|
+
panorama_large: {
|
|
62
|
+
geometry: ImageFile.scale_down_to_cover(1920, 1080),
|
|
63
|
+
format: panorama_format,
|
|
64
|
+
convert_options: '-quality 90 -interlace Plane'
|
|
65
|
+
}
|
|
66
|
+
)
|
|
63
67
|
.merge(social_image_styles)
|
|
64
68
|
end
|
|
65
69
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module Pageflow
|
|
2
2
|
class ImageFileUrlTemplates # rubocop:todo Style/Documentation
|
|
3
3
|
def call
|
|
4
|
-
styles.
|
|
5
|
-
|
|
4
|
+
styles.to_h do |style|
|
|
5
|
+
[style, replace_extension_with_placeholder(
|
|
6
6
|
UrlTemplate.from_attachment(example_file.attachment, style)
|
|
7
|
-
)
|
|
7
|
+
)]
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
# Legal info of a site, resolved for a single locale. Each attribute
|
|
3
|
+
# falls back to the value stored in the site's column when there is
|
|
4
|
+
# no translation for the locale.
|
|
5
|
+
#
|
|
6
|
+
# @since 17.2
|
|
7
|
+
class LegalInfo
|
|
8
|
+
Link = Struct.new(:label, :url, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
def initialize(site, locale)
|
|
11
|
+
@site = site
|
|
12
|
+
@locale = locale
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def imprint
|
|
16
|
+
@imprint ||= link('imprint')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def copyright
|
|
20
|
+
@copyright ||= link('copyright')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def privacy_url
|
|
24
|
+
translated(:privacy_link_url)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def link(kind)
|
|
30
|
+
Link.new(label: translated("#{kind}_link_label"),
|
|
31
|
+
url: translated("#{kind}_link_url"))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def translated(attribute)
|
|
35
|
+
@site.translated(attribute, locale: @locale)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -166,7 +166,7 @@ module Pageflow
|
|
|
166
166
|
"#{[*chain, model].first.old_foreign_key} = #{@old_root_id}"
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
-
Model = Struct.new(:klass, :foreign_key, :level
|
|
169
|
+
Model = Struct.new(:klass, :foreign_key, :level) do
|
|
170
170
|
def self.for(revision_components:)
|
|
171
171
|
revision_components.map do |revision_component|
|
|
172
172
|
Model.new(klass: revision_component, foreign_key: 'revision_id', level: 1)
|
|
@@ -53,6 +53,7 @@ module Pageflow
|
|
|
53
53
|
|
|
54
54
|
def create_redirect
|
|
55
55
|
directory.redirects.where(slug:).delete_all
|
|
56
|
+
PermalinkRedirect.where(slug: slug_was, directory_id: directory_id_was).delete_all
|
|
56
57
|
entry.permalink_redirects.create!(slug: slug_was,
|
|
57
58
|
directory_id: directory_id_was)
|
|
58
59
|
end
|
|
@@ -27,7 +27,10 @@ module Pageflow
|
|
|
27
27
|
has_many :chapters, -> { order(CHAPTER_ORDER) }, through: :storylines
|
|
28
28
|
has_many :pages, -> { reorder(PAGE_ORDER) }, through: :storylines
|
|
29
29
|
|
|
30
|
+
has_many :comment_threads, dependent: :destroy
|
|
31
|
+
|
|
30
32
|
has_many :file_usages, dependent: :destroy
|
|
33
|
+
has_many :file_folders, dependent: :destroy
|
|
31
34
|
|
|
32
35
|
has_many :image_files, -> { extending WithFileUsageExtension },
|
|
33
36
|
through: :file_usages, source: :file, source_type: 'Pageflow::ImageFile'
|
|
@@ -183,7 +186,8 @@ module Pageflow
|
|
|
183
186
|
from: self,
|
|
184
187
|
to: revision
|
|
185
188
|
).perform_for(
|
|
186
|
-
revision_components: Pageflow.config.revision_components.to_a +
|
|
189
|
+
revision_components: Pageflow.config.revision_components.to_a +
|
|
190
|
+
[FileFolder, FileUsage]
|
|
187
191
|
)
|
|
188
192
|
|
|
189
193
|
revision
|
|
@@ -212,7 +216,7 @@ module Pageflow
|
|
|
212
216
|
'manual_start' => manual_start,
|
|
213
217
|
'overview_button_enabled' => overview_button_enabled
|
|
214
218
|
}.delete_if { |_k, v| v.nil? }
|
|
215
|
-
|
|
219
|
+
.merge(read_attribute(:configuration) || {})
|
|
216
220
|
end
|
|
217
221
|
|
|
218
222
|
def self.ransackable_attributes(_auth_object = nil)
|
data/app/models/pageflow/site.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Pageflow
|
|
2
2
|
class Site < ApplicationRecord # rubocop:todo Style/Documentation
|
|
3
|
+
include TranslatedAttributes
|
|
4
|
+
|
|
3
5
|
belongs_to :account
|
|
4
6
|
belongs_to :custom_404_entry, class_name: 'Entry', optional: true
|
|
5
7
|
|
|
@@ -7,6 +9,10 @@ module Pageflow
|
|
|
7
9
|
has_many :entries
|
|
8
10
|
has_many :permalink_directories, -> { order('path ASC') }
|
|
9
11
|
|
|
12
|
+
translated_attributes :imprint_link_label, :imprint_link_url,
|
|
13
|
+
:copyright_link_label, :copyright_link_url,
|
|
14
|
+
:privacy_link_url
|
|
15
|
+
|
|
10
16
|
scope :with_home_url, -> { where.not(home_url: '') }
|
|
11
17
|
scope :for_request, ->(request) { Pageflow.config.site_request_scope.call(all, request) }
|
|
12
18
|
|
|
@@ -16,6 +22,10 @@ module Pageflow
|
|
|
16
22
|
|
|
17
23
|
delegate :enabled_feature_names, to: :account
|
|
18
24
|
|
|
25
|
+
def legal_info(locale:)
|
|
26
|
+
LegalInfo.new(self, locale)
|
|
27
|
+
end
|
|
28
|
+
|
|
19
29
|
def display_name
|
|
20
30
|
name.presence || I18n.t('pageflow.admin.sites.default_name')
|
|
21
31
|
end
|
|
@@ -10,9 +10,11 @@ module Pageflow
|
|
|
10
10
|
@usage.configuration
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
USAGE_ATTRIBUTES = [:configuration, :display_name, :folder_perma_id].freeze
|
|
14
|
+
|
|
13
15
|
def update!(attributes)
|
|
14
|
-
super(attributes.except(
|
|
15
|
-
@usage.update!(attributes.slice(
|
|
16
|
+
super(attributes.except(*USAGE_ATTRIBUTES))
|
|
17
|
+
@usage.update!(attributes.slice(*USAGE_ATTRIBUTES))
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def usage_id
|
|
@@ -27,6 +29,10 @@ module Pageflow
|
|
|
27
29
|
@usage.display_name
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
def folder_perma_id
|
|
33
|
+
@usage.folder_perma_id
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def cache_key
|
|
31
37
|
[@file.cache_key, @usage.cache_key].join('-')
|
|
32
38
|
end
|