pageflow 12.0.4 → 12.1.0

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 (217) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +158 -374
  3. data/README.md +24 -3
  4. data/Rakefile +2 -2
  5. data/admins/pageflow/accounts.rb +30 -4
  6. data/admins/pageflow/entry.rb +59 -9
  7. data/admins/pageflow/membership.rb +57 -6
  8. data/admins/pageflow/user.rb +25 -4
  9. data/app/assets/images/pageflow/themes/default/preview.png +0 -0
  10. data/app/assets/images/pageflow/themes/default/preview_thumbnail.png +0 -0
  11. data/app/assets/javascripts/pageflow/admin/entries.js +5 -3
  12. data/app/assets/javascripts/pageflow/admin/users.js +33 -0
  13. data/app/assets/javascripts/pageflow/admin.js +4 -1
  14. data/app/assets/javascripts/pageflow/audio_context.js +28 -0
  15. data/app/assets/javascripts/pageflow/audio_player/get_media_element_method.js +5 -0
  16. data/app/assets/javascripts/pageflow/audio_player.js +2 -0
  17. data/app/assets/javascripts/pageflow/base.js +4 -22
  18. data/app/assets/javascripts/pageflow/dist/react.js +323 -242
  19. data/app/assets/javascripts/pageflow/editor/api/widget_type.js +23 -0
  20. data/app/assets/javascripts/pageflow/editor/api/widget_types.js +53 -0
  21. data/app/assets/javascripts/pageflow/editor/api.js +9 -1
  22. data/app/assets/javascripts/pageflow/editor/base.js +0 -1
  23. data/app/assets/javascripts/pageflow/editor/collections/pages_collection.js +1 -0
  24. data/app/assets/javascripts/pageflow/editor/collections/subset_collection.js +21 -1
  25. data/app/assets/javascripts/pageflow/editor/collections/themes_collection.js +13 -0
  26. data/app/assets/javascripts/pageflow/editor/collections/widgets_collection.js +23 -8
  27. data/app/assets/javascripts/pageflow/editor/controllers/sidebar_controller.js +7 -1
  28. data/app/assets/javascripts/pageflow/editor/initializers/setup_collections.js +10 -3
  29. data/app/assets/javascripts/pageflow/editor/initializers/setup_widget_types.js +5 -1
  30. data/app/assets/javascripts/pageflow/editor/initializers/stylesheet_reloading.js +8 -3
  31. data/app/assets/javascripts/pageflow/editor/models/edit_lock_container.js +1 -1
  32. data/app/assets/javascripts/pageflow/editor/models/entry.js +7 -4
  33. data/app/assets/javascripts/pageflow/editor/models/entry_configuration.js +1 -1
  34. data/app/assets/javascripts/pageflow/editor/models/file_stage.js +1 -0
  35. data/app/assets/javascripts/pageflow/editor/models/page.js +3 -1
  36. data/app/assets/javascripts/pageflow/editor/models/preview_entry_data.js +2 -2
  37. data/app/assets/javascripts/pageflow/editor/models/theme.js +25 -0
  38. data/app/assets/javascripts/pageflow/editor/models/theming.js +1 -19
  39. data/app/assets/javascripts/pageflow/editor/models/widget.js +22 -1
  40. data/app/assets/javascripts/pageflow/editor/models/widget_configuration.js +5 -0
  41. data/app/assets/javascripts/pageflow/editor/routers/sidebar_router.js +1 -0
  42. data/app/assets/javascripts/pageflow/editor/templates/change_theme_dialog.jst.ejs +23 -0
  43. data/app/assets/javascripts/pageflow/editor/templates/edit_widget.jst.ejs +1 -2
  44. data/app/assets/javascripts/pageflow/editor/templates/inputs/reference.jst.ejs +2 -2
  45. data/app/assets/javascripts/pageflow/editor/templates/static_thumbnail.jst.ejs +1 -0
  46. data/app/assets/javascripts/pageflow/editor/templates/theme_item.jst.ejs +5 -0
  47. data/app/assets/javascripts/pageflow/editor/templates/widget_item.jst.ejs +3 -0
  48. data/app/assets/javascripts/pageflow/editor/utils/stylesheet.js +23 -0
  49. data/app/assets/javascripts/pageflow/editor/views/change_theme_dialog_view.js +76 -0
  50. data/app/assets/javascripts/pageflow/editor/views/configuration_editors/groups/options.js +4 -2
  51. data/app/assets/javascripts/pageflow/editor/views/edit_meta_data_view.js +17 -4
  52. data/app/assets/javascripts/pageflow/editor/views/edit_widget_view.js +11 -21
  53. data/app/assets/javascripts/pageflow/editor/views/edit_widgets_view.js +1 -3
  54. data/app/assets/javascripts/pageflow/editor/views/file_meta_data_item_value_view.js +10 -1
  55. data/app/assets/javascripts/pageflow/editor/views/inputs/reference_input_view.js +36 -4
  56. data/app/assets/javascripts/pageflow/editor/views/inputs/theme_input_view.js +26 -0
  57. data/app/assets/javascripts/pageflow/editor/views/model_thumbnail_view.js +28 -14
  58. data/app/assets/javascripts/pageflow/editor/views/static_thumbnail_view.js +20 -0
  59. data/app/assets/javascripts/pageflow/editor/views/theme_item_view.js +41 -0
  60. data/app/assets/javascripts/pageflow/editor/views/widget_item_view.js +49 -0
  61. data/app/assets/javascripts/pageflow/history.js +7 -1
  62. data/app/assets/javascripts/pageflow/media_player/volume_fading/interval.js +65 -0
  63. data/app/assets/javascripts/pageflow/media_player/volume_fading/noop.js +5 -0
  64. data/app/assets/javascripts/pageflow/media_player/volume_fading/web_audio.js +109 -0
  65. data/app/assets/javascripts/pageflow/media_player/volume_fading.js +14 -65
  66. data/app/assets/javascripts/pageflow/slideshow/dom_order_scroll_navigator.js +5 -2
  67. data/app/assets/javascripts/pageflow/slideshow.js +19 -8
  68. data/app/assets/javascripts/pageflow/ui/views/configuration_editor_view.js +4 -0
  69. data/app/assets/javascripts/pageflow/ui/views/inputs/text_area_input_view.js +10 -5
  70. data/app/assets/javascripts/pageflow/ui/views/inputs/text_input_view.js +4 -48
  71. data/app/assets/javascripts/pageflow/ui/views/mixins/input_with_placeholder_text.js +58 -0
  72. data/app/assets/javascripts/pageflow/vendor.js +16 -0
  73. data/app/assets/javascripts/pageflow/video_player/get_media_element_method.js +6 -0
  74. data/app/assets/javascripts/pageflow/video_player.js +2 -0
  75. data/app/assets/stylesheets/pageflow/admin/entries.scss +1 -1
  76. data/app/assets/stylesheets/pageflow/admin/quotas.scss +1 -1
  77. data/app/assets/stylesheets/pageflow/admin.scss +2 -0
  78. data/app/assets/stylesheets/pageflow/base.scss +0 -1
  79. data/app/assets/stylesheets/pageflow/editor/base.scss +2 -0
  80. data/app/assets/stylesheets/pageflow/editor/change_theme.scss +114 -0
  81. data/app/assets/stylesheets/pageflow/editor/static_thumbnails.scss +4 -0
  82. data/app/assets/stylesheets/pageflow/editor/widgets.scss +26 -2
  83. data/app/assets/stylesheets/pageflow/page.scss +1 -14
  84. data/app/assets/stylesheets/pageflow/themes/default/indicators.scss +12 -0
  85. data/app/assets/stylesheets/pageflow/themes/default/loading_spinner.scss +11 -0
  86. data/app/assets/stylesheets/pageflow/themes/default/logo/alignment.scss +27 -0
  87. data/app/assets/stylesheets/pageflow/themes/default/logo/variant/background_image.scss +20 -2
  88. data/app/assets/stylesheets/pageflow/themes/default/logo/variant/watermark.scss +4 -1
  89. data/app/assets/stylesheets/pageflow/themes/default/page/line_lengths.scss +113 -0
  90. data/app/assets/stylesheets/pageflow/themes/default/page.scss +1 -0
  91. data/app/controllers/pageflow/editor/widgets_controller.rb +15 -1
  92. data/app/controllers/pageflow/entries_controller.rb +1 -1
  93. data/app/helpers/pageflow/admin/entries_helper.rb +0 -9
  94. data/app/helpers/pageflow/admin/memberships_helper.rb +43 -123
  95. data/app/helpers/pageflow/admin/users_helper.rb +15 -0
  96. data/app/helpers/pageflow/entries_helper.rb +3 -1
  97. data/app/helpers/pageflow/entry_json_seed_helper.rb +9 -3
  98. data/app/helpers/pageflow/public_i18n_helper.rb +2 -2
  99. data/app/helpers/pageflow/quota_helper.rb +2 -2
  100. data/app/helpers/pageflow/social_share_helper.rb +3 -2
  101. data/app/helpers/pageflow/themes_helper.rb +11 -3
  102. data/app/helpers/pageflow/widgets_helper.rb +10 -2
  103. data/app/jobs/pageflow/prune_auto_snapshots_job.rb +9 -0
  104. data/app/mailers/pageflow/user_mailer.rb +11 -1
  105. data/app/models/concerns/pageflow/feature_target.rb +1 -1
  106. data/app/models/concerns/pageflow/hosted_file.rb +9 -0
  107. data/app/models/concerns/pageflow/output_source.rb +2 -1
  108. data/app/models/concerns/pageflow/serialization_blacklist.rb +19 -0
  109. data/app/models/concerns/pageflow/serialized_configuration.rb +17 -0
  110. data/app/models/concerns/pageflow/theme_referencer.rb +23 -0
  111. data/app/models/pageflow/account.rb +6 -1
  112. data/app/models/pageflow/account_member_query.rb +6 -12
  113. data/app/models/pageflow/account_role_query.rb +68 -0
  114. data/app/models/pageflow/application_query.rb +13 -0
  115. data/app/models/pageflow/application_record.rb +5 -0
  116. data/app/models/pageflow/audio_file.rb +1 -1
  117. data/app/models/pageflow/auto_snapshot_pruning.rb +26 -0
  118. data/app/models/pageflow/chapter.rb +4 -8
  119. data/app/models/pageflow/draft_entry.rb +2 -1
  120. data/app/models/pageflow/edit_lock.rb +11 -5
  121. data/app/models/pageflow/encoding_confirmation.rb +2 -1
  122. data/app/models/pageflow/entry.rb +13 -2
  123. data/app/models/pageflow/entry_publication.rb +2 -0
  124. data/app/models/pageflow/entry_role_query.rb +24 -9
  125. data/app/models/pageflow/entry_title_or_account_name_query.rb +33 -0
  126. data/app/models/pageflow/file_usage.rb +3 -7
  127. data/app/models/pageflow/folder.rb +1 -1
  128. data/app/models/pageflow/home_button.rb +1 -1
  129. data/app/models/pageflow/image_file.rb +22 -4
  130. data/app/models/pageflow/invitation_form.rb +10 -4
  131. data/app/models/pageflow/managed_user_query.rb +44 -0
  132. data/app/models/pageflow/membership.rb +1 -1
  133. data/app/models/pageflow/overview_button.rb +3 -4
  134. data/app/models/pageflow/page.rb +3 -7
  135. data/app/models/pageflow/potential_memberships.rb +112 -0
  136. data/app/models/pageflow/published_entry.rb +2 -1
  137. data/app/models/pageflow/revision.rb +13 -4
  138. data/app/models/pageflow/storyline.rb +3 -4
  139. data/app/models/pageflow/text_track_file.rb +1 -1
  140. data/app/models/pageflow/theming.rb +15 -10
  141. data/app/models/pageflow/url_template.rb +8 -2
  142. data/app/models/pageflow/user_name_query.rb +30 -0
  143. data/app/models/pageflow/video_file.rb +5 -1
  144. data/app/models/pageflow/widget.rb +3 -1
  145. data/app/models/pageflow/zencoder_attachment.rb +16 -5
  146. data/app/policies/pageflow/account_policy.rb +31 -61
  147. data/app/policies/pageflow/application_policy.rb +6 -0
  148. data/app/policies/pageflow/entry_policy.rb +11 -3
  149. data/app/policies/pageflow/membership_policy.rb +1 -2
  150. data/app/policies/pageflow/user_policy.rb +20 -1
  151. data/app/views/admin/accounts/_form.html.erb +4 -4
  152. data/app/views/admin/accounts/_theming_defaults_inline_help.html.erb +5 -0
  153. data/app/views/admin/memberships/_form.html.erb +9 -14
  154. data/app/views/admin/memberships/_role_hint.html.arb +1 -1
  155. data/app/views/admin/users/invitation.html.erb +18 -9
  156. data/app/views/admin/users/me.html.erb +2 -2
  157. data/app/views/admin/users/quota_state.html.erb +1 -0
  158. data/app/views/components/pageflow/admin/add_membership_button.rb +81 -0
  159. data/app/views/components/pageflow/admin/members_tab.rb +6 -4
  160. data/app/views/components/pageflow/admin/revisions_tab.rb +16 -4
  161. data/app/views/components/pageflow/admin/user_accounts_tab.rb +8 -2
  162. data/app/views/components/pageflow/admin/user_entries_tab.rb +6 -2
  163. data/app/views/components/pageflow/admin/users_tab.rb +9 -5
  164. data/app/views/layouts/pageflow/application.html.erb +2 -1
  165. data/app/views/pageflow/admin/users/_quota_exhausted.html.erb +1 -0
  166. data/app/views/pageflow/admin/users/_quota_state.html.erb +7 -0
  167. data/app/views/pageflow/config/_editor_seeds.json.jbuilder +2 -0
  168. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +1 -0
  169. data/app/views/pageflow/editor/entries/seed.json.erb +3 -1
  170. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +1 -1
  171. data/app/views/pageflow/editor/themings/_theming.json.jbuilder +0 -7
  172. data/app/views/pageflow/editor/widgets/_widget.json.jbuilder +1 -1
  173. data/app/views/pageflow/entries/_entry.html.erb +5 -5
  174. data/app/views/pageflow/entries/edit.html.erb +1 -1
  175. data/app/views/pageflow/entries/show.html.erb +1 -1
  176. data/app/views/pageflow/entry_json_seed/_entry.json.jbuilder +1 -0
  177. data/app/views/pageflow/themes/_theme.json.jbuilder +13 -0
  178. data/config/initializers/admin_resource_tabs.rb +19 -6
  179. data/config/initializers/features.rb +1 -0
  180. data/config/locales/de.yml +26 -3
  181. data/config/locales/en.yml +26 -3
  182. data/db/migrate/20170201074328_add_configuration_to_widgets.rb +5 -0
  183. data/db/migrate/20170222124848_update_video_file_output_presences.rb +1 -1
  184. data/db/migrate/20170315130000_add_theme_name_to_revisions.rb +12 -0
  185. data/db/migrate/20170912165050_reset_copied_snapshot_type.rb +24 -0
  186. data/lib/generators/pageflow/routes/routes_generator.rb +11 -1
  187. data/lib/generators/pageflow/seeds/seeds_generator.rb +5 -0
  188. data/lib/generators/pageflow/seeds/templates/seeds.rb +5 -3
  189. data/lib/generators/pageflow/theme/templates/preview.png +0 -0
  190. data/lib/generators/pageflow/theme/templates/preview_thumbnail.png +0 -0
  191. data/lib/generators/pageflow/theme/theme_generator.rb +3 -0
  192. data/lib/pageflow/ability_mixin.rb +27 -6
  193. data/lib/pageflow/active_admin_can_can_fix.rb +34 -0
  194. data/lib/pageflow/configuration/permissions.rb +27 -0
  195. data/lib/pageflow/configuration.rb +28 -0
  196. data/lib/pageflow/engine.rb +25 -19
  197. data/lib/pageflow/images/palette.png +0 -0
  198. data/lib/pageflow/seeds.rb +1 -1
  199. data/lib/pageflow/theme.rb +8 -0
  200. data/lib/pageflow/version.rb +1 -1
  201. data/lib/pageflow/widget_type.rb +13 -0
  202. data/lib/pageflow/zencoder_video_output_definition.rb +16 -16
  203. data/lib/tasks/pageflow_tasks.rake +14 -0
  204. data/spec/factories/entries.rb +4 -0
  205. data/spec/factories/revisions.rb +10 -0
  206. data/spec/factories/users.rb +6 -0
  207. data/spec/factories/video_files.rb +4 -0
  208. data/vendor/assets/javascripts/audio5.min.js +3 -0
  209. metadata +78 -15
  210. data/app/assets/javascripts/pageflow/editor/models/mixins/widget_subject.js +0 -37
  211. data/app/assets/javascripts/pageflow/editor/utils/reload_stylesheet.js +0 -9
  212. data/app/assets/stylesheets/pageflow/text_variants.scss +0 -24
  213. data/app/views/admin/accounts/_widgets_inline_help.html.erb +0 -5
  214. data/app/views/admin/memberships/_entity_account_input.html.erb +0 -5
  215. data/app/views/admin/memberships/_entity_entry_input.html.erb +0 -5
  216. data/app/views/admin/users/_quota_exhausted.html.erb +0 -1
  217. data/app/views/components/pageflow/admin/add_membership_button_if_needed.rb +0 -62
