pageflow 15.0.0.beta2 → 15.0.0.beta3

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +105 -2
  3. data/README.md +5 -1
  4. data/app/assets/javascripts/pageflow/dist/react-client.js +176 -56
  5. data/app/assets/javascripts/pageflow/dist/react-server.js +176 -56
  6. data/app/assets/javascripts/pageflow/editor/api/widget_type.js +0 -1
  7. data/app/assets/javascripts/pageflow/editor/views/widget_types/media_loading_spinner.js +18 -0
  8. data/app/assets/javascripts/pageflow/editor/views/widget_types/title_loading_spinner.js +3 -0
  9. data/app/assets/javascripts/pageflow/ui/views/tabs_view.js +2 -1
  10. data/app/assets/stylesheets/pageflow/themes/default/base.scss +1 -1
  11. data/app/assets/stylesheets/pageflow/themes/default/loading_spinner.scss +37 -1
  12. data/app/assets/stylesheets/pageflow/themes/default/loading_spinner/media.scss +56 -0
  13. data/app/assets/stylesheets/pageflow/themes/default/loading_spinner/title.scss +10 -50
  14. data/app/assets/stylesheets/pageflow/themes/default/logo/variant/watermark.scss +10 -1
  15. data/app/assets/stylesheets/pageflow/themes/default/player_controls/classic/info_box.scss +5 -0
  16. data/app/assets/stylesheets/pageflow/themes/default/player_controls/slim/info_box.scss +4 -0
  17. data/app/controllers/pageflow/editor/files_controller.rb +6 -3
  18. data/app/helpers/pageflow/meta_tags_helper.rb +3 -3
  19. data/app/helpers/pageflow/social_share_links_helper.rb +1 -1
  20. data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +41 -0
  21. data/app/models/concerns/pageflow/reusable_file.rb +6 -0
  22. data/app/models/concerns/pageflow/uploadable_file.rb +5 -0
  23. data/app/models/pageflow/draft_entry.rb +26 -2
  24. data/app/models/pageflow/membership.rb +3 -4
  25. data/app/models/pageflow/revision.rb +20 -4
  26. data/app/models/pageflow/widget.rb +12 -6
  27. data/config/locales/de.yml +24 -0
  28. data/config/locales/en.yml +24 -0
  29. data/db/migrate/20140418225525_setup_schema.rb +18 -18
  30. data/db/migrate/20190820152900_drop_accounts_themes.rb +8 -0
  31. data/lib/pageflow/built_in_page_type.rb +3 -0
  32. data/lib/pageflow/built_in_widget_type.rb +7 -0
  33. data/lib/pageflow/built_in_widget_types_plugin.rb +6 -0
  34. data/lib/pageflow/entry_export_import.rb +43 -0
  35. data/lib/pageflow/entry_export_import/attachment_files.rb +65 -0
  36. data/lib/pageflow/entry_export_import/entry_serialization.rb +68 -0
  37. data/lib/pageflow/entry_export_import/file_mappings.rb +32 -0
  38. data/lib/pageflow/entry_export_import/page_type_versions.rb +29 -0
  39. data/lib/pageflow/entry_export_import/revision_serialization.rb +58 -0
  40. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +158 -0
  41. data/lib/pageflow/entry_export_import/zip_archive.rb +36 -0
  42. data/lib/pageflow/file_type.rb +17 -4
  43. data/lib/pageflow/page_type.rb +20 -0
  44. data/lib/pageflow/page_types.rb +8 -0
  45. data/lib/pageflow/react/page_type.rb +10 -0
  46. data/lib/pageflow/version.rb +1 -1
  47. data/lib/pageflow/widget_types.rb +9 -1
  48. data/lib/tasks/entry_export_import.rake +27 -0
  49. data/spec/factories/revisions.rb +6 -0
  50. data/spec/factories/test_multi_attachment_files.rb +16 -0
  51. data/spec/factories/test_revision_components.rb +7 -0
  52. metadata +46 -3
