Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

September 2024 release #3868

Open
wants to merge 11 commits into
base: mainline
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,37 @@ jobs:

- name: Prepare database for testing
run: bundle exec rails db:prepare



- 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: 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
run: bundle exec rspec spec/features/ -fd --fail-fast

- name: Report to code climate
run: |
Expand Down
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Metrics/BlockLength:
# Offense count: 7
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 183
Max: 188

# Offense count: 6
# Configuration parameters: AllowedMethods, AllowedPatterns.
Expand Down
8 changes: 4 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.58.0)
haml_lint (0.59.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
Expand Down Expand Up @@ -527,7 +527,7 @@ GEM
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-activemodel-mocks (1.2.0)
rspec-activemodel-mocks (1.2.1)
activemodel (>= 3.0)
activesupport (>= 3.0)
rspec-mocks (>= 2.99, < 4.0)
Expand All @@ -536,7 +536,7 @@ GEM
rspec-expectations (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.1)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (7.0.1)
Expand Down Expand Up @@ -585,7 +585,7 @@ GEM
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
rubocop-rspec (3.0.5)
rubocop-rspec (3.1.0)
rubocop (~> 1.61)
rubocop-rspec_rails (2.30.0)
rubocop (~> 1.61)
Expand Down
87 changes: 87 additions & 0 deletions app/controllers/garden_collaborators_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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
5 changes: 4 additions & 1 deletion app/controllers/gardens_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ def index

@gardens = @gardens.includes(:owner)
@gardens = @gardens.active unless @show_all
@gardens = @gardens.where(owner: @owner) if @owner.present?
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.not(members: { confirmed_at: nil })
.order(:name).paginate(page: params[:page])
respond_with(@gardens)
Expand Down
22 changes: 22 additions & 0 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,38 @@ def member_abilities(member)
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 :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
Expand Down
1 change: 1 addition & 0 deletions app/models/garden.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Garden < ApplicationRecord
has_many :plantings, dependent: :destroy
has_many :crops, through: :plantings
has_many :activities, dependent: :destroy
has_many :garden_collaborators, dependent: :destroy

belongs_to :garden_type, optional: true

Expand Down
24 changes: 24 additions & 0 deletions app/models/garden_collaborator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

class GardenCollaborator < ApplicationRecord
belongs_to :member
belongs_to :garden

validates :member_id, uniqueness: { scope: :garden }
validate :not_garden_owner

def not_garden_owner
return unless member
return unless garden

errors.add(:member_id, "cannot be the garden owner") if garden.owner == member
end

def member_slug
@member&.slug
end

def member_slug=(_slug)
member_slug
end
end
5 changes: 4 additions & 1 deletion app/models/harvest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ def crop_must_match_planting
def owner_must_match_planting
return if planting.blank? # only check if we are linked to a planting

errors.add(:owner, "of harvest must be the same as planting") unless owner == planting.owner
return if owner == planting.owner || planting.garden.garden_collaborators.where(member_id: owner).any?

errors.add(:owner,
"of harvest must be the same as planting, or a collaborator on that garden")
end

def harvest_must_be_after_planting
Expand Down
5 changes: 4 additions & 1 deletion app/models/planting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ def finished_must_be_after_planted
end

def owner_must_match_garden_owner
errors.add(:owner, "must be the same as garden") unless owner == garden.owner
return if owner == garden.owner || garden.garden_collaborators.where(member_id: owner).any?

errors.add(:owner,
"must be the same as garden, or a collaborator on that garden")
end
end
19 changes: 19 additions & 0 deletions app/views/garden_collaborators/_form.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.card.col-md-8.col-lg-7.mx-auto.float-none.white.z-depth-1.py-2.px-2
= bootstrap_form_for(@garden_collaborator.new_record? ? [@garden, @garden_collaborator] : garden_garden_collaborator_path(@garden, @garden_collaborator)) do |f|
.card-body
= required_field_help_text
- if @garden_collaborator.errors.any?
#error_explanation.alert.alert-warning{:role => "alert"}
%h4.alert-heading
= pluralize(@garden_collaborator.errors.size, "error")
prohibited this garden collaborator from being saved
%ul
- @garden_collaborator.errors.full_messages.each do |msg|
%li= msg

.alert.alert-info
Ask your friend, family member or community garden member for their growstuff username to add them as a collaborator on your garden.
= f.text_field :member_slug, maxlength: 255, required: true
.row
.card-footer
.text-right= f.submit 'Save Collaboator'
3 changes: 3 additions & 0 deletions app/views/garden_collaborators/edit.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- content_for :title, "Edit garden collaborator"

= render 'form'
72 changes: 72 additions & 0 deletions app/views/garden_collaborators/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
- content_for :title, "#{@garden} collaborators"

%h1= "#{@garden} collaborators"

- content_for :breadcrumbs do
%li.breadcrumb-item= link_to 'Gardens', gardens_path
%li.breadcrumb-item.active= link_to @garden, gardens_path(@garden)

.row
.col-md-2
- if current_member.present?
.flex-column.nav-pills.layout-nav{"role" => "tablist", "aria-orientation"=>"vertical"}
- if can?(:create, GardenCollaborator.new(garden: @garden))
= link_to url_for([@garden, GardenCollaborator.new(garden: @garden), action: :new]), class: 'btn' do
Add a #{GardenCollaborator.new(garden: @garden).model_name.human}
- else
= render 'shared/signin_signup', to: "record your #{model.to_s.pluralize.downcase}"

%hr/
%p.text-center
#{ENV['GROWSTUFF_SITE_NAME']} helps you track what you're
harvesting from your home garden and see how productive it is.


.col-md-10
- if @garden_collaborators.empty?
%p There are no collaborators to display.
- if can?(:create, GardenCollaborator) && @owner == current_member
= link_to 'Add a garden collaborator', new_garden_garden_collaborator_path, class: 'btn btn-primary'

- else
%section
%h2= page_entries_info @garden_collaborators
= will_paginate @garden_collaborators

- @garden_collaborators.each do |garden_collaborator|
- member = garden_collaborator.member
- cache member do
.card
.card-body
- if can?(:destroy, garden_collaborator)
%div{"style": "float: right"}
= link_to garden_garden_collaborator_path(@garden, garden_collaborator), method: :delete, class: "btn btn-danger" do
Remove access

%h4.login-name= link_to member, member
%div
= render "members/avatar", member: member
%div
= link_to "view all #{member}'s gardens", member_gardens_path(member)
%p
%small
Joined
= distance_of_time_in_words(member.created_at, Time.zone.now)
ago.
- if member.location.present?
= link_to member.location, place_path(member.location)
.card-footer
%ul.nav.nav-justified.small
%li.nav-item.border-right
= link_to member_plantings_path(member) do
= localize_plural(member.plantings.active, Planting)
%li.nav-item.border-right
= link_to member_harvests_path(member) do
= localize_plural(member.harvests, Harvest)
%li.nav-item
= link_to member_seeds_path(member) do
= localize_plural(member.seeds.active, Seed)

.row
.col-12= page_entries_info @garden_collaborators
.col-12= will_paginate @garden_collaborators
3 changes: 3 additions & 0 deletions app/views/garden_collaborators/new.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- content_for :title, "New garden collaborator"

= render 'form'
Empty file.
2 changes: 1 addition & 1 deletion app/views/gardens/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
%li= msg

= f.text_field :name, maxlength: 255, required: true
= f.text_area :description, rows: 6
= f.text_area :description, rows: 6, placeholder: "Tell us about this garden - where is it located? What does it look like? Do you have a link to a photo? Do you have irrigation? What are your plans?"
= f.text_field :location,
value: @garden.location || current_member.location,
class: 'form-control', maxlength: 255
Expand Down
Loading
Loading