@@ -3,7 +3,17 @@ module Pageflow
3
3
  include Resque::Mailer
4
4
 
5
5
  def invitation(options)
6
- options.symbolize_keys!
6
+ # Different versions of resque_mailer either pass:
7
+ #
8
+ # - Hash with string keys (<= 2.4.0)
9
+ # - Hash with symbol keys (2.4.1)
10
+ # - Hash with both string and symbol keys (2.4.2)
11
+ # - HashWithIndifferentAccess (2.4.3)
12
+ #
13
+ # Symbolize keys to support 2.4.1, but do not use bang version
14
+ # (i.e. `smbolize_keys!`) since that is not supported by
15
+ # HashWithIndifferentAccess.
16
+ options = options.symbolize_keys
7
17
 
8
18
  @user = User.find(options[:user_id])
9
19
  @password_token = options[:password_token]
@@ -49,7 +49,7 @@ module Pageflow
49
49
  end
50
50
 
51
51
  def features_configuration
52
- super || {}
52
+ self[:features_configuration] || {}
53
53
  end
54
54
  end
55
55
  end
@@ -71,6 +71,15 @@ module Pageflow
71
71
  url
72
72
  end
73
73
 
74
+ def cache_key
75
+ # Ensure the cache key changes when the state changes. There are
76
+ # cases during processing where the state is updated multiple
77
+ # times in a single second. Since `cache_key` relies on
78
+ # `updated_at`, which only is acurate to the second, we need to
79
+ # prevent caching outdated information.
80
+ "#{super}-#{state}"
81
+ end
82
+
74
83
  # @deprecated Write a migration instead
