feat: private materials with per-user shares
A material may be private: only its author and the users it is shared
with see it. Every student read (list, detail, course detail and
counters, the checklist linked material, the token twins) goes through
one visibility fragment. Existing materials stay public with no author.
GET/PUT /_bm/api/v1/materials/{id}/access let the author, a material
manager, toggle the flag and pick the users; the first change of an
author-less material makes the caller its author.
This commit is contained in:
@@ -18,21 +18,24 @@ const MaterialFilesField = "files"
|
||||
|
||||
// Material is a study material: a note, a link or uploaded files, optionally
|
||||
// tied to a course. A material without published_at is a draft and is not
|
||||
// served by the API.
|
||||
// served by the API. A private material is visible only to its author and to
|
||||
// the users it is shared with (MaterialShare); a public one to every student.
|
||||
type Material struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
CourseID *uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
MeetingID *uint `gorm:"column:meeting_id"`
|
||||
Meeting *Meeting `gorm:"foreignKey:MeetingID"`
|
||||
Title string `gorm:"column:title"`
|
||||
Type string `gorm:"column:type"`
|
||||
Body *string `gorm:"column:body"`
|
||||
Source *string `gorm:"column:source"`
|
||||
URL *string `gorm:"column:url"`
|
||||
PublishedAt *time.Time `gorm:"column:published_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
CourseID *uint `gorm:"column:course_id"`
|
||||
Course *Course `gorm:"foreignKey:CourseID"`
|
||||
MeetingID *uint `gorm:"column:meeting_id"`
|
||||
Meeting *Meeting `gorm:"foreignKey:MeetingID"`
|
||||
Title string `gorm:"column:title"`
|
||||
Type string `gorm:"column:type"`
|
||||
Body *string `gorm:"column:body"`
|
||||
Source *string `gorm:"column:source"`
|
||||
URL *string `gorm:"column:url"`
|
||||
PublishedAt *time.Time `gorm:"column:published_at"`
|
||||
IsPrivate bool `gorm:"column:is_private"`
|
||||
AuthorUserID *uint `gorm:"column:author_user_id"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
|
||||
Tags []Tag `gorm:"many2many:jz_bm_material_tag;joinForeignKey:material_id;joinReferences:tag_id"`
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func (Material) MorphName() string { return `Jz\Bm\Models\Material` }
|
||||
|
||||
// Fillable lists the columns mass assignment may set.
|
||||
func (Material) Fillable() []string {
|
||||
return []string{"course_id", "meeting_id", "title", "type", "body", "source", "url", "published_at"}
|
||||
return []string{"course_id", "meeting_id", "title", "type", "body", "source", "url", "published_at", "is_private", "author_user_id"}
|
||||
}
|
||||
|
||||
// Rules are checked by the admin API on every save.
|
||||
|
||||
11
models/material_share.go
Normal file
11
models/material_share.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
// MaterialShare is the pivot row between a private material and a user it
|
||||
// is shared with.
|
||||
type MaterialShare struct {
|
||||
MaterialID uint `gorm:"column:material_id;primaryKey"`
|
||||
UserID uint `gorm:"column:user_id;primaryKey"`
|
||||
}
|
||||
|
||||
// TableName is the pivot table.
|
||||
func (MaterialShare) TableName() string { return "jz_bm_material_shares" }
|
||||
Reference in New Issue
Block a user