pageflow 13.0.0.beta7 → 13.0.0.rc1

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

Potentially problematic release.


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

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/Rakefile +1 -1
  4. data/admins/pageflow/entry.rb +1 -1
  5. data/admins/pageflow/revisions.rb +3 -4
  6. data/app/assets/javascripts/pageflow/admin/accounts.js +1 -1
  7. data/app/assets/javascripts/pageflow/background_media.js +1 -1
  8. data/app/assets/javascripts/pageflow/base.js +1 -1
  9. data/app/assets/javascripts/pageflow/dist/react.js +1166 -982
  10. data/app/assets/javascripts/pageflow/media_player/volume_binding.js +2 -1
  11. data/app/assets/javascripts/pageflow/media_player/volume_fading/web_audio.js +60 -37
  12. data/app/assets/javascripts/pageflow/slideshow.js +15 -8
  13. data/app/assets/javascripts/pageflow/slideshow/adjacent_pages.js +9 -4
  14. data/app/assets/javascripts/pageflow/slideshow/adjacent_preloader.js +26 -0
  15. data/app/assets/javascripts/pageflow/slideshow/lazy_page_widget.js +2 -2
  16. data/app/assets/javascripts/pageflow/slideshow/successor_preparer.js +49 -0
  17. data/app/assets/javascripts/pageflow/ui/views/inputs/text_input_view.js +29 -5
  18. data/app/assets/javascripts/pageflow/ui/views/mixins/input_view.js +1 -1
  19. data/app/assets/javascripts/pageflow/widgets/multimedia_alert.js +2 -1
  20. data/app/assets/stylesheets/pageflow/admin/tabs_view.scss +3 -3
  21. data/app/assets/stylesheets/pageflow/navigation_mobile.scss +1 -1
  22. data/app/assets/stylesheets/pageflow/page_types/background-image.scss +1 -1
  23. data/app/assets/stylesheets/pageflow/themes/default/anchors.scss +5 -0
  24. data/app/assets/stylesheets/pageflow/themes/default/background_media_unmute_button.scss +45 -36
  25. data/app/assets/stylesheets/pageflow/themes/default/overview/icons/icon_font.scss +4 -1
  26. data/app/assets/stylesheets/pageflow/themes/default/page/anchors.scss +4 -0
  27. data/app/controllers/concerns/pageflow/public_https_mode.rb +5 -1
  28. data/app/controllers/pageflow/entries_controller.rb +10 -4
  29. data/app/jobs/pageflow/upload_file_to_s3_job.rb +1 -1
  30. data/app/models/concerns/pageflow/hosted_file.rb +0 -25
  31. data/app/models/pageflow/widget.rb +20 -9
  32. data/app/views/components/pageflow/admin/revisions_tab.rb +2 -2
  33. data/app/views/components/pageflow/admin/tabs_view.rb +3 -3
  34. data/app/views/pageflow/entries/_entry.html.erb +1 -1
  35. data/app/views/pageflow/entries/show.html.erb +1 -0
  36. data/config/locales/de.yml +2 -0
  37. data/config/locales/en.yml +2 -0
  38. data/lib/pageflow/configuration.rb +10 -0
  39. data/lib/pageflow/primary_domain_entry_redirect.rb +25 -0
  40. data/lib/pageflow/react/widget_type.rb +7 -2
  41. data/lib/pageflow/version.rb +1 -1
  42. data/lib/pageflow/widget_type.rb +8 -0
  43. data/spec/factories/hosted_files.rb +0 -8
  44. metadata +6 -5
  45. data/app/assets/javascripts/pageflow/slideshow/adjacent_preparer.js +0 -53
  46. data/app/assets/javascripts/pageflow/slideshow/progressive_preload.js +0 -42
@@ -10,7 +10,7 @@
10
10
  background-position: center center;
11
11
  background-repeat: no-repeat;
12
12
  background-attachment: fixed;
13
- @include background-size(cover);
13
+ background-size: cover;
14
14
  }
15
15
  }
16
16
 
@@ -1,3 +1,7 @@
1
+ /// Color of the caret icon displayed in front of links. Same as text
2
+ /// color by default.
3
+ $anchor-icon-color: null !default;
4
+
1
5
  %anchor {
2
6
  @include fa-caret-right-icon;
3
7
 
@@ -25,5 +29,6 @@
25
29
  top: 7px;
26
30
  width: 10px;
27
31
  height: 12px;
32
+ color: $anchor-icon-color;
28
33
  }