75
84
  def self.columns(t)
76
85
  t.belongs_to(:entry, index: true)
@@ -40,7 +40,8 @@ module Pageflow
40
40
  end
41
41
 
42
42
  def output_presences
43
- (super || {}).merge(externally_generated_output_presences)
43
+ output_presences = self[:output_presences] || {}
44
+ output_presences.merge(externally_generated_output_presences)
44
45
  end
45
46
 
46
47
  def externally_generated_outputs
@@ -0,0 +1,19 @@
1
+ module Pageflow
2
+ # @api private
3
+ module SerializationBlacklist
4
+ def serializable_hash(options = nil)
5
+ options ||= {}
6
+
7
+ options[:except] = Array(options[:except])
8
+ options[:except].concat(blacklist_for_serialization)
9
+
10
+ super(options)
11
+ end
12
+
13
+ private
14
+
15
+ def blacklist_for_serialization
16
+ []
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Pageflow
2
+ # Add a +configuration+ attribute.
3
+ # This is a hash serialized as JSON.
4
+ # It contains everything related to the object, which includes its text
5
+ # content such as title and body.
6
+ module SerializedConfiguration
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ serialize :configuration, JSON
11
+ end
12
+
13
+ def configuration
14
+ self[:configuration] || {}
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module Pageflow
2
+ module ThemeReferencer
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ validates_inclusion_of(:theme_name, in: :available_theme_names)
7
+ end
8
+
9
+ def theme
10
+ available_themes.get(theme_name)
11
+ end
12
+
13
+ private
14
+
15
+ def available_theme_names
16
+ available_themes.names
17
+ end
18
+
19
+ def available_themes
20
+ raise NotImplementedError
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,7 @@
1
1
  module Pageflow
