mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-01-01 05:57:50 -05:00
This change introduces polymorphic comments, allowing you to comment on Photos, Plantings, Harvests, and Activities, in addition to Posts.
Key changes include:
- **Comment Model:**
- Made `Comment.commentable` a polymorphic association.
- Added a data migration to move existing post comments to the new structure.
- Updated notification creation logic for polymorphic commentables.
- **CommentsController:**
- Refactored to handle various commentable types using a `find_commentable` method.
- **Ability Model:**
- Updated permissions for comment creation, editing (author/admin), and deletion (author/commentable owner/admin).
- **Routes:**
- Added nested comment routes for Photos, Plantings, Harvests, Activities, and Posts using a `commentable` concern with shallow routes.
- **Views:**
- Created generic partials for comment forms (`_form.html.haml`) and display (`_comment.html.haml`, `_comments.html.haml`).
- Integrated these partials into the show pages of all commentable types.
- Updated `comments/new` and `comments/edit` views to be generic.
- Relevant parent controller `show` actions now eager-load comments.
- **Testing:**
- Added extensive model, controller (using shared examples), and feature tests to cover the new polymorphic comment functionality, including permissions and UI interactions for all commentable types.
- Updated and created factories as needed.
This fulfills the issue requirements for adding comments to multiple resource types with appropriate permissions.
197 lines
6.6 KiB
Ruby
197 lines
6.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Ability
|
|
include CanCan::Ability
|
|
|
|
def initialize(member)
|
|
anon_abilities(member)
|
|
member_abilities(member) if member.present?
|
|
admin_abilities(member) if member.present? && member.role?(:admin)
|
|
end
|
|
|
|
def anon_abilities(_member)
|
|
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
|
|
|
# everyone can do these things, even non-logged in
|
|
can :read, :all
|
|
can :read, Follow
|
|
can :followers, Follow
|
|
|
|
# Everyone can see the charts
|
|
can :timeline, Garden
|
|
can :sunniness, Crop
|
|
can :planted_from, Crop
|
|
can :harvested_for, Crop
|
|
|
|
# except these, which don't make sense if you're not logged in
|
|
cannot :read, Notification
|
|
cannot :read, Authentication
|
|
|
|
# and nobody should be able to view this except admins
|
|
cannot :read, Role
|
|
|
|
# nobody should be able to view unapproved crops unless they
|
|
# are wranglers or admins
|
|
cannot :read, Crop
|
|
can :read, Crop, approval_status: "approved"
|
|
# scientific names should only be viewable if associated crop is approved
|
|
cannot :read, ScientificName
|
|
can :read, ScientificName do |sn|
|
|
sn.crop.approved?
|
|
end
|
|
# ... same for alternate names
|
|
cannot :read, AlternateName
|
|
can :read, AlternateName do |an|
|
|
an.crop.approved?
|
|
end
|
|
|
|
cannot :create, GardenType
|
|
cannot :update, GardenType
|
|
cannot :destroy, GardenType
|
|
end
|
|
|
|
def member_abilities(member)
|
|
return unless member
|
|
|
|
# members can see even rejected or pending crops if they requested it
|
|
can :read, Crop, requester_id: member.id
|
|
can :requested, Crop # see list of crops they've requested
|
|
|
|
# managing your own user settings
|
|
can :update, Member, id: member.id
|
|
|
|
# can read/delete notifications that were sent to them
|
|
can :read, Notification, recipient_id: member.id
|
|
can :destroy, Notification, recipient_id: member.id
|
|
can :reply, Notification, recipient_id: member.id
|
|
# can send a private message to anyone but themselves
|
|
# note: sadly, we can't test for this from the view, but it works
|
|
# for the model/controller
|
|
can :create, Notification do |n|
|
|
n.recipient_id != member.id
|
|
end
|
|
# NOTE: we don't support update for notifications
|
|
|
|
# only crop wranglers can create/edit/destroy crops
|
|
if member.role? :crop_wrangler
|
|
can :wrangle, Crop
|
|
can :manage, Crop
|
|
can :manage, ScientificName
|
|
can :manage, AlternateName
|
|
can :openfarm, Crop
|
|
can :gbif, Crop
|
|
end
|
|
|
|
# any member can create a crop provisionally
|
|
can :create, Crop
|
|
|
|
# can create & destroy their own authentications against other sites.
|
|
can :create, Authentication
|
|
can :destroy, Authentication, member_id: member.id
|
|
|
|
# anyone can create a post, like, or comment on a post,
|
|
# but only the author can edit/destroy it.
|
|
can :create, Post
|
|
can :update, Post, author_id: member.id
|
|
can :destroy, Post, author_id: member.id
|
|
can :create, Like
|
|
can :destroy, Like, member_id: member.id
|
|
can :create, Comment
|
|
can :update, Comment, author_id: member.id
|
|
can :destroy, Comment do |comment|
|
|
is_author = comment.author_id == member.id
|
|
is_commentable_owner = false
|
|
if comment.commentable.present?
|
|
if comment.commentable.respond_to?(:owner_id) && comment.commentable.owner_id == member.id
|
|
is_commentable_owner = true
|
|
# Posts use author_id for their "owner"
|
|
elsif comment.commentable.respond_to?(:author_id) && comment.commentable.author_id == member.id
|
|
is_commentable_owner = true
|
|
end
|
|
end
|
|
is_author || is_commentable_owner
|
|
end
|
|
|
|
# same deal for gardens and plantings
|
|
can :create, Garden
|
|
can :update, Garden, owner_id: member.id
|
|
can :destroy, Garden, owner_id: member.id
|
|
|
|
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
|
|
can :destroy, Photo, owner_id: member.id
|
|
|
|
can :create, Seed
|
|
can :update, Seed, owner_id: member.id
|
|
can :destroy, Seed, owner_id: member.id
|
|
can :create, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
|
can :update, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
|
can :destroy, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
|
|
|
# following/unfollowing permissions
|
|
can :create, Follow
|
|
cannot :create, Follow, followed_id: member.id # can't follow yourself
|
|
|
|
can :destroy, Follow
|
|
cannot :destroy, Follow, followed_id: member.id # can't unfollow yourself
|
|
|
|
cannot :create, GardenType
|
|
cannot :update, GardenType
|
|
cannot :destroy, GardenType
|
|
end
|
|
|
|
def admin_abilities(member)
|
|
return unless member.role? :admin
|
|
|
|
can :read, :all
|
|
can :manage, :all
|
|
|
|
# can't delete plant parts if they have harvests associated with them
|
|
cannot :destroy, PlantPart
|
|
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
|