29
34
  }
@@ -1,14 +1,16 @@
1
- $background-media-unmute-button-color: rgba(53, 53, 53, 0.9);
1
+ $background-media-unmute-button-color: rgba(53, 53, 53, 0.9) !default;
2
2
 
3
- $background-media-unmute-button-shadow-color: #ccc;
3
+ $background-media-unmute-button-shadow-color: #ccc !default;
4
4
 
5
- $background-media-unmute-button-icon-color: #ddd;
5
+ $background-media-unmute-button-icon-color: #ddd !default;
6
6
 
7
- $background-media-unmute-button-icon-size: 24px;
7
+ $background-media-unmute-button-icon-size: 24px !default;
8
8
 
9
- $background-media-unmute-button-size: 34px;
9
+ $background-media-unmute-button-size: 34px !default;
10
10
 
11
- $background-media-unmute-button-margin: 10px;
11
+ $background-media-unmute-button-margin: 10px !default;
12
+
13
+ $background-media-unmute-button-custom-image: false !default;
12
14
 
13
15
  .background_media_unmute_button {
14
16
  position: absolute;
@@ -25,22 +27,20 @@ $background-media-unmute-button-margin: 10px;
25
27
  right: 11px;
26
28
  }
27
29
 
28
- &:before {
29
- z-index: 2;
30
- }
31
-
32
- &:after {
33
- content: "";
34
- display: block;
35
- position: absolute;
36
- top: 6px;
37
- left: 6px;
38
- width: $background-media-unmute-button-size;
39
- height: $background-media-unmute-button-size;
40
- border-radius: $background-media-unmute-button-size / 2;
41
- background-color: $background-media-unmute-button-color;
42
- @include box-shadow(0 0 7px $background-media-unmute-button-shadow-color);
43
- z-index: 1;
30
+ @if not $background-media-unmute-button-custom-image {
31
+ &:after {
32
+ content: "";
33
+ display: block;
34
+ position: absolute;
35
+ top: 6px;
36
+ left: 6px;
37
+ width: $background-media-unmute-button-size;
38
+ height: $background-media-unmute-button-size;
39
+ border-radius: $background-media-unmute-button-size / 2;
40
+ background-color: $background-media-unmute-button-color;
41
+ box-shadow: 0 0 7px $background-media-unmute-button-shadow-color;
42
+ z-index: 1;
43
+ }
44
44
  }
45
45
 
46
46
  a {
@@ -48,21 +48,30 @@ $background-media-unmute-button-margin: 10px;
48
48
  width: 100%;
49
49
  height: 100%;
50
50
  z-index: 2;
51
- position: relative;
52
- @include background-icon-center($color: $background-media-unmute-button-icon-color,
53
- $font-size: $background-media-unmute-button-icon-size);
54
- @include fa-volume-down-icon;
55
51
 
56
- &:after {
57
- content: "";
58
- display: block;
59
- position: absolute;
60
- width: 25px;
61
- left: 22px;
62
- top: 22px;
63
- border-top: solid 2px $background-media-unmute-button-icon-color;
64
- border-bottom: solid 1px $background-media-unmute-button-color;
65
- @include transform(translate(-50%, -50%) rotate(45deg));
52
+ @if $background-media-unmute-button-custom-image {
53
+ background-image: image-url("pageflow/themes/#{$theme-name}/background_media_unmute_button.svg");
54
+ background-size: $background-media-unmute-button-size
55
+ $background-media-unmute-button-size;
56
+ background-repeat: no-repeat;
57
+ background-position: 50% 50%;
58
+ } @else {
59
+ position: relative;
60
+ @include background-icon-center($color: $background-media-unmute-button-icon-color,
61
+ $font-size: $background-media-unmute-button-icon-size);
62
+ @include fa-volume-down-icon;
63
+
64
+ &:after {
65
+ content: "";
66
+ display: block;
67
+ position: absolute;
68
+ width: 25px;
69
+ left: 22px;
70
+ top: 22px;
71
+ border-top: solid 2px $background-media-unmute-button-icon-color;
72
+ border-bottom: solid 1px $background-media-unmute-button-color;
73
+ @include transform(translate(-50%, -50%) rotate(45deg));
74
+ }
66
75
  }
67
76
  }
68
77
  }
@@ -4,7 +4,10 @@
4
4
 
5
5
  $button-icon-color,
6
6
  $active-button-icon-color,
7
- $deactivated-button-icon-color
7
+ $deactivated-button-icon-color,
8
+
9
+ $share-icon-color: null,
10
+ $active-share-icon-color: null
8
11
  ) {
9
12
 
10
13
  .button {
@@ -8,10 +8,14 @@ $page-anchor-color: #fff !default;
8
8
  /// Color of links inside the text on inverted pages
9
9
  $page-anchor-inverted-color: #000 !default;
10
10
 
11
+ /// Typography settings of links in page content text
12
+ $page-anchor-typography: () !default;
13
+
11
14
  .contentText a {
12
15
  @extend %anchor;
13
16
  color: $page-anchor-color;
14
17
  pointer-events: all;
18
+ @include typography($page-anchor-typography)
15
19
  }
16
20
 
17
21
  .invert .contentText a {
@@ -2,12 +2,16 @@ module Pageflow
2
2
  module PublicHttpsMode
3
3
  protected
4
4
 
5
- def check_public_https_mode
5
+ def redirect_according_to_public_https_mode
6
6
  if request.ssl? && Pageflow.config.public_https_mode == :prevent
7
7
  redirect_to("http://#{request.host}#{request.fullpath}", status: :moved_permanently)
8
+ true
8
9
  elsif !request.ssl? && Pageflow.config.public_https_mode == :enforce
9
10
  redirect_to("https://#{request.host}#{request.fullpath}", status: :moved_permanently)
11
+ true
10
12
  end
11
13
  end
14
+
15
+ alias_method :check_public_https_mode, :redirect_according_to_public_https_mode
12
16
  end
13
17
  end
@@ -5,10 +5,6 @@ module Pageflow
5
5
 
6
6
  before_action :authenticate_user!, except: [:index, :show, :page]
7
7
 
8
- before_action :check_public_https_mode,
9
- only: [:index, :show],
10
- unless: lambda { |controller| controller.request.format.json? }
11
-
12
8
  after_action :allow_iframe_for_embed, only: :show
13
9
 
14
10
  helper_method :render_to_string
@@ -30,6 +26,12 @@ module Pageflow
30
26
  @entry = PublishedEntry.find(params[:id], entry_request_scope)
31
27
  I18n.locale = @entry.locale
32
28
 
29
+ if redirect_location = entry_redirect(@entry)
30
+ return redirect_to(redirect_location, status: :moved_permanently)
31
+ end
32
+
33
+ return if redirect_according_to_public_https_mode
34
+
33
35
  if !request.format.css?
34
36
  check_entry_password_protection(@entry)
35
37
  end
@@ -97,6 +99,10 @@ module Pageflow
97
99
  Pageflow.config.public_entry_request_scope.call(Entry, request)
98
100
  end
99
101
 
102
+ def entry_redirect(entry)
103
+ Pageflow.config.public_entry_redirect.call(entry, request)
104
+ end
105
+
100
106
  def allow_iframe_for_embed
101
107
  if params[:embed]
102
108
  response.headers.except! 'X-Frame-Options'
@@ -10,7 +10,7 @@ module Pageflow
10
10
  file.attachment_on_s3 = file.attachment_on_filesystem
11
11
  file.save!
12
12
 
13
- file.attachment_on_filesystem.destroy unless file.keep_on_filesystem_after_upload_to_s3?
13
+ file.attachment_on_filesystem.destroy
14
14
  :ok
15
15
  else
16
16
  logger.info "#{file.class.name} #{file.id} not yet transfered to instance."
@@ -47,10 +47,6 @@ module Pageflow
47
47
  self.attachment_on_filesystem = value
48
48
  end
49
49
 
50
- def keep_on_filesystem_after_upload_to_s3?
51
- false
52
- end
53
-
54
50
  def retryable?
55
51
  can_retry?
56
52
  end
@@ -73,27 +69,6 @@ module Pageflow
73
69
  url
74
70
  end
75
71
 
76
- # @deprecated Write a migration instead
77
- def self.columns(t)
78
- t.belongs_to(:entry, index: true)
79
- t.belongs_to(:uploader, index: true)
80
-
81
- t.string(:state)
82
- t.string(:rights)
83
-
84
- t.string(:attachment_on_filesystem_file_name)
85
- t.string(:attachment_on_filesystem_content_type)
86
- t.integer(:attachment_on_filesystem_file_size, limit: 8)
87
- t.datetime(:attachment_on_filesystem_updated_at)
88
-
89
- t.string(:attachment_on_s3_file_name)
90
- t.string(:attachment_on_s3_content_type)
91
- t.integer(:attachment_on_s3_file_size, limit: 8)
92
- t.datetime(:attachment_on_s3_updated_at)
93
-
94
- t.timestamps
95
- end
96
-
97
72
  module ClassMethods
98
73
  def processing_state_machine(&block)
99
74
  state_machine do
@@ -14,14 +14,9 @@ module Pageflow
14
14
  record.save!
15
15
  end
16
16
 
17
- def enabled?(options)
18
- if options[:scope] == :editor
19
- widget_type.enabled_in_editor?
20
- elsif options[:scope] == :preview
21
- widget_type.enabled_in_preview?
22
- else
23
- true
24
- end
17
+ def matches?(options)
18
+ enabled_for_scope?(options[:scope]) &&
19
+ for_insert_point?(options.fetch(:insert_point, :any))
25
20
  end
26
21
 
27
22
  def self.copy_all_to(subject)
@@ -40,10 +35,26 @@ module Pageflow
40
35
  Resolver.new(config, options).result
41
36
  end
42
37
 
38
+ private
39
+
40
+ def enabled_for_scope?(scope)
41
+ if scope == :editor
42
+ widget_type.enabled_in_editor?
43
+ elsif scope == :preview
44
+ widget_type.enabled_in_preview?
45
+ else
46
+ true
47
+ end
48
+ end
49
+
50
+ def for_insert_point?(insert_point)
51
+ insert_point == :any || widget_type.insert_point == insert_point
52
+ end
53
+
43
54
  Resolver = Struct.new(:config, :options) do
44
55
  def result
45
56
  assign_widget_types(all).select do |widget|
46
- widget.enabled?(options)
57
+ widget.matches?(options)
47
58
  end
48
59
  end
49
60
 
@@ -41,7 +41,7 @@ module Pageflow
41
41
  text_node(link_to(t('pageflow.admin.entries.show'), pageflow.revision_path(revision), :class => 'show'))
42
42
  if authorized?(:restore, entry)
43
43
  text_node(link_to(t('pageflow.admin.entries.restore'),
44
- restore_admin_revision_path(revision),
44
+ restore_admin_revision_path(revision, params.permit(:tab)),
45
45
  method: :post,
46
46
  class: 'restore',
47
47
  data: {
@@ -58,7 +58,7 @@ module Pageflow
58
58
 
59
59
  if authorized?(:snapshot, entry)
60
60
  text_node(button_to(t('pageflow.admin.entries.snapshot'),
61
- snapshot_admin_entry_path(entry),
61
+ snapshot_admin_entry_path(entry, params.permit(:tab)),
62
62
  method: :post))
63
63
  end
64
64
  end
@@ -26,7 +26,7 @@ module Pageflow
26
26
  end
27
27
 
28
28
  def build_tab_list
29
- ul(class: 'tabs') do
29
+ ul(class: 'admin_tabs_view-tabs') do
30
30
  tabs.each do |tab|
31
31
  build_tab_item(tab)
32
32
  end
@@ -66,8 +66,8 @@ module Pageflow
66
66
 
67
67
  def tab_container_class(tab_name)
68
68
  [
69
- 'tab_container',
70
- "#{tab_name}_tab_container",
69
+ 'admin_tabs_view-container',
70
+ "admin_tabs_view-#{tab_name}_container",
71
71
  current_tab?(tab_name) ? 'active' : nil
72
72
  ].compact.join(' ')
73
73
  end
@@ -17,5 +17,5 @@
17
17
 
18
18
  <%= render 'pageflow/entries/header', :entry => entry %>
19
19
  <%= render 'pageflow/entries/overview', :entry => entry %>
20
- <%= render_widgets(entry, :scope => widget_scope) %>
20
+ <%= render_widgets(entry, scope: widget_scope, insert_point: :bottom_of_entry) %>
21
21
  <% end %>
@@ -20,6 +20,7 @@
20
20
  <%= render 'pageflow/entries/ie8_hint' %>
21
21
  <%= render 'pageflow/entries/loading_spinner' %>
22
22
  <%= render 'pageflow/entries/multimedia_alert' %>
23
+ <%= render_widgets(@entry, scope: @widget_scope, insert_point: :before_entry) %>
23
24
  <%= render @entry, :widget_scope => @widget_scope %>
24
25
 
25
26
  <script>
@@ -831,6 +831,7 @@ de:
831
831
  users: Benutzer
832
832
  revisions:
833
833
  published_until_hint: Freilassen, um Beitrag unbegrenzt zu veröffentlichen.
834
+ restored: Revision wiederhergestellt
834
835
  themings:
835
836
  additional_cnames_hint: Kommaseparierte Liste von weiteren CNAMES. Ausschließlich zur Auswahl der Redirect URL genutzt.
836
837
  cname_hint: Wird in Share Links verwendet.
@@ -1811,6 +1812,7 @@ de:
1811
1812
  none: "(Kein)"
1812
1813
  placeholder: Standard (%{text})
1813
1814
  text_input_view:
1815
+ max_characters_exceeded: Maximale Zeichenanzahl überschritten (%{max_length})
1814
1816
  required_field: Muss ausgefüllt werden
1815
1817
  url_input_view:
1816
1818
  required_field: Muss ausgefüllt werden
@@ -831,6 +831,7 @@ en:
831
831
  users: User
832
832
  revisions:
833
833
  published_until_hint: Leave blank to publish indefinitely.
834
+ restored: Version restored
834
835
  themings:
835
836
  additional_cnames_hint: Comma separated list of additional CNAMES. Used only to select the correct redirect URL.
836
837
  cname_hint: Used in public sharing URLs.
@@ -1786,6 +1787,7 @@ en:
1786
1787
  none: "(none)"
1787
1788
  placeholder: Default (%{text})
1788
1789
  text_input_view:
1790
+ max_characters_exceeded: Maximum number of characters exceeded (%{max_length})
1789
1791
  required_field: Required field
1790
1792
  url_input_view:
1791
1793
  required_field: Required field
@@ -162,6 +162,15 @@ module Pageflow
162
162
  # end
163
163
  attr_accessor :public_entry_request_scope
164
164
 
165
+ # Either a lambda or an object with a `call` method taking an
166
+ # {Entry} record and an {ActionDispatch::Request} object and
167
+ # returning `nil` or a path to redirect to. Can be used in
168
+ # conjuction with {PrimaryDomainEntryRedirect} to make sure
169
+ # entries are accessed via their account's configured cname.
170
+ #
171
+ # @since 12.4
172
+ attr_accessor :public_entry_redirect
173
+
165
174
  # Either a lambda or an object with a `call` method taking a
166
175
  # {Theming} as paramater and returing a hash of options used to
167
176
  # construct the url of a published entry.
@@ -326,6 +335,7 @@ module Pageflow
326
335
 
327
336
  @theming_request_scope = CnameThemingRequestScope.new
328
337
  @public_entry_request_scope = lambda { |entries, request| entries }
338
+ @public_entry_redirect = ->(_entry, _request) { nil }
329
339
  @public_entry_url_options = Pageflow::ThemingsHelper::DEFAULT_PUBLIC_ENTRY_OPTIONS
330
340
  @entry_embed_url_options = {protocol: 'https'}
331
341
 
@@ -0,0 +1,25 @@
1
+ module Pageflow
2
+ # Use as {Configuration#public_entry_redirect} to make sure entries
3
+ # are accessed via their account's configured cname.
4
+ #
5
+ # @since 12.4
6
+ class PrimaryDomainEntryRedirect
7
+ def call(entry, request)
8
+ theming = entry.theming
9
+
10
+ if theming.cname.present? &&
11
+ !known_domains(theming).include?(request.host)
12
+ [request.protocol, theming.cname, request.fullpath].join
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def known_domains(theming)
19
+ [
20
+ theming.cname,
21
+ (theming.additional_cnames || '').split(',').map(&:strip)
22
+ ].flatten
23
+ end
24
+ end
25
+ end
@@ -1,17 +1,22 @@
1
1
  module Pageflow
2
2
  module React
3
3
  class WidgetType < Pageflow::WidgetType
4
- attr_reader :name, :role
4
+ attr_reader :name, :role, :options
5
5
 
6
- def initialize(name, role)
6
+ def initialize(name, role, options = {})
7
7
  @name = name
8
8
  @role = role
9
+ @options = options
9
10
  end
10
11
 
11
12
  def roles
12
13
  [role]
13
14
  end
14
15
 
16
+ def insert_point
17
+ @options[:insert_point] || super
18
+ end
19
+
15
20
  def render(template, _)
16
21
  template.render(File.join('pageflow', 'react', 'widget'), name: name)
17
22
  end