2
- class Account < ActiveRecord::Base
2
+ class Account < ApplicationRecord
3
3
  include FeatureTarget
4
+ include SerializationBlacklist
4
5
 
5
6
  has_many :entries, dependent: :restrict_with_exception
6
7
  has_many :folders, dependent: :destroy
@@ -22,5 +23,9 @@ module Pageflow
22
23
  theming.account = self
23
24
  end
24
25
  end
26
+
27
+ def blacklist_for_serialization
28
+ [:features_configuration]
29
+ end
25
30
  end
26
31
  end
@@ -1,8 +1,9 @@
1
1
  module Pageflow
2
- class AccountMemberQuery
3
- class Scope
2
+ # Query accounts for members, e.g. based on role
3
+ class AccountMemberQuery < ApplicationQuery
4
+ class Scope < ApplicationQuery::Scope
4
5
  # Account whose members we scope
5
- # @return {Pageflow::Account}
6
+ # @return [Pageflow::Account]
6
7
  attr_reader :account
7
8
 
8
9
  # Base scope which is further scoped according to account role
@@ -12,7 +13,7 @@ module Pageflow
12
13
  # Create scope that can limit base scope to account members at
13
14
  # or above a given role
14
15
  #
15
- # @param {Pageflow::Account} account
16
+ # @param [Pageflow::Account] account
16
17
  # Required. Membership account to check.
