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
|
@@ -15,7 +15,9 @@ module Pageflow
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
if entry
|
|
18
|
-
|
|
18
|
+
respond_to do |format|
|
|
19
|
+
format.html { handle_public_entry_request(entry) }
|
|
20
|
+
end
|
|
19
21
|
elsif site.home_url.present?
|
|
20
22
|
redirect_to(site.home_url, allow_other_host: true)
|
|
21
23
|
else
|
|
@@ -85,11 +87,13 @@ module Pageflow
|
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
def handle_public_entry_request(entry)
|
|
88
|
-
|
|
90
|
+
config = Pageflow.config_for(entry)
|
|
91
|
+
|
|
92
|
+
return if redirect_according_to_entry_redirect(entry, config)
|
|
89
93
|
return if redirect_according_to_public_https_mode
|
|
90
94
|
return unless check_entry_password_protection(entry)
|
|
91
95
|
|
|
92
|
-
delegate_to_entry_type_frontend_app!(entry)
|
|
96
|
+
delegate_to_entry_type_frontend_app!(entry, config:)
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
def redirect_according_to_permalink_redirect
|
|
@@ -107,23 +111,21 @@ module Pageflow
|
|
|
107
111
|
true
|
|
108
112
|
end
|
|
109
113
|
|
|
110
|
-
def redirect_according_to_entry_redirect(entry)
|
|
111
|
-
return false unless (redirect_location =
|
|
114
|
+
def redirect_according_to_entry_redirect(entry, config)
|
|
115
|
+
return false unless (redirect_location = config.public_entry_redirect.call(entry, request))
|
|
112
116
|
|
|
113
117
|
redirect_to(redirect_location, status: :moved_permanently, allow_other_host: true)
|
|
114
118
|
true
|
|
115
119
|
end
|
|
116
120
|
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
EntriesControllerEnvHelper.add_entry_info_to_env(request.env, entry:, mode: :published)
|
|
121
|
+
def delegate_to_entry_type_frontend_app!(entry, config:, override_status: nil)
|
|
122
|
+
EntriesControllerEnvHelper.add_entry_info_to_env(request.env,
|
|
123
|
+
entry:,
|
|
124
|
+
mode: :published,
|
|
125
|
+
embed: params[:embed].present?)
|
|
123
126
|
|
|
124
127
|
delegate_to_rack_app!(entry.entry_type.frontend_app) do |result|
|
|
125
128
|
status, headers, body = result
|
|
126
|
-
config = Pageflow.config_for(entry)
|
|
127
129
|
|
|
128
130
|
allow_iframe_for_embed(headers)
|
|
129
131
|
apply_additional_headers(entry, config, headers)
|
|
@@ -155,7 +157,9 @@ module Pageflow
|
|
|
155
157
|
|
|
156
158
|
if site&.custom_404_entry&.published_without_password_protection?
|
|
157
159
|
entry = PublishedEntry.new(site.custom_404_entry)
|
|
158
|
-
|
|
160
|
+
config = Pageflow.config_for(entry)
|
|
161
|
+
|
|
162
|
+
delegate_to_entry_type_frontend_app!(entry, config:, override_status: 404)
|
|
159
163
|
else
|
|
160
164
|
# Fallback to ApplicationController's handler method
|
|
161
165
|
render_static_404_error_page
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
module Review
|
|
3
|
+
# @api private
|
|
4
|
+
class CommentThreadReadsController < Pageflow::ApplicationController
|
|
5
|
+
respond_to :json
|
|
6
|
+
before_action :authenticate_user!
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
10
|
+
authorize!(:read, entry.to_model)
|
|
11
|
+
|
|
12
|
+
CommentThreadRead.mark(entry: entry.to_model,
|
|
13
|
+
user: current_user,
|
|
14
|
+
comment_thread_perma_ids: known_perma_ids(entry))
|
|
15
|
+
|
|
16
|
+
head :no_content
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Guards against read records piling up for comment threads that
|
|
22
|
+
# do not exist in the entry.
|
|
23
|
+
def known_perma_ids(entry)
|
|
24
|
+
entry.comment_threads
|
|
25
|
+
.where(perma_id: params.fetch(:comment_thread_perma_ids, []))
|
|
26
|
+
.pluck(:perma_id)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
module Review
|
|
3
|
+
# @api private
|
|
4
|
+
class CommentThreadsController < Pageflow::ApplicationController
|
|
5
|
+
respond_to :json
|
|
6
|
+
before_action :authenticate_user!
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
10
|
+
authorize!(:read, entry.to_model)
|
|
11
|
+
|
|
12
|
+
@comment_threads = entry.comment_threads.includes(:resolver, comments: :creator)
|
|
13
|
+
@read_at_by_perma_id =
|
|
14
|
+
CommentThreadRead.read_at_by_perma_id(entry: entry.to_model, user: current_user)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
19
|
+
authorize!(:read, entry.to_model)
|
|
20
|
+
|
|
21
|
+
@comment_thread = entry.comment_threads.build(thread_params)
|
|
22
|
+
@comment_thread.creator = current_user
|
|
23
|
+
|
|
24
|
+
first_comment = @comment_thread.comments.build(first_comment_params)
|
|
25
|
+
first_comment.creator = current_user
|
|
26
|
+
|
|
27
|
+
@comment_thread.save!
|
|
28
|
+
render :create, status: :created
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update
|
|
32
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
33
|
+
authorize!(:read, entry.to_model)
|
|
34
|
+
|
|
35
|
+
@comment_thread = entry.comment_threads.find(params[:id])
|
|
36
|
+
|
|
37
|
+
if ActiveModel::Type::Boolean.new.cast(params[:comment_thread][:resolved])
|
|
38
|
+
@comment_thread.resolve(current_user)
|
|
39
|
+
else
|
|
40
|
+
@comment_thread.unresolve
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
render :create
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def thread_params
|
|
49
|
+
permitted = params.require(:comment_thread)
|
|
50
|
+
.permit(:subject_type, :subject_id, :section_perma_id)
|
|
51
|
+
permitted[:subject_range] = params[:comment_thread][:subject_range]&.permit!
|
|
52
|
+
permitted
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def first_comment_params
|
|
56
|
+
params.require(:comment_thread).require(:comment).permit(:body, :quote)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Pageflow
|
|
2
|
+
module Review
|
|
3
|
+
# @api private
|
|
4
|
+
class CommentsController < Pageflow::ApplicationController
|
|
5
|
+
respond_to :json
|
|
6
|
+
before_action :authenticate_user!
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
10
|
+
authorize!(:read, entry.to_model)
|
|
11
|
+
|
|
12
|
+
thread = entry.comment_threads.find(params[:comment_thread_id])
|
|
13
|
+
@comment = thread.comments.build(comment_params)
|
|
14
|
+
@comment.creator = current_user
|
|
15
|
+
@comment.save!
|
|
16
|
+
|
|
17
|
+
render :create, status: :created
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def update
|
|
21
|
+
entry = DraftEntry.find(params[:entry_id])
|
|
22
|
+
authorize!(:read, entry.to_model)
|
|
23
|
+
|
|
24
|
+
@comment = find_comment(entry)
|
|
25
|
+
|
|
26
|
+
return head(:forbidden) unless @comment.creator == current_user
|
|
27
|
+
|
|
28
|
+
@comment.update!(update_params.merge(edited_at: Time.current))
|
|
29
|
+
|
|
30
|
+
render :create
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def find_comment(entry)
|
|
36
|
+
entry.comment_threads
|
|
37
|
+
.find(params[:comment_thread_id])
|
|
38
|
+
.comments
|
|
39
|
+
.find(params[:id])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def comment_params
|
|
43
|
+
params.require(:comment).permit(:body, :quote)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The quote records the wording the comment referred to when it was
|
|
47
|
+
# written. Editing must not make it describe some later version.
|
|
48
|
+
def update_params
|
|
49
|
+
params.require(:comment).permit(:body)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -39,7 +39,10 @@ module Pageflow
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def delegate_to_entry_type_frontend_app!(entry)
|
|
42
|
-
EntriesControllerEnvHelper.add_entry_info_to_env(request.env,
|
|
42
|
+
EntriesControllerEnvHelper.add_entry_info_to_env(request.env,
|
|
43
|
+
entry:,
|
|
44
|
+
mode: :preview,
|
|
45
|
+
ui_locale: I18n.locale)
|
|
43
46
|
delegate_to_rack_app!(entry.entry_type.frontend_app)
|
|
44
47
|
end
|
|
45
48
|
end
|
|
@@ -19,6 +19,46 @@ module Pageflow
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def entry_comments_indicator(entry, summaries: entry_comment_summaries)
|
|
23
|
+
summary = summaries[entry.id]
|
|
24
|
+
return unless summary&.any?
|
|
25
|
+
|
|
26
|
+
content_tag(:span,
|
|
27
|
+
class: 'entry_comments_indicator',
|
|
28
|
+
data: {tooltip: entry_comments_tooltip(summary)}) do
|
|
29
|
+
safe_join([summary.topic_count.to_s,
|
|
30
|
+
(content_tag(:span, '', class: 'unread_dot') if summary.unread?)].compact)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Built for the whole page at once, so that rendering a row does
|
|
35
|
+
# not query. Views without an index table collection pass their own
|
|
36
|
+
# summaries instead.
|
|
37
|
+
def entry_comment_summaries
|
|
38
|
+
@entry_comment_summaries ||=
|
|
39
|
+
EntryCommentSummary.for_entries(collection, user: current_user)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def entry_comments_tooltip(summary)
|
|
43
|
+
scope = 'pageflow.admin.entries.comments'
|
|
44
|
+
|
|
45
|
+
parts = [t("#{scope}.topic_count", count: summary.topic_count)]
|
|
46
|
+
|
|
47
|
+
if summary.unread_topic_count.positive?
|
|
48
|
+
parts << t("#{scope}.unread_topic_count", count: summary.unread_topic_count)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if summary.unread_reply_count.positive?
|
|
52
|
+
parts << t("#{scope}.unread_reply_count", count: summary.unread_reply_count)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if summary.unread_resolution_count.positive?
|
|
56
|
+
parts << t("#{scope}.unread_resolution_count", count: summary.unread_resolution_count)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
t("#{scope}.tooltip", summary: parts.join(', '))
|
|
60
|
+
end
|
|
61
|
+
|
|
22
62
|
def entry_type_collection(entry_types = Pageflow.config.entry_types)
|
|
23
63
|
entry_types.map(&:name).index_by do |type|
|
|
24
64
|
I18n.t(type, scope: 'activerecord.values.pageflow/entry.type_names')
|
|
@@ -9,8 +9,8 @@ module Pageflow
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def admin_widget_types_collection_for_role(config, role)
|
|
12
|
-
config.widget_types.find_all_by_role(role).
|
|
13
|
-
|
|
12
|
+
config.widget_types.find_all_by_role(role).to_h do |widget_type|
|
|
13
|
+
[I18n.t(widget_type.translation_key), widget_type.name]
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -10,7 +10,9 @@ module Pageflow
|
|
|
10
10
|
config = Pageflow.config_for(entry)
|
|
11
11
|
|
|
12
12
|
json.locale entry.locale
|
|
13
|
-
json.site
|
|
13
|
+
json.site do
|
|
14
|
+
json.privacy_link_url entry.legal_info.privacy_url
|
|
15
|
+
end
|
|
14
16
|
json.enabled_feature_names entry.enabled_feature_names
|
|
15
17
|
json.page_types PageTypesSeed.new(config).as_json
|
|
16
18
|
|
|
@@ -26,8 +28,8 @@ module Pageflow
|
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def as_json
|
|
29
|
-
config.page_types.
|
|
30
|
-
|
|
31
|
+
config.page_types.to_h do |page_type|
|
|
32
|
+
[page_type.name.to_sym, page_type_seed(page_type)]
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
@@ -5,7 +5,9 @@ module Pageflow
|
|
|
5
5
|
include RenderJsonHelper
|
|
6
6
|
|
|
7
7
|
def editor_config_seeds(entry)
|
|
8
|
-
render_json_partial('pageflow/editor/config/seeds',
|
|
8
|
+
render_json_partial('pageflow/editor/config/seeds',
|
|
9
|
+
entry:,
|
|
10
|
+
entry_config: Pageflow.config_for(entry))
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -72,10 +72,12 @@ module Pageflow
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def entry_privacy_link_url(entry)
|
|
75
|
-
|
|
76
|
-
return entry.site.privacy_link_url if entry.site.privacy_link_url.start_with?('javascript:')
|
|
75
|
+
url = entry.legal_info.privacy_url
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
return unless url.present?
|
|
78
|
+
return url if url.start_with?('javascript:')
|
|
79
|
+
|
|
80
|
+
"#{url}?lang=#{entry.locale}"
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
def entry_file_rights(entry)
|
|
@@ -94,50 +96,30 @@ module Pageflow
|
|
|
94
96
|
end
|
|
95
97
|
end
|
|
96
98
|
|
|
97
|
-
# rubocop:todo Metrics/PerceivedComplexity
|
|
98
|
-
# rubocop:todo Metrics/CyclomaticComplexity
|
|
99
99
|
def entry_global_links(entry) # rubocop:todo Metrics/AbcSize
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
target: entry.site.copyright_link_url.start_with?('javascript:') ? nil : '_blank',
|
|
117
|
-
tabindex: 2,
|
|
118
|
-
class: 'copy'
|
|
119
|
-
)
|
|
100
|
+
legal_info = entry.legal_info
|
|
101
|
+
|
|
102
|
+
candidates = [
|
|
103
|
+
[legal_info.imprint.label, legal_info.imprint.url, 'legal'],
|
|
104
|
+
[legal_info.copyright.label, legal_info.copyright.url, 'copy'],
|
|
105
|
+
[I18n.t('pageflow.public.privacy_notice'), entry_privacy_link_url(entry), 'privacy']
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
links = candidates.filter_map do |label, url, css_class|
|
|
109
|
+
next if label.blank? || url.blank?
|
|
110
|
+
|
|
111
|
+
link_to(raw(label),
|
|
112
|
+
url,
|
|
113
|
+
target: url.start_with?('javascript:') ? nil : '_blank',
|
|
114
|
+
tabindex: 2,
|
|
115
|
+
class: css_class)
|
|
120
116
|
end
|
|
121
117
|
|
|
122
|
-
if
|
|
123
|
-
links << link_to(
|
|
124
|
-
I18n.t('pageflow.public.privacy_notice'),
|
|
125
|
-
entry_privacy_link_url(entry),
|
|
126
|
-
target: entry.site.privacy_link_url.start_with?('javascript:') ? nil : '_blank',
|
|
127
|
-
tabindex: 2,
|
|
128
|
-
class: 'privacy'
|
|
129
|
-
)
|
|
130
|
-
end
|
|
118
|
+
return '' if links.empty?
|
|
131
119
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
class: 'hidden') + safe_join(links, ''.html_safe)
|
|
135
|
-
else
|
|
136
|
-
''
|
|
137
|
-
end
|
|
120
|
+
content_tag(:span, I18n.t('pageflow.helpers.entries.global_links'),
|
|
121
|
+
class: 'hidden') + safe_join(links, ''.html_safe)
|
|
138
122
|
end
|
|
139
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
140
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
141
123
|
|
|
142
124
|
def entry_theme_stylesheet_link_tag(entry)
|
|
143
125
|
stylesheet_link_tag(entry.theme.stylesheet_path,
|
|
@@ -51,14 +51,14 @@ module Pageflow
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def entry_file_ids_seed(entry)
|
|
54
|
-
Pageflow.config.file_types.with_thumbnail_support.
|
|
55
|
-
|
|
54
|
+
Pageflow.config.file_types.with_thumbnail_support.to_h do |file_type|
|
|
55
|
+
[file_type.collection_name, entry.find_files(file_type.model).map(&:perma_id)]
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def entry_audio_files_json_seed(entry)
|
|
60
|
-
seed = entry.find_files(AudioFile).
|
|
61
|
-
|
|
60
|
+
seed = entry.find_files(AudioFile).to_h do |audio_file|
|
|
61
|
+
[audio_file.perma_id, audio_file_sources(audio_file)]
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
sanitize_json(seed.to_json).html_safe
|
|
@@ -11,14 +11,15 @@ module Pageflow
|
|
|
11
11
|
value.presence || fallback
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
locales = [I18n.default_locale, *I18n.fallbacks[entry.locale].reverse].uniq &
|
|
15
|
+
I18n.available_locales
|
|
16
|
+
|
|
17
|
+
translations = locales.reduce({}) do |result, locale|
|
|
18
|
+
result.deep_merge(I18n.t('pageflow.public', locale:, default: {}),
|
|
19
|
+
&merge_ignoring_nil)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
{pageflow: {public: translations}}
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
end
|
|
@@ -6,10 +6,13 @@ module Pageflow
|
|
|
6
6
|
include MetaTagsHelper
|
|
7
7
|
|
|
8
8
|
def structured_data_for_entry(entry)
|
|
9
|
+
custom_properties = Pageflow.config_for(entry).entry_structured_data_types.for(entry)
|
|
10
|
+
|
|
9
11
|
content_tag(:script, type: 'application/ld+json') do
|
|
10
12
|
render_json_partial('pageflow/structured_data/entry',
|
|
11
13
|
entry:,
|
|
12
|
-
meta_data: meta_tags_data_for_entry(entry)
|
|
14
|
+
meta_data: meta_tags_data_for_entry(entry),
|
|
15
|
+
custom_properties:)
|
|
13
16
|
end
|
|
14
17
|
end
|
|
15
18
|
|
|
@@ -3,9 +3,7 @@ module Pageflow
|
|
|
3
3
|
include RenderJsonHelper
|
|
4
4
|
|
|
5
5
|
def themes_options_json_seed(config = Pageflow.config)
|
|
6
|
-
config.themes.
|
|
7
|
-
options[theme.name] = theme.options
|
|
8
|
-
}.to_json.html_safe
|
|
6
|
+
config.themes.to_h { |theme| [theme.name, theme.options] }.to_json.html_safe
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
def theme_json_seeds(config)
|
|
@@ -33,7 +33,8 @@ module Pageflow
|
|
|
33
33
|
result[role] << {
|
|
34
34
|
name: widget_type.name,
|
|
35
35
|
translationKey: widget_type.translation_key,
|
|
36
|
-
insertPoint: widget_type.insert_point
|
|
36
|
+
insertPoint: widget_type.insert_point,
|
|
37
|
+
enabledInEditor: widget_type.enabled_in_editor?
|
|
37
38
|
}
|
|
38
39
|
end
|
|
39
40
|
}.to_json.html_safe
|
|
@@ -30,8 +30,12 @@ class PageflowPermalinkInput
|
|
|
30
30
|
def permalink_inputs_html
|
|
31
31
|
return '' if options[:site].permalink_directories.empty? || options[:only_root]
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
template.content_tag(
|
|
34
|
+
:div,
|
|
35
|
+
builder.select(:directory_id, directory_select_options) <<
|
|
36
|
+
builder.text_field(:slug, data: {placeholder: options[:slug_placeholder]}),
|
|
37
|
+
class: 'permalink_inputs'
|
|
38
|
+
)
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
def error_keys
|
|
@@ -14,10 +14,17 @@ module Pageflow
|
|
|
14
14
|
|
|
15
15
|
entry = entry_for_auto_generated_perma_id
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
entry.
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
self.perma_id =
|
|
18
|
+
if entry.new_record?
|
|
19
|
+
# No other process can see the entry yet, so there is nothing
|
|
20
|
+
# to lock.
|
|
21
|
+
entry.increment(:perma_id_counter).perma_id_counter
|
|
22
|
+
else
|
|
23
|
+
entry.with_lock do
|
|
24
|
+
entry.increment!(:perma_id_counter)
|
|
25
|
+
entry.perma_id_counter
|
|
26
|
+
end
|
|
27
|
+
end
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
def entry_for_auto_generated_perma_id
|
|
@@ -4,7 +4,7 @@ module Pageflow
|
|
|
4
4
|
|
|
5
5
|
included do
|
|
6
6
|
belongs_to :uploader, class_name: 'User', optional: true
|
|
7
|
-
belongs_to :entry, optional: true
|
|
7
|
+
belongs_to :entry, class_name: 'Pageflow::Entry', optional: true
|
|
8
8
|
belongs_to :parent_file, polymorphic: true, foreign_type: :parent_file_model_type,
|
|
9
9
|
optional: true
|
|
10
10
|
|
|
@@ -2,10 +2,8 @@ module Pageflow
|
|
|
2
2
|
# @api private
|
|
3
3
|
module SerializationBlacklist
|
|
4
4
|
def serializable_hash(options = nil)
|
|
5
|
-
options
|
|
6
|
-
|
|
7
|
-
options[:except] = Array(options[:except])
|
|
8
|
-
options[:except].concat(blacklist_for_serialization)
|
|
5
|
+
options = (options || {}).dup
|
|
6
|
+
options[:except] = Array(options[:except]) + blacklist_for_serialization
|
|
9
7
|
|
|
10
8
|
super
|
|
11
9
|
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
|
|
@@ -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,
|
|
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
|