@@ -0,0 +1,56 @@
1
+ $media-loading-spinner-duration: 7s;
2
+
3
+ .media_loading_spinner {
4
+ &:before {
5
+ content: "";
6
+ display: block;
7
+ position: absolute;
8
+ top: 0;
9
+ left: 0;
10
+ bottom: 0;
11
+ right: 0;
12
+ z-index: 1;
13
+ background-color: rgba(0, 0, 0, 0.15);
14
+ }
15
+
16
+ &-image {
17
+ background-size: cover;
18
+ height: 100%;
19
+ }
20
+
21
+ &-logo {
22
+ @extend %pageflow_widget_margin_right !optional;
23
+ position: relative;
24
+
25
+ &:after {
26
+ @extend %background_logo;
27
+ content: "";
28
+ display: block;
29
+ z-index: 1;
30
+ opacity: 0;
31
+ }
32
+ }
33
+
34
+ &-logo-invert {
35
+ &:after{
36
+ background-image: image-url("pageflow/themes/#{$theme-name}/logo_header_invert.#{$logo-image-file-extension}");
37
+ }
38
+ }
39
+
40
+ &-fade {
41
+ @include animation(fade_out 1s ease 1);
42
+ @include animation-delay($media-loading-spinner-duration + 0.5s);
43
+ @include animation-fill-mode(forwards);
44
+
45
+ .media_loading_spinner-logo:after {
46
+ @include animation(fade_in 1s ease-out 1);
47
+ @include animation-fill-mode(forwards);
48
+ }
49
+ }
50
+
51
+ &-invert {
52
+ color: #000 !important;
53
+ }
54
+
55
+ }
56
+
@@ -8,25 +8,13 @@ $title-loading-spinner-subtitle-typography: () !default;
8
8
 
9
9
  $title-loading-spinner-phone-subtitle-typography: () !default;
10
10
 
11
- .title_loading_spinner {
12
- $duration: 7s;
11
+ .media_loading_spinner {
13
12
 
14
- &:before {
15
- content: "";
16
- display: block;
17
- position: absolute;
18
- top: 0;
19
- left: 0;
20
- bottom: 0;
21
- right: 0;
22
- z-index: 1;
23
- background-color: rgba(0, 0, 0, 0.15);
24
- }
25
-
26
- &-image {
27
- background-size: cover;
28
- filter: blur(50px);
29
- height: 100%;
13
+ &-fade {
14
+ .media_loading_spinner-titles {
15
+ @include animation(title_fade $media-loading-spinner-duration ease-out 1);
16
+ @include animation-fill-mode(forwards);
17
+ }
30
18
  }
31
19
 
32
20
  &-titles {
@@ -53,7 +41,7 @@ $title-loading-spinner-phone-subtitle-typography: () !default;
53
41
  content: "";
54
42
  display: block;
55
43
  width: 100px;
56
- border-bottom: solid 2px #fff;
44
+ border-bottom: solid 2px;
57
45
  margin: 20px auto;
58
46
  }
59
47
 
@@ -96,36 +84,7 @@ $title-loading-spinner-phone-subtitle-typography: () !default;
96
84
  );
97
85
  }
98
86
  }
99
-
100
- &-logo {
101
- @extend %pageflow_widget_margin_right !optional;
102
- position: relative;
103
-
104
- &:after {
105
- @extend %background_logo;
106
- content: "";
107
- display: block;
108
- z-index: 1;
109
- opacity: 0;
110
- }
111
- }
112
-
113
- &-fade {
114
- @include animation(fade_out 1s ease 1);
115
- @include animation-delay($duration + 0.5s);
116
- @include animation-fill-mode(forwards);
117
-
118
- .title_loading_spinner-titles {
119
- @include animation(title_fade $duration ease-out 1);
120
- @include animation-fill-mode(forwards);
121
- }
122
-
123
- .title_loading_spinner-logo:after {
124
- @include animation(fade_in 1s ease-out 1);
125
- @include animation-fill-mode(forwards);
126
- }
127
- }
128
-
87
+
129
88
  @include keyframes(title_fade) {
130
89
  0% {
131
90
  @include transform(translate(-50%, -30%));
@@ -155,4 +114,5 @@ $title-loading-spinner-phone-subtitle-typography: () !default;
155
114
  opacity: 0;
156
115
  }
157
116
  }