17
18
  # @param [ActiveRecord::Relation<User>] scope
18
19
  # Optional. Membership entity to check.
@@ -24,7 +25,7 @@ module Pageflow
24
25
  # Scope to those members from scope on account who have at least
25
26
  # role
26
27
  #
27
- # @param {String} role
28
+ # @param [String] role
28
29
  # Required. Minimum role that we compare against.
29
30
  # @return [ActiveRecord::Relation<User>]
30
31
  def with_role_at_least(role)
@@ -34,7 +35,6 @@ module Pageflow
34
35
 
35
36
  private
36
37
 
37
- # @api private
38
38
  def memberships_for_account_with_at_least_role(role)
39
39
  options = {roles: Roles.at_least(role), account_id: account.id}
40
40
 
@@ -48,15 +48,9 @@ module Pageflow
48
48
  SQL
49
49
  end
50
50
 
51
- # @api private
52
51
  def membership_is_present
53
52
  'pageflow_account_memberships.entity_id IS NOT NULL'
54
53
  end
55
-
56
- # @api private
57
- def sanitize_sql(sql, interpolations)
58
- ActiveRecord::Base.send(:sanitize_sql_array, [sql, interpolations])
59
- end
60
54
  end
61
55
  end
62
56
  end
@@ -0,0 +1,68 @@
1
+ module Pageflow
2
+ # Query users for their role on accounts based on role
3
+ class AccountRoleQuery < ApplicationQuery
4
+ # Query for accounts where a user has a given role.
5
+ class Scope < Scope
6
+ # Create query scope.
7
+ #
8
+ # @param [User] user
9
+ # User to query roles for.
10
+ # @param [ActiveRecord::Relation] scope
11
+ # Scope of all accounts to filter from.
12
+ def initialize(user, scope)
13
+ @user = user
14
+ @scope = scope
15
+ end
16
+
17
+ # Find all accounts where user has at least given role.
18
+ #
19
+ # @param [String] role
20
+ # Name of role.
21
+ # @return [ActiveRecord::Relation]
22
+ def with_role_at_least(role)
23
+ scope.joins(with_membership_for_account(role))
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :user, :scope
29
+
30
+ def with_membership_for_account(role)
31
+ sanitize_sql(<<-SQL, user_id: user.id, roles: Roles.at_least(role))
32
+ INNER JOIN pageflow_memberships ON
33
+ pageflow_memberships.user_id = :user_id AND
34
+ pageflow_memberships.entity_id = pageflow_accounts.id AND
35
+ pageflow_memberships.entity_type = "Pageflow::Account" AND
36
+ pageflow_memberships.role IN (:roles)
37
+ SQL
38
+ end
39
+ end
40
+
41
+ # Create query that can be used for role comparisons
42
+ #
43
+ # @param [User] user
44
+ # Required. Membership user to check.
45
+ # @param [Pageflow::Account] account
46
+ # Required. Membership entity to check.
47
+ def initialize(user, account)
48
+ @user = user
49
+ @account = account
50
+ end
51
+
52
+ # Return true if there is a membership with at least role for
53
+ # user/account
54
+ #
55
+ # @param [String] role
56
+ # Required. Minimum role that we compare against.
57
+ # @return [Boolean]
58
+ def has_at_least_role?(role)
59
+ @user
60
+ .memberships
61
+ .where(role: Roles.at_least(role))
62
+ .where('(entity_id = :account_id AND '\
63
+ "entity_type = 'Pageflow::Account')",
64
+ account_id: @account.id)
65
+ .any?
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,13 @@
1
+ module Pageflow
2
+ # Abstraction layer for Pageflow's query interface
3
+ class ApplicationQuery
4
+ class Scope
5
+ protected
6
+
7
+ # @api private
8
+ def sanitize_sql(sql, interpolations)
9
+ ActiveRecord::Base.send(:sanitize_sql_array, [sql, interpolations])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Pageflow
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class AudioFile < ActiveRecord::Base
2
+ class AudioFile < ApplicationRecord
3
3
  include HostedFile
