pageflow 15.7.0 → 15.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -199
  3. data/README.md +1 -9
  4. data/Rakefile +4 -1
  5. data/admins/pageflow/entry.rb +32 -6
  6. data/admins/pageflow/membership.rb +5 -1
  7. data/admins/pageflow/user.rb +7 -0
  8. data/app/assets/javascripts/pageflow/admin/entries.js +40 -0
  9. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +65 -0
  10. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  11. data/app/assets/stylesheets/pageflow/editor/base.scss +2 -6
  12. data/app/assets/stylesheets/pageflow/editor/dialogs.scss +2 -0
  13. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +9 -0
  14. data/app/assets/stylesheets/pageflow/editor/info_box.scss +13 -3
  15. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +1 -0
  16. data/app/assets/stylesheets/pageflow/page.scss +0 -2
  17. data/app/assets/stylesheets/pageflow/themes/default/page.scss +1 -1
  18. data/app/assets/stylesheets/pageflow/ui/forms.scss +4 -1
  19. data/app/controllers/pageflow/editor/file_import_controller.rb +32 -42
  20. data/app/controllers/pageflow/entries_controller.rb +25 -1
  21. data/app/helpers/pageflow/admin/permalinks_helper.rb +15 -0
  22. data/app/helpers/pageflow/entries_helper.rb +9 -1
  23. data/app/helpers/pageflow/themings_helper.rb +1 -1
  24. data/app/inputs/pageflow_permalink_input.rb +47 -0
  25. data/app/models/concerns/pageflow/permalinkable.rb +12 -0
  26. data/app/models/concerns/pageflow/reusable_file.rb +5 -0
  27. data/app/models/concerns/pageflow/uploadable_file.rb +4 -0
  28. data/app/models/pageflow/customized_theme.rb +4 -2
  29. data/app/models/pageflow/entry.rb +13 -0
  30. data/app/models/pageflow/entry_at_revision.rb +2 -1
  31. data/app/models/pageflow/entry_duplicate.rb +7 -0
  32. data/app/models/pageflow/image_file_url_templates.rb +2 -2
  33. data/app/models/pageflow/permalink.rb +39 -0
  34. data/app/models/pageflow/permalink_directory.rb +10 -0
  35. data/app/models/pageflow/published_entry.rb +17 -0
  36. data/app/models/pageflow/revision.rb +1 -1
  37. data/app/models/pageflow/theme_customization_file.rb +6 -1
  38. data/app/models/pageflow/theming.rb +1 -0
  39. data/app/views/admin/entries/_form.html.erb +9 -1
  40. data/app/views/admin/entries/_permalink_inputs.html.erb +6 -0
  41. data/app/views/admin/entries/permalink_inputs.html.erb +7 -0
  42. data/app/views/pageflow/editor/file_import/start_import_job.json.jbuilder +10 -0
  43. data/app/views/pageflow/files/_file.json.jbuilder +1 -0
  44. data/config/initializers/mime_types.rb +1 -0
  45. data/config/routes.rb +8 -5
  46. data/db/migrate/20221024100724_create_pageflow_permalink_directories.rb +10 -0
  47. data/db/migrate/20221025074049_add_permalink_attributes_to_entries.rb +5 -0
  48. data/db/migrate/20221027065022_create_pageflow_permalinks.rb +12 -0
  49. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +166 -169
  50. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +44 -2
  51. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +5 -5
  52. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +1 -1
  53. data/entry_types/paged/config/initializers/features.rb +2 -0
  54. data/entry_types/paged/config/locales/{new/help.de.yml → de.yml} +74 -65
  55. data/entry_types/paged/config/locales/{new/help.en.yml → en.yml} +66 -56
  56. data/entry_types/scrolled/app/helpers/pageflow_scrolled/favicon_helper.rb +39 -13
  57. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +55 -0
  58. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +6 -2
  59. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +2 -0
  60. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/_manifest.json.jbuilder +16 -0
  61. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +9 -3
  62. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +16 -10
  63. data/entry_types/scrolled/config/locales/de.yml +265 -76
  64. data/entry_types/scrolled/config/locales/en.yml +266 -77
  65. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +3 -3
  66. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +14 -0
  67. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +1 -1
  68. data/entry_types/scrolled/lib/pageflow_scrolled/web_app_manifest.rb +11 -0
  69. data/entry_types/scrolled/lib/pageflow_scrolled.rb +39 -1
  70. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +3 -2
  71. data/entry_types/scrolled/package/contentElements-editor.js +124 -38
  72. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  73. data/entry_types/scrolled/package/contentElements-frontend.js +321 -27
  74. data/entry_types/scrolled/package/editor.js +1345 -739
  75. data/entry_types/scrolled/package/frontend/EditableInlineText.module-c6672f27.js +5314 -0
  76. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-9fb97827.js → PhonePlatformContext-22e65f92.js} +40 -4
  77. data/entry_types/scrolled/package/frontend/{Viewer-e2290ea0.js → Viewer-6b05522f.js} +6 -40
  78. data/entry_types/scrolled/package/frontend/arrowRight-7e3d9dd5.js +42 -0
  79. data/entry_types/scrolled/package/frontend/{components-6ab26015.js → components-487daafa.js} +546 -361
  80. data/entry_types/scrolled/package/frontend/index.css +1 -1
  81. data/entry_types/scrolled/package/frontend/index.js +197 -3674
  82. data/entry_types/scrolled/package/package.json +3 -2
  83. data/entry_types/scrolled/package/testHelpers.js +12 -2
  84. data/entry_types/scrolled/package/widgets/defaultNavigation.css +1 -1
  85. data/entry_types/scrolled/package/widgets/defaultNavigation.js +35 -32
  86. data/entry_types/scrolled/spec/fixtures/image.ico +0 -0
  87. data/lib/pageflow/entry_type.rb +6 -2
  88. data/lib/pageflow/user_mixin.rb +6 -0
  89. data/lib/pageflow/version.rb +1 -1
  90. data/package/editor.js +122 -149
  91. data/package/frontend.js +19 -2
  92. data/package/testHelpers.js +39 -6
  93. data/spec/factories/entries.rb +17 -0
  94. data/spec/factories/permalink_directory.rb +6 -0
  95. data/spec/factories/permalinks.rb +4 -0
  96. data/spec/factories/published_entries.rb +2 -0
  97. metadata +30 -47
  98. data/entry_types/paged/config/locales/new/video_contain.de.yml +0 -7
  99. data/entry_types/paged/config/locales/new/video_contain.en.yml +0 -7
  100. data/entry_types/scrolled/config/locales/new/before_after_slider.de.yml +0 -8
  101. data/entry_types/scrolled/config/locales/new/before_after_slider.en.yml +0 -8
  102. data/entry_types/scrolled/config/locales/new/center_ragged.de.yml +0 -8
  103. data/entry_types/scrolled/config/locales/new/center_ragged.en.yml +0 -9
  104. data/entry_types/scrolled/config/locales/new/consent.de.yml +0 -25
  105. data/entry_types/scrolled/config/locales/new/consent.en.yml +0 -24
  106. data/entry_types/scrolled/config/locales/new/content_element_categories.de.yml +0 -39
  107. data/entry_types/scrolled/config/locales/new/content_element_categories.en.yml +0 -39
  108. data/entry_types/scrolled/config/locales/new/default_transition.de.yml +0 -14
  109. data/entry_types/scrolled/config/locales/new/default_transition.en.yml +0 -14
  110. data/entry_types/scrolled/config/locales/new/header_line_breaks.de.yml +0 -28
  111. data/entry_types/scrolled/config/locales/new/header_line_breaks.en.yml +0 -27
  112. data/entry_types/scrolled/config/locales/new/header_size.de.yml +0 -17
  113. data/entry_types/scrolled/config/locales/new/header_size.en.yml +0 -17
  114. data/entry_types/scrolled/config/locales/new/iframe_embed.de.yml +0 -39
  115. data/entry_types/scrolled/config/locales/new/iframe_embed.en.yml +0 -39
  116. data/entry_types/scrolled/config/locales/new/inline_loops.de.yml +0 -26
  117. data/entry_types/scrolled/config/locales/new/inline_loops.en.yml +0 -26
  118. data/entry_types/scrolled/config/locales/new/portrait_inline_image.de.yml +0 -9
  119. data/entry_types/scrolled/config/locales/new/portrait_inline_image.en.yml +0 -9
  120. data/entry_types/scrolled/config/locales/new/section_width.de.yml +0 -10
  121. data/entry_types/scrolled/config/locales/new/section_width.en.yml +0 -10
  122. data/entry_types/scrolled/config/locales/new/typography_variants.de.yml +0 -7
  123. data/entry_types/scrolled/config/locales/new/typography_variants.en.yml +0 -7
  124. data/entry_types/scrolled/config/locales/new/video_embed_poster.de.yml +0 -8
  125. data/entry_types/scrolled/config/locales/new/video_embed_poster.en.yml +0 -8
  126. data/entry_types/scrolled/config/locales/new/waveform_styles.de.yml +0 -11
  127. data/entry_types/scrolled/config/locales/new/waveform_styles.en.yml +0 -12
  128. data/entry_types/scrolled/config/locales/new/widgets.de.yml +0 -6
  129. data/entry_types/scrolled/config/locales/new/widgets.en.yml +0 -6
  130. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/favicons/browserconfig.xml +0 -9
  131. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/favicons/mstile-150x150.png +0 -0
  132. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/favicons/safari-pinned-tab.svg +0 -46
  133. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/favicons/site.webmanifest +0 -19
  134. data/entry_types/scrolled/package/frontend/EditableInlineText.module-b9923660.js +0 -993
  135. data/entry_types/scrolled/package/frontend/usePhonePlatform-2857c22b.js +0 -34
