850 lines
22 KiB
Go
850 lines
22 KiB
Go
package lessons
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.ex.umbach.dev/LMS/libcore/models"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
"lms.de/backend/modules/database"
|
|
"lms.de/backend/modules/structs"
|
|
"lms.de/backend/modules/utils"
|
|
"lms.de/backend/socketclients"
|
|
)
|
|
|
|
func GetLessons(c *fiber.Ctx) error {
|
|
// swagger:operation GET /v1/lessons lessons GetLessons
|
|
// ---
|
|
// summary: Get lessons.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// responses:
|
|
// '200':
|
|
// description: Lessons retrieved successfully.
|
|
// schema:
|
|
// type: array
|
|
// items:
|
|
// "$ref": "#/definitions/LessonResponse"
|
|
// '500':
|
|
// description: Failed to retrieve lessons.
|
|
|
|
var lessons []structs.LessonResponse
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&lessons).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
return c.JSON(lessons)
|
|
}
|
|
|
|
func CreateLesson(c *fiber.Ctx) error {
|
|
// swagger:operation POST /v1/lessons lessons CreateLesson
|
|
// ---
|
|
// summary: Create lesson.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson created successfully.
|
|
// schema:
|
|
// "$ref": "#/definitions/CreateLessonResponse"
|
|
// '500':
|
|
// description: Failed to create lesson.
|
|
|
|
lesson := models.Lesson{
|
|
Id: uuid.New().String(),
|
|
OrganizationId: c.Locals("organizationId").(string),
|
|
State: structs.LessonStateDraft,
|
|
Title: "Test",
|
|
CreatorUserId: c.Locals("userId").(string),
|
|
}
|
|
|
|
if err := database.DB.Create(&lesson).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessons,
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonCreated,
|
|
Body: lesson,
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
structs.CreateLessonResponse{
|
|
Id: lesson.Id,
|
|
},
|
|
)
|
|
}
|
|
|
|
func GetLessonContents(c *fiber.Ctx) error {
|
|
// swagger:operation GET /v1/lessons/{lessonId}/contents lessons GetLessonContents
|
|
// ---
|
|
// summary: Get lesson contents.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson contents retrieved successfully.
|
|
// schema:
|
|
// type: array
|
|
// items:
|
|
// "$ref": "#/definitions/LessonContent"
|
|
// '500':
|
|
// description: Failed to retrieve lesson contents.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var lessonContents []structs.GetLessonContentsResponse
|
|
|
|
if err := database.DB.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Find(&lessonContents).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// sort contents by position
|
|
|
|
sort.SliceStable(lessonContents, func(i, j int) bool {
|
|
return lessonContents[i].Position < lessonContents[j].Position
|
|
})
|
|
|
|
return c.JSON(lessonContents)
|
|
}
|
|
|
|
func GetLessonSettings(c *fiber.Ctx) error {
|
|
// swagger:operation GET /v1/lessons/{lessonId}/settings lessons GetLessonSettings
|
|
// ---
|
|
// summary: Get lesson settings.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson settings retrieved successfully.
|
|
// schema:
|
|
// "$ref": "#/definitions/LessonSettings"
|
|
// '500':
|
|
// description: Failed to retrieve lesson settings.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var lessonSettings structs.LessonSettings
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lessonSettings).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
return c.JSON(lessonSettings)
|
|
}
|
|
|
|
func UpdateLessonPreviewTitle(c *fiber.Ctx) error {
|
|
// swagger:operation PATCH /v1/lessons/{lessonId}/preview/title lessons UpdateLessonPreviewTitle
|
|
// ---
|
|
// summary: Update lesson preview title.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonPreviewRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson preview updated successfully.
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonPreviewResponse"
|
|
// '500':
|
|
// description: Failed to update lesson preview.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var body structs.UpdateLessonPreviewTitleRequest
|
|
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("title", body.Title).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicsExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
[]string{utils.SubscribedTopicLessons, utils.SubscribedTopicLessonsEditorId(params.LessonId)},
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonPreviewTitleUpdated,
|
|
Body: struct {
|
|
LessonId string
|
|
Title string
|
|
}{
|
|
LessonId: params.LessonId,
|
|
Title: body.Title,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
fiber.Map{
|
|
"message": "Lesson preview updated successfully",
|
|
},
|
|
)
|
|
}
|
|
|
|
func UpdateLessonPreviewThumbnail(c *fiber.Ctx) error {
|
|
// swagger:operation POST /v1/lessons/{lessonId}/preview/thumbnail lessons UpdateLessonPreviewThumbnail
|
|
// ---
|
|
// summary: Update lesson preview thumbnail.
|
|
// consumes:
|
|
// - multipart/form-data
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonPreviewThumbnailRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson preview updated successfully.
|
|
// '500':
|
|
// description: Failed to update lesson preview.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
fileHeader, err := c.FormFile("file")
|
|
|
|
if err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if fileHeader.Size > utils.MaxImageSize {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if !utils.IsFileTypeAllowed(fileHeader.Header.Get("Content-Type"), utils.AcceptedImageFileTypes) {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
// get current thumbnail
|
|
|
|
lesson := models.Lesson{}
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lesson).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// delete current thumbnail
|
|
|
|
if lesson.ThumbnailUrl != "" {
|
|
utils.DeleteFile(lesson.ThumbnailUrl)
|
|
}
|
|
|
|
fileName := uuid.New().String() + "." + strings.Split(fileHeader.Header["Content-Type"][0], "/")[1]
|
|
|
|
databasePath, publicPath := utils.GetFullImagePath(c.Locals("organizationId").(string), params.LessonId)
|
|
|
|
utils.CreateFolderStructureIfNotExists(publicPath)
|
|
|
|
thumbnailUrl := databasePath + fileName
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("thumbnail_url", thumbnailUrl).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopics(
|
|
c.Locals("organizationId").(string),
|
|
[]string{utils.SubscribedTopicLessons, utils.SubscribedTopicLessonsEditorId(params.LessonId)},
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonPreviewThumbnailUpdated,
|
|
Body: struct {
|
|
LessonId string
|
|
ThumbnailUrl string
|
|
}{
|
|
LessonId: params.LessonId,
|
|
ThumbnailUrl: thumbnailUrl,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.SaveFile(fileHeader, publicPath+fileName)
|
|
}
|
|
|
|
func UpdateLessonState(c *fiber.Ctx) error {
|
|
// swagger:operation PATCH /v1/lessons/{lessonId}/state lessons UpdateLessonState
|
|
// ---
|
|
// summary: Update lesson state.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonStateRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson state updated successfully.
|
|
// '500':
|
|
// description: Failed to update lesson state.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var body structs.UpdateLessonStateRequest
|
|
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if err := database.DB.Model(&models.Lesson{}).
|
|
Where("id = ?", params.LessonId).
|
|
Where("organization_id = ?", c.Locals("organizationId").(string)).
|
|
Update("state", body.State).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonStateUpdated,
|
|
Body: struct {
|
|
LessonId string
|
|
State uint8
|
|
}{
|
|
LessonId: params.LessonId,
|
|
State: body.State,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
fiber.Map{
|
|
"message": "Lesson state updated successfully",
|
|
},
|
|
)
|
|
}
|
|
|
|
func AddLessonContent(c *fiber.Ctx) error {
|
|
// swagger:operation POST /v1/lessons/{lessonId}/contents lessons AddLessonContent
|
|
// ---
|
|
// summary: Add lesson content.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/AddLessonContentRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson content added successfully.
|
|
// schema:
|
|
// "$ref": "#/definitions/AddLessonContentResponse"
|
|
// '500':
|
|
// description: Failed to add lesson content.
|
|
|
|
var params structs.LessonIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var body structs.AddLessonContentRequest
|
|
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
// get last position
|
|
|
|
var lastContent models.LessonContent
|
|
|
|
if err := database.DB.Select("position").Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Order("position desc").First(&lastContent).Error; err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return c.SendStatus(fiber.StatusNotFound)
|
|
}
|
|
|
|
// no content found, set position to 1
|
|
lastContent.Position = 1
|
|
}
|
|
|
|
// create new content
|
|
|
|
content := models.LessonContent{
|
|
Id: uuid.New().String(),
|
|
LessonId: params.LessonId,
|
|
Page: 1,
|
|
Position: lastContent.Position + 1,
|
|
Type: body.Type,
|
|
Data: body.Data,
|
|
}
|
|
|
|
if err := database.DB.Create(&content).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonAddedContent,
|
|
Body: content,
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
structs.AddLessonContentResponse{
|
|
Id: content.Id,
|
|
},
|
|
)
|
|
}
|
|
|
|
func UploadLessonContentFile(c *fiber.Ctx) error {
|
|
// swagger:operation POST /v1/lessons/{lessonId}/contents/{contentId}/file/{type} lessons UploadLessonContentFile
|
|
// ---
|
|
// summary: Upload lesson content file.
|
|
// consumes:
|
|
// - multipart/form-data
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: contentId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: file
|
|
// in: formData
|
|
// required: true
|
|
// type: file
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson content file uploaded successfully.
|
|
// '500':
|
|
// description: Failed to upload lesson content file.
|
|
|
|
var params structs.UploadLessonContentFileParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
fileHeader, err := c.FormFile("file")
|
|
|
|
if err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if params.Type == "image" {
|
|
if fileHeader.Size > utils.MaxImageSize {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if !utils.IsFileTypeAllowed(fileHeader.Header.Get("Content-Type"), utils.AcceptedImageFileTypes) {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
} else if params.Type == "video" {
|
|
if fileHeader.Size > utils.MaxVideoSize {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if !utils.IsFileTypeAllowed(fileHeader.Header.Get("Content-Type"), utils.AcceptedVideoFileTypes) {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
} else {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
// get current file
|
|
|
|
content := models.LessonContent{}
|
|
|
|
if err := database.DB.Select("type", "data").Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// delete current image
|
|
|
|
if content.Type == utils.LessonContentTypeImage && content.Data != "" {
|
|
utils.DeleteFile(content.Data)
|
|
}
|
|
|
|
fileName := uuid.New().String() + "." + strings.Split(fileHeader.Header["Content-Type"][0], "/")[1]
|
|
|
|
databasePath, publicPath := utils.GetFullImagePath(c.Locals("organizationId").(string), params.LessonId)
|
|
|
|
utils.CreateFolderStructureIfNotExists(publicPath)
|
|
|
|
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", databasePath+fileName).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"error": "Failed to save file",
|
|
})
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonContentFileUpdated,
|
|
Body: struct {
|
|
ContentId string
|
|
LessonId string
|
|
Data string
|
|
}{
|
|
ContentId: params.ContentId,
|
|
LessonId: params.LessonId,
|
|
Data: databasePath + fileName,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(fiber.Map{
|
|
"Data": databasePath + fileName,
|
|
})
|
|
}
|
|
|
|
func UpdateLessonContent(c *fiber.Ctx) error {
|
|
// swagger:operation PATCH /v1/lessons/{lessonId}/contents/{contentId} lessons UpdateLessonContent
|
|
// ---
|
|
// summary: Update lesson content.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: contentId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonContentRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson content updated successfully.
|
|
// '500':
|
|
// description: Failed to update lesson content.
|
|
|
|
var params structs.LessonIdAndContentIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var body structs.UpdateLessonContentRequest
|
|
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", body.Data).Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonContentUpdated,
|
|
Body: struct {
|
|
ContentId string
|
|
LessonId string
|
|
Data string
|
|
}{
|
|
ContentId: params.ContentId,
|
|
LessonId: params.LessonId,
|
|
Data: body.Data,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
fiber.Map{
|
|
"message": "Lesson content updated successfully",
|
|
},
|
|
)
|
|
}
|
|
|
|
func UpdateLessonContentPosition(c *fiber.Ctx) error {
|
|
// swagger:operation PATCH /v1/lessons/{lessonId}/contents/{contentId}/position lessons UpdateLessonContentPosition
|
|
// ---
|
|
// summary: Update lesson content position.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: contentId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/UpdateLessonContentPositionRequest"
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson content position updated successfully.
|
|
// '500':
|
|
// description: Failed to update lesson content position.
|
|
|
|
var params structs.LessonIdAndContentIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
var body structs.UpdateLessonContentPositionRequest
|
|
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
// update position
|
|
|
|
newPosition := body.Position
|
|
|
|
// Begin a transaction to ensure consistency
|
|
tx := database.DB.Begin()
|
|
if tx.Error != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// Fetch the current position of the content being moved
|
|
var currentContent models.LessonContent
|
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(¤tContent).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusNotFound)
|
|
}
|
|
|
|
oldPosition := currentContent.Position
|
|
|
|
if oldPosition == newPosition {
|
|
// No need to update if the position hasn't changed
|
|
return c.JSON(fiber.Map{
|
|
"message": "Lesson content position updated successfully",
|
|
})
|
|
}
|
|
|
|
if oldPosition < newPosition {
|
|
// Items between oldPosition and newPosition need to be shifted down
|
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
|
Where("position > ? AND position <= ?", oldPosition, newPosition).
|
|
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
} else {
|
|
// Items between newPosition and oldPosition need to be shifted up
|
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
|
Where("position >= ? AND position < ?", newPosition, oldPosition).
|
|
Update("position", gorm.Expr("position + 1")).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// Update the position of the moved content
|
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("position", newPosition).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// Commit the transaction
|
|
if err := tx.Commit().Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonContentUpdatedPosition,
|
|
Body: struct {
|
|
ContentId string
|
|
LessonId string
|
|
Position uint16
|
|
}{
|
|
ContentId: params.ContentId,
|
|
LessonId: params.LessonId,
|
|
Position: body.Position,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
fiber.Map{
|
|
"message": "Lesson content position updated successfully",
|
|
},
|
|
)
|
|
}
|
|
|
|
func DeleteLessonContent(c *fiber.Ctx) error {
|
|
// swagger:operation DELETE /v1/lessons/{lessonId}/contents/{contentId} lessons DeleteLessonContent
|
|
// ---
|
|
// summary: Delete lesson content.
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: lessonId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// - name: contentId
|
|
// in: path
|
|
// required: true
|
|
// type: string
|
|
// responses:
|
|
// '200':
|
|
// description: Lesson content deleted successfully.
|
|
// '500':
|
|
// description: Failed to delete lesson content.
|
|
|
|
var params structs.LessonIdAndContentIdParam
|
|
|
|
if err := c.ParamsParser(¶ms); err != nil {
|
|
return c.SendStatus(fiber.StatusBadRequest)
|
|
}
|
|
|
|
// Begin a transaction to ensure consistency
|
|
tx := database.DB.Begin()
|
|
if tx.Error != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// Fetch the current position of the content being deleted
|
|
var content models.LessonContent
|
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusNotFound)
|
|
}
|
|
|
|
// Delete the content
|
|
if err := tx.Delete(&content).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// Shift down the positions of all contents with a higher position
|
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
|
Where("position > ?", content.Position).
|
|
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
|
tx.Rollback()
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
// Commit the transaction
|
|
if err := tx.Commit().Error; err != nil {
|
|
return c.SendStatus(fiber.StatusInternalServerError)
|
|
}
|
|
|
|
socketclients.BroadcastMessageToTopicStartsWithExceptBrowserTabSession(
|
|
c.Locals("organizationId").(string),
|
|
utils.SubscribedTopicLessonsId(params.LessonId),
|
|
c.Locals("browserTabSession").(string),
|
|
structs.SendSocketMessage{
|
|
Cmd: utils.SendCmdLessonDeletedContent,
|
|
Body: struct {
|
|
ContentId string
|
|
LessonId string
|
|
}{
|
|
ContentId: params.ContentId,
|
|
LessonId: params.LessonId,
|
|
},
|
|
},
|
|
)
|
|
|
|
return c.JSON(
|
|
fiber.Map{
|
|
"message": "Lesson content deleted successfully",
|
|
},
|
|
)
|
|
}
|