diff --git a/app/controllers/admin/contents_controller.rb b/app/controllers/admin/contents_controller.rb index 5841fa84eb..0a41cc389a 100644 --- a/app/controllers/admin/contents_controller.rb +++ b/app/controllers/admin/contents_controller.rb @@ -10,14 +10,14 @@ module Admin def update params.each do |name, value| - if ContentConfig.has_preference?(name) || ContentConfig.has_attachment?(name) - ContentConfig.public_send("#{name}=", value) + if value.is_a?(ActionDispatch::Http::UploadedFile) + blob = store_file(value) + update_preference("#{name}_blob_id", blob.id) + else + update_preference(name, value) end end - # Save any uploaded images - ContentConfig.save - flash[:success] = t(:successfully_updated, resource: I18n.t('admin.contents.edit.your_content')) @@ -26,6 +26,22 @@ module Admin private + def store_file(attachable) + ActiveStorage::Blob.create_and_upload!( + io: attachable.open, + filename: attachable.original_filename, + content_type: attachable.content_type, + service_name: :local, + identify: false, + ) + end + + def update_preference(name, value) + return unless ContentConfig.has_preference?(name) + + ContentConfig.public_send("#{name}=", value) + end + def preference_sections [ PreferenceSections::HeaderSection.new, diff --git a/app/models/concerns/file_preferences.rb b/app/models/concerns/file_preferences.rb new file mode 100644 index 0000000000..68682b3263 --- /dev/null +++ b/app/models/concerns/file_preferences.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module FilePreferences + extend ActiveSupport::Concern + + included do + @default_urls = {} + end + + class_methods do + def file_preference(name, default_url: nil) + preference "#{name}_blob_id", :integer + @default_urls[name] = default_url if default_url + end + + def default_url(name) + @default_urls[name] + end + end + + def preference_type(key) + if has_preference?("#{key}_blob_id") + :file + else + super(key) + end + end + + def url_for(name) + blob = blob_for(name) + + if blob + Rails.application.routes.url_helpers.url_for(blob) + else + self.class.default_url(name) + end + end + + def blob_for(name) + blob_id = get_preference("#{name}_blob_id") + ActiveStorage::Blob.find_by(id: blob_id) if blob_id + end +end diff --git a/app/models/content_configuration.rb b/app/models/content_configuration.rb index 0f34598cd7..d0160407a4 100644 --- a/app/models/content_configuration.rb +++ b/app/models/content_configuration.rb @@ -1,23 +1,17 @@ # frozen_string_literal: true -require 'open_food_network/paperclippable' - -class ContentConfiguration < Spree::Preferences::FileConfiguration - include OpenFoodNetwork::Paperclippable +class ContentConfiguration < Spree::Preferences::Configuration + include FilePreferences # Header - preference :logo, :file - preference :logo_mobile, :file - preference :logo_mobile_svg, :file - has_attached_file :logo, default_url: "/default_images/ofn-logo.png" - has_attached_file :logo_mobile - has_attached_file :logo_mobile_svg, default_url: "/default_images/ofn-logo-mobile.svg" + file_preference :logo, default_url: "/default_images/ofn-logo.png" + file_preference :logo_mobile + file_preference :logo_mobile_svg, default_url: "/default_images/ofn-logo-mobile.svg" # Home page preference :home_page_alert_html, :text - preference :home_hero, :file + file_preference :home_hero, default_url: "/default_images/home.jpg" preference :home_show_stats, :boolean, default: true - has_attached_file :home_hero, default_url: "/default_images/home.jpg" # Map preference :open_street_map_enabled, :boolean, default: false @@ -66,8 +60,7 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration preference :menu_7_icon_name, :string, default: "ofn-i_013-help" # Footer - preference :footer_logo, :file - has_attached_file :footer_logo, default_url: "/default_images/ofn-logo-footer.png" + file_preference :footer_logo, default_url: "/default_images/ofn-logo-footer.png" # Other preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet" diff --git a/app/models/spree/preferences/file_configuration.rb b/app/models/spree/preferences/file_configuration.rb deleted file mode 100644 index 421484993f..0000000000 --- a/app/models/spree/preferences/file_configuration.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -module Spree - module Preferences - class FileConfiguration < Configuration - def self.preference(name, type, *args) - if type == :file - # Active Storage blob id: - super "#{name}_blob_id", :integer, *args - - # Paperclip attachment attributes: - super "#{name}_file_name", :string, *args - super "#{name}_content_type", :string, *args - super "#{name}_file_size", :integer, *args - super "#{name}_updated_at", :string, *args - - else - super name, type, *args - end - end - - def get_preference(key) - if !has_preference?(key) && has_attachment?(key) - # Call Paperclip's attachment method: - public_send key - elsif key.ends_with?("_blob") - # Find referenced Active Storage blob: - blob_id = super("#{key}_id") - ActiveStorage::Blob.find_by(id: blob_id) - else - super key - end - end - alias :[] :get_preference - - def preference_type(name) - if has_attachment? name - :file - else - super name - end - end - - # Spree's Configuration responds to preference methods via method_missing, but doesn't - # override respond_to?, which consequently reports those methods as unavailable. Paperclip - # errors if respond_to? isn't correct, so we override it here. - def respond_to?(method, include_all = false) - name = method.to_s.delete('=') - reference_name = "#{name}_id" - - super(self.class.preference_getter_method(name), include_all) || - super(reference_name, include_all) || - super(method, include_all) - end - - def has_attachment?(name) - self.class.respond_to?(:attachment_definitions) && - self.class.attachment_definitions.key?(name.to_sym) - end - end - end -end diff --git a/app/views/admin/contents/_fieldset.html.haml b/app/views/admin/contents/_fieldset.html.haml index 509bf49f43..0821e4e837 100644 --- a/app/views/admin/contents/_fieldset.html.haml +++ b/app/views/admin/contents/_fieldset.html.haml @@ -8,5 +8,8 @@ - text = t(key) .field = label_tag(key, text + ': ') + tag(:br) if type != :boolean - = preference_field_tag(key, ContentConfig[key], :type => type) + - if type == :file + = file_field_tag(key, type: type) + - else + = preference_field_tag(key, ContentConfig[key], type: type) = label_tag(key, text) + tag(:br) if type == :boolean diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 49da911e20..899e19ca72 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,5 +1,5 @@ :css - #tagline:before { background-image: url("#{ContentConfig.home_hero.url}") } + #tagline:before { background-image: url("#{ContentConfig.url_for(:home_hero)}") } - content_for :page_alert do diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 0607ec2cb5..b213f52c07 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -4,7 +4,7 @@ %meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/ %meta{property: "og:title", content: content_for?(:title) ? yield(:title) : t(:title)} %meta{property: "og:description", content: content_for?(:description) ? yield(:description) : t(:site_meta_description)} - %meta{property: "og:image", content: content_for?(:image) ? yield(:image) : ContentConfig.logo.url} + %meta{property: "og:image", content: content_for?(:image) ? yield(:image) : ContentConfig.url_for(:logo)} - if !Rails.env.production? || @noindex_meta_tag %meta{name: "robots", content: "noindex"} %title= content_for?(:title) ? "#{yield(:title)} - #{t(:title)}".html_safe : "#{t(:welcome_to)} #{t(:title)}" diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index 2536308b57..0160dfd727 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -15,7 +15,7 @@ %table{:bgcolor => "#f2f2f2"} %tr %td - %img{src: ContentConfig.footer_logo.url, width: "144", height: "50"}/ + %img{src: ContentConfig.url_for(:footer_logo), width: "144", height: "50"}/ %td{:align => "right"} %h6.collapse = Spree::Config[:site_name] diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml index 4e7a5d05e1..4d6723892d 100644 --- a/app/views/shared/_footer.html.haml +++ b/app/views/shared/_footer.html.haml @@ -96,7 +96,7 @@ .row.legal .small-12.medium-3.medium-offset-2.columns.text-left %a{href: main_app.root_path} - %img{src: ContentConfig.footer_logo.url, width: "220"} + %img{src: ContentConfig.url_for(:footer_logo), width: "220"} .small-12.medium-5.columns.text-left %p.text-small = t '.footer_legal_call' diff --git a/app/views/shared/menu/_large_menu.html.haml b/app/views/shared/menu/_large_menu.html.haml index 1b48aa6a72..d8cb828ca6 100644 --- a/app/views/shared/menu/_large_menu.html.haml +++ b/app/views/shared/menu/_large_menu.html.haml @@ -3,7 +3,7 @@ %ul.nav-logo %li.ofn-logo %a{href: main_app.root_path} - %img{src: ContentConfig.logo.url} + %img{src: ContentConfig.url_for(:logo)} %li.powered-by %img{src: '/favicon.ico'} %span diff --git a/app/views/shared/menu/_mobile_menu.html.haml b/app/views/shared/menu/_mobile_menu.html.haml index 83c3d1c44d..aca1fa70d6 100644 --- a/app/views/shared/menu/_mobile_menu.html.haml +++ b/app/views/shared/menu/_mobile_menu.html.haml @@ -6,7 +6,7 @@ %section.left .ofn-logo %a{href: main_app.root_path} - %img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"} + %img{src: ContentConfig.url_for(:logo_mobile), srcset: ContentConfig.url_for(:logo_mobile_svg), width: "75", height: "26"} %section.right{"ng-cloak" => true} %span.cart-span{"ng-class" => "{ dirty: Cart.dirty || Cart.empty(), 'pure-dirty': Cart.dirty }"} diff --git a/app/views/shared/menu/_offcanvas_menu.html.haml b/app/views/shared/menu/_offcanvas_menu.html.haml index b6e553bc8c..ca8b907cf7 100644 --- a/app/views/shared/menu/_offcanvas_menu.html.haml +++ b/app/views/shared/menu/_offcanvas_menu.html.haml @@ -2,7 +2,7 @@ %ul.off-canvas-list %li.ofn-logo %a{href: main_app.root_path} - %img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"} + %img{src: ContentConfig.url_for(:logo_mobile), srcset: ContentConfig.url_for(:logo_mobile_svg), width: "75", height: "26"} - [*1..7].each do |menu_number| - menu_name = "menu_#{menu_number}" - if ContentConfig[menu_name].present? diff --git a/lib/open_food_network/paperclippable.rb b/lib/open_food_network/paperclippable.rb deleted file mode 100644 index e510c6af82..0000000000 --- a/lib/open_food_network/paperclippable.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -# Allow use of Paperclip's has_attached_file on non-ActiveRecord classes -# https://gist.github.com/basgys/5712426 - -module OpenFoodNetwork - module Paperclippable - def self.included(base) - base.extend(ActiveModel::Naming) - base.extend(ActiveModel::Callbacks) - base.include(ActiveModel::Validations) - base.include(Paperclip::Glue) - - # Paperclip required callbacks - base.define_model_callbacks(:save, only: [:after]) - base.define_model_callbacks(:commit, only: [:after]) - base.define_model_callbacks(:destroy, only: [:before, :after]) - - # Initialise an ID - base.__send__(:attr_accessor, :id) - base.instance_variable_set :@id, 1 - end - - # ActiveModel requirements - def to_model - self - end - - def valid?() true end - - def new_record?() true end - - def destroyed?() true end - - def save - run_callbacks :save do - end - true - end - - def errors - obj = Object.new - def obj.[](_key) [] end - - def obj.full_messages() [] end - - def obj.any?() false end - obj - end - end -end diff --git a/spec/models/content_configuration_spec.rb b/spec/models/content_configuration_spec.rb index 8d2cc5a8d0..141d4111cf 100644 --- a/spec/models/content_configuration_spec.rb +++ b/spec/models/content_configuration_spec.rb @@ -5,10 +5,10 @@ require 'spec_helper' describe ContentConfiguration do describe "default logos and home_hero" do it "sets a default url with existing image" do - expect(image_exist?(ContentConfig.logo.options[:default_url])).to be true - expect(image_exist?(ContentConfig.logo_mobile_svg.options[:default_url])).to be true - expect(image_exist?(ContentConfig.home_hero.options[:default_url])).to be true - expect(image_exist?(ContentConfig.footer_logo.options[:default_url])).to be true + expect(image_exist?(ContentConfig.url_for(:logo))).to be true + expect(image_exist?(ContentConfig.url_for(:logo_mobile_svg))).to be true + expect(image_exist?(ContentConfig.url_for(:home_hero))).to be true + expect(image_exist?(ContentConfig.url_for(:footer_logo))).to be true end def image_exist?(default_url) diff --git a/spec/models/spree/preferences/file_configuration_spec.rb b/spec/models/spree/preferences/file_configuration_spec.rb deleted file mode 100644 index b68eeb270e..0000000000 --- a/spec/models/spree/preferences/file_configuration_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -module Spree - module Preferences - class TestConfiguration < FileConfiguration - preference :name, :string - - include OpenFoodNetwork::Paperclippable - preference :logo, :file - has_attached_file :logo - end - - describe FileConfiguration do - let(:c) { TestConfiguration.new } - - describe "getting preferences" do - it "returns regular preferences" do - c.name = 'foo' - expect(c.get_preference(:name)).to eq('foo') - end - - it "returns file preferences" do - expect(c.get_preference(:logo)).to be_a Paperclip::Attachment - end - - it "returns regular preferences via []" do - c.name = 'foo' - expect(c[:name]).to eq('foo') - end - - it "returns file preferences via []" do - expect(c[:logo]).to be_a Paperclip::Attachment - end - end - - describe "getting preference types" do - it "returns regular preference types" do - expect(c.preference_type(:name)).to eq(:string) - end - - it "returns file preference types" do - expect(c.preference_type(:logo)).to eq(:file) - end - end - - describe "respond_to?" do - it "responds to preference getters" do - expect(c.respond_to?(:name)).to be true - end - - it "responds to preference setters" do - expect(c.respond_to?(:name=)).to be true - end - end - end - end -end