@@ -31,6 +31,7 @@ $pageflow-hint-color: #666 !default;
31
31
  @import "pageflow/admin/hint";
32
32
  @import "pageflow/admin/icon_button";
33
33
  @import "pageflow/admin/icon_link";
34
+ @import "pageflow/admin/permalink_input";
34
35
  @import "pageflow/admin/publication_state_indicator";
35
36
  @import "pageflow/admin/quotas";
36
37
  @import "pageflow/admin/status_tags";
@@ -40,7 +40,7 @@
40
40
  padding: 0;
41
41
  }
42
42
 
43
- h2 {
43
+ .sidebar-header {
44
44
  font-size: inherit;
45
45
  font-weight: 500;
46
46
  margin: 15px 0 5px 0;
@@ -159,12 +159,8 @@ main.ui-layout-pane {
159
159
  border-bottom-right-radius: rounded(lg);
160
160
  }
161
161
 
162
- main > div {
162
+ #entry_preview {
163
163
  height: 100%;
164
-
165
- > .container {
166
- height: 100%;
167
- }
168
164
  }
169
165
 
170
166
  #help_entries_seed {
@@ -56,6 +56,8 @@
56
56
  }
57
57
 
58
58
  .dialog-sub_header {
59
+ font-size: inherit;
60
+ font-weight: 500;
59
61
  border-bottom: solid 1px var(--ui-on-surface-color-lighter);
60
62
  margin: 0;
61
63
  padding: space(1) 0;
@@ -9,6 +9,14 @@
9
9
  > button.has_icon_only {
10
10
  @include icon-only-button;
11
11
  }
12
+
13
+ &.full_width {
14
+ width: 100%;
15
+
16
+ > button {
17
+ width: 100%;
18
+ }
19
+ }
12
20
  }
13
21
 
14
22
  .drop_down_button_menu {
@@ -24,6 +32,7 @@
24
32
  border-radius: rounded(sm);
25
33
  box-shadow: var(--ui-box-shadow);
26
34
  background-color: var(--ui-surface-color);
35
+ box-sizing: border-box;
27
36
 
28
37
  &.is_visible {
29
38
  @include transition(visibility 0ms, opacity 100ms);
@@ -5,8 +5,9 @@
5
5
 
6
6
  .shortcuts {
7
7
  dt {
8
- display: inline-block;
9
- margin-top: 0.5em;
8
+ display: block;
9
+ margin: space(3) 0 space(2.5);
10
+ position: relative;
10
11
  }
11
12
 
12
13
  dd {
@@ -17,8 +18,17 @@
17
18
  border: solid 1px currentColor;
18
19
  border-radius: 4px;
19
20
  padding: 3px 5px;
20
- display: inline-block;
21
21
  font-size: 11px;
22
22
  }
23
23
  }
24
+
25
+ .inline_help {
26
+ padding-top: 36px;
27
+ }
28
+
29
+ // Make sure the label is on top when an inline help text is displayed
30
+ dt:hover .shortcut {
31
+ z-index: 4;
32
+ position: relative;
33
+ }
24
34
  }
@@ -59,6 +59,7 @@
59
59
  color: var(--ui-on-error-color);
60
60
  }
61
61
  } @else {
62
+ &.hover:not(:disabled):not(.disabled),
62
63
  &:hover:not(:disabled):not(.disabled) {
63
64
  border: 1px solid var(--ui-primary-color);
64
65
  }
@@ -39,7 +39,6 @@
39
39
  }