158
- }
117
+
118
+ }
@@ -15,6 +15,9 @@ $logo-watermark-variant-fade-in-with-header: false !default;
15
15
  /// Set opacity value of watermark logos
16
16
  $logo-watermark-variant-opacity: 0.3 !default;
17
17
 
18
+ /// Height of the logo in phone layout.
19
+ $logo-watermark-mobile-height: null !default;
20
+
18
21
  @mixin logo-variant-watermark(
19
22
  $top,
20
23
  $width,
@@ -32,7 +35,13 @@ $logo-watermark-variant-opacity: 0.3 !default;
32
35
  height: $phone-height;
33
36
  top: 21px;
34
37
  }
35
-
38
+ @if $logo-watermark-mobile-height {
39
+ @include mobile {
40
+ width: $logo-watermark-mobile-height * $width / $height;
41
+ height: $logo-watermark-mobile-height;
42
+ top: 21px;
43
+ }
44
+ }
36
45
  @include logo-alignment;
37
46
  }
38
47
 
@@ -78,6 +78,11 @@ $classic-player-controls-info-box-border-radius: 4px !default;
78
78
 
79
79
  .js &-info_box {
80
80
  pointer-events: none;
81
+
82
+ a {
83
+ pointer-events: all;
84
+ }
85
+
81
86
  margin: 0;
82
87
  margin-bottom: 60px + $classic-player-controls-border-width * 4;
83
88
  width: 434px;
@@ -73,6 +73,10 @@ $slim-player-controls-info-box-header-typography: () !default;
73
73
  @include transition(opacity 0.2s linear, visibility 0.2s linear, transform 0.2s ease);
74
74
  pointer-events: none;
75
75
 
76
+ a {
77
+ pointer-events: all;
78
+ }
79
+
76
80
  position: absolute;
77
81
  left: 50%;
78
82
  @include transform(translate3d(-50%, 0, 0));
@@ -19,11 +19,11 @@ module Pageflow
19
19
  authorize!(:edit, entry.to_model)
20
20
  verify_edit_lock!(entry)
21
21
 
22
- @file = entry.create_file!(file_type.model, create_params)
22
+ @file = entry.create_file!(file_type, create_params)
23
23
  @file.publish! if params[:no_upload]
24
24
 
25
25
  respond_with(:editor, @file)
26
- rescue ActiveRecord::RecordInvalid => e
26
+ rescue ActiveRecord::RecordInvalid, DraftEntry::InvalidForeignKeyCustomAttributeError => e
27
27
  debug_log_with_backtrace(e)
28
28
  head :unprocessable_entity
29
29
  end
@@ -118,7 +118,10 @@ module Pageflow
118
118
  end
119
119
 
120
120
  def file_custom_params
121
- file_params.permit(file_type.custom_attributes)
121
+ file_params.permit(file_type
122
+ .custom_attributes
123
+ .select { |_, options| options[:permitted_create_param] }
124
+ .keys)
122
125
  end
123
126
 
124
127
  def file_params
@@ -7,9 +7,9 @@ module Pageflow
7
7
 
8
8
  def meta_tags_data_for_entry(entry)
9
9
  {
10
- keywords: entry.keywords.presence || Pageflow.config.default_keywords_meta_tag,
11
- author: entry.author.presence || Pageflow.config.default_author_meta_tag,
12
- publisher: entry.publisher.presence || Pageflow.config.default_publisher_meta_tag
10
+ keywords: entry.keywords,
11
+ author: entry.author,
12
+ publisher: entry.publisher
13
13
  }
14
14
  end
15
15
  end
@@ -8,7 +8,7 @@ module Pageflow
8
8
  google: 'https://plus.google.com/share?url=%{url}',
9
9
  linked_in: 'https://www.linkedin.com/shareArticle?mini=true&url=%{url}',
10
10
  telegram: 'tg://msg?text=%{url}',
11
- twitter: 'http://twitter.com/home?status=%{url}',
11
+ twitter: 'https://twitter.com/intent/tweet?url=%{url}',
12
12
  whats_app: 'WhatsApp://send?text=%{url}'
13
13
  }.freeze