4
4
  include EncodedFileStateMachine
5
5
 
@@ -0,0 +1,26 @@
1
+ module Pageflow
2
+ # @api private
3
+ module AutoSnapshotPruning
4
+ extend self
5
+
6
+ def dirty_entry_ids(options)
7
+ Revision
8
+ .auto_snapshots
9
+ .where('created_at < ?', options.fetch(:created_before))
10
+ .select('COUNT(*) as revisions_count, entry_id')
11
+ .group('entry_id')
12
+ .having('revisions_count > ?', options.fetch(:keep_count))
13
+ .map(&:entry_id)
14
+ end
15
+
16
+ def prune(entry, options)
17
+ entry
18
+ .revisions
19
+ .auto_snapshots
20
+ .where('created_at < ?', options[:created_before])
21
+ .order('created_at DESC')
22
+ .offset(options[:keep_count])
23
+ .each(&:destroy)
24
+ end
25
+ end
26
+ end
@@ -1,16 +1,12 @@
1
1
  module Pageflow
2
- class Chapter < ActiveRecord::Base
2
+ class Chapter < ApplicationRecord
3
+ include SerializedConfiguration
4
+
3
5
  belongs_to :storyline, touch: true
4
- has_many :pages, -> { order('position ASC') }
6
+ has_many :pages, -> { order('position ASC') }, dependent: :destroy
5
7
 
6
8
  delegate :entry, to: :storyline
7
9
 
8
- serialize :configuration, JSON
9
-
10
- def configuration
11
- super || {}
12
- end
13
-
14
10
  def copy_to(storyline)
15
11
  chapter = dup
16
12
  storyline.chapters << chapter
@@ -22,6 +22,7 @@ module Pageflow
22
22
  :image_files, :video_files, :audio_files,
23
23
  :locale,
24
24
  :author, :publisher, :keywords,
25
+ :theme,
25
26
  :to => :draft)
26
27
 
27
28
  def initialize(entry, draft = nil)
@@ -99,7 +100,7 @@ module Pageflow
99
100
  end
100
101
 
101
102
  def overview_button
102
- OverviewButton.new(draft, theming)
103
+ OverviewButton.new(draft)
103
104
  end
104
105
 
105
106
  def resolve_widgets(options = {})
@@ -1,8 +1,9 @@
1
1
  module Pageflow
2
- class EditLock < ActiveRecord::Base
3
- TIME_TO_LIVE = 10.seconds
4
-
5
- scope :active, ->(time = Time.now - TIME_TO_LIVE) { where('pageflow_edit_locks.updated_at >= ?', time) }
2
+ class EditLock < ApplicationRecord
3
+ scope :active, (lambda do
4
+ time = Time.now - EditLock.time_to_live
5
+ where('pageflow_edit_locks.updated_at >= ?', time)
6
+ end)
6
7
 
7
8
  class Error < RuntimeError
8
9
  def code
@@ -24,6 +25,11 @@ module Pageflow
24
25
  belongs_to :user
25
26
  belongs_to :entry, :inverse_of => :edit_lock
26
27
 
28
+ def self.time_to_live
29
+ timer_tolerance = 2.3
30
+ Pageflow.config.edit_lock_polling_interval * timer_tolerance
31
+ end
32
+
27
33
  def held_by?(user)
28
34
  self.user == user
29
35
  end
@@ -33,7 +39,7 @@ module Pageflow
33
39
  end
34
40
 
35
41
  def timed_out?
36
- Time.now > self.updated_at + TIME_TO_LIVE
42
+ Time.now > updated_at + EditLock.time_to_live
37
43
  end
38
44
 
39
45
  def acquire(current_user, options = {})
@@ -3,13 +3,14 @@ module Pageflow
3
3
  class QuotaExceededError < RuntimeError
4
4
  end
5
5
 
6
- attr_reader :entry, :attributes, :encoding_quota, :user
6
+ attr_reader :entry, :attributes, :encoding_quota, :user, :account
7
7
 
8
8
  def initialize(entry, attributes, encoding_quota, user)
