Compare commits

..

10 Commits

Author SHA1 Message Date
Cesy
b3b664b06c Rename for CodeFactor: member_routing_spec.rb to members_controller_routing_spec.rb 2023-12-21 09:30:20 +00:00
Cesy
c04dbd4599 Rename for CodeFactor: follows_routing_spec.rb to follows_controller_routing_spec.rb 2023-12-21 09:29:04 +00:00
Cesy
76da9edc23 Rename for CodeFactor: updates_routing_spec.rb to posts_controller_updates_routing_spec.rb
For codefactor
2023-12-21 09:27:50 +00:00
Cesy
f8ee706023 Rename harvests_routing_spec.rb to harvests_controller_routing_spec.rb
Making codeclimate happier
2023-12-21 09:25:30 +00:00
Cesy
8f5fd12863 Merge branch 'dev' into rubocop-fixes 2023-12-21 09:20:55 +00:00
Daniel O'Connor
65af9c88b1 Merge branch 'dev' into rubocop-fixes 2023-12-13 21:18:42 +10:30
Daniel O'Connor
a3e6edde7c Rubocop 2023-09-16 07:58:26 +00:00
Daniel O'Connor
dcfb088638 Rubocop 2023-09-16 07:57:09 +00:00
Daniel O'Connor
bf0f6c7579 Rubcop 2023-09-16 07:55:57 +00:00
Daniel O'Connor
73566832b9 Fix todo 2023-09-16 07:54:44 +00:00
505 changed files with 5186 additions and 16293 deletions

47
.codeclimate.yml Normal file
View File

@@ -0,0 +1,47 @@
plugins:
brakeman:
enabled: false # codeclimate's brakeman is stuck in rails 5 rules
bundler-audit:
enabled: true
coffeelint:
enabled: true
duplication:
enabled: true
config:
languages:
- ruby
- javascript
editorconfig:
enabled: true
eslint:
enabled: true
fixme:
enabled: true
haml-lint:
enabled: true
nodesecurity:
enabled: true
rubocop:
enabled: true
channel: "rubocop-1-11"
scss-lint:
enabled: true
shellcheck:
enabled: true
ratings:
paths:
- "**.rb"
- "**.ru"
- "**.js"
- "**.coffee"
- "**.scss"
- "**.haml"
- Gemfile.lock
exclude_paths:
- config/
- db/
- spec/
- public/
- app/assets/stylesheets/bootstrap-accessibility.css
- app/assets/javascripts/bootstrap*
- app/assets/stylesheets/leaflet_overrides.scss

View File

@@ -1,2 +1,2 @@
# Ruby version
VARIANT=3.3.4
VARIANT=3.1.4

View File

@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/devcontainers/ruby:3.4-trixie
FROM mcr.microsoft.com/devcontainers/ruby:0-3.1-bullseye
# Install Rails
RUN gem install rails:7.0.8
RUN gem install rails:7.0.7 webdrivers:5.2.0
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains
@@ -12,22 +12,11 @@ ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.git
#RUN bundle exec rake db:migrate
# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends bash-completion
# Chrome for testing packages. https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-doesnt-launch-on-linux
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends ca-certificates fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install additional gems.
# RUN gem install <your-gem-names-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
# We run as the vscode user.
# Make sure this is writable as https://github.com/rubygems/rubygems/issues/6272 was removed
RUN su root -c "mkdir -p /usr/local/rvm/gems/default/cache/bundler/"
RUN su root -c "mkdir -p /usr/local/rvm/gems/default/bundler/gems/"
RUN su root -c "chown -R vscode:rvm /usr/local/rvm/gems/"
RUN su root -c "chmod -R 0755 /usr/local/rvm/gems/"

View File

@@ -14,24 +14,16 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or the host.
"forwardPorts": [3000, 5432, 9200, 8081],
"forwardPorts": [3000, 5432, 9200],
// Use 'postCreateCommand' to run commands after the container is created.
// these don't actually work as postCreateCommands, you need to run them manually
// for some unknown reason, Github codespaces use rbenv and rvm simultaneously
// and you need both to be correct for it to work
"postCreateCommand": "nice -n 19 bundle install && nice -n 19 bundle exec rake db:create && nice -n 19 bundle exec rake db:migrate && nice -n19 bundle exec rake db:seed",
"postCreateCommand": "nice -n 19 bundle install && nice -n 19 bundle exec rake db:create && nice -n 19 bundle exec rake db:migrate && nice -n19 bundle exec rake db:seed"
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"karunamurti.haml",
"redhat.vscode-yaml",
"CraigMaslowski.erb"
]
}
}
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"

View File

@@ -27,7 +27,7 @@ services:
command: sleep infinity
db:
image: postgres:17
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data

View File

@@ -7,10 +7,53 @@ updates:
time: "07:00"
open-pull-requests-limit: 20
versioning-strategy: lockfile-only
ignore:
- dependency-name: active_median
versions:
- "< 0.2.2, >= 0.2.1.a"
- dependency-name: rubocop
versions:
- "< 0.63, >= 0.62.a"
- dependency-name: rubocop
versions:
- "< 0.64, >= 0.63.a"
- dependency-name: sidekiq
versions:
- 5.2.4
- dependency-name: oj
versions:
- 3.11.4
- dependency-name: listen
versions:
- 3.5.0
- dependency-name: rspec-rails
versions:
- 4.1.0
- 5.0.0
- dependency-name: searchkick
versions:
- 4.4.3
- dependency-name: rswag-api
versions:
- 2.3.3
- dependency-name: rswag-specs
versions:
- 2.3.3
- dependency-name: rswag-ui
versions:
- 2.3.3
- dependency-name: timecop
versions:
- 0.9.3
- dependency-name: puma
versions:
- 5.2.0
- dependency-name: capybara
versions:
- 3.35.1
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
time: "07:00"
open-pull-requests-limit: 10

40
.github/workflows/brakeman-analysis.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
# This workflow integrates Brakeman with GitHub's Code Scanning feature
# Brakeman is a static analysis security vulnerability scanner for Ruby on Rails applications
name: Brakeman Scan
# This section configures the trigger for the workflow. Feel free to customize depending on your convention
on:
pull_request:
branches: [ dev ]
jobs:
brakeman-scan:
name: Brakeman Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v4
# Customize the ruby version depending on your needs
- name: Setup Ruby
uses: ruby/setup-ruby@v1
- name: Setup Brakeman
env:
BRAKEMAN_VERSION: '5.1.1' # SARIF support is provided in Brakeman version 4.10+
run: |
gem install brakeman --version $BRAKEMAN_VERSION
# Execute Brakeman CLI and generate a SARIF output with the security issues identified during the analysis
- name: Scan
continue-on-error: true
run: |
brakeman -f sarif -o output.sarif.json .
# Upload the SARIF file generated in the previous step
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: output.sarif.json

View File

@@ -1,109 +0,0 @@
name: CI Features - Admin
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (admin/)
run: bundle exec rspec spec/features/admin/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Comments
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (comments/)
run: bundle exec rspec spec/features/comments/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Conversations
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (conversations/)
run: bundle exec rspec spec/features/conversations/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Crops
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (crops/)
run: bundle exec rspec spec/features/crops/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Gardens
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (gardens/)
run: bundle exec rspec spec/features/gardens/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Harvests
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (harvests/)
run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Home
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (home/)
run: bundle exec rspec spec/features/home/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Members
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (members/)
run: bundle exec rspec spec/features/members/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Admin
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (places/)
run: bundle exec rspec spec/features/places/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Plantings
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (plantings/)
run: bundle exec rspec spec/features/plantings/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Posts
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (posts/)
run: bundle exec rspec spec/features/posts/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Seeds
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (seeds/)
run: bundle exec rspec spec/features/seeds/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,109 +0,0 @@
name: CI Features - Timeline
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (timeline/)
run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,118 +0,0 @@
name: CI Features
on: [pull_request]
jobs:
rspec:
runs-on: ubuntu-latest
services:
db:
image: postgres
env:
##
# The Postgres service fails its docker health check unless you
# specify these environment variables
#
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: growstuff_test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
DATABASE_URL: postgres://postgres:postgres@localhost:5432/growstuff_test
DEVISE_SECRET_KEY: secret
ELASTIC_SEARCH_VERSION: "7.5.1-amd64"
GROWSTUFF_EMAIL: "noreply@test.growstuff.org"
GROWSTUFF_FLICKR_KEY: secretkey"
GROWSTUFF_FLICKR_SECRET: secretsecret
GROWSTUFF_SITE_NAME: "Growstuff (travis)"
RAILS_ENV: test
RAILS_SECRET_TOKEN: supersecret
steps:
- name: Checkout this repo
uses: actions/checkout@v6
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Start Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.5.1
##
# Cache Yarn modules
#
# See https://github.com/actions/cache/blob/master/examples.md#node---yarn for details
#
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install required OS packages
run: |
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
with:
node-version: '26'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install required JS packages
run: yarn install
- name: install chrome
run: sudo apt-get install google-chrome-stable
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (*_spec)
run: bundle exec rspec spec/features/*_spec.rb -fd
- name: Run rspec (percy/)
run: bundle exec rspec spec/features/percy/ -fd
- name: Run rspec (photos/)
run: bundle exec rspec spec/features/photos/ -fd
- name: Run rspec (rss/)
run: bundle exec rspec spec/features/rss/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: screenshots
path: tmp/screenshots

View File

@@ -6,7 +6,7 @@ jobs:
contributors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install ruby version specified in .ruby-version
uses: ruby/setup-ruby@v1
with:
@@ -53,7 +53,7 @@ jobs:
steps:
- name: Checkout this repo
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Configure sysctl limits
run: |
@@ -76,7 +76,7 @@ jobs:
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache
uses: actions/cache@v5
uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -89,9 +89,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: '26'
node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1
@@ -106,25 +106,17 @@ jobs:
- name: Prepare database for testing
run: bundle exec rails db:prepare
- name: precompile assets
run: bundle exec rails assets:precompile
- name: index into elastic search
run: bundle exec rails search:reindex
- name: Run rspec (report results to Percy.io and CodeClimate)
run: bundle exec rspec spec -fd
- name: Run rspec (lib)
run: bundle exec rspec spec/lib/ -fd --fail-fast
- name: Run rspec (services)
run: bundle exec rspec spec/services/ -fd --fail-fast
- name: Run rspec (models)
run: bundle exec rspec spec/models/ -fd --fail-fast
- name: Run rspec (controllers)
run: bundle exec rspec spec/controllers/ -fd --fail-fast
- name: Run rspec (views)
run: bundle exec rspec spec/views/ -fd --fail-fast
- name: Run rspec (routing)
run: bundle exec rspec spec/routing/ -fd --fail-fast
- name: Run rspec (request)
run: bundle exec rspec spec/requests/ -fd --fail-fast
- name: Report to code climate
run: |
gem install codeclimate-test-reporter
codeclimate-test-reporter

View File

@@ -1,17 +1,13 @@
inherit_from: .rubocop_todo.yml
plugins:
- rubocop-factory_bot
- rubocop-capybara
require:
- rubocop-rails
- rubocop-rspec
- rubocop-rspec_rails
- rubocop-rake
AllCops:
NewCops: enable
Exclude:
- 'db/schema.rb'
- 'vendor/**/*'
TargetRailsVersion: 7.1
TargetRailsVersion: 6.0
Rails:
Enabled: true
@@ -24,7 +20,6 @@ Naming/FileName:
RSpec/DescribeClass:
Exclude:
- 'spec/tasks/import_spec.rb'
- 'spec/views/**/*.rb'
- 'spec/features/**/*.rb'
@@ -72,7 +67,4 @@ Layout/LineLength:
Rails/SkipsModelValidations:
Exclude:
- 'db/migrate/20190317023129_finished_boolean.rb'
- 'db/migrate/20251128200506_add_description_to_crops.rb'
- 'db/migrate/20240810160538_set_default_language_for_existing_alternate_names.rb'
- 'db/migrate/20240101010102_populate_crop_fields_from_openfarm_data.rb'
- 'db/seeds.rb'

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
3.4.8
3.1.4