40
40
 
41
41
  .scroller > div {
42
- pointer-events: none;
43
42
  width: 75%;
44
43
 
45
44
  @include mobile {
@@ -70,7 +69,6 @@
70
69
  }
71
70
 
72
71
  .scroller > div {
73
- pointer-events: none;
74
72
  width: 100%;
75
73
 
76
74
  @include mobile {
@@ -9,7 +9,7 @@ $page-typography: () !default;
9
9
  $page-content-text-typography: () !default;
10
10
 
11
11
  /// Typography for content text in phone layout.
12
- $page-content-text-phone-typography: () !default
12
+ $page-content-text-phone-typography: () !default;
13
13
 
14
14
  /// Base font size for page content
15
15
  $page-content-font-size: 1em !default;
@@ -32,7 +32,8 @@ textarea,
32
32
  [type="tel"],
33
33
  [type="time"],
34
34
  [type="week"],
35
- [multiple] {
35
+ [multiple],
36
+ button[aria-haspopup] {
36
37
  font-size: 13px;
37
38
  display: block;
38
39
  width: 100%;
@@ -43,6 +44,7 @@ textarea,
43
44
  border-radius: rounded();
44
45
  color: var(--ui-on-surface-color);
45
46
 
47
+ &[aria-expanded=true],
46
48
  &:focus {
47
49
  border-color: var(--ui-on-surface-color-light);
48
50
  box-shadow: 0 0 0 2px var(--ui-selection-color-light);
@@ -222,6 +224,7 @@ textarea.short {
222
224
  }
223
225
 
224
226
  .input-disabled {
227
+ button,
225
228
  select,
226
229
  input,
227
230
  textarea,
@@ -4,7 +4,6 @@ module Pageflow
4
4
  # appropriate file importer
5
5
  class FileImportController < Pageflow::ApplicationController
6
6
  before_action :authenticate_user!
7
- respond_to :json
8
7
 
9
8
  def search
10
9
  result = file_importer.search file_importer_credentials, search_query
@@ -12,30 +11,35 @@ module Pageflow
12
11
  end
13
12
 
14
13
  def files_meta_data
15
- result = file_importer.files_meta_data file_importer_credentials, selected_files
14
+ result = file_importer.files_meta_data(file_importer_credentials,
15
+ params.require(:files))
16
16
  render json: {data: result}
17
17
  end
18
18
 
19
19
  def start_import_job
20
20
  entry = DraftEntry.find(entry_name)
21
- result = []
22
- selected_files.each do |key, file|
23
- file = key if file.nil?
24
- file = file.permit(:file_name,
25
- :rights,
26
- :content_type,
27
- :file_size,
28
- :url,
29
- configuration: :alt)
30
- entry_file, import_model = create_import_model entry, file
31
- FileImportJob.perform_later import_model.id, file_importer_credentials
32
- result.push file_response(entry_file, file)
21
+ authorize!(:edit, entry.to_model)
22
+
23
+ @items = files_params.map do |file_params|
24
+ file = entry.create_file!(file_type, file_params.except(:url))
25
+ file_import = create_file_import(file, file_params)
26
+
27
+ FileImportJob.perform_later(file_import.id, file_importer_credentials)
28
+
29
+ {file: file, source_url: file_params[:url]}
33
30
  end
34
- render json: {data: result}
35
31
  end
36
32
 
37
33
  private
38
34
 
35
+ def create_file_import(file, file_params)
36
+ FileImport.create!(entry: file.entry,
37
+ file_id: file.id,
38
+ file_type: file_type.model.name,
39
+ file_importer: file_importer.name,
40
+ download_options: file_params.to_json)
41
+ end
42
+
39
43
  def authentication_provider
40
44
  file_importer.authentication_provider
41
45
  end
@@ -48,35 +52,21 @@ module Pageflow
48
52
  params.require(:entry_id)
49
53
  end
50
54
 
51
- def selected_files
52
- params.require(:files)
53
- end
54
-
55
- def collection_name
56
- params.require(:collection)
57
- end
58
-
59
- def create_import_model(entry, file)
60
- entry_file, file_type = create_entry_file entry, file
61
- download_options = file.as_json
62
- import_model = FileImport.create!(entry: entry_file.entry,
63
- file_id: entry_file.id,
64
- file_type: file_type.model.name,
65
- file_importer: file_importer.name,
66
- download_options: download_options.to_json)
67
- [entry_file, import_model]
68
- end
69
-
70
- def create_entry_file(entry, file)
71
- file_type = Pageflow.config.file_types.find_by_collection_name!(collection_name)
72
- [entry.create_file!(file_type, file.except(:url)), file_type]
55
+ def files_params
56
+ params
57
+ .permit(files: [:file_name,
58
+ :rights,
59
+ :content_type,
60
+ :file_size,
61
+ :url,
62
+ configuration: :alt])
63
+ .require(:files)
73
64
  end
74
65
 
75
- def file_response(entry_file, file)
76
- entry_file = entry_file.as_json
77
- entry_file['type'] = params[:collection]
78
- entry_file['source_url'] = file[:url]
79
- entry_file
66
+ def file_type
67
+ @file_type ||= Pageflow.config.file_types.find_by_collection_name!(
68
+ params.require(:collection)
69
+ )
80
70
  end
81
71
 
82
72
  def file_importer
@@ -14,7 +14,7 @@ module Pageflow
14
14
  def show
15
15
  respond_to do |format|
16
16
  format.html do
17
- entry = PublishedEntry.find(params[:id], entry_request_scope)
17
+ entry = find_by_permalink || find_by_slug
18
18
 
19
19
  return if redirect_according_to_entry_redirect(entry)
20
20
  return if redirect_according_to_public_https_mode
@@ -25,6 +25,18 @@ module Pageflow
25
25
  end
26
26
  end
27
27
 
28
+ def manifest
29
+ respond_to do |format|
30
+ format.webmanifest do
31
+ entry = PublishedEntry.find(params[:id], entry_request_scope)
32
+
33
+ return head :not_found unless entry.entry_type.web_app_manifest
34
+
35
+ render json: entry.entry_type.web_app_manifest.call(entry)
36
+ end
37
+ end
38
+ end
39
+
28
40
  def stylesheet
29
41
  respond_to do |format|
30
42
  format.css do
@@ -42,6 +54,18 @@ module Pageflow
42
54
 
43
55
  protected
44
56
 
57
+ def find_by_permalink
58
+ PublishedEntry.find_by_permalink(
59
+ directory: params[:directory],
60
+ slug: params[:id],
61
+ scope: entry_request_scope
62
+ )
63
+ end
64
+
65
+ def find_by_slug
66
+ PublishedEntry.find(params[:id], entry_request_scope)
67
+ end
68
+
45
69
  def entry_request_scope
46
70
  Pageflow.config.public_entry_request_scope.call(Entry, request)
47
71
  end
@@ -0,0 +1,15 @@
1
+ module Pageflow
2
+ module Admin
3
+ # @api private
4
+ module PermalinksHelper
5
+ def collection_for_permalink_directories(theming, permalink)
6
+ options_from_collection_for_select(
7
+ theming.permalink_directories,
8
+ 'id',
9
+ 'path',
10
+ permalink.directory_id
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -36,7 +36,15 @@ module Pageflow
36
36
  .reverse_merge(trailing_slash: entry.theming.trailing_slash_in_canonical_urls)
37
37
  .reverse_merge(Pageflow.config.theming_url_options(entry.theming) || {})
38
38
 
39
- routes.short_entry_url(entry.to_model, params)
39
+ if entry.permalink.present?
40
+ routes.permalink_url(
41
+ entry.permalink.directory&.path || '',
42
+ entry.permalink.slug,
43
+ params
44
+ )
45
+ else
46
+ routes.short_entry_url(entry.to_model, params)
47
+ end
40
48
  end
41
49
 
42
50
  def ensure_entry_with_revision(entry)
@@ -5,7 +5,7 @@ module Pageflow
5
5
  end
6
6
 
7
7
  def pretty_theming_url(theming)
8
- public_root_url(Pageflow.config.theming_url_options(theming))
8
+ pageflow.public_root_url(Pageflow.config.theming_url_options(theming))
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,47 @@
1
+ class PageflowPermalinkInput
2
+ include Formtastic::Inputs::Base
3
+ include Formtastic::Inputs::Base::Stringish
4
+
5
+ def to_html
6
+ input_wrapping do
7
+ label_html << permalink_html
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def permalink_html
14
+ template.content_tag(
15
+ :div,
16
+ base_url_html << permalink_inputs_html,
17
+ class: 'permalink'
18
+ )
19
+ end
20
+
21
+ def base_url_html
22
+ template.content_tag(
23
+ :div,
24
+ options[:base_url].gsub(%r{^https?://}, ''),
25
+ class: 'permalink_base_url'
26
+ )
27
+ end
28
+
29
+ def permalink_inputs_html
30
+ return '' if options[:directory_collection].empty?
31
+
32
+ builder.select(:directory_id, options[:directory_collection]) <<
33
+ builder.text_field(:slug, placeholder: options[:slug_placeholder])
34
+ end
35
+
36
+ def error_keys
37
+ [:directory, :slug]
38
+ end
39
+
40
+ def wrapper_html_options
41
+ if options[:directory_collection].empty?
42
+ super.merge(style: 'display: none')
43
+ else
44
+ super
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,12 @@
1
+ module Pageflow
2
+ # @api private
3
+ module Permalinkable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to :permalink, optional: true
8
+
9
+ accepts_nested_attributes_for :permalink, update_only: true
10
+ end
11
+ end
12
+ end
@@ -87,6 +87,11 @@ module Pageflow
87
87
  'unused'
88
88
  end
89
89
 
90
+ # Overwritten with the extension of the file.
91
+ def extension
92
+ 'unused'
93
+ end
94
+
90
95
  # Overwritten in UploadableFile with attachment filename.
91
96
  def file_name
92
97
  'unused'
@@ -80,6 +80,10 @@ module Pageflow
80
80
  File.basename(attachment.original_filename, '.*')
81
81
  end
82
82
 
83
+ def extension
84
+ File.extname(attachment.original_filename).sub(/\A\./, '')
85
+ end
86
+
83
87
  def can_upload?
84
88
  uploading?
85
89
  end
@@ -26,11 +26,13 @@ module Pageflow
26
26
  new(theme,
27
27
  config.transform_theme_customization_overrides.call(
28
28
  theme_customization.overrides,
29
- entry
29
+ entry: entry,
30
+ theme: theme
30
31
  ),
31
32
  config.transform_theme_customization_files.call(
32
33
  theme_customization.selected_files.transform_values(&:urls),
33
- entry
34
+ entry: entry,
35
+ theme: theme
34
36
  ))
35
37
  end
36
38
  end
@@ -5,6 +5,7 @@ module Pageflow
5
5
 
6
6
  include FeatureTarget
7
7
  include EntryPublicationStates
8
+ include Permalinkable
8
9
  include SerializationBlacklist
9
10
 
10
11
  extend FriendlyId
@@ -126,6 +127,18 @@ module Pageflow
126
127
  [:title, [:title, :id]]
127
128
  end
128
129
 
130
+ def default_permalink_slug
131
+ title.to_s.parameterize
132
+ end
133
+
134
+ def self.ransackable_attributes(_auth_object)
135
+ %w[title type_name created_at edited_at first_published_at]
136
+ end
137
+
138
+ def self.ransackable_associations(_auth_object)
139
+ %w[account published_revision]
140
+ end
141
+
129
142
  def self.ransackable_scopes(_)
130
143
  [:with_publication_state, :published]
131
144
  end
@@ -16,6 +16,7 @@ module Pageflow
16
16
  delegate(:id, :slug,
17
17
  :entry_type,
18
18
  :account, :theming,
19
+ :permalink,
19
20
  :feature_state, :enabled_feature_names,
20
21
  :edit_lock,
21
22
  :password_digest,
@@ -42,7 +43,7 @@ module Pageflow
42
43
  end
43
44
 
44
45
  def theme
45
- @theme ||= CustomizedTheme.find(entry: self, theme: revision.theme)
46
+ @theme ||= CustomizedTheme.find(entry: entry, theme: revision.theme)
46
47
  end
47
48
 
48
49
  def home_button
@@ -3,6 +3,7 @@ module Pageflow
3
3
  def create!
4
4
  create_entry
5
5
 
6
+ copy_permalink
6
7
  copy_draft
7
8
  copy_memberships
8
9
 
@@ -21,6 +22,12 @@ module Pageflow
21
22
  @new_entry = Entry.create!(new_attributes)
22
23
  end
23
24
 
25
+ def copy_permalink
26
+ return if original_entry.permalink.blank?
27
+
28
+ new_entry.create_permalink!(directory: original_entry.permalink.directory)
29
+ end
30
+
24
31
  def copy_draft
25
32
  original_entry.draft.copy do |revision|
26
33
  revision.entry = new_entry
@@ -9,11 +9,11 @@ module Pageflow
9
9
  private
10
10
 
11
11
  def styles
12
- example_file.attachment_styles(example_file.attachment).keys
12
+ example_file.attachment_styles(example_file.attachment).keys + [:original]
13
13
  end
14
14
 
15
15
  def example_file
16
- @example_file ||= ImageFile.new(id: 0, file_name: ':basename.jpg')
16
+ @example_file ||= ImageFile.new(id: 0, file_name: ':basename.:extension')
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,39 @@
1
+ module Pageflow
2
+ # @api private
3
+ class Permalink < ApplicationRecord
4
+ extend FriendlyId
5
+ friendly_id :slug_candidates, use: :scoped, scope: :directory
6
+
7
+ before_validation :set_default_slug
8
+
9
+ has_one :entry
10
+
11
+ belongs_to :directory, class_name: 'PermalinkDirectory'
12
+
13
+ validates(:slug,
14
+ format: /\A[0-9a-zA-Z_-]+\z/,
15
+ uniqueness: {scope: :directory})
16
+
17
+ validate :belongs_to_same_theming_as_entry
18
+
19
+ private
20
+
21
+ def set_default_slug
22
+ self.slug = entry.default_permalink_slug if slug == ''
23
+ end
24
+
25
+ def slug_candidates
26
+ [entry.title, "#{entry.title}-#{entry.id}"]
27
+ end
28
+
29
+ def should_generate_new_friendly_id?
30
+ slug.nil?
31
+ end
32
+
33
+ def belongs_to_same_theming_as_entry
34
+ return if !directory || !entry || entry.theming_id == directory.theming_id
35
+
36
+ errors.add(:directory, :invalid)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ module Pageflow
2
+ # @api private
3
+ class PermalinkDirectory < ApplicationRecord
4
+ belongs_to :theming
5
+
6
+ validates(:path,
7
+ format: %r{\A([0-9a-zA-Z-]+/)*\z},
8
+ uniqueness: {scope: :theming_id})
9
+ end
10
+ end
@@ -46,6 +46,15 @@ module Pageflow
46
46
  PublishedEntry.new(scope.published.find(id))
47
47
  end
48
48
 
49
+ def self.find_by_permalink(directory: nil, slug:, scope:)
50
+ wrap(
51
+ scope.published.includes(permalink: :directory).where(
52
+ pageflow_permalink_directories: {path: directory || ''},
53
+ pageflow_permalinks: {slug: slug}
54
+ ).first
55
+ )
56
+ end
57
+
49
58
  def cache_key
50
59
  [
51
60
  self.class.model_name.cache_key,
@@ -72,5 +81,13 @@ module Pageflow
72
81
  def share_image_file
73
82
  PositionedFile.wrap(find_file_by_perma_id(ImageFile, share_image_id), share_image_x, share_image_y)
74
83
  end
84
+
85
+ class << self
86
+ private
87
+
88
+ def wrap(entry)
89
+ entry && new(entry)
90
+ end
91
+ end
75
92
  end
76
93
  end
@@ -230,7 +230,7 @@ module Pageflow
230
230
  end
231
231
 
232
232
  def available_themes
233
- Pageflow.config_for(entry).themes
233
+ @available_themes ||= Pageflow.config_for(entry).themes
234
234
  end
235
235
  end
236
236
  end
@@ -39,7 +39,7 @@ module Pageflow
39
39
  # A hash of urls based on the styles that were defined when
40
40
  # registering the entry type.
41
41
  def urls
42
- attachment_styles.map { |style, _|
42
+ styles_from_options.map { |style, _|
43
43
  [style, attachment.url(style)]
44
44
  }.to_h
45
45
  end
@@ -51,6 +51,11 @@ module Pageflow
51
51
 
52
52
  # @api private
53
53
  def attachment_styles
54
+ styles_from_options.except(:original)
55
+ end
56
+
57
+ # @api private
58
+ def styles_from_options
54
59
  styles = options_from_entry_type.fetch(:styles, {})
55
60
  styles.respond_to?(:call) ? styles.call(self) : styles
56
61
  end
@@ -3,6 +3,7 @@ module Pageflow
3
3
  belongs_to :account
4
4
 
5
5
  has_many :entries
6
+ has_many :permalink_directories, -> { order('path ASC') }
6
7
 
7
8
  scope :with_home_url, -> { where.not(home_url: '') }
8
9
  scope :for_request, ->(request) { Pageflow.config.theming_request_scope.call(all, request) }