diff --git a/.github/workflows/brakeman-analysis.yml b/.github/workflows/brakeman-analysis.yml index 3ab65523f8..b2720566de 100644 --- a/.github/workflows/brakeman-analysis.yml +++ b/.github/workflows/brakeman-analysis.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Brakeman env: - BRAKEMAN_VERSION: '4.10' # SARIF support is provided in Brakeman version 4.10+ + BRAKEMAN_VERSION: '6.0.1' # SARIF support is provided in Brakeman version 4.10+ run: | gem install brakeman --version $BRAKEMAN_VERSION diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 08ad0917ff..445e7feae1 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.7'] # , '2.7', '3.0' + ruby-version: ['3.1'] # , '2.7', '3.0' steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 065e9487ff..6ba0b9feb7 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ Design .passenger .vagrant -storage/ \ No newline at end of file +storage/ +.byebug_history diff --git a/.rubocop.yml b/.rubocop.yml index c13fc0824e..6b2c027b2f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,7 @@ inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.1 Bundler/OrderedGems: Enabled: false diff --git a/.ruby-version b/.ruby-version index a603bb50a2..0aec50e6ed 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.5 +3.1.4 diff --git a/Dockerfile b/Dockerfile index fd10e467f0..a0ae774afc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # docker-compose up # docker-compose exec web bundle exec rake db:create db:schema:load ffcrm:demo:load -FROM ruby:2.7 +FROM ruby:3.1 LABEL author="Steve Kenworthy" diff --git a/Gemfile b/Gemfile index ee15d9ed13..2f687f717b 100644 --- a/Gemfile +++ b/Gemfile @@ -79,6 +79,7 @@ group :test do gem 'zeus', platform: :ruby unless ENV["CI"] gem 'timecop' gem 'sqlite3', '~> 1.4.0' + gem 'webrick' end group :heroku do diff --git a/Gemfile.lock b/Gemfile.lock index b29f75c224..16bc9fbaf1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM autoprefixer-rails (>= 9.1.0) popper_js (>= 2.9.2, < 3) sassc-rails (>= 2.0.0) - brakeman (5.4.1) + brakeman (6.0.1) builder (3.2.4) byebug (11.1.3) cancancan (3.5.0) @@ -160,7 +160,7 @@ GEM devise-i18n (1.11.0) devise (>= 4.9.0) diff-lcs (1.5.0) - dynamic_form (1.1.4) + dynamic_form (1.2.0) email_reply_parser_ffcrm (0.5.0) erubi (1.12.0) execjs (2.8.1) @@ -232,7 +232,7 @@ GEM mini_mime (1.1.2) mini_racer (0.6.4) libv8-node (~> 16.19.0.0) - minitest (5.18.1) + minitest (5.19.0) msgpack (1.6.0) nenv (0.3.0) net-imap (0.3.6) @@ -254,9 +254,9 @@ GEM nenv (~> 0.1) shellany (~> 0.0) orm_adapter (0.5.0) - paper_trail (12.0.0) - activerecord (>= 5.2) - request_store (~> 1.1) + paper_trail (15.0.0) + activerecord (>= 6.1) + request_store (~> 1.4) parallel (1.23.0) parser (3.2.2.3) ast (~> 2.4.1) @@ -276,7 +276,7 @@ GEM puma (6.3.0) nio4r (~> 2.0) racc (1.7.1) - rack (2.2.7) + rack (2.2.8) rack-test (2.1.0) rack (>= 1.3) rails (6.1.7.4) @@ -342,7 +342,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) regexp_parser (2.8.1) - request_store (1.5.0) + request_store (1.5.1) rack (>= 1.4) responders (3.1.0) actionpack (>= 5.2) @@ -441,6 +441,7 @@ GEM nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (~> 4.0, < 4.11) + webrick (1.8.1) websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) @@ -448,7 +449,7 @@ GEM will_paginate (4.0.0) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.8) + zeitwerk (2.6.11) zeus (0.15.14) method_source (>= 0.6.7) @@ -496,7 +497,7 @@ DEPENDENCIES mini_magick mini_racer nokogiri (>= 1.8.1) - paper_trail (~> 12.0.0) + paper_trail (~> 15.0.0) pg premailer pry-rails @@ -531,6 +532,7 @@ DEPENDENCIES tzinfo-data uglifier webdrivers + webrick will_paginate zeus diff --git a/config/application.rb b/config/application.rb index f4846fcdbd..80c5731238 100644 --- a/config/application.rb +++ b/config/application.rb @@ -73,6 +73,18 @@ class Application < Rails::Application # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += %i[password encrypted_password password_salt password_confirmation] + + # Enable support for loading via Psych, required by PaperTrail + config.active_record.use_yaml_unsafe_load = false + config.active_record.yaml_column_permitted_classes = [ + ::ActiveRecord::Type::Time::Value, + ::ActiveSupport::TimeWithZone, + ::ActiveSupport::TimeZone, + ::BigDecimal, + ::Date, + ::Symbol, + ::Time + ] end end diff --git a/fat_free_crm.gemspec b/fat_free_crm.gemspec index cde648f13c..e0f47781ba 100755 --- a/fat_free_crm.gemspec +++ b/fat_free_crm.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |gem| gem.email = ['mike@fatfreecrm.com', 'steveyken@gmail.com', 'daniel.oconnor@gmail.com'] gem.files = Dir["{app,config,db,lib,vendor,public,bin,log/script}/**/*", "MIT-LICENSE", "Rakefile", "README.md", "config.ru", "CHANGELOG.md", "CONTRIBUTING.md"] gem.version = FatFreeCRM::VERSION::STRING - gem.required_ruby_version = '>= 2.7.0' + gem.required_ruby_version = '>= 3.1' gem.license = 'MIT' gem.add_dependency 'rails', '~> 6.1.0' @@ -27,7 +27,7 @@ Gem::Specification.new do |gem| gem.add_dependency 'select2-rails' gem.add_dependency 'simple_form' gem.add_dependency 'will_paginate' - gem.add_dependency 'paper_trail', '~> 12.0.0' + gem.add_dependency 'paper_trail', '~> 15.0.0' gem.add_dependency 'devise', '~> 4.6' gem.add_dependency 'devise-encryptable', '~> 0.2.0' gem.add_dependency 'acts_as_commentable', '~> 6.0.0' diff --git a/lib/fat_free_crm/errors.rb b/lib/fat_free_crm/errors.rb index e7e3fb52e6..39fffa279a 100644 --- a/lib/fat_free_crm/errors.rb +++ b/lib/fat_free_crm/errors.rb @@ -21,16 +21,8 @@ def self.included(base) end end - def each_with_explicit_error - attribute_names.each do |attribute| - self[attribute].each do |error| - if error.start_with?('^') - yield :base, error[1..-1] # Drop the attribute. - else - yield attribute, error # This is default Rails3 behavior. - end - end - end + def each_with_explicit_error(&block) + @errors.each(&block) end end end diff --git a/spec/features/support/browser.rb b/spec/features/support/browser.rb index e5235b4c7b..cbf9c4681d 100644 --- a/spec/features/support/browser.rb +++ b/spec/features/support/browser.rb @@ -11,18 +11,18 @@ if ENV['BROWSER'] == 'chrome' Capybara.register_driver :selenium do |app| - capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: { args: %w[no-sandbox headless disable-gpu] }) - Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities) + options = Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: { args: %w[no-sandbox headless disable-gpu] }) + Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end else # For local testing in an environment with a display or remote X server configured # such as WSL2, use NO_HEADLESS=1 bundle exec rspec spec/features # - # For modern firefox, use MARIONETTE=1 bundle exec rspec spec/features + # NB the marionette setting is deprecated. For modern firefox, install the geckodriver. Capybara.register_driver :selenium do |app| - options = Selenium::WebDriver::Firefox::Options.new - options.args << '--headless' unless ENV['NO_HEADLESS'].present? - capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(marionette: ENV['MARIONETTE'].present?) - Capybara::Selenium::Driver.new(app, browser: :firefox, options: options, desired_capabilities: capabilities) + options = Selenium::WebDriver::Options.firefox + options.add_argument('-headless') unless ENV['NO_HEADLESS'].present? + + Capybara::Selenium::Driver.new(app, browser: :firefox, options: options) end end diff --git a/spec/lib/fat_free_crm/i18n_spec.rb b/spec/lib/fat_free_crm/i18n_spec.rb new file mode 100644 index 0000000000..3db187210f --- /dev/null +++ b/spec/lib/fat_free_crm/i18n_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Copyright (c) 2008-2013 Michael Dvorkin and contributors. +# +# Fat Free CRM is freely distributable under the terms of MIT license. +# See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php +#------------------------------------------------------------------------------ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +class TestController < ActionController::Base + include FatFreeCRM::I18n +end + +describe 'I18n.t()' do + let(:entity_string) { 'entities' } + let(:hidden_count) { 10 } + let(:test_controller) { TestController.new } + + it 'should translate hash arguments' do + expect(test_controller.t(:not_showing_hidden_entities, entity: entity_string, count: hidden_count)) + .to eq("Not showing 10 hidden entities.") + end +end diff --git a/spec/models/polymorphic/version_spec.rb b/spec/models/polymorphic/version_spec.rb index fd2106c162..af5b474130 100644 --- a/spec/models/polymorphic/version_spec.rb +++ b/spec/models/polymorphic/version_spec.rb @@ -104,6 +104,23 @@ @version = Version.where(related_id: @item.id, related_type: @item.class.name, whodunnit: PaperTrail.request.whodunnit, event: 'create').first expect(@version).not_to eq(nil) end + + # NOTE: This to ensure that the yaml_column_permitted_classes config option in + # application.rb is set correctly. If this test fails, then you need to add the + # class to the list of permitted classes. + it "should include version object changes when updating existing #{item}" do + if @item.respond_to?(:full_name) + previous_name_attributes = @item.attributes.slice('first_name', 'last_name') + @item.update(first_name: "Billy", last_name: "Bones") + else + previous_name_attributes = @item.attributes.slice('name') + @item.update(name: "Billy Bones") + end + @version = Version.where(@conditions.merge(event: 'update')).first + + expect(@version.object).to be_present + expect(@version.reify.attributes).to include(previous_name_attributes) + end end end