31
.travis.yml Normal file
View File

@@ -0,0 +1,31 @@
sudo: required
language: ruby
dist: bionic
branches:
only:
- mainline
- dev
cache:
bundler: true
yarn: true
directories:
- tmp/cache/assets/test/sprockets
env:
global:
- secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
before_deploy:
- bundle exec script/heroku_maintenance.rb on
deploy:
provider: heroku
api_key:
secure: "WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg="
on:
repo: Growstuff/growstuff
app:
dev: growstuff-staging
mainline: growstuff-prod
run:
- "script/deploy-tasks.sh"
- restart
after_deploy:
- bundle exec script/heroku_maintenance.rb off

View File

@@ -12,7 +12,6 @@ submit the change with your pull request.
- Miles Gould / [pozorvlak](https://github.com/pozorvlak)
- Mackenzie Morgan / [maco](https://github.com/maco)
- Brenda Wallace / [br3nda](https://github.com/br3nda)
- Daniel O'Connor / [CloCkWeRX](https://github.com/CloCkWeRX)
## Contributors
@@ -69,6 +68,7 @@ submit the change with your pull request.
- Jym Paul Carandang / [jacarandang](https://github.com/jacarandang)
- Anthony Atkinson / [sha1sum](https://github.com/sha1sum)
- Terence Conquest / [twconquest](https://github.com/twconquest)
- Daniel O'Connor / [CloCkWeRX](https://github.com/CloCkWeRX)
- DV Dasari / [dv2](https://github.com/dv2)
- Eric Tillberg / [Thrillberg](https://github.com/Thrillberg)
- Lucas Nogueira / [lucasnogueira](https://github.com/lucasnogueira)
@@ -93,13 +93,9 @@ submit the change with your pull request.
- Martina Simicic / [simicic](https://github.com/simicic)
- Rowan Crawford / [wombleton](https://github.com/wombleton)
- Ítalo Pires / [italopires](https://github.com/italopires)
- Bennett Zink / [bennett-zink](https://github.com/bennett-zink)
- Dominick Thornton / [domthor](https://github.com/domthor)
## Bots
### Security and Dependency Updates
- `codefactor-io[bot]`
- DeppBot / [deppbot](https://github.com/deppbot)
- `dependabot[bot]` [dependabot](https://github.com/dependabot-bot) / [dependabot-preview](https://github.com/apps/dependabot-preview)
- [google-labs-jules[bot]](https://github.com/apps/google-labs-jules)

56
Gemfile
View File

@@ -5,13 +5,13 @@ source 'https://rubygems.org'
# Match ruby version in .ruby-version
ruby File.read('.ruby-version')
gem 'rails', '~> 7.2.0'
gem 'rails', '~> 7.0.7'
# Keeping old sprockets
# https://github.com/rails/sprockets-rails/issues/444#issuecomment-637817050
gem "sprockets", "<4"
gem 'bundler', '>= 2.4.22'
gem 'bundler', '>= 2.3.10'
gem 'coffee-rails'
gem 'haml'
@@ -24,7 +24,7 @@ gem 'rswag-api'
gem 'rswag-ui'
# CSS framework
gem "bootstrap", ">= 5.0.0"
gem "bootstrap", ">= 4.6.2"
gem 'material-sass', '4.1.1'
# Icons used by bootstrap/material-sass
@@ -33,18 +33,18 @@ gem 'material_icons'
# icons
gem 'font-awesome-sass'
gem 'terser'
gem 'uglifier' # JavaScript compressor
gem 'oj' # Speeds up json
# planting and harvest predictions
# based on median values for the crop
gem 'active_median'
gem 'active_median', '0.2.0'
gem 'active_record_union'
gem 'flickraw'
gem 'jquery-rails'
gem 'jquery-ui-rails', github: 'jquery-ui-rails/jquery-ui-rails', tag: 'v7.0.0' # See https://github.com/jquery-ui-rails/jquery-ui-rails/issues/146
gem 'jquery-ui-rails'
gem 'cancancan' # for checking member privileges
gem 'csv_shaper' # CSV export
@@ -91,15 +91,16 @@ gem 'bootstrap-datepicker-rails'
# DRY-er easier bootstrap 4 forms
gem "bootstrap_form", ">= 4.5.0"
# For connecting to other services (eg Flickr)
# For connecting to other services (eg Twitter)
gem 'omniauth', '~> 1.3'
gem 'omniauth-flickr', '>= 0.0.15'
gem 'omniauth-twitter'
# Pretty charts
gem "chartkick"
# clever elastic search
gem 'elasticsearch', '~> 7.0.0'
gem 'elasticsearch', '< 7.0.0'
gem 'searchkick'
gem "hashie", ">= 3.5.3"
@@ -123,24 +124,10 @@ gem 'rack-protection', '>= 2.0.1'
gem 'mailboxer', '>= 0.15.1'
gem 'faraday'
gem 'faraday_middleware'
gem 'rack-cors'
gem 'icalendar'
# for signups as requested by email service
gem 'recaptcha'
# External APIs for data
gem "gbifrb"
gem "msgpack"
# Pinned due to RAILS_ENV=production bundle exec rake assets:precompile failing with ArgumentError: wrong number of arguments (given 1, expected 0) (ArgumentError)
# /tmp/build_8301a541/vendor/bundle/ruby/3.3.0/gems/connection_pool-3.0.2/lib/connection_pool.rb:48:in `initialize'
# /tmp/build_8301a541/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.3/lib/active_support/cache/mem_cache_store.rb:63:in `new'
gem "connection_pool", "< 3"
group :production do
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
gem 'dalli'
@@ -166,23 +153,18 @@ group :development, :test do
gem 'factory_bot_rails' # for creating test data
gem 'faker'
gem 'haml-rails' # HTML templating language
gem 'pry'
gem 'query_diet'
gem 'rspec-activemodel-mocks'
gem 'rspec-rails' # unit testing framework
gem 'rswag-specs'
gem 'rubocop-capybara'
gem 'rubocop-factory_bot'
gem 'rubocop-rails'
gem 'rubocop-rake'
gem 'rubocop-rspec'
gem 'rubocop-rspec_rails'
gem 'webrat' # provides HTML matchers for view tests
gem 'crowdin-cli' # for translations
gem 'dotenv-rails'
# cli utils
gem 'haml-i18n-extractor', require: false
gem 'haml_lint', '>= 0.25.1', require: false # Checks haml files for goodness
gem 'i18n-tasks', require: false # adds tests for finding missing and unused translations
gem 'rspectre', require: false # finds unused code in specs
@@ -190,19 +172,15 @@ group :development, :test do
end
group :test do
gem 'axe-core-capybara'
gem 'axe-core-rspec'
gem "percy-capybara", "~> 5.0.0"
gem 'codeclimate-test-reporter', require: false
gem 'rails-controller-testing'
gem "rspec-rebound"
gem 'selenium-webdriver'
gem 'timecop'
gem 'vcr'
gem 'webdrivers'
end
gem "i18n_data", "~> 1.1"
group :travis do
gem 'platform-api'
end
gem "paper_trail", "~> 17.0"
gem 'sitemap_generator'
gem 'aws-sdk-s3', '~> 1', '>= 1.114.0'
gem "percy-capybara", "~> 5.0.0"

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
# 🌱 Growstuff
![Build status](https://github.com/Growstuff/growstuff/workflows/CI/badge.svg)
[![Code Climate](https://codeclimate.com/github/Growstuff/growstuff/badges/gpa.svg)](https://codeclimate.com/github/Growstuff/growstuff)
Welcome to the Growstuff project.
@@ -14,17 +15,11 @@ Growstuff was founded in 2012 and has been built by dozens of
[contributors](CONTRIBUTORS.md). We are an inclusive, welcoming project, and
encourage participation from people of all backgrounds and skill levels.
## Want to contribute?
Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking.
Dive on in and submit your PRs!
Vibe Coding is more than okay, just make sure you indicate if you have done so and ensure there are tests.
## Important links
* [Issues](https://github.com/orgs/Growstuff/projects/1) (features we're
working on, known bugs, etc)
* [![Gitter](https://badges.gitter.im/Growstuff/growstuff.svg)](https://gitter.im/Growstuff/growstuff?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
* [Wiki](https://github.com/Growstuff/growstuff/wiki) (general documentation, etc.)
## For coders
@@ -36,10 +31,6 @@ frontend features. We welcome contributions -- see
* To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide).
* You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API).
### For Home Automation enthusiasts
https://github.com/Growstuff/homeassistant-growstuff/
## For designers, writers, researchers, data wranglers, and other contributors
There are heaps of ways to get involved and contribute no matter what
@@ -67,3 +58,5 @@ For more information about this project, contact [info@growstuff.org](mailto:inf
Security Issues: If you find an authorization bypass or data breach, please contact our maintainers directly at [maintainers@growstuff.org](mailto:maintainers@growstuff.org).
You can also contact us on [Twitter](http://twitter.com/growstufforg/) or
[Facebook](https://www.facebook.com/pages/Growstuff/1531133417099494) or [Github](https://github.com/Growstuff/growstuff/issues)..

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 266 KiB

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<!-- MIT Licenced, https://www.svgrepo.com/svg/336823/plan -->
<svg width="800px" height="800px" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" fill="white" fill-opacity="0.01"/>
<path d="M5 19H43V41C43 42.1046 42.1046 43 41 43H7C5.89543 43 5 42.1046 5 41V19Z" fill="#2F88FF" stroke="#000000" stroke-width="4" stroke-linejoin="round"/>
<path d="M5 10C5 8.89543 5.89543 8 7 8H41C42.1046 8 43 8.89543 43 10V19H5V10Z" stroke="#000000" stroke-width="4" stroke-linejoin="round"/>
<path d="M16 31L22 37L34 25" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 5V13" stroke="#000000" stroke-width="4" stroke-linecap="round"/>
<path d="M32 5V13" stroke="#000000" stroke-width="4" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 916 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -18,17 +18,6 @@
// = require jquery-ui/widgets/autocomplete
// = require bootstrap-sprockets
// = require bootstrap-datepicker
// = require bootstrap
// = require material
// = require_tree .
document.addEventListener('DOMContentLoaded', function(event) {
var popoverTrigger = Array.prototype.slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
popoverTrigger.map(function(popoverTrigger2) {
return new bootstrap.Popover(popoverTrigger2);
});
var tooltipTrigger = Array.prototype.slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTrigger.map(function(tooltipTrigger2) {
return new bootstrap.Tooltip(tooltipTrigger2);
});
});

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
jQuery ->
$("a[rel=popover]").popover()
$(".tooltip").tooltip()
$("a[rel=tooltip]").tooltip()

View File

@@ -5,7 +5,7 @@ jQuery ->
$(".remove-altname-row").css("display", "inline-block")
-$ ->
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='scientific-name-auto-suggest form-control' id='sci_name[INDEX]' data-source-url='/scientific_names/gbif_suggest')'></input><span class='help-block'>Scientific name of crop</span><input type='text' id='sci_gbif_key[INDEX]' class=''></div><div class='col-md-2'></div></div>"
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='form-control', id='sci_name[INDEX]')'></input><span class='help-block'>Scientific name of crop.</span></div><div class='col-md-2'></div></div>"
sci_index = $('#scientific_names .template').length + 1
@@ -21,7 +21,7 @@ jQuery ->
element = document.getElementById(tmp)
element.remove()
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control' id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control', id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
alt_index = $('#alternate_names .template').length + 1

View File

@@ -1,19 +1,11 @@
$(document).ready(function() {
$('.like-btn').show();
/**
* Handles the result of an ajax call and updates UI
*
* @param {object} data JSON data from ajax response
* @param {string} type object type (ie: post, activity, etc)
*/
var likeableSuccess = function(data, type) {
var target = '.' + type + '-' + data.id;
var objectClass = type.charAt(0).toUpperCase() + type.slice(1);
var likeButton = $(target + ' .' + type + '-like');
var likeBadge = $(target + ' .like-badge');
$('.post-like').on('ajax:success', function(event, data) {
var likeButton = $('#post-' + data.id + ' .post-like');
var likeBadge = $('#post-'+ data.id + ' .like-badge');
$(target + ' .like-count').text(data.like_count);
$('#post-' + data.id + ' .like-count').text(data.like_count);
if (data.liked_by_member) {
likeBadge.addClass('liked');
likeButton.data('method', 'delete');
@@ -22,26 +14,11 @@ $(document).ready(function() {
} else {
likeBadge.removeClass('liked');
likeButton.data('method', 'post');
likeButton.attr('href', '/likes.json?type=' + objectClass + '&id=' + data.id);
likeButton.attr('href', '/likes.json?type=Post&id=' + data.id);
likeButton.text('Like');
}
};
$('.post-like').on('ajax:success', function(event, data) {
likeableSuccess(data, 'post');
});
$('.activity-like').on('ajax:success', function(event, data) {
likeableSuccess(data, 'activity');
});
$('.planting-like').on('ajax:success', function(event, data) {
likeableSuccess(data, 'planting');
});
$('.harvest-like').on('ajax:success', function(event, data) {
likeableSuccess(data, 'harvest');
});
$('.photo-like').on('ajax:success', function(event, data) {
var likeBadge = $('#photo-'+ data.id + ' .like-badge');

View File

@@ -0,0 +1 @@
$('.dropdown-toggle').dropdown();

View File

@@ -1,31 +0,0 @@
# TODO: This assumes one autocomplete per page.
# Needs to be a function so that when we append one of these, it gets uniquely associated with the hidden controls.
jQuery ->
if el = $( '.scientific-name-auto-suggest' )
id = $( '.scientific-name-auto-suggest-id' )
el.autocomplete
minLength: 3,
source: el.attr( 'data-source-url' ),
focus: ( event, ui ) ->
el.val( ui.item.canonicalName )
id.val( ui.item.nameKey )
false
select: ( event, ui ) ->
el.val( ui.item.canonicalName )
id.val( ui.item.nameKey )
false
response: ( event, ui ) ->
id.val( "" )
for item in ui.content
if item.name == el.val()
id.val( item.nameKey )
if el.data( 'uiAutocomplete' )
el.data( 'uiAutocomplete' )._renderItem = ( ul, item ) ->
$( '<li class="list-group-item"></li>' )
.data( 'item.autocomplete', item )
.append( "<a>#{item.canonicalName} (#{item.scientificName}) - #{item.rank}</a>" )
.appendTo( ul )

View File

@@ -1,22 +0,0 @@
$(document).ready(function() {
var url = location.href.replace(/\/$/, '');
if (location.hash) {
var hash = url.split('#');
var triggerEl = document.querySelector('#myTab a[href="#' + hash[1] + '"]');
var tab = new bootstrap.Tab(triggerEl);
tab.show();
url = location.href.replace(/\/#/, '#');
history.replaceState(null, null, url);
setTimeout(function() {
$(window).scrollTop(0);
}, 20);
}
$('a[data-bs-toggle="tab"]').on('click', function() {
var newUrl;
var hash = $(this).attr('href');
newUrl = url.split('#')[0] + hash;
history.replaceState(null, null, newUrl);
});
});

View File

@@ -0,0 +1,3 @@
$(function() {
$('[data-toggle="tooltip"]').tooltip();
});

View File

@@ -1,13 +1,7 @@
.crop-icon {
height: 1em;
}
.card-footer {
.btn-group-vertical {
.btn {
text-wrap: initial
}
}
}
.crop-thumbnail {
.text {
bottom: 0;
@@ -25,7 +19,7 @@
padding-bottom: 0;
}
.crop-sci-name {
h5.crop-sci-name {
background-color: $beige;
color: $black;
font-size: 0.7em;

View File

@@ -1,8 +1,6 @@
// stats shown on homepage. eg. "999 members..."
.stats {
a {
font-weight: bold;
}
font-weight: bold;
}
.crops,
@@ -16,27 +14,3 @@
.homepage--list-item {
height: 100px;
}
.releases {
.card {
.card-header {
}
.card-body {
h2 {
background-color: transparent;
color: black;
box-shadow: none;
}
img {
border: 0.5em solid #111;
margin-left: 5%;
margin-right: 5%;
width: 90%;
}
ul {
margin-bottom: 1em;
margin-top: 1em;
}
}
}
}

View File

@@ -33,22 +33,16 @@
}
}
.card {
.badge-location {
background-color: darken($blue, 10%);
border-radius: 5%;
color: $white;
.location-not-set {
background-image: image-url("location-not-set.en.png");
background-position: center;
background-repeat: no-repeat;
height: 250px;
width: 100%;
}
.fa-map-marker {
padding-right: 0.5em;
}
}
.badge {
text-overflow: ellipsis;
white-space: normal;
align-items: end;
max-height: 1rlh;
-webkit-line-clamp: 1;
}
}
.badge-location {
background-color: darken($blue, 10%);
border-radius: 5%;
color: $white;
}

View File

@@ -47,13 +47,4 @@
margin-left: auto;
}
}
.card-footer {
max-height: 2em;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 0.5em;
padding-top: 0;
}
}

View File

@@ -1,6 +1,6 @@
.seed-card {
.text {
opacity: 0.95;
opacity: 0.8;
margin: 0;
padding: 0;
position: absolute;

View File

@@ -1,10 +1,10 @@
//$screen-md-min: 1028px
// Base colours
$beige: #f4f2ef;
$beige: #f3f1ee;
$brown: #413f3b;
$green: #57803c;
$green: #5f8e43;
$blue: #2f4365;
$red: #ff4d43;
$orange: #ffa500;

View File

@@ -30,7 +30,3 @@
@import "predictions";
@import "homepage";
@import "maps";
@view-transition {
navigation: auto;
}

View File

@@ -10,33 +10,9 @@
width: 100%;
}
#navbarSupportedContent {
ul {
flex-direction: column-reverse;
flex-wrap: nowrap;
li.nav-item {
display: block;
a {
display: grid;
grid-template-columns: 2em 1fr 2em;
}
a.dropdown-toggle::after {
width: 100%;
text-align: right;
}
}
}
}
.crop-actions {
flex-direction: column;
width: 100%;
a {
margin: auto;
.navbar .nav > li {
display: block;
}
}
.navbar .navbar-form {
padding-left: 0;
@@ -78,12 +54,6 @@
margin-bottom: 0;
}
.card-footer {
.d-flex {
flex-direction: column;
}
}
.sidebar {
border-left: 0;
margin-left: 0;

View File

@@ -8,10 +8,6 @@ body {
font-family: $default-font;
}
.navbar {
flex-wrap: nowrap;
align-items: flex-start
}
.navbar-brand {
.site-name {
font-family: "Modak", cursive;
@@ -20,7 +16,7 @@ body {
}
img {
height: 100%;
height: 60px;
}
}
@@ -105,15 +101,13 @@ section {
background: $white;
box-shadow: 1px 3px 3px 1px darken($beige, 20%);
cursor: pointer;
transition:
0.1s transform cubic-bezier(0.155, 1.105, 0.295, 1.12),
0.1s box-shadow,
0.1s -webkit-transform cubic-bezier(0.155, 1.105, 0.295, 1.12);
transition: 0.3s transform cubic-bezier(0.155, 1.105, 0.295, 1.12), 0.3s box-shadow,
0.3s -webkit-transform cubic-bezier(0.155, 1.105, 0.295, 1.12);
}
.card:hover {
box-shadow:
1px 3px 3px 1px darken($beige, 30%);
box-shadow: 0 10px 20px darken($beige, 30%), 0 4px 8px darken($beige, 40%);
transform: scale(1.1);
}
}
@@ -132,8 +126,6 @@ section {
border-radius: 5%;
margin: 0.5em 0.5em 0.5em 0;
width: 200px;
align-items: stretch;
justify-content: space-between;
.img-card {
border-top-left-radius: 5%;
@@ -152,6 +144,10 @@ section {
color: $brown;
}
}
.card:hover {
background-color: $beige;
}
}
.img-cover,
@@ -358,9 +354,7 @@ ul.thumbnail-buttons {
text-align: center;
}
}
.text-muted {
color: $blue;
}
.jumbotron {
background-color: $beige;
margin-bottom: 1em;
@@ -373,17 +367,12 @@ ul.thumbnail-buttons {
// signup widget on homepage
.signup {
background-color: darken($green, 20%);
border: 1px solid darken($green, 20%);
color: $white;
background-color: lighten($green, 40%);
border: 1px solid lighten($green, 20%);
border-radius: 6px;
line-height: 200%;
padding: 15px;
text-align: center;
.btn-info {
background-color: $blue;
}
}
.info {

View File

@@ -1,119 +0,0 @@
# frozen_string_literal: true
class ActivitiesController < DataController
def index
@show_all = params[:all] == '1'
where = {}
where['active'] = true unless @show_all
if params[:member_slug]
@owner = Member.find_by(slug: params[:member_slug])
where['owner_id'] = @owner.id unless @owner.nil?
end
@activities = Activity.search(
where:,
page: params[:page],
limit: 30,
boost_by: [:created_at],
load: false
)
@filename = "Growstuff-#{specifics}Activities-#{Time.zone.now.to_fs(:number)}.csv"
respond_with(@activities)
end
def show
if @activity.finished? && @activity.owner == current_member && (@activity.updated_at + 2.weeks) > Time.now
@repeat_link = new_activity_path(
name: @activity.name,
garden_id: @activity.garden_id,
planting_id: @activity.planting_id,
category: @activity.category,
description: @activity.description,
due_date: 2.weeks.from_now.to_date
)
end
respond_with @activity
end
def new
@activity = Activity.new(
owner: current_member,
due_date: Date.today
)
@activity.name = params[:name] if params[:name]
@activity.description = params[:description] if params[:description]
@activity.category = params[:category] if params[:category]
@activity.due_date = params[:due_date] if params[:due_date]
if params[:garden_id]
@activity.garden = Garden.find_by(
owner: current_member,
id: params[:garden_id]
)
end
if params[:planting_id]
@activity.planting = Planting.find_by(
owner: current_member,
id: params[:planting_id]
)
end
respond_with @activity
end
def edit
# the following are needed to display the form but aren't used
@gardens = @activity.owner.gardens.active.order_by_name
@plantings = @activity.owner.plantings.active
end
def create
@activity = Activity.new(activity_params)
@activity.owner = current_member
@activity.due_date ||= Date.today
if @activity.save
if params[:repeat_times].to_i > 0
repeat_times = params[:repeat_times].to_i
repeat_weeks = params[:repeat_weeks].to_i
repeat_times.times do |i|
new_activity = @activity.dup
new_activity.due_date = @activity.due_date + (i + 1) * repeat_weeks.weeks
new_activity.save
end
end
end
respond_with @activity
end
def update
@activity.update(activity_params)
respond_with @activity
end
def destroy
@activity.destroy
respond_with @activity, location: @activity.garden
end
private
def activity_params
params.require(:activity).permit(
:name, :description, :category, :finished,
:garden_id, :planting_id, :due_date,
:repeat_times, :repeat_weeks
)
end
def specifics
return if @owner.blank?
"#{@owner.to_param}-"
end
end

View File

@@ -1,40 +0,0 @@
# frozen_string_literal: true
module Admin
class CropCompanionsController < AdminController
before_action :set_crop
def index
@crop_companions = @crop.crop_companions
end
def new
@crop_companion = @crop.crop_companions.new
end
def create
@crop_companion = @crop.crop_companions.new(crop_companion_params)
if @crop_companion.save
redirect_to admin_crop_crop_companions_path(@crop), notice: 'Companion was successfully created.'
else
render :new
end
end
def destroy
@crop_companion = @crop.crop_companions.find(params[:id])
@crop_companion.destroy
redirect_to admin_crop_crop_companions_path(@crop), notice: 'Companion was successfully destroyed.'
end
private
def set_crop
@crop = Crop.find_by!(slug: params[:crop_slug])
end
def crop_companion_params
params.require(:crop_companion).permit(:crop_b_id, :source_url)
end
end
end

View File

@@ -1,19 +0,0 @@
# frozen_string_literal: true
class Admin::CropsController < ApplicationController
before_action :authenticate_member!
before_action :authorize_admin!
def index
@versions = PaperTrail::Version.where(item_type: 'Crop').order(created_at: :desc).limit(100)
member_ids = @versions.map(&:whodunnit).compact.map(&:to_i)
@members = Member.where(id: member_ids).index_by(&:id)
@crop_wranglers = Role.crop_wranglers
end
private
def authorize_admin!
authorize! :wrangle, Crop
end
end

View File

@@ -8,7 +8,7 @@ module Admin
responders :flash
def index
@roles = Role.all.order(:name).paginate(page: params[:page])
@roles = Role.all.order(:name)
respond_with @roles
end
@@ -33,7 +33,7 @@ module Admin
def destroy
@role.destroy
respond_with @role, location: admin_roles_path, notice: "Role was successfully deleted"
respond_with @role, location: admin_roles_path
end
private

View File

@@ -1,24 +0,0 @@
# frozen_string_literal: true
module Admin
class VersionsController < ApplicationController
before_action :authenticate_member!
before_action :authorize_admin!
def revert
@version = PaperTrail::Version.find(params[:id])
@object = @version.reify
if @object.save
redirect_to admin_crops_path, notice: "Reverted to version from #{@version.created_at.strftime('%B %d, %Y')}"
else
redirect_to admin_crops_path, alert: "Could not revert to version from #{@version.created_at.strftime('%B %d, %Y')}. Errors: #{@object.errors.full_messages.to_sentence}"
end
end
private
def authorize_admin!
authorize! :wrangle, Crop
end
end
end

View File

@@ -8,7 +8,7 @@ class AdminController < ApplicationController
def newsletter
authorize! :manage, :all
@members = Member.confirmed.wants_newsletter.all.paginate(page: params[:page], per_page: 100)
@members = Member.confirmed.wants_newsletter.all
respond_with @members
end
end

View File

@@ -9,7 +9,7 @@ class AlternateNamesController < ApplicationController
# GET /alternate_names
# GET /alternate_names.json
def index
@alternate_names = AlternateName.all.order(:name).paginate(page: params[:page], per_page: 100)
@alternate_names = AlternateName.all.order(:name)
respond_with(@alternate_names)
end
@@ -57,6 +57,6 @@ class AlternateNamesController < ApplicationController
private
def alternate_name_params
params.require(:alternate_name).permit(:crop_id, :name, :creator_id, :language)
params.require(:alternate_name).permit(:crop_id, :name, :creator_id)
end
end

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: true
module Api
module V1
class ActivitiesController < BaseController
end
end
end

View File

@@ -4,40 +4,6 @@ module Api
module V1
class BaseController < JSONAPI::ResourceController
abstract
protect_from_forgery with: :null_session
before_action :authenticate_member_from_token!
before_action :enforce_member_for_write_operations!, only: %i(create update destroy)
rescue_from CanCan::AccessDenied do
head :forbidden
end
def context
{
current_user: current_user,
current_ability: current_ability,
controller: self,
action: params[:action]
}
end
private
attr_reader :current_user
def enforce_member_for_write_operations!
head :unauthorized unless current_user
end
def authenticate_member_from_token!
authenticate_with_http_token do |token, _options|
auth = Authentication.find_by(token: token, provider: 'api')
if auth.present?
@current_user = auth.member
return true
end
end
end
end
end
end

View File

@@ -2,8 +2,6 @@
class ApplicationController < ActionController::Base
protect_from_forgery
# Working from codespaces, we want to turn off validation
skip_before_action :verify_authenticity_token if Rails.env.development? && ENV['CODESPACE_NAME']
include ApplicationHelper
@@ -78,7 +76,6 @@ class ApplicationController < ActionController::Base
:tos_agreement,
# profile stuff
:bio, :location, :latitude, :longitude,
:website_url, :instagram_handle, :facebook_handle, :bluesky_handle, :other_url,
# email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder,
# update password

View File

@@ -13,55 +13,43 @@ class CommentsController < ApplicationController
end
def new
@commentable = find_commentable
@comment = Comment.new
if @commentable
@comments = @commentable.comments
@post = Post.find_by(id: params[:post_id])
if @post
@comments = @post.comments
respond_with(@comments)
else
redirect_to(request.referer || root_url,
alert: "Can't post a comment on a non-existent commentable")
alert: "Can't post a comment on a non-existent post")
end
end
def edit
# TODO: Why does this need a collection of comments?
@comments = @comment.commentable.comments
@commentable = @comment.commentable
@comments = @comment.post.comments
end
def create
@comment = Comment.new(comment_params)
@commentable = @comment.commentable
@comment.author = current_member
@comment.save
respond_with @comment, location: @commentable
respond_with @comment, location: @comment.post
end
def update
@comment.update(body: comment_params['body'])
respond_with @comment, location: @comment.commentable
respond_with @comment, location: @comment.post
end
def destroy
@commentable = @comment.commentable
@post = @comment.post
@comment.destroy
respond_with(@commentable)
respond_with(@post)
end
private
def find_commentable
return unless params[:comment]
if params[:comment][:commentable_type] == 'Photo'
Photo.find(params[:comment][:commentable_id])
elsif params[:comment][:commentable_type] == 'Post'
Post.find(params[:comment][:commentable_id])
end
end
def comment_params
params.require(:comment).permit(:body, :commentable_id, :commentable_type)
params.require(:comment).permit(:body, :post_id)
end
end

View File

@@ -10,6 +10,7 @@ class CropsController < ApplicationController
responders :flash
def index
@sort = params[:sort]
@crops = Crop.search('*', boost_by: %i(plantings_count harvests_count),
limit: 100,
page: params[:page],
@@ -39,9 +40,9 @@ class CropsController < ApplicationController
respond_with @crops
end
def gbif
def openfarm
@crop = Crop.find(params[:crop_slug])
@crop.update_gbif_data!
@crop.update_openfarm_data!
respond_with @crop, location: @crop
end
@@ -57,15 +58,7 @@ class CropsController < ApplicationController
page: params[:page],
per_page: Crop.per_page,
current_member:)
respond_to do |format|
format.html do
render
end
format.json do
render json: @crops.to_a
end
end
respond_with @crops
end
def show
@@ -73,11 +66,9 @@ class CropsController < ApplicationController
format.html do
@posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page])
@companions = @crop.companions.approved
member_ids = @crop.versions.map(&:whodunnit).compact.map(&:to_i)
@version_members = Member.where(id: member_ids).index_by(&:id)
end
format.svg do
icon_data = @crop.svg_icon.presence || File.read(Rails.root.join("app/assets/images/icons/sprout.svg"))
icon_data = @crop.svg_icon.presence || File.read(Rails.root.join('app', 'assets', 'images', 'icons', 'sprout.svg'))
send_data(icon_data, type: "image/svg+xml", disposition: "inline")
end
format.json do
@@ -109,12 +100,7 @@ class CropsController < ApplicationController
@crop.approval_status = "pending"
end
if Crop.transaction { @crop.save && save_crop_names }
notify_wranglers
else
@crop.alternate_names.build
@crop.scientific_names.build
end
notify_wranglers if Crop.transaction { @crop.save && save_crop_names }
respond_with @crop
end
@@ -133,7 +119,7 @@ class CropsController < ApplicationController
if @crop.approval_status_changed?(from: "pending", to: "approved")
notifier.deliver_now!
@crop.update_gbif_data!
@crop.update_openfarm_data!
end
else
@crop.approval_status = @crop.approval_status_was
@@ -149,34 +135,6 @@ class CropsController < ApplicationController
respond_with @crop
end
def data_improvement
@active_tab = params[:tab] || 'photos'
@crops = case @active_tab
when 'photos'
Crop.approved.where(photo_associations_count: 0).order(plantings_count: :desc)
when 'descriptions'
Crop.approved.where(description: [nil, '']).order(plantings_count: :desc)
when 'youtube'
Crop.approved.where(en_youtube_url: [nil, '']).order(plantings_count: :desc)
when 'alternate_names'
Crop.approved.left_joins(:alternate_names).where(alternate_names: { id: nil }).order(plantings_count: :desc)
when 'wikidata'
crops_with_wikidata = Crop.joins(:scientific_names).where.not(scientific_names: { wikidata_id: nil }).distinct
Crop.approved.where.not(id: crops_with_wikidata).order(plantings_count: :desc)
when 'row_spacing'
Crop.approved.where(row_spacing: nil).order(plantings_count: :desc)
when 'sun_requirements'
Crop.approved.where(sun_requirements: [nil, '']).order(plantings_count: :desc)
when 'height'
Crop.approved.where(height: nil).order(plantings_count: :desc)
when 'public_food_key'
Crop.approved.where(public_food_key: [nil, '']).order(plantings_count: :desc)
else
Crop.none
end
end
private
def notifier
@@ -189,7 +147,7 @@ class CropsController < ApplicationController
end
def save_crop_names
AlternateName.create!(names_params(:alt_name).map { |n| { name: n, creator_id: current_member.id, crop_id: @crop.id, language: "EN" } })
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
@@ -204,28 +162,26 @@ class CropsController < ApplicationController
def recreate_names(param_name, name_type)
return if params[param_name].blank?
@crop.send("#{name_type}_names").each(&:destroy)
params[param_name].each_value do |value|
next if value.empty?
if name_type == 'alternate'
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id, language: "EN")
else
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id)
end
destroy_names(name_type)
params[param_name].each do |_i, value|
create_name!(name_type, value) unless value.empty?
end
end
def destroy_names(name_type)
@crop.send("#{name_type}_names").each(&:destroy)
end
def create_name!(name_type, value)
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id)
end
def crop_params
params.require(:crop).permit(
:name, :en_wikipedia_url, :en_youtube_url,
:name, :en_wikipedia_url,
:parent_id, :perennial,
:request_notes, :reason_for_rejection,
:rejection_notes,
:description,
:public_food_key,
:row_spacing, :spread, :height,
:sowing_method, :sun_requirements, :growing_degree_days,
scientific_names_attributes: %i(scientific_name _destroy id)
)
end
@@ -241,12 +197,12 @@ class CropsController < ApplicationController
def crop_json_fields
{
include: {
plantings: {
plantings: {
include: {
owner: { only: %i(id login_name location latitude longitude) }
}
},
scientific_names: { only: [:name] }, alternate_names: { only: %i(name language) }
scientific_names: { only: [:name] }, alternate_names: { only: [:name] }
}
}
end

View File

@@ -1,87 +0,0 @@
# frozen_string_literal: true
class GardenCollaboratorsController < ApplicationController
before_action :authenticate_member!, except: %i(index show)
before_action :load_garden
load_and_authorize_resource id_param: :slug
respond_to :html
responders :flash
def index
@garden_collaborators = @garden.garden_collaborators.paginate(page: params[:page])
respond_with(@garden_collaborators)
end
def show
@garden_collaborator = GardenCollaborator.find(params[:garden_collaborator_id])
respond_with(@garden_collaborator)
end
def new
@garden_collaborator = GardenCollaborator.new(garden: @garden)
authorize! :create, @garden_collaborator
respond_with(@garden_collaborator)
end
def edit
@garden_collaborator = GardenCollaborator.find(params[:id])
authorize! :update, @garden_collaborator
respond_with(@garden_collaborator)
end
def create
@garden_collaborator = GardenCollaborator.new(garden: @garden)
authorize! :create, @garden_collaborator
@member = Member.find_by(slug: params[:garden_collaborator][:member_slug])
@garden_collaborator.member = @member
if @garden_collaborator.save
redirect_to garden_garden_collaborators_path(@garden)
else
respond_with(@garden_collaborator)
end
end
def update
@garden_collaborator = GardenCollaborator.find(params[:id])
authorize! :update, @garden_collaborator
@member = Member.find_by(slug: params[:garden_collaborator][:member_slug])
@garden_collaborator.member = @member
@garden_collaborator.save
respond_with(@garden_collaborator)
end
def destroy
@garden_collaborator = GardenCollaborator.find(params[:id])
authorize! :destroy, @garden_collaborator
if @garden_collaborator.destroy
redirect_to garden_garden_collaborators_path(@garden)
else
respond_with(@garden_collaborator)
end
end
private
def load_garden
@garden = Garden.find_by(slug: params[:garden_slug])
end
def garden_collaborator_params
params.require(:garden_collaborator).permit(
:member_slug
)
end
end

View File

@@ -4,23 +4,18 @@ class GardensController < DataController
def index
@owner = Member.find_by(slug: params[:member_slug])
@show_all = params[:all] == '1'
@show_jump_to = params[:member_slug].present? || false
@show_jump_to = params[:member_slug].present? ? true : false
@gardens = @gardens.includes(:owner)
@gardens = @gardens.active unless @show_all
if @owner.present?
@gardens = @gardens.left_joins(:garden_collaborators)
@gardens = @gardens.where(owner: @owner).or(@gardens.where(garden_collaborators: { member: @owner }))
end
@gardens = @gardens.where(owner: @owner) if @owner.present?
@gardens = @gardens.where.not(members: { confirmed_at: nil })
.order(:name).paginate(page: params[:page])
respond_with(@gardens)
end
def show
@current_plantings = @garden.plantings.current.where.not(failed: true).includes(:crop, :owner).order(planted_at: :desc)
@current_activities = @garden.activities.current.includes(:owner).order(created_at: :desc)
@finished_activities = @garden.activities.finished.includes(:owner).order(created_at: :desc)
@current_plantings = @garden.plantings.current.includes(:crop, :owner).order(planted_at: :desc)
@finished_plantings = @garden.plantings.finished.includes(:crop)
@suggested_companions = Crop.approved.where(
id: CropCompanion.where(crop_a_id: @current_plantings.select(:crop_id)).select(:crop_b_id)
@@ -39,10 +34,7 @@ class GardensController < DataController
def create
@garden.owner_id = current_member.id
if @garden.save
link = new_activity_path(name: 'Weed the garden bed', garden_id: @garden.id, due_date: 2.weeks.from_now.to_date)
flash[:notice] = t('gardens.created_prompt_html', link: link).html_safe
end
flash[:notice] = I18n.t('gardens.created') if @garden.save
respond_with(@garden)
end

View File

@@ -10,6 +10,4 @@ class HomeController < ApplicationController
# the relevant class methods directly in the view, so that fragment
# caching will be effective.
end
def community_gardens; end
end

View File

@@ -58,7 +58,7 @@ class LikesController < ApplicationController
def failed(like, message)
respond_to do |format|
format.json { render(json: { error: message }, status: :forbidden) }
format.json { render(json: { 'error': message }, status: :forbidden) }
format.html do
flash[:error] = message
if like&.likeable

View File

@@ -16,6 +16,7 @@ class MembersController < ApplicationController
def show
@member = Member.confirmed.kept.find_by!(slug: params[:slug])
@twitter_auth = @member.auth('twitter')
@flickr_auth = @member.auth('flickr')
@posts = @member.posts
@@ -60,7 +61,7 @@ class MembersController < ApplicationController
end
def unsubscribe
verifier = ActiveSupport::MessageVerifier.new(ENV.fetch('RAILS_SECRET_TOKEN', nil))
verifier = ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
decrypted_message = verifier.verify(params[:message])
@member = Member.find(decrypted_message[:member_id])

View File

@@ -21,7 +21,7 @@ class MessagesController < ApplicationController
return if params[:recipient_id].blank?
@recipient = Member.find_by(id: params[:recipient_id])
nil if @recipient.nil?
return if @recipient.nil?
end
def create

View File

@@ -28,7 +28,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
@authentication = action.establish_authentication(auth, member)
if action.member_created?
raise "Invalid provider" unless %w(flickr).index(auth['provider'].to_s)
raise "Invalid provider" unless %w(twitter flickr).index(auth['provider'].to_s)
session["devise.#{auth['provider']}_data"] = request.env["omniauth.auth"]
sign_in member
@@ -41,7 +41,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def after_sign_in_path_for(resource)
if resource.tos_agreement
super
super resource
else
finish_signup_path(resource)
end

View File

@@ -21,7 +21,6 @@ class PhotosController < ApplicationController
def show
@crops = Crop.distinct.joins(:photo_associations).where(photo_associations: { photo: @photo })
@comment = Comment.new(commentable: @photo)
respond_with(@photo)
end
@@ -64,7 +63,7 @@ class PhotosController < ApplicationController
def photo_params
params.require(:photo).permit(:source_id, :source, :title, :license_name,
:license_url, :thumbnail_url, :fullsize_url, :link_url, :date_taken)
:license_url, :thumbnail_url, :fullsize_url, :link_url)
end
# Item with photos attached

View File

@@ -6,7 +6,7 @@ class PlantPartsController < ApplicationController
responders :flash
def index
@plant_parts = PlantPart.all.order(:name).paginate(page: params[:page], per_page: 100)
@plant_parts = PlantPart.all.order(:name)
respond_with(@plant_parts)
end

View File

@@ -3,7 +3,6 @@
class PlantingsController < DataController
after_action :update_crop_medians, only: %i(create update destroy)
after_action :update_planting_medians, only: :update
respond_to :ics, only: [:index] # TODO: This can be shifted up when all relevant controllers respond to ical
def index
@show_all = params[:all] == '1'
@@ -30,14 +29,13 @@ class PlantingsController < DataController
)
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_fs(:number)}.csv"
respond_with(@plantings)
end
def show
@photos = @planting.photos.includes(:owner).order(date_taken: :desc)
@harvests = Harvest.search(where: { planting_id: @planting.id })
@current_activities = @planting.activities.current.includes(:owner).order(created_at: :desc)
@finished_activities = @planting.activities.finished.includes(:owner).order(created_at: :desc)
@matching_seeds = matching_seeds
@crop = @planting.crop
@@ -46,12 +44,6 @@ class PlantingsController < DataController
.where.not(id: @planting.id)
.includes(:owner, :crop, :garden)
.limit(6)
if @planting.finished? && @planting.garden.plantings.current.none? && (@planting.updated_at + 2.weeks) > Time.zone.now
@cultivate_soil_link = new_activity_path(name: 'Cultivate soil', garden_id: @planting.garden_id, category: "Soil Cultivation",
description: "Recently finished #{@planting.crop.name} planting. Prepare for next planting.")
end
respond_with @planting
end
@@ -98,32 +90,6 @@ class PlantingsController < DataController
respond_with @planting, location: @planting.garden
end
def transplant
# The `load_and_authorize_resource` in DataController will handle finding the
# planting and authorizing the action.
# We still need to authorize the new garden
new_garden = Garden.find(params[:garden_id])
authorize! :update, new_garden
# Mark original planting as finished
@planting.update(finished: true, finished_at: Time.zone.now)
# Create a new planting
new_planting = @planting.dup
new_planting.garden = new_garden
new_planting.slug = nil # let friendly_id generate a new slug
new_planting.finished = false
new_planting.finished_at = nil
if new_planting.save
redirect_to edit_planting_path(new_planting), notice: 'Planting was successfully transplanted.'
else
# if the save fails, we should probably roll back the finishing of the original planting
@planting.update(finished: false, finished_at: nil)
redirect_to @planting, alert: "There was an error transplanting the planting: #{new_planting.errors.full_messages.to_sentence}"
end
end
private
def update_crop_medians
@@ -140,7 +106,7 @@ class PlantingsController < DataController
:crop_id, :description, :garden_id, :planted_at,
:parent_seed_id,
:quantity, :sunniness, :planted_from, :finished,
:finished_at, :failed, :overall_rating
:finished_at
)
end

View File

@@ -3,10 +3,9 @@
class RegistrationsController < Devise::RegistrationsController
respond_to :json
prepend_before_action :check_captcha, only: [:create] # Change this to be any actions you want to protect with recaptcha.
def edit
@flickr_auth = current_member.auth('flickr')
@twitter_auth = current_member.auth('twitter')
@flickr_auth = current_member.auth('flickr')
render "edit"
end
@@ -38,12 +37,6 @@ class RegistrationsController < Devise::RegistrationsController
end
end
def regenerate_api_token
current_member.regenerate_api_token
set_flash_message :notice, :api_token_regenerated
redirect_to edit_member_registration_path + '#apps'
end
def destroy
if @member.valid_password?(params.require(:member)[:current_password])
@member.discard
@@ -53,25 +46,6 @@ class RegistrationsController < Devise::RegistrationsController
render "edit"
end
end
private
def sign_up_params
params.require(:member).permit(:login_name, :email, :tos_agreement, :newsletter, :password, :password_confirmation)
end
def check_captcha
return if verify_recaptcha # verify_recaptcha(action: 'signup') for v3
self.resource = resource_class.new sign_up_params
resource.validate # Look for any other validation errors besides reCAPTCHA
set_minimum_password_length
respond_with_navigational(resource) do
flash.discard(:recaptcha_error) # We need to discard flash to avoid showing it on the next page reload
render :new
end
end
end
# check if we need the current password to update fields

View File

@@ -1,15 +1,15 @@
# frozen_string_literal: true
class ScientificNamesController < ApplicationController
before_action :authenticate_member!, except: %i(index show gbif_suggest)
load_and_authorize_resource except: [:gbif_suggest]
before_action :authenticate_member!, except: %i(index show)
load_and_authorize_resource
respond_to :html, :json
responders :flash
# GET /scientific_names
# GET /scientific_names.json
def index
@scientific_names = ScientificName.all.order(:name).paginate(page: params[:page], per_page: 100)
@scientific_names = ScientificName.all.order(:name)
respond_with(@scientific_names)
end
@@ -35,7 +35,7 @@ class ScientificNamesController < ApplicationController
def create
@scientific_name = ScientificName.new(scientific_name_params)
@scientific_name.creator = current_member
gbif_sync!(@scientific_name)
@scientific_name.save
respond_with(@scientific_name.crop)
end
@@ -43,9 +43,7 @@ class ScientificNamesController < ApplicationController
# PUT /scientific_names/1
# PUT /scientific_names/1.json
def update
@scientific_name.assign_attributes(scientific_name_params)
gbif_sync!(@scientific_name)
@scientific_name.save
@scientific_name.update(scientific_name_params)
respond_with(@scientific_name.crop)
end
@@ -58,26 +56,9 @@ class ScientificNamesController < ApplicationController
respond_with(@crop)
end
def gbif_suggest
render json: gbif_service.suggest(params[:term])
end
private
def gbif_sync!(model)
return unless model.gbif_key
result = gbif_service.fetch(model.gbif_key)
model.gbif_rank = result["rank"]
model.gbif_status = result["status"]
end
def scientific_name_params
params.require(:scientific_name).permit(:crop_id, :name, :gbif_key, :wikidata_id)
end
def gbif_service
GbifService.new
params.require(:scientific_name).permit(:crop_id, :name)
end
end

View File

@@ -19,8 +19,6 @@ class SeedsController < DataController
where['parent_planting'] = @planting.id
end
where['tradeable_to'] = params[:tradeable_to] if params[:tradeable_to].present?
@show_all = (params[:all] == '1')
where['finished'] = false unless @show_all
@@ -37,13 +35,12 @@ class SeedsController < DataController
end
def show
@photos = @seed.photos.includes(:owner).order(created_at: :desc, id: :desc).paginate(page: params[:page], per_page: 30)
@photos = @seed.photos.includes(:owner).order(created_at: :desc).paginate(page: params[:page])
respond_with(@seed)
end
def new
@seed = Seed.new
@seed.source = 'my own seed saving'
if params[:planting_slug]
@planting = Planting.find_by(slug: params[:planting_slug])
@@ -57,8 +54,6 @@ class SeedsController < DataController
def create
@seed = Seed.new(seed_params)
@seed.source ||= 'my own seed saving'
@seed.finished ||= false
@seed.owner = current_member
@seed.crop = @seed.parent_planting.crop if @seed.parent_planting
flash[:notice] = "Successfully added #{@seed.crop} seed to your stash." if @seed.save
@@ -86,7 +81,7 @@ class SeedsController < DataController
:crop_id, :description, :quantity, :plant_before,
:parent_planting_id, :saved_at,
:days_until_maturity_min, :days_until_maturity_max,
:organic, :gmo, :source,
:organic, :gmo,
:heirloom, :tradable_to, :slug,
:finished, :finished_at
)

View File

@@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'nokogiri'
module ApplicationHelper
def parse_date(str)
str ||= '' # Date.parse barfs on nil
@@ -22,32 +21,6 @@ module ApplicationHelper
classes
end
# Similar to Rails' time_ago_in_words, but gives a more standard
# output like "in 3 days" or "5 months ago".
# Also handles the case where from_time is a Date and to_time is a Date
# (in which case it just says "today" if they're the same date).
#
# NOTE: This is similar to distance_of_time_in_words but different enough
# that I think it's worth having a separate helper for it.
#
# from_time - the starting time (Time or Date)
# to_time - the ending time (Time or Date). Default: now (Time.zone.now)
# include_seconds - whether to include seconds in the calculation
#
# Returns a string like "in 3 days" or "5 months ago"
def standard_time_distance(from_time, to_time = 0, include_seconds = false)
return 'today' if from_time.is_a?(Date) && (from_time == to_time)
return 'now' if from_time == to_time
return "#{distance_of_time_in_words(from_time, to_time, include_seconds:)} ago" if from_time < to_time
"in #{distance_of_time_in_words(from_time, to_time, include_seconds:)}"
end
def count_github_contibutors
File.open(Rails.root.join('CONTRIBUTORS.md')).readlines.grep(/^-/).size
end
# Produces a cache key for uniquely identifying cached fragments.
def cache_key_for(klass, identifier = "all")
count = klass.count
@@ -55,11 +28,10 @@ module ApplicationHelper
"#{klass.name.downcase.pluralize}/#{identifier}-#{count}-#{max_updated_at}"
end
# A helper to replace the complex template compilation mess
# of HAML, Tilt, and dynamic compilation with interpolated ruby.
def markdownify(text)
translator = Haml::Filters::GrowstuffMarkdown.new
translator.expand_members!(translator.expand_crops!(text.to_s))
def required_field_help_text
asterisk = tag.span('*', class: ['red'])
text = tag.em('denotes a required field')
tag.div(asterisk + ' '.html_safe + text, class: ['margin-bottom'])
end
#
@@ -77,6 +49,7 @@ module ApplicationHelper
uri.query = "&width=#{size}&height=#{size}" if uri.host == 'graph.facebook.com'
# TODO: Assess twitter - https://dev.twitter.com/overview/general/user-profile-images-and-banners
# TODO: Assess flickr - https://www.flickr.com/services/api/misc.buddyicons.html
return uri.to_s
@@ -120,22 +93,4 @@ module ApplicationHelper
def og_description(description)
strip_tags(description).split(' ')[0..20].join(' ')
end
def github_releases
return [] if Rails.env.test?
feed_url = 'https://github.com/Growstuff/growstuff/releases.atom'
Rails.cache.fetch(feed_url, expires_in: 1.day) do
response = Faraday.get(feed_url)
doc = Nokogiri::XML(response.body)
doc.xpath('//xmlns:entry').first(2).map do |entry|
{
title: entry.xpath('xmlns:title').text,
content: entry.xpath('xmlns:content').text,
link: entry.xpath('xmlns:link/@href').text,
updated: entry.xpath('xmlns:updated').text
}
end
end
end
end

View File

@@ -13,7 +13,7 @@ module AutoSuggestHelper
resource = resource.class.name.downcase
source_path = Rails.application.routes.url_helpers.send("search_#{source}s_path", format: :json)
%(
<input id="#{source}" class="auto-suggest #{options[:class]}" #{'required="required"' if options[:required]}
<input id="#{source}" class="auto-suggest #{options[:class]}"
type="text" value="#{default}" data-source-url="#{source_path}",
placeholder="e.g. lettuce">
<noscript class="text-warning">

View File

@@ -2,7 +2,6 @@
module ButtonsHelper
include IconsHelper
def garden_plant_something_button(garden, classes: "btn btn-default")
return unless can? :edit, garden
@@ -19,30 +18,6 @@ module ButtonsHelper
end
end
def garden_plan_something_button(garden, classes: "btn btn-default")
return unless can? :edit, garden
link_to new_activity_path(garden_id: garden.id), class: classes do
activity_icon + ' ' + t('buttons.new_activity')
end
end
def plan_something_button
return unless can? :create, Activity
link_to new_activity_path, class: "btn btn-default" do
activity_icon + ' ' + t('buttons.new_activity')
end
end
def planting_plan_something_button(planting, classes: "btn btn-default")
return unless can? :edit, planting
link_to new_activity_path(planting_id: planting.id), class: classes do
activity_icon + ' ' + t('buttons.new_activity')
end
end
def garden_mark_active_button(garden, classes: 'btn')
link_to t('buttons.mark_as_active'),
garden_path(garden, garden: { active: 1 }),
@@ -53,7 +28,7 @@ module ButtonsHelper
link_to t('buttons.mark_as_inactive'),
garden_path(garden, garden: { active: 0 }),
method: :put, class: classes,
data: { confirm: I18n.t('gardens.confirm_deactivate') }
data: { confirm: 'All plantings associated with this garden will be marked as finished. Are you sure?' }
end
def create_button(model_to_create, path, icon, label)
@@ -84,34 +59,8 @@ module ButtonsHelper
edit_button(edit_planting_path(planting), classes:)
end
def activity_edit_button(activity, classes: "btn btn-raised btn-info")
edit_button(edit_activity_path(slug: activity.slug), classes:)
end
def activity_copy_button(activity, classes: 'btn')
link_to new_activity_path(
name: activity.name,
description: activity.description,
category: activity.category,
garden_id: activity.garden_id,
planting_id: activity.planting_id,
due_date: activity.due_date
), class: classes do
copy_icon + ' ' + t('buttons.copy')
end
end
def activity_finish_button(activity, classes: 'btn btn-default btn-secondary')
return unless can?(:edit, activity) || activity.finished
link_to activity_path(slug: activity.slug, activity: { finished: 1 }),
method: :put, class: "#{classes} append-date" do
finished_icon + ' ' + t('buttons.mark_as_finished')
end
end
def planting_finish_button(planting, classes: 'btn btn-default btn-secondary')
return unless can?(:edit, planting) || planting.finished || planting.failed
return unless can?(:edit, planting) || planting.finished
link_to planting_path(slug: planting.slug, planting: { finished: 1 }),
method: :put, class: "#{classes} append-date" do
@@ -119,15 +68,6 @@ module ButtonsHelper
end
end
def planting_failed_button(planting, classes: 'btn btn-default btn-secondary')
return unless can?(:edit, planting) || planting.finished || planting.failed
link_to planting_path(slug: planting.slug, planting: { failed: 1 }),
method: :put, class: "#{classes}" do
finished_icon + ' ' + t('buttons.mark_as_failed')
end
end
def seed_finish_button(seed, classes: 'btn btn-default')
return unless can?(:create, Planting) && seed.active
@@ -145,7 +85,7 @@ module ButtonsHelper
end
def planting_save_seeds_button(planting, classes: 'btn btn-default')
return unless can?(:edit, planting) && !planting.failed?
return unless can?(:edit, planting)
link_to new_planting_seed_path(planting_slug: planting.slug), class: classes do
seed_icon + ' ' + t('buttons.save_seeds')

View File

@@ -1,92 +1,20 @@
# frozen_string_literal: true
module CropsHelper
def crop_or_parent(crop, attribute)
default = crop.send(attribute)
return default if default.present?
parent = crop
while parent = parent.parent
return parent.send(attribute) if parent&.send(attribute).present?
end
# For scopes, arrays, etc return the empty value
default
end
def display_seed_availability(member, crop)
seeds = member.seeds.where(crop:)
total_quantity = seeds.where.not(quantity: nil).sum(:quantity)
return "You don't have any seeds of this crop." if seeds.none?
if total_quantity == 0
"You have an unknown quantity of seeds of this crop."
else
if total_quantity != 0
"You have #{total_quantity} #{Seed.model_name.human(count: total_quantity)} of this crop."
else
"You have an unknown quantity of seeds of this crop."
end
end
def crop_ebay_seeds_url(crop)
"https://www.ebay.com/sch/i.html?_nkw=#{CGI.escape crop.name}"
end
def youtube_video_id(url)
return unless url
regex = %r{(?:youtube(?:-nocookie)?\.com/(?:[^/\n\s]+/\S+/|(?:v|e(?:mbed)?)/|\S*?[?&]v=)|youtu\.be/)([a-zA-Z0-9_-]{11})}
match = url.match(regex)
match[1] if match
end
def crop_jsonld_data(crop, full_attributes: true)
same_as_urls = [crop.en_wikipedia_url]
crop.scientific_names.each do |scientific_name|
same_as_urls << "https://www.wikidata.org/wiki/#{scientific_name.wikidata_id}" if scientific_name.wikidata_id.present?
end
subject_of_entities = []
if full_attributes
if crop.en_youtube_url.present?
subject_of_entities << {
'@type': "VideoObject",
url: crop.en_youtube_url
}
end
crop.posts.each do |post|
subject_of_entities << {
'@type': "SocialMediaPosting",
url: post_url(post),
author: {
'@type': 'Person',
name: post.author.login_name
},
'datePublished': post.created_at
}
end
images = []
crop.photos.each do |photo|
images << photo.fullsize_url
end
end
# TODO: Review plantings, seeds, harvests as a subtype of social media post or event that ended? Or creative work?
# has_many :plantings, dependent: :destroy
# has_many :seeds, dependent: :destroy
# has_many :harvests, dependent: :destroy
{
'@context': "https://schema.org",
'@type': "BioChemEntity",
name: crop.name,
taxonomicRange: crop.scientific_names.map(&:name),
description: crop.description,
sameAs: same_as_urls,
alternateName: crop.alternate_names.map(&:name),
subjectOf: subject_of_entities,
image: images
}.compact
"https://rover.ebay.com/rover/1/705-53470-19255-0/1?icep_ff3=9&pub=5575213277&toolid=10001&campid=5337940151&customid=&icep_uq=#{CGI.escape crop.name}&icep_sellerId=&icep_ex_kw=&icep_sortBy=12&icep_catId=181003&icep_minPrice=&icep_maxPrice=&ipn=psmain&icep_vectorid=229515&kwid=902099&mtid=824&kw=lg"
end
end

View File

@@ -7,8 +7,6 @@ module EventHelper
def event_description(event)
render "#{event.event_type.pluralize}/description", event_model: resolve_model(event)
rescue ActionView::MissingTemplate
"#{event.event_type.humanize.downcase}d"
end
def resolve_model(event)

View File

@@ -11,12 +11,10 @@ module GardensHelper
end
end
# TODO: Not used?
def display_garden_name(garden)
truncate(garden.name, length: 50, separator: ' ', omission: '... ')
end
# TODO: Only used by specs?
def display_garden_plantings(plantings)
if plantings.blank?
"None"

View File

@@ -59,10 +59,6 @@ module IconsHelper
image_icon 'delete'
end
def copy_icon
icon('far', 'copy')
end
def add_photo_icon
image_icon 'add-photo'
end
@@ -75,10 +71,6 @@ module IconsHelper
icon('fas', 'seedling')
end
def activity_icon
icon('fas', 'fa-truck-pickup')
end
def post_icon
image_icon 'post'
end
@@ -101,7 +93,7 @@ module IconsHelper
def plant_part_icon(name)
if File.exist? Rails.root.join('app', 'assets', 'images', 'icons', 'plant_parts', "#{name}.svg")
image_tag "icons/plant_parts/#{name}.svg", class: 'img img-icon', 'aria-hidden' => "true", alt: name
image_tag "icons/plant_parts/#{name}.svg", class: 'img img-icon', 'aria-hidden' => "true"
else
planting_icon
end
@@ -109,7 +101,7 @@ module IconsHelper
def crop_icon(crop)
if crop.svg_icon.present?
image_tag(crop_path(crop, format: 'svg'), class: 'crop-icon', alt: crop)
image_tag(crop_path(crop, format: 'svg'), class: 'crop-icon')
elsif crop.parent.present?
crop_icon(crop.parent)
else
@@ -131,6 +123,6 @@ module IconsHelper
end
def image_icon(icon)
image_tag "icons/#{icon}.svg", class: 'img img-icon', 'aria-hidden' => "true", alt: icon
image_tag "icons/#{icon}.svg", class: 'img img-icon', 'aria-hidden' => "true"
end
end

View File

@@ -43,14 +43,6 @@ module PlantingsHelper
(planting.first_harvest_predicted_at - Time.zone.today).to_i
end
# Returns a list of gardens the planting can be transplanted to
# based on the planting's owner.
def transplantable_gardens_by_owner(planting)
garden_ids = planting.owner.gardens.select(:id).to_a + GardenCollaborator.where(member_id: planting.owner.id).select(:garden_id).to_a
Garden.active.where.not(id: planting.garden_id).where(id: garden_ids)
end
def days_from_now_to_last_harvest(planting)
return unless planting.planted_at.present? && planting.last_harvest_predicted_at.present?

View File

@@ -1,7 +1,12 @@
# frozen_string_literal: true
module PostsHelper
def display_post_truncated(post, length: 300)
truncate(strip_tags(post.body), length:,
separator: ' ', omission: '... ') { link_to "Read more", post_path(post) }
end
def post_stripped_tags(post, length: 300)
truncate(strip_tags(markdownify(post.body)), length:)
truncate(strip_tags(post.body), length:)
end
end

View File

@@ -2,15 +2,15 @@
class NotifierMailer < ApplicationMailer
# include NotificationsHelper
default from: "Growstuff <#{ENV.fetch('GROWSTUFF_EMAIL', nil)}>"
default from: "Growstuff <#{ENV['GROWSTUFF_EMAIL']}>"
def verifier
unless ENV['RAILS_SECRET_TOKEN']
raise "RAILS_SECRET_TOKEN environment variable" \
"not set - have you created config/application.yml?"
raise "RAILS_SECRET_TOKEN environment variable"\
"not set - have you created config/application.yml?"
end
ActiveSupport::MessageVerifier.new(ENV.fetch('RAILS_SECRET_TOKEN', nil))
ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
end
def notify(notification)
@@ -27,7 +27,7 @@ class NotifierMailer < ApplicationMailer
def planting_reminder(member)
@member = member
@sitename = ENV.fetch('GROWSTUFF_SITE_NAME', nil)
@sitename = ENV['GROWSTUFF_SITE_NAME']
@late = []
@super_late = []

View File

@@ -76,11 +76,9 @@ class Ability
if member.role? :crop_wrangler
can :wrangle, Crop
can :manage, Crop
can :manage, CropCompanion
can :manage, ScientificName
can :manage, AlternateName
can :openfarm, Crop
can :gbif, Crop
end
# any member can create a crop provisionally
@@ -109,42 +107,12 @@ class Ability
can :create, Planting
can :update, Planting, garden: { owner_id: member.id }, crop: { approval_status: 'approved' }
can :destroy, Planting, garden: { owner_id: member.id }, crop: { approval_status: 'approved' }
can :update, Planting do |planting|
planting.garden.garden_collaborators.where(member_id: member.id).any?
end
can :transplant, Planting, garden: { owner_id: member.id }
can :transplant, Planting do |planting|
planting.garden.garden_collaborators.where(member_id: member.id).any?
end
can :destroy, Planting do |planting|
planting.garden.garden_collaborators.where(member_id: member.id).any?
end
can :create, GardenCollaborator, garden: { owner_id: member.id }
can :update, GardenCollaborator, garden: { owner_id: member.id }
can :destroy, GardenCollaborator, garden: { owner_id: member.id }
can :create, Activity
can :update, Activity, owner_id: member.id
can :destroy, Activity, owner_id: member.id
can :update, Activity do |activity|
activity.garden&.garden_collaborators&.where(member_id: member.id)&.any?
end
can :destroy, Activity do |activity|
activity.garden&.garden_collaborators&.where(member_id: member.id)&.any?
end
can :create, Harvest
can :update, Harvest, owner_id: member.id
can :destroy, Harvest, owner_id: member.id
can :update, Harvest, owner_id: member.id, planting: { owner_id: member.id }
can :destroy, Harvest, owner_id: member.id, planting: { owner_id: member.id }
can :update, Harvest do |harvest|
harvest.planting&.garden&.garden_collaborators&.where(member_id: member.id)&.any?
end
can :destroy, Harvest do |harvest|
harvest.planting&.garden&.garden_collaborators&.where(member_id: member.id)&.any?
end
can :create, Photo
can :update, Photo, owner_id: member.id
@@ -180,10 +148,5 @@ class Ability
can :destroy, PlantPart do |pp|
pp.harvests.empty?
end
# Admins can't delete themselves
cannot :destroy, Member
can :destroy, Member do |other_member|
other_member&.id != member.id
end
end
end

View File

@@ -1,49 +0,0 @@
# frozen_string_literal: true
class Activity < ApplicationRecord
extend FriendlyId
include Ownable
include Finishable
include SearchActivities
include Likeable
belongs_to :garden, optional: true
belongs_to :planting, optional: true
friendly_id :activity_slug, use: %i(slugged finders)
CATEGORIES = ["General", "Weeding", "Soil Cultivation", "Fertilizing", "Pruning", "Topical Application/Treating", "Watering"]
validates :name, presence: true
validates :category, inclusion: { in: CATEGORIES }, presence: true
validates :owner, presence: true
validates :slug, uniqueness: true
delegate :location, :latitude, :longitude, to: :owner
delegate :login_name, :slug, :location, to: :owner, prefix: true
def activity_slug
"#{owner.login_name}-#{name}-#{id}".downcase.tr(' ', '-')
end
def to_s
name
end
def garden_name
garden&.name
end
def garden_slug
garden&.slug
end
def planting_name
planting&.crop&.name
end
def planting_slug
planting&.crop&.slug
end
end

View File

@@ -5,7 +5,6 @@ class AlternateName < ApplicationRecord
belongs_to :creator, class_name: 'Member', inverse_of: :created_alternate_names
validates :name, presence: true
validates :crop, presence: true
validates :language, presence: true
after_commit :reindex

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: true
class AustralianFoodClassificationData < ApplicationRecord
belongs_to :crop,
foreign_key: :public_food_key,
primary_key: :public_food_key,
inverse_of: :australian_food_classification_data
end

View File

@@ -2,27 +2,26 @@
class Comment < ApplicationRecord
belongs_to :author, class_name: 'Member', inverse_of: :comments
belongs_to :commentable, polymorphic: true, counter_cache: true
# validates :body, presence: true
belongs_to :post, counter_cache: true
scope :post_order, -> { order(created_at: :asc) } # for display on post page
after_create do
recipient = commentable.author.id
recipient = post.author.id
sender = author.id
# don't send notifications to yourself
if recipient != sender
Notification.create(
recipient_id: recipient,
sender_id: sender,
subject: "#{author} commented on #{commentable.subject}",
subject: "#{author} commented on #{post.subject}",
body:,
notifiable: commentable
post_id: post.id
)
end
end
def to_s
"#{author.login_name} commented on #{commentable.subject}"
"#{author.login_name} commented on #{post.subject}"
end
end

View File

@@ -8,12 +8,7 @@ module Finishable
scope :current, -> { where.not(finished: true) }
def active
# Plantings can fail. At the moment, activities and seeds cannot.
if respond_to?(:failed)
!finished && !failed
else
!finished
end
!finished
end
end
end

View File

@@ -1,11 +0,0 @@
# frozen_string_literal: true
module GbifData
extend ActiveSupport::Concern
included do
def update_gbif_data!
GbifService.new.update_crop(self)
end
end
end

View File

@@ -9,8 +9,8 @@ module MemberFlickr
if @flickr.nil?
flickr_auth = auth('flickr')
if flickr_auth
FlickRaw.api_key = ENV.fetch('GROWSTUFF_FLICKR_KEY', nil)
FlickRaw.shared_secret = ENV.fetch('GROWSTUFF_FLICKR_SECRET', nil)
FlickRaw.api_key = ENV['GROWSTUFF_FLICKR_KEY']
FlickRaw.shared_secret = ENV['GROWSTUFF_FLICKR_SECRET']
@flickr = FlickRaw::Flickr.new
@flickr.access_token = flickr_auth.token
@flickr.access_secret = flickr_auth.secret

View File

@@ -4,10 +4,22 @@ module OpenFarmData
extend ActiveSupport::Concern
included do
def update_openfarm_data!
OpenfarmService.new.update_crop(self)
end
def of_photo
fetch_attr('main_image_path')
end
def height
fetch_attr('height')
end
def spread
fetch_attr('spread')
end
def svg_icon
icon = fetch_attr('svg_icon')
return icon if icon.present?
@@ -19,18 +31,42 @@ module OpenFarmData
fetch_attr('tags_array')
end
def description
fetch_attr('description')
end
def row_spacing
fetch_attr('row_spacing')
end
def common_names
fetch_attr('common_names')
end
def guides_count
fetch_attr('guides_count')
end
def binomial_name
fetch_attr('binomial_name')
end
def sowing_method
fetch_attr('sowing_method')
end
def main_image_path
fetch_attr('main_image_path')
end
def sun_requirements
fetch_attr('sun_requirements')
end
def growing_degree_days
fetch_attr('growing_degree_days')
end
def processing_pictures
fetch_attr('processing_pictures')
end
@@ -39,6 +75,6 @@ module OpenFarmData
def fetch_attr(key)
return if openfarm_data.blank?
openfarm_data.dig('attributes', key)
openfarm_data.fetch('attributes', {}).fetch(key, nil)
end
end

View File

@@ -38,7 +38,7 @@ module PredictHarvest
# status
def harvest_time?
return false if crop.perennial || finished || failed
return false if crop.perennial || finished
# We have harvests but haven't finished
harvests.size.positive? ||

View File

@@ -8,12 +8,12 @@ module PredictPlanting
before_save :calculate_lifespan
def calculate_lifespan
self.lifespan = (planted_at.present? && finished_at.present? && !failed? ? finished_at - planted_at : nil)
self.lifespan = (planted_at.present? && finished_at.present? ? finished_at - planted_at : nil)
end
# dates
def finish_predicted_at
if planted_at.blank? || failed?
if planted_at.blank?
nil
elsif crop.median_lifespan.present?
planted_at + crop.median_lifespan.days
@@ -34,18 +34,15 @@ module PredictPlanting
end
def actual_lifespan
return unless planted_at.present? && finished_at.present? && !failed?
return unless planted_at.present? && finished_at.present?
(finished_at - planted_at).to_i
end
def age_in_days
return if planted_at.blank?
return if failed?
known_last_day ||= finished_at || Time.zone.today
known_last_day = Time.zone.today if known_last_day > Time.zone.today
(known_last_day - planted_at).to_i
end
@@ -53,9 +50,9 @@ module PredictPlanting
Rails.cache.fetch("#{cache_key_with_version}/percentage_grown", expires_in: 8.hours) do
if finished?
100
elsif !planted? || failed?
elsif !planted?
0
elsif crop.perennial || (finish_predicted_at.nil? && finished_at.nil?) # This covers future dated finished_at that hasn't occurrred yet.
elsif crop.perennial || finish_predicted_at.nil?
nil
else
calculate_percentage_grown
@@ -74,29 +71,16 @@ module PredictPlanting
end
def late?
crop.annual? && !finished && !failed &&
crop.annual? && !finished &&
planted_at.present? &&
finish_predicted_at.present? &&
finish_predicted_at <= Time.zone.today
end
# Deactivate any plantings over time_limit that are super late in small batches.
def self.archive!(time_limit: 3.years.ago, limit: 1000)
active_plantings = Planting.annual.active.where("planted_at < ?", time_limit).order(planted_at: :asc).limit(limit)
active_plantings.each do |planting|
if planting.finish_is_predicatable? && planting.super_late?
planting.finished = true
planting.save
end
end
end
private
def calculate_percentage_grown
return 0 if age_in_days.to_i < 0
percent = (age_in_days.to_f / expected_lifespan.to_f) * 100
percent = (age_in_days / expected_lifespan.to_f) * 100
(percent > 100 ? 100 : percent)
end
end

View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true
module SearchActivities
extend ActiveSupport::Concern
included do
searchkick merge_mappings: true,
settings: { number_of_shards: 1, number_of_replicas: 0 },
mappings: {
properties: {
active: { type: :boolean },
created_at: { type: :integer },
updated_at: { type: :integer },
due_date: { type: :date }
}
}
def search_data
{
slug:,
active:,
finished: finished?,
name:,
due_date:,
category:,
garden_id:,
garden_name: garden&.name,
garden_slug: garden&.garden_slug,
planting_id:,
planting_name: planting&.crop&.name,
planting_slug: planting&.slug,
description:,
# owner
owner_id:,
owner_login_name:,
owner_slug:,
# timestamps
created_at: created_at.to_i,
updated_at: updated_at.to_i
}
end
def self.homepage_records(limit)
records = []
owners = []
1..limit.times do
where = {
# photos_count: { gt: 0 },
owner_id: { not: owners }
}
one_record = search('*',
limit: 1,
where:,
boost_by: [:created_at],
load: false).first
return records if one_record.nil?
owners << one_record.owner_id
records << one_record
end
records
end
end
end

View File

@@ -53,9 +53,7 @@ module SearchHarvests
owners = []
1..limit.times do
where = {
# Disabled for now so that more relevant harvests are
# surfaced; even if we're falling back to crop photos.
# photos_count: { gt: 0 },
photos_count: { gt: 0 },
owner_id: { not: owners }
}
one_record = search('*',

Some files were not shown because too many files have changed in this diff Show More