14
14
 
@@ -0,0 +1,41 @@
1
+ module Pageflow
2
+ module EntryExportImport
3
+ # Uploads a file attachment associated with an ReusableFile
4
+ # and publishes the file afterwards
5
+ class UploadAndPublishFileJob < ApplicationJob
6
+ queue_as :file_upload
7
+
8
+ def perform(reusable_file, exported_id, archive_file_name)
9
+ archive = ZipArchive.new(archive_file_name)
10
+
11
+ reusable_file.attachments_for_export.each do |attachment|
12
+ archive_path = AttachmentFiles.archive_path(reusable_file,
13
+ attachment,
14
+ exported_id: exported_id)
15
+
16
+ archive.extract_to_tempfile(archive_path) do |tempfile|
17
+ # Paperclip skips post processing anyway since the name of
18
+ # the tempfile does not pass the validation defined in
19
+ # UploadableFile. To be explicit, we disable post
20
+ # processing manually as well.
21
+ attachment.post_processing = false
22
+ attachment.assign(tempfile)
23
+
24
+ # Calling `attachment.assign` changes the
25
+ # `<attachment-name>_file_name` attribute based on the
26
+ # name of the tempfile. To prevent `flush_writes` from
27
+ # using these new values when constructing the
28
+ # destionation path, we need to undo the attribute
29
+ # changes. Restoring the attributes does not reset the
30
+ # list of files queued for write in the attachment.
31
+ reusable_file.restore_attributes
32
+
33
+ attachment.flush_writes
34
+ end
35
+ end
36
+
37
+ reusable_file.publish!
38
+ end
39
+ end
40
+ end
41
+ end
@@ -83,6 +83,12 @@ module Pageflow
83
83
  'unused'
84
84
  end
85
85
 
86
+ # Overwritten with the list of attachments of the file type
87
+ # that should get included in export archives.
88
+ def attachments_for_export
89
+ []
90
+ end
91
+
86
92
  # Overwritten in UploadableFile based on initial state_machine-state.
87
93
  # Defaults to false for files that only use the ReusableFile module
88
94
  def can_upload?
@@ -65,6 +65,11 @@ module Pageflow
65
65
  end
66
66
 
67
67
  # ReusableFile-overrides:
68
+
69
+ def attachments_for_export
70
+ [attachment]
71
+ end
72
+
68
73
  def url
69
74
  if attachment.present?
70
75
  attachment.url
@@ -2,6 +2,8 @@ module Pageflow
2
2
  class DraftEntry
3
3
  include ActiveModel::Conversion
4
4
 
5
+ class InvalidForeignKeyCustomAttributeError < StandardError; end
6
+
5
7
  attr_reader :entry, :draft
6
8
 
