package controllers import ( "fmt" "net/http" "os/exec" "path/filepath" "strconv" "github.com/gin-gonic/gin" "git.dynamicdiscord.de/kalipso/zineshop/models" //"git.dynamicdiscord.de/kalipso/zineshop/services" "git.dynamicdiscord.de/kalipso/zineshop/repositories" ) type CRUDController interface { Create(*gin.Context) GetAll(*gin.Context) GetById(*gin.Context) Update(*gin.Context) Delete(*gin.Context) } type ShopItemController interface { CRUDController ShopItemView(*gin.Context) AddItemView(*gin.Context) AddItemHandler(*gin.Context) AddItemsView(*gin.Context) AddItemsHandler(*gin.Context) CreateTag(*gin.Context) GetAllTags(*gin.Context) EditItemView(*gin.Context) EditItemHandler(*gin.Context) DeleteItemView(*gin.Context) DeleteItemHandler(*gin.Context) TagView(*gin.Context) TagHandler(*gin.Context) AddTagHandler(*gin.Context) } type shopItemController struct{} func NewShopItemController() ShopItemController { return &shopItemController{} } func (rc *shopItemController) GetAll(c *gin.Context) { shopItems, err := repositories.ShopItems.GetAll() if err != nil { ReplyError(c, fmt.Errorf("Could not query shopItems")) return } c.JSON(http.StatusOK, shopItems) } func (rc *shopItemController) GetById(c *gin.Context) { shopItem, err := repositories.ShopItems.GetById(c.Param("id")) if err != nil { ReplyError(c, fmt.Errorf("Could not query shopItem: %v", err)) return } ReplyOK(c, shopItem) } // this currently creates quite big preview images // workaround is running the following command in the uploads folder: // for file in *.png; do convert "$file" -resize 35% "$file"; done func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.ShopItem, error) { defaultImagePath := "static/img/zine.jpg" name := ctx.PostForm("name") abstract := ctx.PostForm("abstract") description := ctx.PostForm("description") categoryStr := ctx.PostForm("category") variantNames := ctx.PostFormArray("variant-name[]") variantValues := ctx.PostFormArray("variant-value[]") tagIds := ctx.PostFormArray("tags[]") image, err := ctx.FormFile("image") dstImage := defaultImagePath printMode := ctx.PostForm("print-mode") if err == nil { dstImage = filepath.Join("static/uploads", image.Filename) if err := ctx.SaveUploadedFile(image, dstImage); err != nil { return models.ShopItem{}, fmt.Errorf("Could not save image") } } dstPdf := "" pdf, err := ctx.FormFile("pdf") if err == nil { dstPdf = filepath.Join("static/uploads", pdf.Filename) fmt.Println("Saving pdf at ", dstPdf) if err := ctx.SaveUploadedFile(pdf, dstPdf); err != nil { return models.ShopItem{}, fmt.Errorf("Could not save PDF") } if dstImage == defaultImagePath { dstImage = dstPdf + ".preview.png" cmd := exec.Command("pdftoppm", "-png", "-singlefile", dstPdf, dstPdf+".preview") _, err := cmd.Output() if err != nil { fmt.Println("Error during pdftoppm: ", err.Error()) } cmd2 := exec.Command("convert", dstImage, "-resize", "35%", dstImage) _, err = cmd2.Output() if err != nil { fmt.Println("Error during resizing preview image: ", err.Error()) } } } else { fmt.Println(err) } if name == "" || description == "" { return models.ShopItem{}, fmt.Errorf("Name or description empty") } category, err := models.ParseCategory(categoryStr) if err != nil { return models.ShopItem{}, err } var variants []models.ItemVariant fmt.Println("VariantNames: ", variantNames) fmt.Println("VariantValues: ", variantValues) if len(variantNames) != len(variantValues) { return models.ShopItem{}, fmt.Errorf("Different number of variant names and values") } for idx := range variantNames { if variantValues[idx] == "" || variantNames[idx] == "" { continue } price, err := strconv.ParseFloat(variantValues[idx], 64) if err != nil { return models.ShopItem{}, fmt.Errorf("Could not variant parse price") } variants = append(variants, models.ItemVariant{ Name: variantNames[idx], Price: price, }) } shopItem := models.ShopItem{ Name: name, Abstract: abstract, Description: description, Category: category, IsPublic: true, BasePrice: rc.GetBasePrice(variants), Image: dstImage, Pdf: dstPdf, Variants: variants, PrintMode: printMode, } fmt.Println("Creating Shopitem: ", shopItem) for _, tagId := range tagIds { tag, err := repositories.Tags.GetById(tagId) if err != nil { return models.ShopItem{}, fmt.Errorf("Could not get tag by id") } shopItem.Tags = append(shopItem.Tags, tag) } return shopItem, nil } func (rc *shopItemController) GetBasePrice(variants []models.ItemVariant) float64 { result := 0.0 for idx, variant := range variants { if idx == 0 { result = variant.Price continue } if variant.Price < result { result = variant.Price } } return result } func (rc *shopItemController) Create(c *gin.Context) { shopItem, err := rc.NewShopItemFromForm(c) if err != nil { ReplyError(c, fmt.Errorf("shopItem creation failed: %s", err)) return } _, err = repositories.ShopItems.Create(shopItem) if err != nil { ReplyError(c, fmt.Errorf("shopItem creation failed: %s", err)) return } ReplyOK(c, "shopItem was created") } func (rc *shopItemController) Update(c *gin.Context) { shopItemId, err := strconv.Atoi(c.Param("id")) if err != nil { ReplyError(c, fmt.Errorf("shopItem with Id '%s' does not exist", c.Param("id"))) return } shopItem, err := rc.NewShopItemFromForm(c) if err != nil { ReplyError(c, err) return } shopItem.ID = uint(shopItemId) _, err = repositories.ShopItems.Update(shopItem) if err != nil { ReplyError(c, fmt.Errorf("shopItem creation failed: %s", err)) return } ReplyOK(c, "shopItem was updated") } // TODO: delete associated cartitems func (rc *shopItemController) Delete(c *gin.Context) { err := repositories.ShopItems.DeleteById(c.Param("id")) if err != nil { ReplyError(c, fmt.Errorf("shopItem deletion failed: %s", err)) return } ReplyOK(c, "shopItem was deleted") } func (rc *shopItemController) ShopItemView(c *gin.Context) { shopItem, err := repositories.ShopItems.GetById(c.Param("id")) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": "Item does not exist"}}) return } //TODO: get tags by item tags, err := repositories.Tags.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) return } data := CreateSessionData(c, gin.H{ "shopItem": shopItem, "tags": tags, }) c.HTML(http.StatusOK, "shopitem.html", data) } func (rc *shopItemController) AddItemView(c *gin.Context) { tags, err := repositories.Tags.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "additem.html", gin.H{"error": err}) } data := CreateSessionData(c, gin.H{ "error": "", "success": "", "tags": tags, }) c.HTML(http.StatusOK, "additem.html", data) } func (rc *shopItemController) AddItemHandler(c *gin.Context) { errorHandler := func(err error, tags []models.Tag) { data := CreateSessionData(c, gin.H{ "error": err, "success": "", "tags": tags, }) c.HTML(http.StatusOK, "additem.html", data) } tags, err := repositories.Tags.GetAll() if err != nil { errorHandler(err, tags) return } shopItem, err := rc.NewShopItemFromForm(c) if err != nil { errorHandler(err, tags) return } _, err = repositories.ShopItems.Create(shopItem) if err != nil { errorHandler(err, tags) return } data := CreateSessionData(c, gin.H{ "error": "", "success": fmt.Sprintf("Item '%s' Registered", shopItem.Name), "tags": tags, }) c.HTML(http.StatusOK, "additem.html", data) } func (rc *shopItemController) AddItemsView(c *gin.Context) { data := CreateSessionData(c, gin.H{}) c.HTML(http.StatusOK, "batchupload.html", data) } func (rc *shopItemController) AddItemsHandler(c *gin.Context) { errorHandler := func(err error) { data := CreateSessionData(c, gin.H{ "error": err, }) c.HTML(http.StatusBadRequest, "batchupload.html", data) } form, err := c.MultipartForm() if err != nil { errorHandler(err) return } files := form.File["pdf"] var shopItems []models.ShopItem for _, file := range files { dstPdf := filepath.Join("static/uploads", file.Filename) if err := c.SaveUploadedFile(file, dstPdf); err != nil { errorHandler(err) return } dstImage := dstPdf + ".preview.png" cmd := exec.Command("pdftoppm", "-png", "-singlefile", dstPdf, dstPdf+".preview") _, err := cmd.Output() if err != nil { fmt.Println("Error during pdftoppm: ", err.Error()) } cmd2 := exec.Command("convert", dstImage, "-resize", "35%", dstImage) _, err = cmd2.Output() if err != nil { fmt.Println("Error during resizing preview image: ", err.Error()) } category, err := models.ParseCategory("Zine") if err != nil { errorHandler(err) return } variants := []models.ItemVariant{ { Name: "B/W", Price: 1.0, }, } shopItem := models.ShopItem{ Name: file.Filename, Abstract: file.Filename, Description: file.Filename, Category: category, IsPublic: true, BasePrice: rc.GetBasePrice(variants), Image: dstImage, Pdf: dstPdf, Variants: variants, PrintMode: "CreateBooklet", } _, err = repositories.ShopItems.Create(shopItem) if err != nil { errorHandler(err) return } shopItems = append(shopItems, shopItem) } msg := "The Following items were registered:\n" for _, item := range shopItems { msg += fmt.Sprintf("%s\n", item.Name) } data := CreateSessionData(c, gin.H{ "error": "", "success": msg, }) c.HTML(http.StatusOK, "batchupload.html", data) } func GetCheckedTags(item models.ShopItem) ([]models.CheckedTag, error) { allTags, err := repositories.Tags.GetAll() if err != nil { return nil, err } var tags []models.CheckedTag for _, tag := range allTags { tmpTag := models.CheckedTag{ Tag: tag, Checked: "", } for _, itemTag := range item.Tags { if itemTag.Name == tmpTag.Name { tmpTag.Checked = "checked" break } } tags = append(tags, tmpTag) } return tags, nil } func (rc *shopItemController) getEditTemplatData(shopItem models.ShopItem) (gin.H, error) { tags, err := GetCheckedTags(shopItem) if err != nil { return gin.H{}, err } priceBW := "" priceColored := "" for _, variant := range shopItem.Variants { if variant.Name == "B/W" { priceBW = fmt.Sprintf("%.2f", variant.Price) } if variant.Name == "Colored" { priceColored = fmt.Sprintf("%.2f", variant.Price) } } templateData := gin.H{ "error": "", "success": "", "shopItem": shopItem, "tags": tags, "priceBW": priceBW, "priceColored": priceColored, } id := fmt.Sprintf("%d", shopItem.ID) nextShopItem, err := repositories.ShopItems.GetNextOfId(id) if err == nil { fmt.Println("Setting nextitem") fmt.Println(nextShopItem) templateData["nextShopItem"] = nextShopItem } previousShopItem, err := repositories.ShopItems.GetPreviousOfId(id) if err == nil { templateData["previousShopItem"] = previousShopItem } return templateData, nil } func (rc *shopItemController) EditItemView(c *gin.Context) { id := c.Param("id") shopItem, err := repositories.ShopItems.GetById(id) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) } templateData, err := rc.getEditTemplatData(shopItem) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) } data := CreateSessionData(c, templateData) c.HTML(http.StatusOK, "edititem.html", data) } func (rc *shopItemController) EditItemHandler(c *gin.Context) { shopItem, err := rc.NewShopItemFromForm(c) if err != nil { c.HTML(http.StatusBadRequest, "edititem.html", gin.H{"error": err}) return } newShopItem, err := repositories.ShopItems.GetById(c.Param("id")) if err != nil { c.HTML(http.StatusBadRequest, "edititem.html", gin.H{"error": err}) return } newShopItem.Name = shopItem.Name newShopItem.Abstract = shopItem.Abstract newShopItem.Description = shopItem.Description newShopItem.Category = shopItem.Category newShopItem.Variants = shopItem.Variants newShopItem.BasePrice = shopItem.BasePrice newShopItem.IsPublic = shopItem.IsPublic if len(shopItem.Tags) != 0 { newShopItem.Tags = shopItem.Tags } if shopItem.Image != "static/img/zine.jpg" { newShopItem.Image = shopItem.Image } if shopItem.Pdf != "" { newShopItem.Pdf = shopItem.Pdf } newShopItem.PrintMode = shopItem.PrintMode templateData, err := rc.getEditTemplatData(newShopItem) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) } _, err = repositories.ShopItems.Update(newShopItem) if err != nil { templateData["error"] = err data := CreateSessionData(c, templateData) c.HTML(http.StatusOK, "edititem.html", data) return } templateData["success"] = fmt.Sprintf("Item '%s' Updated", newShopItem.Name) data := CreateSessionData(c, templateData) c.HTML(http.StatusOK, "edititem.html", data) } func (rc *shopItemController) DeleteItemView(c *gin.Context) { shopItem, err := repositories.ShopItems.GetById(c.Param("id")) tags, err := repositories.Tags.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "deleteitem.html", gin.H{"error": err}) } fmt.Println(shopItem) data := CreateSessionData(c, gin.H{ "error": "", "success": "", "shopItem": shopItem, "tags": tags, }) c.HTML(http.StatusOK, "deleteitem.html", data) } func (rc *shopItemController) DeleteItemHandler(c *gin.Context) { err := repositories.ShopItems.DeleteById(c.Param("id")) if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "deleteitem.html", data) } shopItems, _ := repositories.ShopItems.GetAll() fmt.Println(len(shopItems)) data := CreateSessionData(c, gin.H{ "title": "shopItem Page", "shopItems": shopItems, }) fmt.Println(data) c.HTML(http.StatusOK, "index.html", data) } func (rc *shopItemController) TagHandler(ctx *gin.Context) { name := ctx.PostForm("name") color := ctx.PostForm("color") action := ctx.PostForm("action") tag, err := repositories.Tags.GetById(ctx.Param("id")) if err != nil { fmt.Println(err) ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) return } if action == "update" { tag.Name = name tag.Color = color tag, err = repositories.Tags.Update(tag) if err != nil { fmt.Println(err) ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) return } } if action == "delete" { repositories.Tags.DeleteById(ctx.Param("id")) } rc.TagView(ctx) } func (rc *shopItemController) AddTagHandler(c *gin.Context) { tag, err := models.NewTag(c) if err != nil { fmt.Println(err) c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) return } _, err = repositories.Tags.Create(tag) if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "tagview.html", data) return } rc.TagView(c) } func (rc *shopItemController) TagView(c *gin.Context) { tags, err := repositories.Tags.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"data": gin.H{"error": err}}) } data := CreateSessionData(c, gin.H{ "tags": tags, }) if err != nil { c.HTML(http.StatusBadRequest, "tagview.html", data) } c.HTML(http.StatusOK, "tagview.html", data) } func (rc *shopItemController) CreateTag(c *gin.Context) { tag, err := models.NewTagByJson(c) if err != nil { ReplyError(c, err) } _, err = repositories.Tags.Create(tag) if err != nil { ReplyError(c, fmt.Errorf("shopItem creation failed: %s", err)) return } //userID := user.(models.User).ID //rc.DB.Model(&models.shopItem{}).Where("id = ?"), room.ID).Association("Admins").Append(&models.User{ID: userID}) //if result.Error != nil { // ReplyError(c, fmt.Errorf("shopItem creation failed: %s", result.Error)) // return //} ReplyOK(c, fmt.Sprintf("tag '%s' was created", tag.Name)) } func (rc *shopItemController) GetAllTags(c *gin.Context) { tags, err := repositories.Tags.GetAll() if err != nil { ReplyError(c, fmt.Errorf("Could not query Tags")) return } c.JSON(http.StatusOK, tags) } func ReplyError(ctx *gin.Context, err error) { ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } func ReplyOK(ctx *gin.Context, message any) { ctx.JSON(http.StatusOK, message) }