diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e12101..14dfc88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,18 +13,11 @@ jobs: fail-fast: false matrix: alchemy_branch: - - 7.2-stable - - 7.3-stable - - 7.4-stable + - 8.0-stable ruby: - "3.2" - "3.3" - "3.4" - exclude: - - alchemy_branch: 7.2-stable - ruby: "3.4" - - alchemy_branch: 7.3-stable - ruby: "3.4" env: ALCHEMY_BRANCH: ${{ matrix.alchemy_branch }} steps: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4b1304f..d79d40f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,14 +10,14 @@ jobs: Standard: runs-on: ubuntu-latest env: - ALCHEMY_BRANCH: 7.4-stable + ALCHEMY_BRANCH: 8.0-stable steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Ruby and gems uses: ruby/setup-ruby@v1 with: - ruby-version: "3.1" + ruby-version: "3.4" bundler-cache: true - name: Lint Ruby files run: bundle exec standardrb diff --git a/Gemfile b/Gemfile index b28bba7..428aa55 100644 --- a/Gemfile +++ b/Gemfile @@ -17,10 +17,12 @@ gemspec # gem 'byebug', group: [:development, :test] gem "sqlite3", "~> 2.2" -alchemy_branch = ENV.fetch("ALCHEMY_BRANCH", "7.4-stable") +alchemy_branch = ENV.fetch("ALCHEMY_BRANCH", "8.0-stable") gem "alchemy_cms", github: "AlchemyCMS/alchemy_cms", branch: alchemy_branch -gem "alchemy-devise", github: "AlchemyCMS/alchemy-devise", branch: "7.4-stable" +gem "alchemy-devise", github: "AlchemyCMS/alchemy-devise", branch: alchemy_branch gem "rubocop", require: false gem "standard", "~> 1.25", require: false gem "pry-byebug" + +gem "propshaft", "~> 1.3" diff --git a/alchemy-json_api.gemspec b/alchemy-json_api.gemspec index d9e137c..8d8c001 100644 --- a/alchemy-json_api.gemspec +++ b/alchemy-json_api.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "Rakefile", "README.md"] - spec.add_dependency "alchemy_cms", [">= 7.2.0", "< 8"] + spec.add_dependency "alchemy_cms", [">= 8.0.0.b", "< 9"] spec.add_dependency "jsonapi.rb", [">= 1.6.0", "< 2.2"] spec.add_development_dependency "factory_bot" diff --git a/app/serializers/alchemy/json_api/element_serializer.rb b/app/serializers/alchemy/json_api/element_serializer.rb index 9624f31..d6b10c8 100644 --- a/app/serializers/alchemy/json_api/element_serializer.rb +++ b/app/serializers/alchemy/json_api/element_serializer.rb @@ -14,7 +14,7 @@ class ElementSerializer < BaseSerializer cache_options store: Rails.cache, namespace: "alchemy-jsonapi" attribute :deprecated do |element| - !!element.definition[:deprecated] + !!element.definition.deprecated end has_many :ingredients, diff --git a/app/serializers/alchemy/json_api/ingredient_picture_serializer.rb b/app/serializers/alchemy/json_api/ingredient_picture_serializer.rb index 62ae44f..0a1291e 100644 --- a/app/serializers/alchemy/json_api/ingredient_picture_serializer.rb +++ b/app/serializers/alchemy/json_api/ingredient_picture_serializer.rb @@ -72,7 +72,7 @@ class IngredientPictureSerializer < BaseSerializer end attribute :image_mime_type do |ingredient| - "image/#{ingredient.picture.image_file_format}" + ingredient.picture.image_file_format end attribute :image_file_size do |ingredient| diff --git a/spec/dummy/config/alchemy/page_layouts.yml b/spec/dummy/config/alchemy/page_layouts.yml index 6ef6402..cf00c56 100644 --- a/spec/dummy/config/alchemy/page_layouts.yml +++ b/spec/dummy/config/alchemy/page_layouts.yml @@ -25,12 +25,10 @@ autogenerate: [all_you_can_eat, right_column, left_column] - name: news - feed: true unique: true insert_elements_at: top elements: [headline, news] autogenerate: [news] - feed_elements: [news] - name: contact unique: true diff --git a/spec/dummy/config/initializers/alchemy.rb b/spec/dummy/config/initializers/alchemy.rb index 9809e36..d3d9e6c 100644 --- a/spec/dummy/config/initializers/alchemy.rb +++ b/spec/dummy/config/initializers/alchemy.rb @@ -1,3 +1,271 @@ -# frozen_string_literal: true +Alchemy.configure do |config| + # == This is the global Alchemy configuration file + # -Alchemy.signup_path = "/admin/pages" unless Rails.env.test? + # === Auto Log Out Time + # + # The amount of time of inactivity in minutes after which the user is kicked out of his current session. + # + # NOTE: This is only active in production environments + # + # config.auto_logout_time = 30 + + # === Page caching + # + # Enable/Disable page caching globally. + # + # NOTE: You can enable/disable page caching for single Alchemy::Definitions in the page_layout.yml file. + # + # config.cache_pages = true + + # === Sitemap + # + # Alchemy creates a XML, Google compatible, sitemap for you. + # + # The url is: http://your-domain.tld/sitemap.xml + # + # ==== Config Options: + # + # show_root [Boolean] # Show language root page in sitemap? + # show_flag [Boolean] # Enables the Checkbox in Page#update overlay. So your customer can set the visibility of pages in the sitemap. + # + # config.sitemap.tap do |sitemap| + # sitemap.show_root = true + # sitemap.show_flag = false + # end + + # === Default items per page in admin views + # + # In Alchemy's Admin, change how many items you would get shown per page by Kaminari + # config.items_per_page = 15 + + # === Preview window URL configuration + # + # By default Alchemy uses its internal page preview renderer, + # but you can configure it to be any URL instead. + # + # Basic Auth is supported. + # + # config.preview = { + # host: https://www.my-static-site.com + # auth: + # username: <%= ENV["BASIC_AUTH_USERNAME"] %%> + # password: <%= ENV["BASIC_AUTH_PASSWORD"] %%> + # } + # Preview config per site is supported as well. + # + # config.preview = { + # My site name: + # host: https://www.my-static-site.com + # auth: + # username: <%= ENV["BASIC_AUTH_USERNAME"] %%> + # password: <%= ENV["BASIC_AUTH_PASSWORD"] %%> + # } + + # === Picture rendering settings + # + # Alchemy uses Dragonfly to render images. Settings for image rendering are specific to elements and are defined in elements.yml + # + # Example: + # - name: some_element + # ingredients: + # - role: some_picture + # type: Picture + # settings: + # hint: true + # crop: true # turns on image cropping + # size: '500x500' # image will be cropped to this size + # + # See http://markevans.github.com/dragonfly for further info. + # + # ==== Global Options: + # + # output_image_quality [Integer] # If image gets rendered as JPG or WebP this is the quality setting for it. (Default 85) + # preprocess_image_resize [String] # Use this option to resize images to the given size when they are uploaded to the image library. Downsizing example: '1000x1000>' (Default nil) + # image_output_format [String] # The global image output format setting. (Default +original+) + # + # NOTE: You can always override the output format in the settings of your ingredients in elements.yml, I.E. {format: 'gif'} + # + # config.output_image_quality = 85 + # config.preprocess_image_resize = nil + # config.image_output_format = "original" + + # This is used by the seeder to create the default site. + # config.default_site.tap do |default_site| + # default_site.name = "Default Site" + # default_site.host = "*" + # end + + # This is the default language when seeding. + config.default_language.tap do |default_language| + default_language.code = "en" + default_language.name = "English" + # default_language.page_layout = "index" + # default_language.frontpage_name = "Index" + end + + # === Mailer Settings: + # + # To send emails via contact forms, you can create your form fields here and set which fields are to be validated. + # + # === Validating fields: + # + # Pass the field name as a symbol and a message_id (will be translated) to :validate_fields: + # + # ==== Options: + # + # page_layout_name: [String] # A +Alchemy::PageDefinition+ name. Used to render the contactform on a page with this layout. + # fields: [Array] # An Array of fieldnames. + # validate_fields: [Array] # An Array of fieldnames to be validated on presence. + # + # ==== Translating validation messages: + # + # The validation messages are passed through ::I18n.t so you can translate it in your language yml file. + # + # ==== Example: + # + # de: + # activemodel: + # attributes: + # alchemy/message: + # firstname: Vorname + # + # config.mailer.tap do |mailer| + # mailer.page_layout_name = "contact" + # mailer.forward_to_page = false + # mailer.mail_success_page = "thanks" + # mailer.mail_from = "your.mail@your-domain.com" + # mailer.mail_to = "your.mail@your-domain.com" + # mailer.subject = "A new contact form message" + # mailer.fields = ["salutation", "firstname", "lastname", "address", "zip", "city", "phone", "email", "message"] + # mailer.validate_fields = ["lastname", "email"] + # end + + # === User roles + # + # You can add own user roles. + # + # Further documentation for the auth system used please visit: + # + # https://github.com/ryanb/cancan/wiki + # + # ==== Translating User roles + # + # Userroles can be translated inside your the language yml file under: + # + # alchemy: + # user_roles: + # rolename: Name of the role + # + # config.user_roles = ["member", "author", "editor", "admin"] + + # === Uploader Settings + # + # upload_limit [Integer] # Set an amount of files upload limit of files which can be uploaded at once. Set 0 for unlimited. + # file_size_limit* [Integer] # Set a file size limit in mega bytes for a per file limit. + # + # *) Allow filetypes to upload. Pass * to allow all kind of files. + # + # config.uploader.tap do |uploader| + # uploader.upload_limit = 50 + # uploader.file_size_limit = 100 + # uploader.allowed_filetypes.tap do |file_types| + # file_types.alchemy_attachments = ["*"] + # file_types.alchemy_pictures = ["jpg", "jpeg", "gif", "png", "svg", "webp"] + # end + # end + + # === Link Target Options + # + # Values for the link target selectbox inside the page link overlay. + # The value gets attached as a data-link-target attribute to the link. + # + # == Example: + # + # Open all links set to overlay inside an jQuery UI Dialog Window. + # + # jQuery(a[data-link-target="overlay"]).dialog(); + # + # config.link_target_options = ["blank"] + + # === Format matchers + # + # Named aliases for regular expressions that can be used in various places. + # The most common use case is the format validation of ingredients, or attribute validations of your individual models. + # + # == Example: + # + # validates_format_of :url, with: Alchemy.config.format_matchers.url + # + # config.format_matchers.tap do |format| + # format.email = /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/ + # format.url = /\A[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix + # format.link_url = /^(tel:|mailto:|\/|[a-z]+:\/\/)/ + # end + + # The layout used for rendering the +alchemy/admin/pages#show+ action. + # config.admin_page_preview_layout = "application" + + # The sizes for the preview size select in the page editor. + # config.page_preview_sizes = ["360", "640", "768", "1024", "1280", "1440"] + + # Enable full text search configuration + # + # It enables a searchable checkbox in the page form to toggle + # the searchable field. These information can used in a search + # plugin (e.g. https://github.com/AlchemyCMS/alchemy-pg_search). + # + # == Example + # + # # config/initializers/alchemy.rb + # Alchemy.config.page_searchable_checkbox = true + # + # config.show_page_searchable_checkbox = false + + # The storage adapter for Pictures and Attachments + # + config.storage_adapter = "dragonfly" + + # Additional JS modules to be imported in the Alchemy admin UI + # + # Be sure to also pin the modules with +Alchemy.importmap+. + # + # == Example + # + # Alchemy.importmap.pin "flatpickr/de", + # to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js" + # + # config.admin_js_imports << "flatpickr/de" + + # Additional importmaps to be included in the Alchemy admin UI + # + # Be sure to also pin modules with +Alchemy.importmap+. + # + # config.admin_importmaps.add( + # importmap_path: root.join("config/importmap.rb"), + # source_paths: [ + # root.join("app/javascript") + # ], + # name: "admin_extension" + # ) + + # Additional stylesheets to be included in the Alchemy admin UI + # config.admin_stylesheets.add("my_app/admin_extension") + + # Define page publish targets + # + # A publish target is a ActiveJob that gets performed + # whenever a user clicks the publish page button. + # + # Use this to trigger deployment hooks of external + # services in an asychronous way. + # + # config.publish_targets << "MyPublishJob" + + # Configure tabs in the link dialog + # + # With this configuration that tabs in the link dialog can be extended + # without overwriting or defacing the Admin Interface. + # + # config.link_dialog_tabs << "Acme::LinkTab" +end diff --git a/spec/dummy/config/initializers/devise.rb b/spec/dummy/config/initializers/devise.rb index 5e1f7e4..8708eb4 100644 --- a/spec/dummy/config/initializers/devise.rb +++ b/spec/dummy/config/initializers/devise.rb @@ -14,7 +14,7 @@ # confirmation, reset password and unlock tokens in the database. # Devise will use the `secret_key_base` as its `secret_key` # by default. You can change it below and use your own secret key. - # config.secret_key = 'a3e5b8e7089c0196c17e4b22a708f36d193da5d859e65ee6af46031f84c341eaa2a6050063d9294a415fc505c1591ff20a1c63babddb8cab9010a5ac711008a8' + # config.secret_key = '2feb8af0d2b0ffe7ae73a39502e5f4fbd2cc3a17a8d2d7a6abc706ab35ec0fc249a0801aa2d56956a6aaddfadf60f186447a2d79c7fa7a6a7997ee41b5f13874' # ==> Controller configuration # Configure the parent class to the devise controllers. @@ -24,7 +24,7 @@ # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. - config.mailer_sender = Alchemy::Config.get(:mailer)['mail_from'] + config.mailer_sender = Alchemy.config.mailer.mail_from # Configure the class responsible to send e-mails. config.mailer = "Alchemy::Notifications" @@ -126,7 +126,7 @@ config.stretches = Rails.env.test? ? 1 : 12 # Set up a pepper to generate the hashed password. - # config.pepper = '73e2f6b111518b9a30148f5cc4d17a48b409d2cbce73f95e4349e845e40b77f53e5e2299bb7fc7cdc6fee147c282c6e57ec7921ea02d829c60f0dcd84ae84d43' + # config.pepper = '797fe652e0a115d80a704292969fe8e604ec5aa4bbcabff9dd44f2ef780c2eeeac926a2903fd12dad82f85862e53790cbae7184ca09b0aabfe28fc9f6b242251' # Send a notification to the original email when the user's email is changed. # config.send_email_changed_notification = false @@ -188,7 +188,7 @@ # ==> Configuration for :timeoutable # The time you want to timeout the user session without activity. After this # time the user will be asked for credentials again. Default is 30 minutes. - config.timeout_in = Rails.env.development? ? nil : Alchemy::Config.get(:auto_logout_time).minutes + config.timeout_in = Rails.env.development? ? nil : Alchemy.config.auto_logout_time.minutes # ==> Configuration for :lockable # Defines which strategy will be used to lock an account. diff --git a/spec/dummy/config/initializers/dragonfly.rb b/spec/dummy/config/initializers/dragonfly.rb index 514d77e..e132d1b 100644 --- a/spec/dummy/config/initializers/dragonfly.rb +++ b/spec/dummy/config/initializers/dragonfly.rb @@ -16,7 +16,6 @@ Dragonfly.app(:alchemy_pictures).configure do dragonfly_url nil plugin :imagemagick - plugin :svg secret "976ee38cf5e6d65dbf58f1d355825ba33239ab7a76a432818cd592526e9c78b5" url_format "/pictures/:job/:name.:ext" diff --git a/spec/dummy/config/webpacker.yml b/spec/dummy/config/webpacker.yml deleted file mode 100644 index 8581ac0..0000000 --- a/spec/dummy/config/webpacker.yml +++ /dev/null @@ -1,96 +0,0 @@ -# Note: You must restart bin/webpack-dev-server for changes to take effect - -default: &default - source_path: app/javascript - source_entry_path: packs - public_root_path: public - public_output_path: packs - cache_path: tmp/cache/webpacker - check_yarn_integrity: false - webpack_compile_output: true - - # Additional paths webpack should lookup modules - # ['app/assets', 'engine/foo/app/assets'] - resolved_paths: [] - - # Reload manifest.json on all requests so we reload latest compiled packs - cache_manifest: false - - # Extract and emit a css file - extract_css: false - - static_assets_extensions: - - .jpg - - .jpeg - - .png - - .gif - - .tiff - - .ico - - .svg - - .eot - - .otf - - .ttf - - .woff - - .woff2 - - extensions: - - .mjs - - .js - - .sass - - .scss - - .css - - .module.sass - - .module.scss - - .module.css - - .png - - .svg - - .gif - - .jpeg - - .jpg - -development: - <<: *default - compile: true - - # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules - check_yarn_integrity: true - - # Reference: https://webpack.js.org/configuration/dev-server/ - dev_server: - https: false - host: localhost - port: 3035 - public: localhost:3035 - hmr: false - # Inline should be set to true if using HMR - inline: true - overlay: true - compress: true - disable_host_check: true - use_local_ip: false - quiet: false - pretty: false - headers: - 'Access-Control-Allow-Origin': '*' - watch_options: - ignored: '**/node_modules/**' - - -test: - <<: *default - compile: true - - # Compile test packs to a separate directory - public_output_path: packs-test - -production: - <<: *default - - # Production depends on precompilation of packs prior to booting for performance. - compile: false - - # Extract and emit a css file - extract_css: true - - # Cache manifest.json for performance - cache_manifest: true diff --git a/spec/requests/alchemy/json_api/admin/layout_pages_spec.rb b/spec/requests/alchemy/json_api/admin/layout_pages_spec.rb index c93dc69..23f515a 100644 --- a/spec/requests/alchemy/json_api/admin/layout_pages_spec.rb +++ b/spec/requests/alchemy/json_api/admin/layout_pages_spec.rb @@ -107,7 +107,7 @@ context "with caching enabled" do before do allow(Rails.application.config.action_controller).to receive(:perform_caching) { true } - stub_alchemy_config(:cache_pages, true) + stub_alchemy_config(cache_pages: true) end it "sets cache headers" do diff --git a/spec/requests/alchemy/json_api/admin/pages_spec.rb b/spec/requests/alchemy/json_api/admin/pages_spec.rb index e3aba0a..d34ed8a 100644 --- a/spec/requests/alchemy/json_api/admin/pages_spec.rb +++ b/spec/requests/alchemy/json_api/admin/pages_spec.rb @@ -30,7 +30,7 @@ context "with caching enabled" do before do allow(Rails.application.config.action_controller).to receive(:perform_caching) { true } - stub_alchemy_config(:cache_pages, true) + stub_alchemy_config(cache_pages: true) end it "sets cache headers" do diff --git a/spec/requests/alchemy/json_api/pages_spec.rb b/spec/requests/alchemy/json_api/pages_spec.rb index 72bb181..8533928 100644 --- a/spec/requests/alchemy/json_api/pages_spec.rb +++ b/spec/requests/alchemy/json_api/pages_spec.rb @@ -38,7 +38,7 @@ context "with caching enabled" do before do allow(Rails.application.config.action_controller).to receive(:perform_caching) { true } - stub_alchemy_config(:cache_pages, true) + stub_alchemy_config(cache_pages: true) end it "sets public cache headers" do @@ -101,7 +101,7 @@ it "sets private cache headers" do get alchemy_json_api.page_path(page) - expect(response.headers["Cache-Control"]).to eq("max-age=600, private, must-revalidate") + expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate") end end @@ -229,7 +229,7 @@ context "with caching enabled" do before do allow(Rails.application.config.action_controller).to receive(:perform_caching) { true } - stub_alchemy_config(:cache_pages, true) + stub_alchemy_config(cache_pages: true) end it "sets public cache headers of latest published page" do @@ -293,7 +293,7 @@ it "sets private cache headers" do get alchemy_json_api.pages_path - expect(response.headers["Cache-Control"]).to eq("max-age=600, private, must-revalidate") + expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate") end end @@ -384,7 +384,7 @@ context "with caching enabled" do before do allow(Rails.application.config.action_controller).to receive(:perform_caching) { true } - stub_alchemy_config(:cache_pages, true) + stub_alchemy_config(cache_pages: true) end it "sets constant etag" do diff --git a/spec/serializers/alchemy/json_api/ingredient_audio_serializer_spec.rb b/spec/serializers/alchemy/json_api/ingredient_audio_serializer_spec.rb index 22f56cf..6639621 100644 --- a/spec/serializers/alchemy/json_api/ingredient_audio_serializer_spec.rb +++ b/spec/serializers/alchemy/json_api/ingredient_audio_serializer_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Alchemy::JsonApi::IngredientAudioSerializer do let(:element) { FactoryBot.build_stubbed(:alchemy_element) } - let(:attachment) { FactoryBot.build_stubbed(:alchemy_attachment) } + let(:attachment) { FactoryBot.create(:alchemy_attachment) } let(:ingredient) do Alchemy::Ingredients::Audio.new( role: "audio", diff --git a/spec/serializers/alchemy/json_api/ingredient_file_serializer_spec.rb b/spec/serializers/alchemy/json_api/ingredient_file_serializer_spec.rb index fbbc29a..317b234 100644 --- a/spec/serializers/alchemy/json_api/ingredient_file_serializer_spec.rb +++ b/spec/serializers/alchemy/json_api/ingredient_file_serializer_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Alchemy::JsonApi::IngredientFileSerializer do let(:ingredient) { FactoryBot.build_stubbed(:alchemy_ingredient_file, title: "File", css_class: "custom") } - let(:attachment) { FactoryBot.build_stubbed(:alchemy_attachment) } + let(:attachment) { FactoryBot.create(:alchemy_attachment) } subject(:serializer) { described_class.new(ingredient) } diff --git a/spec/serializers/alchemy/json_api/ingredient_picture_serializer_spec.rb b/spec/serializers/alchemy/json_api/ingredient_picture_serializer_spec.rb index 56d610a..30bec09 100644 --- a/spec/serializers/alchemy/json_api/ingredient_picture_serializer_spec.rb +++ b/spec/serializers/alchemy/json_api/ingredient_picture_serializer_spec.rb @@ -12,7 +12,7 @@ ) end - let(:picture) { FactoryBot.create(:alchemy_picture, image_file_size: 301) } + let(:picture) { FactoryBot.create(:alchemy_picture) } subject(:serializer) { described_class.new(ingredient) } @@ -27,7 +27,7 @@ expect(subject[:image_name]).to eq("image") expect(subject[:image_file_name]).to eq("image.png") expect(subject[:image_mime_type]).to eq("image/png") - expect(subject[:image_file_size]).to eq(301) + expect(subject[:image_file_size]).to eq(70) expect(subject[:image_dimensions]).to eq(width: 1, height: 1) end diff --git a/spec/serializers/alchemy/json_api/ingredient_video_serializer_spec.rb b/spec/serializers/alchemy/json_api/ingredient_video_serializer_spec.rb index 6a2b3ff..76a95dd 100644 --- a/spec/serializers/alchemy/json_api/ingredient_video_serializer_spec.rb +++ b/spec/serializers/alchemy/json_api/ingredient_video_serializer_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Alchemy::JsonApi::IngredientVideoSerializer do let(:element) { FactoryBot.build_stubbed(:alchemy_element) } - let(:attachment) { FactoryBot.build_stubbed(:alchemy_attachment) } + let(:attachment) { FactoryBot.create(:alchemy_attachment) } let(:ingredient) do Alchemy::Ingredients::Video.new( role: "video",