7
9
  delegate(:id, :slug,
@@ -38,8 +40,10 @@ module Pageflow
38
40
  entry.title
39
41
  end
40
42
 
41
- def create_file!(model, attributes)
42
- file = model.create!(attributes.except(:configuration)) do |f|
43
+ def create_file!(file_type, attributes)
44
+ check_foreign_key_custom_attributes(file_type.custom_attributes, attributes)
45
+
46
+ file = file_type.model.create!(attributes.except(:configuration)) do |f|
43
47
  f.entry = entry
44
48
  end
45
49
 
@@ -109,5 +113,25 @@ module Pageflow
109
113
  def resolve_widgets(options = {})
110
114
  widgets.resolve(Pageflow.config_for(entry), options)
111
115
  end
116
+
117
+ private
118
+
119
+ def check_foreign_key_custom_attributes(custom_attributes, attributes)
120
+ custom_attributes
121
+ .each do |attribute_name, options|
122
+ file_type = options[:model]
123
+ file_id = attributes[attribute_name]
124
+
125
+ next if !file_type || file_is_used(file_type, file_id)
126
+
127
+ raise(InvalidForeignKeyCustomAttributeError,
128
+ "Custom attribute #{attribute_name} references #{file_type} #{file_id} " \
129
+ 'which is not used in this revsion')
130
+ end
131
+ end
132
+
133
+ def file_is_used(file_type, file_id)
134
+ draft.file_usages.where(file_type: file_type, file_id: file_id).exists?
135
+ end
112
136
  end
113
137
  end
@@ -11,7 +11,7 @@ module Pageflow
11
11
  foreign_key: 'entity_id',
12
12
  optional: true
13
13
 
14
- validates :user, :entity, :role, presence: true
14
+ validates :entity, :role, presence: true
15
15
  validates :user_id, uniqueness: {scope: [:entity_type, :entity_id]}
16
16
  validate :account_membership_exists, if: :on_entry?
17
17
  validates :role,
@@ -38,9 +38,8 @@ module Pageflow
38
38
  private
39
39
 
40
40
  def account_membership_exists
41
- unless user.accounts.include?(entity.account)
42
- errors[:base] << 'Entry Membership misses presupposed Membership on account of entry'
43
- end
41
+ errors[:base] << 'Entry Membership misses presupposed Membership on account of entry' if
42
+ user.present? && !user.accounts.include?(entity.account)
44
43
  end
45
44
 
46
45
  def on_entry?
@@ -103,6 +103,18 @@ module Pageflow
103
103
  self[:share_providers] || entry.theming.default_share_providers
104
104
  end
105
105
 
106
+ def author
107
+ read_attribute(:author) || Pageflow.config.default_author_meta_tag
108
+ end
109
+
110
+ def publisher
111
+ read_attribute(:publisher) || Pageflow.config.default_publisher_meta_tag
112
+ end
113
+
114
+ def keywords
115
+ read_attribute(:keywords) || Pageflow.config.default_keywords_meta_tag
116
+ end
117
+
106
118
  def active_share_providers
107
119
  share_providers.select { |_k, v| v }.keys
108
120
  end
@@ -161,16 +173,20 @@ module Pageflow
161
173
  file_usage.copy_to(revision)
162
174
  end
163
175
 
164
- Pageflow.config.revision_components.each do |model|
165
- model.all_for_revision(self).each do |record|
166
- record.copy_to(revision)
167
- end
176
+ find_revision_components.each do |revision_component|
177
+ revision_component.copy_to(revision)
168
178
  end
169
179
 
170
180
  revision.save!
171
181
  revision
172
182
  end
173
183
 
184
+ def find_revision_components
185
+ Pageflow.config.revision_components.flat_map do |model|
186
+ model.all_for_revision(self).to_a
187
+ end
188
+ end
189
+
174
190
  def self.depublish_all
175
191
  published.update_all(:published_until => Time.now)
176
192
  end
@@ -61,10 +61,14 @@ module Pageflow
61
61
  private
62
62
 
63
63
  def all
64
- placeholders_by_role
65
- .merge(defaults_by_role)
66
- .merge(from_db_by_role)
67
- .values
64
+ initial_widgets = placeholders_by_role.merge(defaults_by_role)
65
+ initial_widgets.merge(from_db_by_role) { |_role_key, old_val, new_val|
66
+ if old_val.configuration.present?
67
+ new_val.configuration = {} if new_val.configuration.nil?
68
+ new_val.configuration = old_val.configuration.merge(new_val.configuration)
69
+ end
70
+ new_val
71
+ }.values
68
72
  end
69
73
 
70
74
  def from_db_by_role
@@ -73,13 +77,15 @@ module Pageflow
73
77
 
74
78
  def defaults_by_role
75
79
  config.widget_types.defaults_by_role.each_with_object({}) do |(role, widget_type), result|
76
- result[role] = Widget.new(role: role, type_name: widget_type.name, subject: nil)
80
+ result[role] = Widget.new(role: role, type_name: widget_type.name,
81
+ subject: nil,
82
+ configuration:
83
+ config.widget_types.default_configuration(role))
77
84
  end
78
85
  end
79
86
 
80
87
  def placeholders_by_role
81
88
  return {} unless options[:include_placeholders]
82
-
83
89
  config.widget_types.roles.each_with_object({}) do |role, result|
84
90
  result[role] = Widget.new(role: role, type_name: nil, subject: nil)
85
91
  end