Files
zineshop/controllers/shopItemController.go
kalipso 9e638dcfc2
All checks were successful
Go / build (push) Successful in 12m19s
add tested/not tested indicator on print view
2025-07-02 00:10:37 +02:00

629 lines
14 KiB
Go

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)
EditItemView(*gin.Context)
EditItemHandler(*gin.Context)
DeleteItemView(*gin.Context)
DeleteItemHandler(*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,
WasPrinted: false,
}
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",
WasPrinted: false,
}
_, 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
newShopItem.WasPrinted = false
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 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)
}