9
9
  @entry = entry
10
10
  @attributes = attributes
11
11
  @encoding_quota = encoding_quota
12
12
  @user = user
13
+ @account = entry.account
13
14
  end
14
15
 
15
16
  def exceeding?
@@ -1,10 +1,11 @@
1
1
  module Pageflow
2
- class Entry < ActiveRecord::Base
2
+ class Entry < ApplicationRecord
3
3
  class PasswordMissingError < StandardError
4
4
  end
5
5
 
6
6
  include FeatureTarget
7
7
  include EntryPublicationStates
8
+ include SerializationBlacklist
8
9
 
9
10
  extend FriendlyId
10
11
  friendly_id :slug_candidates, :use => [:finders, :slugged]
@@ -38,10 +39,15 @@ module Pageflow
38
39
 
39
40
  scope :editing, -> { joins(:edit_lock).merge(Pageflow::EditLock.active) }
40
41
 
42
+ scope(:include_account_name,
43
+ lambda do
44
+ joins(:account).select('pageflow_entries.*, pageflow_accounts.name AS account_name')
45
+ end)
46
+
41
47
  attr_accessor :skip_draft_creation
42
48
 
43
49
  after_create unless: :skip_draft_creation do
44
- create_draft!(home_button_enabled: theming.home_button_enabled_by_default)
50
+ create_draft!
45
51
  draft.storylines.create!(configuration: {main: true})
46
52
  theming.copy_defaults_to(draft)
47
53
  end
@@ -87,6 +93,7 @@ module Pageflow
87
93
  restored_revision.copy do |revision|
88
94
  revision.restored_from = restored_revision
89
95
  revision.frozen_at = nil
96
+ revision.snapshot_type = nil
90
97
  revision.published_at = nil
91
98
  revision.published_until = nil
92
99
  revision.password_protected = nil
@@ -109,6 +116,10 @@ module Pageflow
109
116
  [:with_publication_state, :published]
110
117
  end
111
118
 
119
+ def blacklist_for_serialization
120
+ [:password_digest, :features_configuration]
121
+ end
122
+
112
123
  private
113
124
 
114
125
  def folder_belongs_to_same_account
@@ -16,6 +16,8 @@ module Pageflow
16
16
  def save!
17
17
  assumed_quota.verify_not_exceeded!
18
18
  entry.publish(attributes.merge(creator: user))
19
+
20
+ Pageflow.config.hooks.invoke(:entry_published, entry: entry)
19
21
  end
20
22
 
21
23
  private
@@ -1,11 +1,12 @@
1
1
  module Pageflow
2
- class EntryRoleQuery
3
- class Scope
2
+ class EntryRoleQuery < ApplicationQuery
3
+ class Scope < Scope
4
4
  attr_reader :user, :scope
5
5
 
6
- def initialize(user, scope)
6
+ def initialize(user, scope, table_alias_prefix: nil)
7
7
  @user = user
8
8
  @scope = scope
9
+ @table_alias_prefix = table_alias_prefix
9
10
  end
10
11
 
11
12
  def with_role_at_least(role)
@@ -15,10 +16,16 @@ module Pageflow
15
16
  .where(either_membership_is_present)
16
17
  end
17
18
 
19
+ def with_account_role_at_least(role)
20
+ scope
21
+ .joins(memberships_for_account_of_entries_with_at_least_role(role))
22
+ .where(entry_account_membership_is_present)
23
+ end
24
+
18
25
  private
19
26
 
20
27
  def memberships_for_entries_with_at_least_role(role)
