diff --git a/.codeclimate.yml b/.codeclimate.yml index a16f0902f..64bfaef0c 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,7 +1,7 @@ engines: rubocop: enabled: true - channel: "rubocop-0-88" + channel: "rubocop-0-89" scss-lint: enabled: true shellcheck: @@ -11,7 +11,7 @@ engines: coffeelint: enabled: true brakeman: - enabled: true + enabled: false # codeclimate's brakeman is stuck in rails 5 rules bundler-audit: enabled: true duplication: @@ -23,6 +23,7 @@ engines: ratings: paths: - "**.rb" + - "**.ru" - "**.js" - "**.coffee" - "**.scss" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..111c4027e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: [push, pull_request] + +jobs: + contributors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install ruby version specified in .ruby-version + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Install gem bundle + run: | + gem install bundler + bundle install + - name: Check contributors + run: | + bundle exec script/check_contributors_md.rb diff --git a/.rubocop.yml b/.rubocop.yml index 9472c8915..fe401f324 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,7 @@ AllCops: Exclude: - 'db/schema.rb' - 'vendor/**/*' - TargetRailsVersion: 5.2 + TargetRailsVersion: 6.0 Rails: Enabled: true @@ -60,3 +60,103 @@ Rails/SkipsModelValidations: Exclude: - 'db/migrate/20190317023129_finished_boolean.rb' - 'db/seeds.rb' + + +Layout/EmptyLinesAroundAttributeAccessor: + Enabled: true +Layout/SpaceAroundMethodCallOperator: + Enabled: true +Lint/DeprecatedOpenSSLConstant: + Enabled: true +Lint/DuplicateElsifCondition: + Enabled: true +Lint/MixedRegexpCaptureTypes: + Enabled: true +Lint/RaiseException: + Enabled: true +Lint/StructNewOverride: + Enabled: true +Style/AccessorGrouping: + Enabled: true +Style/ArrayCoercion: + Enabled: true +Style/BisectedAttrAccessor: + Enabled: false +Style/CaseLikeIf: + Enabled: true +Style/ExponentialNotation: + Enabled: true +Style/HashAsLastArrayItem: + Enabled: true +Style/HashEachMethods: + Enabled: true +Style/HashLikeCase: + Enabled: true +Style/HashTransformKeys: + Enabled: true +Style/HashTransformValues: + Enabled: true +Style/RedundantAssignment: + Enabled: true +Style/RedundantFetchBlock: + Enabled: true +Style/RedundantFileExtensionInRequire: + Enabled: true +Style/RedundantRegexpCharacterClass: + Enabled: true +Style/RedundantRegexpEscape: + Enabled: true +Style/SlicingWithRange: + Enabled: true +Rails/ActiveRecordCallbacksOrder: + Enabled: true +Rails/FindById: + Enabled: true +Rails/Inquiry: + Enabled: true +Rails/MailerName: + Enabled: true +Rails/MatchRoute: + Enabled: true +Rails/NegateInclude: + Enabled: true +Rails/Pluck: + Enabled: true +Rails/PluckInWhere: + Enabled: true +Rails/RenderInline: + Enabled: true +Rails/RenderPlainText: + Enabled: true +Rails/ShortI18n: + Enabled: true +Rails/WhereExists: + Enabled: true +Lint/BinaryOperatorWithIdenticalOperands: + Enabled: true +Lint/DuplicateRescueException: + Enabled: true +Lint/EmptyConditionalBody: + Enabled: true +Lint/FloatComparison: + Enabled: true +Lint/MissingSuper: + Enabled: true +Lint/OutOfRangeRegexpRef: + Enabled: true +Lint/SelfAssignment: + Enabled: true +Lint/TopLevelReturnWithArgument: + Enabled: true +Lint/UnreachableLoop: + Enabled: true +Style/ExplicitBlockArgument: + Enabled: true +Style/GlobalStdStream: + Enabled: true +Style/OptionalBooleanParameter: + Enabled: true +Style/SingleArgumentDig: + Enabled: true +Style/StringConcatenation: + Enabled: true diff --git a/.travis.yml b/.travis.yml index 661372812..ba7ddb1ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,24 +4,29 @@ dist: bionic services: - postgresql - xvfb +branches: + only: + - mainline + - dev cache: bundler: true + yarn: true directories: - tmp/cache/assets/test/sprockets addons: - postgresql: "9.4" + postgresql: "9.4" # Matches production code_climate: repo_token: secure: "PfhLGBKRgNqhKuYCJsK+VPhdAzcgWFGeeOyxC/eS8gtlvIISVdgyZE+r30uIei0DFI6zEiN62eW4d+xtT4j7/e2ZcAcx7U52mza/SnQNuu3nCGQDJB8VOvV5NbnwXfi8vfr4e889Mt7k3ocd2c4gqB4UtRqrzhygj7HN+B/GfEk=" env: - matrix: - - ELASTIC_SEARCH_VERSION="7.5.1-amd64" COVERAGE=true RAILS_ENV=test # Future target (production needs upgrading) - - STATIC_CHECKS=true global: - secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4=" + - ELASTIC_SEARCH_VERSION="7.5.1-amd64" + - COVERAGE=true - GROWSTUFF_EMAIL="noreply@test.growstuff.org"g - GROWSTUFF_SITE_NAME="Growstuff (travis)" - - RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + - RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'] + - RAILS_ENV=test before_install: - sudo apt clean - sudo apt update @@ -31,22 +36,14 @@ before_install: - ./script/install_linters.sh - ./script/check_elasticsearch.sh script: - - > - if [ "${STATIC_CHECKS}" = "true" ]; then - ./script/check_static.rb - else - bundle exec rails db:create db:migrate; - bundle exec rails assets:precompile; - bundle exec rails search:reindex; - bundle exec rspec spec; - # npx percy exec -- bundle exec rspec spec # <- run this to send screen shots to percy - fi; + - bundle exec rails db:create db:migrate + - bundle exec rails assets:precompile + - bundle exec rails search:reindex + - bundle exec rspec spec -fd after_script: - > - if [ "${COVERAGE}" = "true" ]; then - gem install codeclimate-test-reporter - codeclimate-test-reporter - fi + gem install codeclimate-test-reporter + codeclimate-test-reporter before_deploy: - bundle exec script/heroku_maintenance.rb on deploy: diff --git a/Gemfile b/Gemfile index 325a228f5..8a8273e4d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' ruby '2.6.5' -gem 'rails', '5.2.4.3' +gem 'rails', '6.0.3.2' # Keeping old sprockets # https://github.com/rails/sprockets-rails/issues/444#issuecomment-637817050 @@ -38,7 +38,7 @@ gem 'oj' # Speeds up json # planting and harvest predictions # based on median values for the crop -gem 'active_median', '0.1.4' # needs postgresql update https://github.com/Growstuff/growstuff/issues/1757 +gem 'active_median', '0.2.0' gem 'active_record_union' gem 'flickraw' @@ -112,7 +112,7 @@ gem 'rake', '>= 10.0.0' gem "responders" # allows soft delete. Used for members. -gem 'discard', '~> 1.0' +gem 'discard', '>= 1.2' gem 'xmlrpc' # fixes rake error - can be removed if not needed later @@ -175,7 +175,7 @@ end group :test do gem 'codeclimate-test-reporter', require: false - gem 'percy-capybara', '~> 4.0.0' + gem 'percy-capybara' gem 'rails-controller-testing' gem 'selenium-webdriver' gem 'timecop' diff --git a/Gemfile.lock b/Gemfile.lock index ed60a1dd6..b38063760 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,57 +3,71 @@ GEM remote: https://rails-assets.org/ specs: abstract_type (0.0.7) - actioncable (5.2.4.3) - actionpack (= 5.2.4.3) + actioncable (6.0.3.2) + actionpack (= 6.0.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.4.3) - actionpack (= 5.2.4.3) - actionview (= 5.2.4.3) - activejob (= 5.2.4.3) + actionmailbox (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) + mail (>= 2.7.1) + actionmailer (6.0.3.2) + actionpack (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.4.3) - actionview (= 5.2.4.3) - activesupport (= 5.2.4.3) + actionpack (6.0.3.2) + actionview (= 6.0.3.2) + activesupport (= 6.0.3.2) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.4.3) - activesupport (= 5.2.4.3) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.0.3.2) + actionpack (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) + nokogiri (>= 1.8.5) + actionview (6.0.3.2) + activesupport (= 6.0.3.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) + rails-html-sanitizer (~> 1.1, >= 1.2.0) active_link_to (1.0.5) actionpack addressable - active_median (0.1.4) - activerecord + active_median (0.2.0) + activerecord (>= 4.2) active_record_union (1.3.0) activerecord (>= 4.0) active_utils (3.3.17) activesupport (>= 4.2) i18n - activejob (5.2.4.3) - activesupport (= 5.2.4.3) + activejob (6.0.3.2) + activesupport (= 6.0.3.2) globalid (>= 0.3.6) - activemodel (5.2.4.3) - activesupport (= 5.2.4.3) - activerecord (5.2.4.3) - activemodel (= 5.2.4.3) - activesupport (= 5.2.4.3) - arel (>= 9.0) - activestorage (5.2.4.3) - actionpack (= 5.2.4.3) - activerecord (= 5.2.4.3) + activemodel (6.0.3.2) + activesupport (= 6.0.3.2) + activerecord (6.0.3.2) + activemodel (= 6.0.3.2) + activesupport (= 6.0.3.2) + activestorage (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) marcel (~> 0.3.1) - activesupport (5.2.4.3) + activesupport (6.0.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) adamantium (0.2.0) ice_nine (~> 0.11.0) memoizable (~> 0.4.0) @@ -63,20 +77,20 @@ GEM abstract_type (~> 0.0.7) adamantium (~> 0.2) equalizer (~> 0.0.11) - arel (9.0.0) ast (2.4.1) - autoprefixer-rails (9.7.6) + autoprefixer-rails (9.8.6.1) execjs - bcrypt (3.1.13) + bcrypt (3.1.15) better_errors (2.7.1) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) + bigdecimal (2.0.0) bluecloth (2.2.0) bonsai-elasticsearch-rails (7.0.1) elasticsearch-model (< 8) elasticsearch-rails (< 8) - bootstrap (4.5.0) + bootstrap (4.5.2) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.14.3, < 2) sassc-rails (>= 2.0.0) @@ -140,16 +154,16 @@ GEM concord (0.1.5) adamantium (~> 0.2.0) equalizer (~> 0.0.9) - concurrent-ruby (1.1.6) + concurrent-ruby (1.1.7) connection_pool (2.2.3) - coveralls (0.8.19) - json (>= 1.8, < 3) - simplecov (~> 0.12.0) - term-ansicolor (~> 1.3) - thor (~> 0.19.1) - tins (~> 1.6) + coveralls (0.7.1) + multi_json (~> 1.3) + rest-client + simplecov (>= 0.7) + term-ansicolor + thor crass (1.0.6) - csv_shaper (1.3.1) + csv_shaper (1.3.2) activesupport (>= 3.0.0) dalli (2.7.10) database_cleaner (1.8.5) @@ -159,10 +173,12 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - diff-lcs (1.3) + diff-lcs (1.4.4) discard (1.2.0) activerecord (>= 4.2, < 7) docile (1.1.5) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) dotenv-rails (2.7.6) dotenv (= 2.7.6) @@ -183,7 +199,7 @@ GEM equalizer (0.0.11) erubi (1.9.0) erubis (2.7.0) - excon (0.73.0) + excon (0.76.0) execjs (2.7.0) factory_bot (6.1.0) activesupport (>= 5.0.0) @@ -196,11 +212,11 @@ GEM multipart-post (>= 1.2, < 3) faraday_middleware (1.0.0) faraday (~> 1.0) - ffi (1.13.0) + ffi (1.13.1) flickraw (0.9.10) font-awesome-sass (5.13.0) sassc (>= 1.11) - friendly_id (5.3.0) + friendly_id (5.4.0) activerecord (>= 4.0.0) geocoder (1.6.3) gibbon (1.2.1) @@ -243,7 +259,10 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - httparty (0.18.0) + http-accept (1.7.0) + http-cookie (1.0.3) + domain_name (~> 0.5) + httparty (0.18.1) mime-types (~> 3.0) multi_xml (>= 0.5.2) i18n (1.8.5) @@ -271,7 +290,7 @@ GEM js-routes (1.4.9) railties (>= 4) sprockets-rails - json (2.3.0) + json (2.3.1) json-schema (2.8.1) addressable (>= 2.4) jsonapi-resources (0.10.2) @@ -279,7 +298,7 @@ GEM concurrent-ruby railties (>= 4.1) jsonapi-swagger (0.8.0) - jwt (2.2.1) + jwt (2.2.2) kgio (2.11.3) kramdown (2.3.0) rexml @@ -292,7 +311,7 @@ GEM listen (3.2.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.6.0) + loofah (2.7.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) @@ -320,9 +339,10 @@ GEM mini_portile2 (2.4.0) minitest (5.14.1) moneta (1.0.0) - multi_json (1.14.1) + multi_json (1.15.0) multi_xml (0.6.0) multipart-post (2.1.1) + netrc (0.11.0) nio4r (2.5.2) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) @@ -333,11 +353,12 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - oj (3.10.8) + oj (3.10.12) + bigdecimal (>= 1.0, < 3) omniauth (1.9.1) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) - omniauth-facebook (6.0.0) + omniauth-facebook (7.0.0) omniauth-oauth2 (~> 1.2) omniauth-flickr (0.0.20) multi_json (~> 1.12) @@ -345,8 +366,8 @@ GEM omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.6.0) - oauth2 (~> 1.1) + omniauth-oauth2 (1.7.0) + oauth2 (~> 1.4) omniauth (~> 1.9) omniauth-twitter (1.4.0) omniauth-oauth (~> 1.1) @@ -355,7 +376,7 @@ GEM parallel (1.19.2) parser (2.7.1.4) ast (~> 2.4.1) - percy-capybara (4.0.2) + percy-capybara (4.2.0) pg (0.21.0) platform-api (3.0.0) heroics (~> 0.1.1) @@ -374,18 +395,20 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.2.4.3) - actioncable (= 5.2.4.3) - actionmailer (= 5.2.4.3) - actionpack (= 5.2.4.3) - actionview (= 5.2.4.3) - activejob (= 5.2.4.3) - activemodel (= 5.2.4.3) - activerecord (= 5.2.4.3) - activestorage (= 5.2.4.3) - activesupport (= 5.2.4.3) + rails (6.0.3.2) + actioncable (= 6.0.3.2) + actionmailbox (= 6.0.3.2) + actionmailer (= 6.0.3.2) + actionpack (= 6.0.3.2) + actiontext (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) + activemodel (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) bundler (>= 1.3.0) - railties (= 5.2.4.3) + railties (= 6.0.3.2) sprockets-rails (>= 2.0.0) rails-assets-leaflet (1.5.1) rails-assets-leaflet.markercluster (1.4.1) @@ -399,20 +422,20 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - rails-i18n (5.1.3) + rails-i18n (6.0.0) i18n (>= 0.7, < 2) - railties (>= 5.0, < 6) + railties (>= 6.0.0, < 7) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging rails_serve_static_assets (0.0.5) rails_stdout_logging (0.0.5) - railties (5.2.4.3) - actionpack (= 5.2.4.3) - activesupport (= 5.2.4.3) + railties (6.0.3.2) + actionpack (= 6.0.3.2) + activesupport (= 6.0.3.2) method_source rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + thor (>= 0.20.3, < 2.0) rainbow (3.0.0) raindrops (0.19.1) rake (13.0.1) @@ -425,6 +448,11 @@ GEM responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) rexml (3.2.4) rspec (3.9.0) rspec-core (~> 3.9.0) @@ -466,23 +494,23 @@ GEM rswag-ui (2.3.1) actionpack (>= 3.1, < 7.0) railties (>= 3.1, < 7.0) - rubocop (0.88.0) + rubocop (0.89.1) parallel (~> 1.10) parser (>= 2.7.1.1) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.7) rexml - rubocop-ast (>= 0.1.0, < 1.0) + rubocop-ast (>= 0.3.0, < 1.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (0.2.0) - parser (>= 2.7.0.1) + rubocop-ast (0.3.0) + parser (>= 2.7.1.4) rubocop-rails (2.7.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 0.87.0) - rubocop-rspec (1.42.0) - rubocop (>= 0.87.0) + rubocop-rspec (1.43.2) + rubocop (~> 0.87) ruby-progressbar (1.10.1) ruby-units (2.3.1) ruby-vips (2.0.17) @@ -505,7 +533,7 @@ GEM sprockets (> 3.0) sprockets-rails tilt - scout_apm (2.6.8) + scout_apm (2.6.9) parser searchkick (4.4.1) activemodel (>= 5) @@ -514,12 +542,12 @@ GEM selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) - sexp_processor (4.14.1) + sexp_processor (4.15.0) sidekiq (6.1.1) connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.2.0) - simplecov (0.12.0) + simplecov (0.13.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -538,7 +566,7 @@ GEM tins (~> 1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - thor (0.19.4) + thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) timecop (0.9.1) @@ -549,6 +577,9 @@ GEM thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) unicode-display_width (1.7.0) unicorn (5.6.0) kgio (~> 2.6) @@ -575,7 +606,7 @@ GEM nokogiri (>= 1.2.0) rack (>= 1.0) rack-test (>= 0.5.3) - websocket-driver (0.7.2) + websocket-driver (0.7.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) will_paginate (3.3.0) @@ -584,12 +615,13 @@ GEM xmlrpc (0.3.0) xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.4.0) PLATFORMS ruby DEPENDENCIES - active_median (= 0.1.4) + active_median (= 0.2.0) active_record_union active_utils better_errors @@ -614,7 +646,7 @@ DEPENDENCIES dalli database_cleaner devise - discard (~> 1.0) + discard (>= 1.2) dotenv-rails elasticsearch (< 7.0.0) factory_bot_rails @@ -651,14 +683,14 @@ DEPENDENCIES omniauth-facebook omniauth-flickr (>= 0.0.15) omniauth-twitter - percy-capybara (~> 4.0.0) + percy-capybara pg (< 1.0.0) platform-api puma query_diet rack-cors rack-protection (>= 2.0.1) - rails (= 5.2.4.3) + rails (= 6.0.3.2) rails-assets-leaflet.markercluster! rails-controller-testing rails_12factor diff --git a/app/assets/stylesheets/_crops.scss b/app/assets/stylesheets/_crops.scss index 4649812df..26fd86e8b 100644 --- a/app/assets/stylesheets/_crops.scss +++ b/app/assets/stylesheets/_crops.scss @@ -7,7 +7,7 @@ bottom: 0; color: $white; margin: 0; - opacity: .8; + opacity: 0.8; position: absolute; text-align: center; width: 100%; @@ -22,7 +22,7 @@ h5.crop-sci-name { background-color: $beige; color: $black; - font-size: .7em; + font-size: 0.7em; margin-top: 0; padding-top: 0; } @@ -45,7 +45,7 @@ .planting { .crop-card { height: 100%; - margin: .1em; + margin: 0.1em; min-height: 300px; } @@ -85,7 +85,7 @@ @include media-breakpoint-down(xs) { .index-cards { .crop-thumbnail { - margin: .2em; + margin: 0.2em; width: 30%; } } diff --git a/app/assets/stylesheets/_harvests.scss b/app/assets/stylesheets/_harvests.scss index b8c317ea5..7c248f9ea 100644 --- a/app/assets/stylesheets/_harvests.scss +++ b/app/assets/stylesheets/_harvests.scss @@ -2,7 +2,7 @@ .text { color: $white; margin: 0; - opacity: .8; + opacity: 0.8; position: absolute; text-align: center; top: 30%; @@ -12,7 +12,7 @@ h3, h4, h5 { - margin: 0 + margin: 0; } h3 { diff --git a/app/assets/stylesheets/_members.scss b/app/assets/stylesheets/_members.scss index 84df767ca..da5f4b206 100644 --- a/app/assets/stylesheets/_members.scss +++ b/app/assets/stylesheets/_members.scss @@ -2,7 +2,7 @@ border-radius: 12px; height: 200px; margin: 1em; - padding: .25em; + padding: 0.25em; div { display: inline-block; @@ -11,12 +11,11 @@ } } -.member-thumbnail div~div { +.member-thumbnail div ~ div { padding-left: 1em; width: 15em; } - .member-chip { background-color: lighten($green, 30%); @@ -35,7 +34,7 @@ } .location-not-set { - background-image: image-url('location-not-set.en.png'); + background-image: image-url("location-not-set.en.png"); background-position: center; background-repeat: no-repeat; height: 250px; diff --git a/app/assets/stylesheets/_variables.scss b/app/assets/stylesheets/_variables.scss index b39c7f297..3e1848e19 100644 --- a/app/assets/stylesheets/_variables.scss +++ b/app/assets/stylesheets/_variables.scss @@ -15,30 +15,36 @@ $body-bg: $beige; $text-color: $brown; $link-color: $green; -$default-font: 'Raleway', -"Fira Sans", -Helvetica, -Arial, -sans-serif; +$default-font: "Raleway", "Fira Sans", Helvetica, Arial, sans-serif; -$primary: (color: $green, +$primary: ( + color: $green, dark: darken($green, 20%), - light: lighten($green, 20%)); -$secondary: (color: $blue, + light: lighten($green, 20%), +); +$secondary: ( + color: $blue, dark: darken($blue, 20%), - light: lighten($blue, 20%)); -$success: (color: $green, + light: lighten($blue, 20%), +); +$success: ( + color: $green, dark: darken($green, 20%), - light: lighten($green, 20%)); -$danger: (color: $red, + light: lighten($green, 20%), +); +$danger: ( + color: $red, dark: darken($red, 20%), - light: lighten($red, 20%)); -$dark: (color: $brown, + light: lighten($red, 20%), +); +$dark: ( + color: $brown, dark: darken($brown, 20%), - light: lighten($brown, 20%)); + light: lighten($brown, 20%), +); - -$grid-breakpoints: ( // Extra small screen / phone +$grid-breakpoints: ( + // Extra small screen / phone xs: 0, // Small screen / phone sm: 576px, @@ -47,7 +53,8 @@ $grid-breakpoints: ( // Extra small screen / phone // Large screen / desktop lg: 1200px, // Extra large screen / wide desktop - xl: 1800px); + xl: 1800px +); // Nav bar $navbar-default-bg: $brown; diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 6c7d7ffe5..1825ff9fb 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,33 +1,32 @@ // Import original variables so they can be used in overrides -@import 'variables'; -@import 'material'; - -@import 'jquery-ui/autocomplete'; -@import 'bootstrap-datepicker'; -@import 'leaflet'; -@import 'leaflet.markercluster'; +@import "variables"; +@import "material"; +@import "jquery-ui/autocomplete"; +@import "bootstrap-datepicker"; +@import "leaflet"; +@import "leaflet.markercluster"; // Font Awesome -@import 'font-awesome-sprockets'; -@import 'font-awesome'; -@import 'material_icons'; -@import 'rails_bootstrap_forms'; +@import "font-awesome-sprockets"; +@import "font-awesome"; +@import "material_icons"; +@import "rails_bootstrap_forms"; -@import 'overrides'; -@import 'mobile'; +@import "overrides"; +@import "mobile"; -@import 'crops'; -@import 'harvests'; -@import 'likes'; -@import 'members'; -@import 'notifications'; -@import 'plantings'; -@import 'photos'; -@import 'posts'; -@import 'seeds'; +@import "crops"; +@import "harvests"; +@import "likes"; +@import "members"; +@import "notifications"; +@import "plantings"; +@import "photos"; +@import "posts"; +@import "seeds"; -@import 'predictions'; -@import 'homepage'; -@import 'maps'; +@import "predictions"; +@import "homepage"; +@import "maps"; diff --git a/app/assets/stylesheets/mobile.scss b/app/assets/stylesheets/mobile.scss index 760e5eaa7..25a03fa84 100644 --- a/app/assets/stylesheets/mobile.scss +++ b/app/assets/stylesheets/mobile.scss @@ -1,7 +1,6 @@ // Overrides applying only to mobile view. This must be at the end of the overrides file. // Extra small devices (portrait phones, less than 576px) @include media-breakpoint-down(md) { - .container { margin: 2px; padding: 0; @@ -11,7 +10,7 @@ width: 100%; } - .navbar .nav>li { + .navbar .nav > li { display: block; } @@ -33,11 +32,10 @@ } .site-name:after { - content: ''; + content: ""; display: inline-block; width: 100%; } - } h1 { @@ -66,14 +64,13 @@ height: 300px; } - section .btn { width: 100%; } .index-cards { .card { - margin: .2em; + margin: 0.2em; width: 48%; // Shrink title to fit more on page @@ -87,7 +84,6 @@ object-fit: cover; width: 100%; } - } } } diff --git a/app/assets/stylesheets/overrides.scss b/app/assets/stylesheets/overrides.scss index edf152e83..1158d5268 100755 --- a/app/assets/stylesheets/overrides.scss +++ b/app/assets/stylesheets/overrides.scss @@ -10,9 +10,9 @@ body { .navbar-brand { .site-name { - font-family: 'Modak', cursive; + font-family: "Modak", cursive; font-size: 3em; - margin: .3em; + margin: 0.3em; } img { @@ -46,7 +46,6 @@ body { font-size: 5em; } - .ellipsis { overflow: hidden; text-overflow: ellipsis; @@ -82,9 +81,8 @@ h3 { font-size: 120%; } - section { - margin: .5em 0 0; + margin: 0.5em 0 0; padding: 0 0 1em; h2 { @@ -92,7 +90,7 @@ section { box-shadow: 1px 1px 1px 1px darken($beige, 20%); color: $white; font-weight: normal; - padding: .2em; + padding: 0.2em; a { color: $white; @@ -117,7 +115,7 @@ section { .card { background: $white; border-radius: 5%; - margin: .5em .5em .5em 0; + margin: 0.5em 0.5em 0.5em 0; width: 200px; .img-card { @@ -232,7 +230,6 @@ ul.associations { // footer footer { - #footer1, #footer2, #footer3 { @@ -370,7 +367,7 @@ ul.thumbnail-buttons { } .info { - padding: .5em; + padding: 0.5em; text-align: center; } } @@ -386,7 +383,7 @@ ul.thumbnail-buttons { display: inline-block; height: 30px; line-height: 30px; - margin: .2em; + margin: 0.2em; padding: 0 25px; a { @@ -411,14 +408,14 @@ ul.thumbnail-buttons { .progress-fade::before { background: $beige; bottom: 0; - content: ''; + content: ""; display: block; left: 0; - opacity: .7; + opacity: 0.7; position: absolute; right: 0; top: 0; - transition: background .3s linear; + transition: background 0.3s linear; } .progress-fade:hover::before { diff --git a/app/controllers/conversations_controller.rb b/app/controllers/conversations_controller.rb index 2f794aaf2..4d80aba68 100644 --- a/app/controllers/conversations_controller.rb +++ b/app/controllers/conversations_controller.rb @@ -7,9 +7,10 @@ class ConversationsController < ApplicationController before_action :check_current_subject_in_conversation, only: %i(show update destroy) def index - @conversations = if @box.eql? "inbox" + @conversations = case @box + when "inbox" mailbox.inbox - elsif @box.eql? "sent" + when "sent" mailbox.sentbox else mailbox.trash @@ -59,7 +60,7 @@ class ConversationsController < ApplicationController 'sent' => { 'total' => mailbox.sentbox.size, 'unread' => 0 }, 'trash' => { 'total' => mailbox.trash.size, 'unread' => 0 } } - @box = if params[:box].blank? || !@boxes.keys.include?(params[:box]) + @box = if params[:box].blank? || @boxes.keys.exclude?(params[:box]) 'inbox' else params[:box] diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index baf56aa1a..265103b7b 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -62,9 +62,6 @@ class CropsController < ApplicationController end def show - @crop = Crop.includes( - :scientific_names, :alternate_names, :parent, :varieties - ).find_by!(slug: params[:slug]) respond_to do |format| format.html do @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) @@ -89,7 +86,6 @@ class CropsController < ApplicationController end def edit - @crop = Crop.find_by!(slug: params[:slug]) @crop.alternate_names.build if @crop.alternate_names.blank? @crop.scientific_names.build if @crop.scientific_names.blank? end @@ -110,14 +106,13 @@ class CropsController < ApplicationController end def update - @crop = Crop.find_by!(slug: params[:slug]) - if can?(:wrangle, @crop) @crop.approval_status = 'rejected' if params.fetch("reject", false) @crop.approval_status = 'approved' if params.fetch("approve", false) end @crop.creator = current_member if @crop.approval_status == "pending" + if @crop.update(crop_params) recreate_names('alt_name', 'alternate') recreate_names('sci_name', 'scientific') @@ -145,26 +140,22 @@ class CropsController < ApplicationController def notifier case @crop.approval_status when "approved" - Notifier.crop_request_approved(@crop.requester, @crop) + NotifierMailer.crop_request_approved(@crop.requester, @crop) when "rejected" - Notifier.crop_request_rejected(@crop.requester, @crop) + NotifierMailer.crop_request_rejected(@crop.requester, @crop) end end def save_crop_names - params[:alt_name]&.values&.each do |value| - create_name!('alternate', value) unless value.empty? - end - params[:sci_name]&.values&.each do |value| - create_name!('scientific', value) unless value.empty? - end + AlternateName.create!(names_params(:alt_name).map { |n| { name: n, creator_id: current_member.id, crop_id: @crop.id } }) + ScientificName.create!(names_params(:sci_name).map { |n| { name: n, creator_id: current_member.id, crop_id: @crop.id } }) end def notify_wranglers return if current_member.role? :crop_wrangler Role.crop_wranglers&.each do |w| - Notifier.new_crop_request(w, @crop).deliver_now! + NotifierMailer.new_crop_request(w, @crop).deliver_now! end end @@ -187,19 +178,18 @@ class CropsController < ApplicationController def crop_params params.require(:crop).permit( - :en_wikipedia_url, - :name, - :parent_id, - :perennial, - :request_notes, - :reason_for_rejection, + :name, :en_wikipedia_url, + :parent_id, :perennial, + :request_notes, :reason_for_rejection, :rejection_notes, - scientific_names_attributes: %i(scientific_name - _destroy - id) + scientific_names_attributes: %i(scientific_name _destroy id) ) end + def names_params(name_type) + params.require(name_type).values&.reject { |n| n.empty? } + end + def filename "Growstuff-Crops-#{Time.zone.now.to_s(:number)}.csv" end @@ -212,8 +202,7 @@ class CropsController < ApplicationController owner: { only: %i(id login_name location latitude longitude) } } }, - scientific_names: { only: [:name] }, - alternate_names: { only: [:name] } + scientific_names: { only: [:name] }, alternate_names: { only: [:name] } } } end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 322c5a66e..739878909 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -2,6 +2,7 @@ class PhotosController < ApplicationController before_action :authenticate_member!, except: %i(index show) + before_action :set_crop_and_planting, only: :index after_action :expire_homepage, only: %i(create destroy) load_and_authorize_resource respond_to :html, :json @@ -13,19 +14,10 @@ class PhotosController < ApplicationController end def index - where = {} - if params[:crop_slug] - @crop = Crop.find params[:crop_slug] - where = { crops: @crop.id } - elsif params[:planting_id] - @planting = Planting.find params[:planting_id] - where = { planting_id: @planting.id } - end - @photos = Photo.search( load: false, boost_by: [:created_at], - where: where, + where: index_where_clause, page: params[:page], limit: Photo.per_page ) @@ -112,4 +104,19 @@ class PhotosController < ApplicationController pager.replace photos end end + + def index_where_clause + if params[:crop_slug] + { crops: @crop.id } + elsif params[:planting_id] + { planting_id: @planting.id } + else + {} + end + end + + def set_crop_and_planting + @crop = Crop.find params[:crop_slug] if params[:crop_slug] + @planting = Planting.find params[:planting_id] if params[:planting_id] + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d47919429..a84b5d6b7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -29,9 +29,9 @@ module ApplicationHelper end def required_field_help_text - asterisk = content_tag :span, '*', class: ['red'] - text = content_tag :em, 'denotes a required field' - content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom'] + asterisk = tag.span('*', class: ['red']) + text = tag.em('denotes a required field') + tag.div(asterisk + ' '.html_safe + text, class: ['margin-bottom']) end # @@ -69,14 +69,13 @@ module ApplicationHelper def show_inactive_tickbox_path(type, owner: nil, crop: nil, show_all: false) all = show_all ? '' : 1 - path = if owner.present? - public_send("member_#{type}_path", owner, all: all) - elsif crop.present? - public_send("crop_#{type}_path", crop, all: all) - else - public_send("#{type}_path", all: all) - end - path + if owner.present? + public_send("member_#{type}_path", owner, all: all) + elsif crop.present? + public_send("crop_#{type}_path", crop, all: all) + else + public_send("#{type}_path", all: all) + end end def title(type, owner, crop, planting) diff --git a/app/mailers/notifier.rb b/app/mailers/notifier_mailer.rb similarity index 98% rename from app/mailers/notifier.rb rename to app/mailers/notifier_mailer.rb index c2b68d796..345c3cad3 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier_mailer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Notifier < ApplicationMailer +class NotifierMailer < ApplicationMailer # include NotificationsHelper default from: "Growstuff <#{ENV['GROWSTUFF_EMAIL']}>" diff --git a/app/models/comment.rb b/app/models/comment.rb index 4ac02ba34..6590b6649 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -4,7 +4,7 @@ class Comment < ApplicationRecord belongs_to :author, class_name: 'Member', inverse_of: :comments belongs_to :post, counter_cache: true - scope :post_order, -> { reorder("created_at ASC") } # for display on post page + scope :post_order, -> { order(created_at: :asc) } # for display on post page after_create do recipient = post.author.id diff --git a/app/models/concerns/ownable.rb b/app/models/concerns/ownable.rb index 8dac572a5..f6405ddc6 100644 --- a/app/models/concerns/ownable.rb +++ b/app/models/concerns/ownable.rb @@ -4,8 +4,7 @@ module Ownable extend ActiveSupport::Concern included do - belongs_to :owner, class_name: 'Member', - foreign_key: 'owner_id', counter_cache: true + belongs_to :owner, class_name: 'Member', counter_cache: true default_scope { joins(:owner).merge(Member.kept) } # Ensures the owner still exists end diff --git a/app/models/crop.rb b/app/models/crop.rb index c5de8aa2c..b47999fb5 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -50,7 +50,7 @@ class Crop < ApplicationRecord ## Wikipedia urls are only necessary when approving a crop validates :en_wikipedia_url, format: { - with: %r{\Ahttps?:\/\/en\.wikipedia\.org\/wiki\/[[:alnum:]%_\.()-]+\z}, + with: %r{\Ahttps?://en\.wikipedia\.org/wiki/[[:alnum:]%_.()-]+\z}, message: 'is not a valid English Wikipedia URL' }, if: :approved? diff --git a/app/models/garden.rb b/app/models/garden.rb index bbf2e7c72..b0be0f03c 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -14,6 +14,8 @@ class Garden < ApplicationRecord # set up geocoding geocoded_by :location + before_validation :strip_blanks + after_validation :cleanup_area after_validation :geocode after_validation :empty_unwanted_geocodes after_save :mark_inactive_garden_plantings_as_finished @@ -25,7 +27,6 @@ class Garden < ApplicationRecord validates :location, length: { maximum: 255 } validates :slug, uniqueness: true - before_validation :strip_blanks validates :name, uniqueness: { scope: :owner_id } validates :name, format: { without: /\n/, message: "must contain no newlines" }, @@ -51,8 +52,6 @@ class Garden < ApplicationRecord message: "%s is not a valid area unit" }, allow_blank: true - after_validation :cleanup_area - def cleanup_area self.area = nil if area&.zero? self.area_unit = nil if area.blank? diff --git a/app/models/photo.rb b/app/models/photo.rb index 18cf971f7..64bf79348 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -7,7 +7,7 @@ class Photo < ApplicationRecord PHOTO_CAPABLE = %w(Garden Planting Harvest Seed Post Crop).freeze - has_many :photo_associations, foreign_key: :photo_id, dependent: :delete_all, inverse_of: :photo + has_many :photo_associations, dependent: :delete_all, inverse_of: :photo # This doesn't work, ActiveRecord tries to use the polymoriphinc photographable # relationship instead. diff --git a/app/models/planting.rb b/app/models/planting.rb index dc73b196c..3247fc015 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -25,10 +25,9 @@ class Planting < ApplicationRecord # # Ancestry of food - belongs_to :parent_seed, class_name: 'Seed', # parent - foreign_key: 'parent_seed_id', - optional: true, - inverse_of: :child_plantings + belongs_to :parent_seed, class_name: 'Seed', # parent, + optional: true, + inverse_of: :child_plantings has_many :child_seeds, class_name: 'Seed', # children foreign_key: 'parent_planting_id', inverse_of: :parent_planting, diff --git a/app/models/post.rb b/app/models/post.rb index e5a84c82e..057402187 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -14,10 +14,10 @@ class Post < ApplicationRecord has_many :crop_posts, dependent: :delete_all has_many :crops, through: :crop_posts + after_create :send_notification # # Triggers after_save :update_crop_posts_association - after_create :send_notification default_scope { joins(:author).merge(Member.kept) } # Ensures the owner still exists @@ -68,7 +68,7 @@ class Post < ApplicationRecord crop_name = Regexp.last_match(1) crop = Crop.case_insensitive_name(crop_name).first # create association - crops << crop if crop && !crops.include?(crop) + crops << crop if crop && crops.exclude?(crop) end end @@ -78,12 +78,12 @@ class Post < ApplicationRecord body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_REGEX) do |_m| # find member case-insensitively and add to list of recipients member = Member.case_insensitive_login_name(Regexp.last_match(1)).first - recipients << member if member && !recipients.include?(member) + recipients << member if member && recipients.exclude?(member) end body.scan(Haml::Filters::GrowstuffMarkdown::MEMBER_AT_REGEX) do |_m| # find member case-insensitively and add to list of recipients - member = Member.case_insensitive_login_name(Regexp.last_match(1)[1..-1]).first - recipients << member if member && !recipients.include?(member) + member = Member.case_insensitive_login_name(Regexp.last_match(1)[1..]).first + recipients << member if member && recipients.exclude?(member) end # don't send notifications to yourself recipients.map(&:id).each do |recipient_id| diff --git a/app/models/seed.rb b/app/models/seed.rb index 2e853462f..761f40ffe 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -16,7 +16,7 @@ class Seed < ApplicationRecord # # Relationships belongs_to :crop - belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id', + belongs_to :parent_planting, class_name: 'Planting', optional: true, inverse_of: :child_seeds # parent has_many :child_plantings, class_name: 'Planting', foreign_key: 'parent_seed_id', dependent: :nullify, diff --git a/app/views/notifier/_planting.haml b/app/views/notifier_mailer/_planting.haml similarity index 100% rename from app/views/notifier/_planting.haml rename to app/views/notifier_mailer/_planting.haml diff --git a/app/views/notifier/_signature.html.haml b/app/views/notifier_mailer/_signature.html.haml similarity index 100% rename from app/views/notifier/_signature.html.haml rename to app/views/notifier_mailer/_signature.html.haml diff --git a/app/views/notifier/crop_request_approved.html.haml b/app/views/notifier_mailer/crop_request_approved.html.haml similarity index 100% rename from app/views/notifier/crop_request_approved.html.haml rename to app/views/notifier_mailer/crop_request_approved.html.haml diff --git a/app/views/notifier/crop_request_rejected.html.haml b/app/views/notifier_mailer/crop_request_rejected.html.haml similarity index 100% rename from app/views/notifier/crop_request_rejected.html.haml rename to app/views/notifier_mailer/crop_request_rejected.html.haml diff --git a/app/views/notifier/new_crop_request.html.haml b/app/views/notifier_mailer/new_crop_request.html.haml similarity index 100% rename from app/views/notifier/new_crop_request.html.haml rename to app/views/notifier_mailer/new_crop_request.html.haml diff --git a/app/views/notifier/notify.html.haml b/app/views/notifier_mailer/notify.html.haml similarity index 100% rename from app/views/notifier/notify.html.haml rename to app/views/notifier_mailer/notify.html.haml diff --git a/app/views/notifier/planting_reminder.html.haml b/app/views/notifier_mailer/planting_reminder.html.haml similarity index 100% rename from app/views/notifier/planting_reminder.html.haml rename to app/views/notifier_mailer/planting_reminder.html.haml diff --git a/app/views/places/_map_attribution.html.haml b/app/views/places/_map_attribution.html.haml index f6c2e2a28..dc002de2c 100644 --- a/app/views/places/_map_attribution.html.haml +++ b/app/views/places/_map_attribution.html.haml @@ -2,5 +2,3 @@ Map data © = link_to "OpenStreetMap", "https://openstreetmap.org" contributors under = link_to "ODbL", "https://www.openstreetmap.org/copyright" -| Imagery © -= link_to "CloudMade", "https://cloudmade.com" diff --git a/bin/setup b/bin/setup index c2e43ceb2..a593c68ec 100755 --- a/bin/setup +++ b/bin/setup @@ -2,7 +2,6 @@ # frozen_string_literal: true require 'fileutils' -include FileUtils # path to your application root. APP_ROOT = File.expand_path('..', __dir__) @@ -11,24 +10,31 @@ def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do - # This script is a starting point to setup your application. +FileUtils.chdir APP_ROOT do + # This script is a way to setup or update your development environment automatically. + # This script is idempotent, so that you can run it at anytime and get an expectable outcome. # Add necessary setup steps to this file. puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') + puts "Install JavaScript dependencies" + system('bin/yarn') - # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' - # end + puts "\n== Copying sample files ==" + unless File.exist?('config/database.yml') + FileUtils.cp 'config/database.yml.example', 'config/database.yml' + end + unless File.exist?('.env') + FileUtils.cp 'env-example', '.env' + end + + puts "\n== Checking for elastic search ==" + system! 'script/check_elasticsearch.sh' puts "\n== Preparing database ==" - system! 'bin/rails db:setup' + system! 'bin/rails db:prepare' puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' diff --git a/config.ru b/config.ru index 61c04e13f..667e328d5 100644 --- a/config.ru +++ b/config.ru @@ -2,5 +2,5 @@ # This file is used by Rack-based servers to start the application. -require ::File.expand_path('../config/environment', __FILE__) +require ::File.expand_path('config/environment', __dir__) run Rails.application diff --git a/config/application.rb b/config/application.rb index b86ae6c53..fc4928511 100644 --- a/config/application.rb +++ b/config/application.rb @@ -12,21 +12,7 @@ Bundler.require(*Rails.groups) module Growstuff class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 5.1 - - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. - - # Custom directories with classes and modules you want to be autoloadable. - # config.autoload_paths += %W(#{config.root}/extras) - - # Only load the plugins named here, in the order given (default is alphabetical). - # :all can be used as a placeholder for all plugins not explicitly named. - # config.plugins = [ :exception_notification, :ssl_requirement, :all ] - - # Activate observers that should always be running. - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + config.load_defaults 6.0 I18n.config.enforce_available_locales = true @@ -52,17 +38,6 @@ module Growstuff # Enable escaping HTML in JSON. config.active_support.escape_html_entities_in_json = true - # Use SQL instead of Active Record's schema dumper when creating the database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - - # Enforce whitelist mode for mass assignment. - # This will create an empty whitelist of attributes available for mass-assignment for all models - # in your app. As such, your models will need to explicitly whitelist or blacklist accessible - # parameters by using an attr_accessible or attr_protected declaration. - # config.active_record.whitelist_attributes = true - # Enable the asset pipeline config.assets.enabled = true @@ -93,17 +68,6 @@ module Growstuff Gibbon::API.throws_exceptions = false config.newsletter_list_id = ENV['GROWSTUFF_MAILCHIMP_NEWSLETTER_ID'] - # This is Growstuff's global Cloudmade key. If you fork Growstuff for - # another project/website not run by the folks at http://growstuff.org/, - # then please change this key. (You can get one of your own at - # http://account.cloudmade.com/ and it's free/gratis for up to 500k tiles.) - # We'd much prefer to set this as an environment variable (as we do - # with most other things) but it turns out those aren't available at - # asset compile time on Heroku, when we need this to insert into our - # Javascript. Sigh. And yes, we know about user-env-compile but it - # didn't work for us. - config.cloudmade_key = '29a2d9e3cb3d429490a8f338b2388b1d' - # config.active_record.raise_in_transactional_callbacks = true config.middleware.insert_before 0, Rack::Cors do allow do diff --git a/config/environments/test.rb b/config/environments/test.rb index f875cc078..1cfd2c15a 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -61,7 +61,7 @@ Geocoder.configure(lookup: :test) Geocoder::Lookup::Test.add_stub( "Amundsen-Scott Base, Antarctica", [ { - 'coordinates' => [-90.0, 0.0] + 'coordinates' => [-90.0, 0.0] } ] ) @@ -69,7 +69,7 @@ Geocoder::Lookup::Test.add_stub( Geocoder::Lookup::Test.add_stub( "Philippines", [ { - 'coordinates' => [12.7503486, 122.7312101], + 'coordinates' => [12.7503486, 122.7312101], 'address' => 'Manila, Mnl, Philippines', 'state' => 'Manila', 'state_code' => 'Mnl', diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 497f5667c..f3bcce546 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -12,6 +12,8 @@ # policy.object_src :none # policy.script_src :self, :https # policy.style_src :self, :https +# # If you are using webpack-dev-server then specify webpack-dev-server host +# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? # # Specify URI for violation reports # # policy.report_uri "/csp-violation-report-endpoint" @@ -20,6 +22,9 @@ # If you are using UJS then enable automatic nonce generation # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } +# Set the nonce only to specific directives +# Rails.application.config.content_security_policy_nonce_directives = %w(script-src) + # Report CSP violations to a specified URI # For further information see the following documentation: # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 66c3995ae..3773df6c8 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -237,4 +237,3 @@ Devise.setup do |config| # Later we may wish to ask for user_photos,user_location, however this means we need to be reviewed by facebook config.omniauth :facebook, ENV['GROWSTUFF_FACEBOOK_KEY'], ENV['GROWSTUFF_FACEBOOK_SECRET'], scope: 'email,public_profile', display: 'page', info_fields: 'email,name,first_name,last_name,id' end - diff --git a/config/initializers/new_framework_defaults_6_0.rb b/config/initializers/new_framework_defaults_6_0.rb new file mode 100644 index 000000000..92240ef5f --- /dev/null +++ b/config/initializers/new_framework_defaults_6_0.rb @@ -0,0 +1,45 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 6.0 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Don't force requests from old versions of IE to be UTF-8 encoded. +# Rails.application.config.action_view.default_enforce_utf8 = false + +# Embed purpose and expiry metadata inside signed and encrypted +# cookies for increased security. +# +# This option is not backwards compatible with earlier Rails versions. +# It's best enabled when your entire app is migrated and stable on 6.0. +# Rails.application.config.action_dispatch.use_cookies_with_metadata = true + +# Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification. +# Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false + +# Return false instead of self when enqueuing is aborted from a callback. +# Rails.application.config.active_job.return_false_on_aborted_enqueue = true + +# Send Active Storage analysis and purge jobs to dedicated queues. +# Rails.application.config.active_storage.queues.analysis = :active_storage_analysis +# Rails.application.config.active_storage.queues.purge = :active_storage_purge + +# When assigning to a collection of attachments declared via `has_many_attached`, replace existing +# attachments instead of appending. Use #attach to add new attachments without replacing existing ones. +# Rails.application.config.active_storage.replace_on_assign_to_many = true + +# Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail. +# +# The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob), +# will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions. +# If you send mail in the background, job workers need to have a copy of +# MailDeliveryJob to ensure all delivery jobs are processed properly. +# Make sure your entire app is migrated and stable on 6.0 before using this setting. +# Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob" + +# Enable the same cache key to be reused when the object being cached of type +# `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count) +# of the relation's cache key into the cache version to support recycling cache key. +# Rails.application.config.active_record.collection_cache_versioning = true diff --git a/config/puma.rb b/config/puma.rb index bcf8670ad..a47b28904 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,45 +1,40 @@ # frozen_string_literal: true -# Overview of config: -# https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server - # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } -threads threads_count, threads_count +max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +threads min_threads_count, max_threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch('PORT') { 3000 } +port ENV.fetch("PORT") { 3000 } # Specifies the `environment` that Puma will run in. # -environment ENV.fetch('RAILS_ENV') { 'development' } +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } # Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together +# Workers are forked web server processes. If using threads and workers together # the concurrency of the application would be max `threads` * `workers`. # Workers do not work on JRuby or Windows (both of which do not support # processes). # -workers ENV.fetch('WEB_CONCURRENCY') { 2 } +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code # before forking the application. This takes advantage of Copy On Write # process behavior so workers use less memory. # -preload_app! - -on_worker_boot do - # Worker specific setup for Rails 4.1+ - # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot - ActiveRecord::Base.establish_connection -end +# preload_app! # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart diff --git a/db/migrate/20171028230429_create_median_function.rb b/db/migrate/20171028230429_create_median_function.rb index 1b1e44e57..ec86bced1 100644 --- a/db/migrate/20171028230429_create_median_function.rb +++ b/db/migrate/20171028230429_create_median_function.rb @@ -2,10 +2,11 @@ class CreateMedianFunction < ActiveRecord::Migration[4.2] def up - ActiveMedian.create_function + # commented out, because we upgraded the gem later and this function was removed + # ActiveMedian.create_function end def down - ActiveMedian.drop_function + # ActiveMedian.drop_function end end diff --git a/db/migrate/20191119030244_cms_tags.rb b/db/migrate/20191119030244_cms_tags.rb index acaba82c8..87dd06933 100644 --- a/db/migrate/20191119030244_cms_tags.rb +++ b/db/migrate/20191119030244_cms_tags.rb @@ -6,16 +6,16 @@ class CmsTags < ActiveRecord::Migration[5.2] layout.content = layout.content.gsub(%r{\{\{ ?cms:page:([\w/]+) ?\}\}}, '{{ cms:text \1 }}') if layout.content.is_a? String # {{cms:page:page_header:string}} -> {{ cms:text page_header }} - layout.content = layout.content.gsub(/\{\{ ?cms:page:([\w]+):string ?\}\}/, '{{ cms:text \1 }}') if layout.content.is_a? String + layout.content = layout.content.gsub(/\{\{ ?cms:page:(\w+):string ?\}\}/, '{{ cms:text \1 }}') if layout.content.is_a? String # {{cms:page:content:rich_text}} -> {{ cms:wysiwyg content }} - layout.content = layout.content.gsub(/\{\{ ?cms:page:([\w]+):rich_text ?\}\}/, '{{ cms:wysiwyg \1 }}') if layout.content.is_a? String - layout.content = layout.content.gsub(/\{\{ ?cms:page:([\w]+):([^:]*) ?\}\}/, '{{ cms:\2 \1 }}') if layout.content.is_a? String + layout.content = layout.content.gsub(/\{\{ ?cms:page:(\w+):rich_text ?\}\}/, '{{ cms:wysiwyg \1 }}') if layout.content.is_a? String + layout.content = layout.content.gsub(/\{\{ ?cms:page:(\w+):([^:]*) ?}}/, '{{ cms:\2 \1 }}') if layout.content.is_a? String if layout.content.is_a? String - layout.content = layout.content.gsub(/\{\{ ?cms:field:([\w]+):string ?\}\}/, '{{ cms:text \1, render: false }}') + layout.content = layout.content.gsub(/\{\{ ?cms:field:(\w+):string ?\}\}/, '{{ cms:text \1, render: false }}') end if layout.content.is_a? String - layout.content = layout.content.gsub(/\{\{ ?cms:field:([\w]+):([^:]*) ?\}\}/, '{{ cms:\2 \1, render: false }}') + layout.content = layout.content.gsub(/\{\{ ?cms:field:(\w+):([^:]*) ?}}/, '{{ cms:\2 \1, render: false }}') end # {{ cms:partial:main/homepage }} -> {{ cms:partial "main/homepage" }} @@ -27,7 +27,7 @@ class CmsTags < ActiveRecord::Migration[5.2] if layout.content.is_a? String layout.content = layout.content.gsub(%r{\{\{ ?cms:(\w+):([\w/-]+):([\w/-]+):([\w/-]+) ?\}\}}, '{{ cms:\1 \2 \3 \4}}') end - layout.content = layout.content.gsub(/\{\{ ?cms:(\w+):([\w]+):([^:]*) ?\}\}/, '{{ cms:\1 \2, "\3" }}') if layout.content.is_a? String + layout.content = layout.content.gsub(/\{\{ ?cms:(\w+):(\w+):([^:]*) ?}}/, '{{ cms:\1 \2, "\3" }}') if layout.content.is_a? String layout.content = layout.content.gsub(/cms:rich_text/, 'cms:wysiwyg') if layout.content.is_a? String layout.content = layout.content.gsub(/cms:integer/, 'cms:number') if layout.content.is_a? String if layout.content.is_a? String @@ -49,20 +49,20 @@ class CmsTags < ActiveRecord::Migration[5.2] fragment.content = fragment.content.gsub(%r{\{\{ ?cms:partial:([\w/]+) ?\}\}}, '{{ cms:partial \1 }}') end - fragment.content = fragment.content.gsub(/\{\{ ?cms:page:([\w]+):string ?\}\}/, '{{ cms:text \1 }}') if fragment.content.is_a? String + fragment.content = fragment.content.gsub(/\{\{ ?cms:page:(\w+):string ?\}\}/, '{{ cms:text \1 }}') if fragment.content.is_a? String if fragment.content.is_a? String - fragment.content = fragment.content.gsub(/\{\{ ?cms:page:([\w]+):rich_text ?\}\}/, '{{ cms:wysiwyg \1 }}') + fragment.content = fragment.content.gsub(/\{\{ ?cms:page:(\w+):rich_text ?\}\}/, '{{ cms:wysiwyg \1 }}') end fragment.content = fragment.content.gsub(%r{\{\{ ?cms:page:([\w/]+) ?\}\}}, '{{ cms:text \1 }}') if fragment.content.is_a? String - fragment.content = fragment.content.gsub(/\{\{ ?cms:page:([\w]+):([^:]*) ?\}\}/, '{{ cms:\2 \1 }}') if fragment.content.is_a? String + fragment.content = fragment.content.gsub(/\{\{ ?cms:page:(\w+):([^:]*) ?}}/, '{{ cms:\2 \1 }}') if fragment.content.is_a? String if fragment.content.is_a? String - fragment.content = fragment.content.gsub(/\{\{ ?cms:field:([\w]+):([^:]*) ?\}\}/, '{{ cms:\2 \1, render: false }}') + fragment.content = fragment.content.gsub(/\{\{ ?cms:field:(\w+):([^:]*) ?}}/, '{{ cms:\2 \1, render: false }}') end - fragment.content = fragment.content.gsub(/\{\{ ?cms:(\w+):([\w]+) ?\}\}/, '{{ cms:\1 \2 }}') if fragment.content.is_a? String + fragment.content = fragment.content.gsub(/\{\{ ?cms:(\w+):(\w+) ?\}\}/, '{{ cms:\1 \2 }}') if fragment.content.is_a? String if fragment.content.is_a? String - fragment.content = fragment.content.gsub(/\{\{ ?cms:(\w+):([\w]+):([^:]*) ?\}\}/, '{{ cms:\1 \2, "\3" }}') + fragment.content = fragment.content.gsub(/\{\{ ?cms:(\w+):(\w+):([^:]*) ?}}/, '{{ cms:\1 \2, "\3" }}') end fragment.save if fragment.changed? end diff --git a/db/migrate/20200801084007_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb b/db/migrate/20200801084007_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb new file mode 100644 index 000000000..2a0973a97 --- /dev/null +++ b/db/migrate/20200801084007_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb @@ -0,0 +1,8 @@ +# This migration comes from active_storage (originally 20180723000244) +class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0] + def up + return if foreign_key_exists?(:active_storage_attachments, column: :blob_id) + + add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id if table_exists?(:active_storage_blobs) + end +end diff --git a/db/migrate/20200801210054_missing_uniqueness.rb b/db/migrate/20200801210054_missing_uniqueness.rb new file mode 100644 index 000000000..8b7580727 --- /dev/null +++ b/db/migrate/20200801210054_missing_uniqueness.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class MissingUniqueness < ActiveRecord::Migration[6.0] + def change + add_index(:garden_types, [:name], unique: true) + add_index(:garden_types, [:slug], unique: true) + end +end diff --git a/db/migrate/20200815012538_remove_median_function.rb b/db/migrate/20200815012538_remove_median_function.rb new file mode 100644 index 000000000..fd7118978 --- /dev/null +++ b/db/migrate/20200815012538_remove_median_function.rb @@ -0,0 +1,6 @@ +class RemoveMedianFunction < ActiveRecord::Migration[6.0] + def change + # No longer needed, after upgrading to activemedian 0.2.0 + ActiveMedian.drop_function + end +end diff --git a/db/schema.rb b/db/schema.rb index ed75b6452..0d0d6a357 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,15 +2,15 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_12_26_051019) do +ActiveRecord::Schema.define(version: 2020_08_15_012538) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -225,6 +225,8 @@ ActiveRecord::Schema.define(version: 2019_12_26_051019) do t.text "slug", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.index ["name"], name: "index_garden_types_on_name", unique: true + t.index ["slug"], name: "index_garden_types_on_slug", unique: true end create_table "gardens", id: :serial, force: :cascade do |t| diff --git a/env-example b/env-example index 1a046716c..a7d3ef65e 100644 --- a/env-example +++ b/env-example @@ -61,3 +61,5 @@ GROWSTUFF_FACEBOOK_SECRET="" # set this flag to "true". GROWSTUFF_ELASTICSEARCH="true" GROWSTUFF_EMAIL='noreply@dev.growstuff.org' +ELASTIC_SEARCH_VERSION="7.5.1-amd64" + diff --git a/lib/actions/oauth_signup_action.rb b/lib/actions/oauth_signup_action.rb index dd81d2ec9..63e6dd4f5 100644 --- a/lib/actions/oauth_signup_action.rb +++ b/lib/actions/oauth_signup_action.rb @@ -11,7 +11,7 @@ class Growstuff::OauthSignupAction # variable # def find_or_create_from_authorization(auth) - member ||= Member.where(email: auth.info.email).first_or_create do |m| + member ||= Member.kept.where(email: auth.info.email).first_or_create do |m| m.email = auth.info.email m.password = Devise.friendly_token[0, 20] @@ -41,7 +41,7 @@ class Growstuff::OauthSignupAction def establish_authentication(auth, member) name = determine_name(auth) - authentication = member.authentications + member.authentications .create_with( name: name, token: auth['credentials']['token'], @@ -53,8 +53,6 @@ class Growstuff::OauthSignupAction name: name, member_id: member.id ) - - authentication end def member_created? diff --git a/lib/haml/filters/growstuff_markdown.rb b/lib/haml/filters/growstuff_markdown.rb index d5869e7fb..e84465be1 100644 --- a/lib/haml/filters/growstuff_markdown.rb +++ b/lib/haml/filters/growstuff_markdown.rb @@ -17,8 +17,8 @@ module Haml::Filters CROP_REGEX = /(?