From 5c972738db1686748f65671a98ff7b2544db73ea Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 9 Aug 2021 01:29:58 -0700 Subject: [PATCH] WIP migrating to wide ContentPage usage --- app/authorizers/content_page_authorizer.rb | 48 ++++++++++++++++++++++ app/controllers/application_controller.rb | 1 + app/controllers/content_controller.rb | 44 ++++++++++++-------- app/models/concerns/has_content.rb | 32 ++++++++++++++- app/models/page_types/content_page.rb | 15 +++++++ app/services/permission_service.rb | 6 ++- app/views/content/list/_cards.html.erb | 13 ++---- 7 files changed, 131 insertions(+), 28 deletions(-) create mode 100644 app/authorizers/content_page_authorizer.rb diff --git a/app/authorizers/content_page_authorizer.rb b/app/authorizers/content_page_authorizer.rb new file mode 100644 index 00000000..3bc57016 --- /dev/null +++ b/app/authorizers/content_page_authorizer.rb @@ -0,0 +1,48 @@ +class ContentPageAuthorizer < CoreContentAuthorizer + def self.creatable_by?(user) + return false unless user.present? + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) + + if resource.page_type == 'Universe' + return true if PermissionService.user_has_fewer_owned_universes_than_plan_limit?(user: user) + + else + is_premium_page = Rails.application.config.content_types[:premium].include?(resource.page_type) + return true if !is_premium_page + return true if is_premium_page && PermissionService.user_is_on_premium_plan?(user: user) + end + + return false + end + + def readable_by?(user) + return true if PermissionService.content_is_public?(content: resource) + return true if PermissionService.user_owns_content?(user: user, content: resource) + + if resource.page_type == 'Universe' + return true if PermissionService.user_can_contribute_to_universe?(user: user, universe: resource) + else + return true if PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource) + end + + return false + end + + def updatable_by?(user) + return true if PermissionService.user_owns_content?(user: user, content: resource) + + if resource.page_type == 'Universe' + return true if PermissionService.user_can_contribute_to_universe?(user: user, universe: resource) + else + return true if PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource) + end + + return false + end + + def deletable_by?(user) + [ + PermissionService.user_owns_content?(user: user, content: resource) + ].any? + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2207a0a5..cc98399c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -177,6 +177,7 @@ class ApplicationController < ActionController::Base end filtered_fields = ContentPage.polymorphic_content_fields.map(&:to_s) + filtered_fields.push 'universe_id' unless page_type == Universe.name pages_to_add.each do |page_data| filtered_page_data = page_data.attributes.slice(*filtered_fields) @linkables_raw[page_type].push ContentPage.new(filtered_page_data) diff --git a/app/controllers/content_controller.rb b/app/controllers/content_controller.rb index cdfab67f..d74d44df 100644 --- a/app/controllers/content_controller.rb +++ b/app/controllers/content_controller.rb @@ -21,33 +21,45 @@ class ContentController < ApplicationController before_action :set_sidenav_expansion, except: [:api_sort] def index + cache_linkable_content_for_each_content_type + @content_type_class = content_type_from_controller(self.class) pluralized_content_name = @content_type_class.name.downcase.pluralize @page_title = "My #{pluralized_content_name}" # Create the default fields for this user if they don't have any already + # TODO: uh, this probably doesn't belong here! @content_type_class.attribute_categories(current_user) - if @universe_scope.present? && @content_type_class != Universe - @content = @universe_scope.send(pluralized_content_name) - .includes(:page_tags, :image_uploads) - .unarchived + # Linkables cache is already scoped per-universe, includes contributor pages + @content = @linkables_raw.fetch(@content_type_class.name, []) - @show_scope_notice = true - else - @content = ( - current_user.send(pluralized_content_name).unarchived.includes(:page_tags, :image_uploads) + - current_user.send("contributable_#{pluralized_content_name}").unarchived.includes(:page_tags, :image_uploads) - ) + @show_scope_notice = @universe_scope.present? && content_type_class != Universe - if @content_type_class != Universe - my_universe_ids = current_user.universes.pluck(:id) - @content.concat(@content_type_class.where(universe_id: my_universe_ids).unarchived) - end - end + # if @universe_scope.present? && @content_type_class != Universe + # # Linkables cache is already scoped per-universe - @content = @content.to_a.flatten.uniq + # @content = @current_user_content.fetch(@content_type_class.name, []) + # .select { |page| page.universe_id == @universe_scope.id } + + # @content = @universe_scope.send(pluralized_content_name) + # .includes(:page_tags, :image_uploads) + # .unarchived + + # @show_scope_notice = true + # else + # @content = ( + # current_user.send(pluralized_content_name).unarchived.includes(:page_tags, :image_uploads) + + # current_user.send("contributable_#{pluralized_content_name}").unarchived.includes(:page_tags, :image_uploads) + # ) + + # if @content_type_class != Universe + # my_universe_ids = current_user.universes.pluck(:id) + # @content.concat(@content_type_class.where(universe_id: my_universe_ids).unarchived) + # end + # end + # @content = @content.to_a.flatten.uniq # Filters @page_tags = PageTag.where( diff --git a/app/models/concerns/has_content.rb b/app/models/concerns/has_content.rb index b5b4b936..7951d542 100644 --- a/app/models/concerns/has_content.rb +++ b/app/models/concerns/has_content.rb @@ -16,16 +16,46 @@ module HasContent has_many :attribute_categories has_many :attribute_values, class_name: 'Attribute', dependent: :destroy + def content_with_multiple_queries( + content_types: Rails.application.config.content_type_names[:all], + page_scoping: { user_id: self.id }, + universe_id: nil + ) + @content_by_page_type = {} + content_types.each do |content_type| + type_specific_fields = ContentPage.polymorphic_content_fields + type_specific_fields.push 'universe_id' unless content_type == 'Universe' + + pages_of_this_type = content_type.constantize + .where(page_scoping) + .select(type_specific_fields) + + if content_type != 'Universe' && universe_id.present? + pages_of_this_type = pages_of_this_type.where(universe_id: universe_id) + end + + @content_by_page_type[content_type] = [] + pages_of_this_type.each do |page_data| + @content_by_page_type[content_type].push ContentPage.new(page_data.attributes) + end + end + + @content_by_page_type + end + # { # characters: [...], # locations: [...] # } def content( - content_types: Rails.application.config.content_types[:all].map(&:name), + content_types: Rails.application.config.content_type_names[:all], page_scoping: { user_id: self.id }, universe_id: nil ) + return content_with_multiple_queries(content_types: content_types, page_scoping: page_scoping, universe_id: universe_id) + return {} if content_types.empty? + # TODO: we should return early if we already have @content_by_page_type!!! polymorphic_content_fields = ContentPage.polymorphic_content_fields where_conditions = page_scoping.map { |key, value| "#{key} = #{value}" }.join(' AND ') + ' AND deleted_at IS NULL AND archived_at IS NULL' diff --git a/app/models/page_types/content_page.rb b/app/models/page_types/content_page.rb index 12e265a0..22c2ab64 100644 --- a/app/models/page_types/content_page.rb +++ b/app/models/page_types/content_page.rb @@ -1,7 +1,14 @@ class ContentPage < ApplicationRecord + include Rails.application.routes.url_helpers + belongs_to :user belongs_to :universe + attr_accessor :favorite + + include Authority::Abilities + self.authorizer_name = 'ContentPageAuthorizer' + def random_image_including_private(format: :small) ImageUpload.where(content_type: self.page_type, content_id: self.id).sample.try(:src, format) || "card-headers/#{self.page_type.downcase.pluralize}.jpg" end @@ -23,6 +30,14 @@ class ContentPage < ApplicationRecord end def view_path + send("#{self.page_type.downcase}_path", self.id) + end + def edit_path + send("edit_#{self.page_type.downcase}_path", self.id) + end + + def self.polymorphic_content_fields + [:id, :name, :favorite, :page_type, :user_id, :created_at, :updated_at, :deleted_at, :archived_at, :privacy] end end diff --git a/app/services/permission_service.rb b/app/services/permission_service.rb index 654e123c..98244b22 100644 --- a/app/services/permission_service.rb +++ b/app/services/permission_service.rb @@ -27,7 +27,11 @@ class PermissionService < Service def self.user_can_contribute_to_containing_universe?(user:, content:) return false if user.nil? return true if [AttributeCategory, AttributeField, Attribute].include?(content.class) #todo audit this - content.universe.present? && user.contributable_universes.pluck(:id).include?(content.universe.id) + + return true if user.contributable_universe_ids.include?(content.universe_id) + return true if user.universes.pluck(:id).include?(content.universe_id) + + return false end def self.content_has_no_containing_universe?(content:) diff --git a/app/views/content/list/_cards.html.erb b/app/views/content/list/_cards.html.erb index a08b5ece..f0e04816 100644 --- a/app/views/content/list/_cards.html.erb +++ b/app/views/content/list/_cards.html.erb @@ -4,14 +4,7 @@
<%= render partial: 'content/display/favorite_control', locals: { content: content } %> - <% content_image = asset_path("card-headers/#{content_type.name.downcase.pluralize}.jpg") %> - <% if content.respond_to?(:image_uploads) %> - <% images = content.image_uploads %> - <% if images.any? %> - <% content_image = images.sample.src(:medium) %> - <% end %> - <% end %> -
+
@@ -35,13 +28,13 @@
<% if current_user.can_update?(content) %> - <%= link_to edit_polymorphic_path(content), class: 'green-text right', target: content.is_a?(Document) ? '_new' : '_self' do %> + <%= link_to content.edit_path, class: 'green-text right', target: content.is_a?(Document) ? '_new' : '_self' do %> <%= content_type.icon %> Edit <% end %> <% end %> <% if current_user.can_read?(content) %> - <%= link_to polymorphic_path(content), class: 'blue-text text-lighten-1' do %> + <%= link_to content.view_path, class: 'blue-text text-lighten-1' do %> <%= content_type.icon %> View <% end %>