21
- join_memberships(table_alias: 'pageflow_entry_memberships',
28
+ join_memberships(table_alias: table_alias_for('entry'),
22
29
  user_id: user.id,
23
30
  roles: Roles.at_least(role),
24
31
  entity_id_column: 'pageflow_entries.id',
@@ -26,7 +33,7 @@ module Pageflow
26
33
  end
27
34
 
28
35
  def memberships_for_account_of_entries_with_at_least_role(role)
29
- join_memberships(table_alias: 'pageflow_entry_account_memberships',
36
+ join_memberships(table_alias: table_alias_for('entry_account'),
30
37
  user_id: user.id,
31
38
  roles: Roles.at_least(role),
32
39
  entity_id_column: 'pageflow_entries.account_id',
@@ -47,12 +54,20 @@ module Pageflow
47
54
  end
48
55
 
49
56
  def either_membership_is_present
50
- 'pageflow_entry_memberships.entity_id IS NOT NULL OR ' \
51
- 'pageflow_entry_account_memberships.entity_id IS NOT NULL'
57
+ [entry_membership_is_present,
58
+ entry_account_membership_is_present].join(' OR ')
59
+ end
60
+
61
+ def entry_membership_is_present
62
+ "#{table_alias_for(:entry)}.entity_id IS NOT NULL"
63
+ end
64
+
65
+ def entry_account_membership_is_present
66
+ "#{table_alias_for(:entry_account)}.entity_id IS NOT NULL"
52
67
  end
53
68
 
54
- def sanitize_sql(sql, interpolations)
55
- ActiveRecord::Base.send(:sanitize_sql_array, [sql, interpolations])
69
+ def table_alias_for(type)
70
+ [@table_alias_prefix, 'pageflow', type, 'memberships'].compact.join('_')
56
71
  end
57
72
  end
58
73
 
@@ -0,0 +1,33 @@
1
+ module Pageflow
2
+ # @api private
3
+ class EntryTitleOrAccountNameQuery < ApplicationQuery
4
+ class Scope < Scope
5
+ def initialize(term, scope)
6
+ @term = term
7
+ @scope = scope
8
+ end
9
+
10
+ def resolve
11
+ scope
12
+ .joins(:account)
13
+ .references(:pageflow_accounts)
14
+ .where(word_conditions(term))
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :term, :scope
20
+
21
+ def word_conditions(term)
22
+ term.split(' ').map { |word|
23
+ word_condition(word)
24
+ }.join(' AND ')
25
+ end
26
+
27
+ def word_condition(word)
28
+ sanitize_sql('(pageflow_entries.title LIKE :word OR pageflow_accounts.name LIKE :word)',
29
+ word: "%#{word}%")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,14 +1,10 @@
1
1
  module Pageflow
2
- class FileUsage < ActiveRecord::Base
2
+ class FileUsage < ApplicationRecord
3
+ include SerializedConfiguration
4
+
3
5
  belongs_to :revision
4
6
  belongs_to :file, polymorphic: true
5
7
 
6
- serialize :configuration, JSON
7
-
8
- def configuration
9
- super || {}
10
- end
11
-
12
8
  def copy_to(revision)
13
9
  revision.file_usages << dup
14
10
  end
@@ -1,5 +1,5 @@
1
1
  module Pageflow
2
- class Folder < ActiveRecord::Base
2
+ class Folder < ApplicationRecord
3
3
  belongs_to :account
4
4
  has_many :entries
5
5
 
@@ -13,7 +13,7 @@ module Pageflow
13
13
 
14
14
  def enabled?
15
15
  revision.home_button_enabled? &&
16
- theming.theme.has_home_button? &&
16
+ revision.theme.has_home_button? &&
17
17
  url.present?
18
18
  end
19
19
 
@@ -1,27 +1,38 @@
1
1
  module Pageflow
2
- class ImageFile < ActiveRecord::Base
2
+ class ImageFile < ApplicationRecord
3
3
  include ImageFileStateMachine
4
4
  include UploadedFile
5
5
 
6
6
  STYLES = lambda do |attachment|
7
7
  panorama_format = File.extname(attachment.original_filename) == '.png' ? :PNG : :JPG
8
8
 
9
- Pageflow.config.thumbnail_styles
9
+ Pageflow
10
+ .config.thumbnail_styles
10
11
  .merge(print: ['300x300>', :JPG],
11
12
  medium: ['1024x1024>', :JPG],
12
13
  large: ['1920x1920>', :JPG],
13
14
  ultra: ['3840x3840>', :JPG],
14
15
  panorama_medium: ['1024x1024^', panorama_format],
15
- panorama_large: ['1920x1080^', panorama_format])
16
+ panorama_large: ['1920x1080^', panorama_format],
17
+ panorama_mask: ['1920x1080^', panorama_format])
16
18
  end
17
19
 
20
+ SOURCE_FILE_OPTIONS = {
21
+ # Prevent anti aliasing. Otherwise, when processing color map
22
+ # images, borders between areas are blurred.
23
+ panorama_mask: '-filter point'
24
+ }.freeze
25
+
26
+ palette_path = File.expand_path('../../../../lib/pageflow/images/palette.png', __FILE__)
27
+
18
28
  CONVERT_OPTIONS = {
19
29
  print: '-quality 10 -interlace Plane',
20
30
  medium: '-quality 70 -interlace Plane',
21
31
  large: '-quality 70 -interlace Plane',
22
32
  ultra: '-quality 90 -interlace Plane',
23
33
  panorama_medium: '-quality 70 -interlace Plane',
24
- panorama_large: '-quality 70 -interlace Plane'
34
+ panorama_large: '-quality 70 -interlace Plane',
35
+ panorama_mask: "-quality 70 -interlace Plane -dither None -colors 64 -remap #{palette_path}"
25
36
  }.freeze
26
37
 
27
38
  has_attached_file(:unprocessed_attachment,
@@ -31,6 +42,7 @@ module Pageflow
31
42
  Pageflow.config.paperclip_s3_default_options
32
43
  .merge(default_url: ':pageflow_placeholder',
33
44
  styles: STYLES,
45
+ source_file_options: SOURCE_FILE_OPTIONS,
34
46
  convert_options: CONVERT_OPTIONS))
35
47
 
36
48
  do_not_validate_attachment_file_type(:unprocessed_attachment)
@@ -72,6 +84,12 @@ module Pageflow
72
84
  end
73
85
  end
74
86
 
87
+ def panorama_mask_url
88
+ if processed_attachment.present?
89
+ attachment.url(:panorama_mask)
90
+ end
91
+ end
92
+
75
93
  private
76
94
 
77
95
  def save_image_dimensions