BrainMinder/cmd/web/items_handlers.go

985 lines
30 KiB
Go

package main
import (
"bytes"
"fmt"
"net/http"
"strconv"
"strings"
"brainminder.speedtech.it/internal/request"
"brainminder.speedtech.it/internal/response"
"brainminder.speedtech.it/internal/validator"
"brainminder.speedtech.it/models"
"github.com/alexedwards/flow"
)
type itemForm struct {
Id int64 `form:"Id"`
Type_id int64 `form:"Type_id"`
Title string `form:"Title"`
Summary string `form:"Summary"`
Description string `form:"Description"`
Notebooks []string `form:"Notebooks"`
Tags string `form:"Tags"`
On_dashboard int `form:"On_dashboard"`
Categories []string `form:"Categories"`
Type_icon string
Type_title string
Type_show_summary int
Type_show_description int
FieldsSection map[string][]models.Field
FieldsValues map[int64]map[int]string
Relations []models.ItemRelation
Validator validator.Validator `form:"-"`
}
type itemShareForm struct {
Id int64 `form:"Id"`
Item_id int64 `form:"Item_id"`
Token string `form:"Token"`
Start_datetime string `form:"start_datetime"`
End_datetime string `form:"end_datetime"`
Password string `form:"password"`
Permission_read int `form:"read"`
Permission_edit int `form:"edit"`
Validator validator.Validator `form:"-"`
}
func (form *itemForm) Validate(w http.ResponseWriter, r *http.Request, app *application, data map[string]any) bool {
var fullBuf = new(bytes.Buffer)
form.Validator.CheckField(form.Title != "", "Title", "Title is required")
//form.Validator.CheckField(form.Description != "", "Description", "Description is required")
if form.Validator.HasErrors() {
w.Header().Add("HX-Retarget", "#message")
data["messageType"] = "failure"
data["messageContent"] = "Impossible to save the item"
data["messageFieldErrors"] = form.Validator.FieldErrors
err := response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusUnprocessableEntity)
return false
}
return true
}
func (form *itemShareForm) Validate(w http.ResponseWriter, r *http.Request, app *application, data map[string]any) bool {
var fullBuf = new(bytes.Buffer)
//form.Validator.CheckField(form.Title != "", "Title", "Title is required")
//form.Validator.CheckField(form.Description != "", "Description", "Description is required")
if form.Validator.HasErrors() {
w.Header().Add("HX-Retarget", "#message")
data["messageType"] = "failure"
data["messageContent"] = "Impossible to save the item"
data["messageFieldErrors"] = form.Validator.FieldErrors
err := response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusUnprocessableEntity)
return false
}
return true
}
func (app *application) itemsRelationAdd(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
itemModel := models.NewItemModel(app.db)
categoryModel := &models.CategoryModel{DB: app.db}
var fullBuf = new(bytes.Buffer)
related_item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "related_item_id"), 10, 64)
relatedItem, _, _ := itemModel.One(related_item_id)
data["relatedItem"] = relatedItem
data["categoriesMap"] = categoryModel.AllAsMap()
err := response.HXFragment(fullBuf, []string{"items/add_relation.tmpl"}, "item:add_relation", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusOK)
}
func (app *application) itemsSearch(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
itemModel := models.NewItemModel(app.db)
var fullBuf = new(bytes.Buffer)
switch r.Method {
case http.MethodPost:
categoryModel := &models.CategoryModel{DB: app.db}
data["categoriesMap"] = categoryModel.AllAsMap()
err := r.ParseForm()
if err != nil {
app.serverError(w, r, err)
return
}
criteria := make(map[string]any)
criteria["notebook_id"] = app.getCurrentNotebok_id(r)
rows, _, _ := itemModel.Search(r.PostForm.Get("SearchText"), criteria)
data["items"] = &rows
err = response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
if err != nil {
app.serverError(w, r, err)
}
if r.Header.Get("HX-Target") == "page-content" {
err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
}
fullBuf.WriteTo(w)
}
w.WriteHeader(http.StatusOK)
}
func (app *application) itemsSearchForRelations(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
itemModel := models.NewItemModel(app.db)
var fullBuf = new(bytes.Buffer)
categoryModel := &models.CategoryModel{DB: app.db}
data["categoriesMap"] = categoryModel.AllAsMap()
data["item_id"], _ = strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
err := r.ParseForm()
if err != nil {
app.serverError(w, r, err)
return
}
criteria := make(map[string]any)
criteria["notebook_id"] = app.getCurrentNotebok_id(r)
rows, _, _ := itemModel.Search(r.Form.Get("SearchText"), criteria)
data["items"] = &rows
err = response.HXFragment(fullBuf, []string{"items/list_for_relations.tmpl"}, "items:list-for-relations", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusOK)
}
func (app *application) items(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
itemModel := models.NewItemModel(app.db)
var fullBuf = new(bytes.Buffer)
categoryModel := &models.CategoryModel{DB: app.db}
data["categoriesMap"] = categoryModel.AllAsMap()
params := r.URL.Query()
offset_str := r.URL.Query().Get("offset")
if len(offset_str) == 0 {
offset_str = "0"
}
offset, _ := strconv.ParseInt(offset_str, 10, 64)
data["offset"] = offset
switch r.Method {
case http.MethodGet:
criteria := make(map[string]any)
criteriaName := "itemsSearch"
data["criteriaName"] = criteriaName
criteriaParam := r.URL.Query()["criteriaName"]
if len(criteriaParam) > 0 {
if criteriaParam[0] == criteriaName {
criteria_values := app.getSessionValue(w, r, criteriaName)
if criteria_values != nil {
criteria = criteria_values.(map[string]any)
}
}
} else {
app.removeSessionValue(w, r, criteriaName)
}
criteria["notebook_id"] = app.getCurrentNotebok_id(r)
rows, _, _ := itemModel.Find(criteria, offset)
data["items"] = &rows
if r.Header.Get("HX-Request") == "true" {
out := params.Get("out")
if out == "rows" {
err := response.HXFragment(fullBuf, []string{"items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list_rows", data)
if err != nil {
app.serverError(w, r, err)
}
} else {
err := response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
}
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/index_all.tmpl", "items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl", "items/all_list_title.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
case http.MethodPost:
err := r.ParseForm()
if err != nil {
app.serverError(w, r, err)
return
}
category_id, _ := strconv.ParseInt(r.PostForm.Get("category_id"), 10, 64)
criteria := map[string]any{
"Title": r.PostForm.Get("Title"),
"Tags": r.PostForm.Get("Tags"),
"category_id": category_id,
}
criteriaName := "itemsSearch"
data["criteriaName"] = criteriaName
app.saveSessionValue(w, r, criteriaName, criteria)
rows, _, _ := itemModel.Find(criteria, offset)
data["items"] = &rows
err = response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
if err != nil {
app.serverError(w, r, err)
}
if r.Header.Get("HX-Target") == "page-content" {
err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
}
fullBuf.WriteTo(w)
}
w.WriteHeader(http.StatusOK)
}
func (app *application) itemsType(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
type_id, _ := strconv.ParseInt(flow.Param(r.Context(), "type_id"), 10, 64)
itemModel := models.NewItemModel(app.db)
typeModel := &models.TypeModel{DB: app.db}
aType, _, _ := typeModel.One(type_id)
fieldModel := &models.FieldModel{DB: app.db}
fields, _, _ := fieldModel.ByTypeOnList(type_id)
data["type"] = &aType
data["Fields"] = fields
categoryModel := &models.CategoryModel{DB: app.db}
data["categoriesMap"] = categoryModel.AllAsMap()
params := r.URL.Query()
offset_str := params.Get("offset")
if len(offset_str) == 0 {
offset_str = "0"
}
offset, _ := strconv.ParseInt(offset_str, 10, 64)
data["offset"] = offset
var fullBuf = new(bytes.Buffer)
switch r.Method {
case http.MethodGet:
criteria := make(map[string]any)
criteriaName := fmt.Sprintf("itemsTypeSearch_%d", type_id)
data["criteriaName"] = criteriaName
criteriaParam := r.URL.Query()["criteriaName"]
if len(criteriaParam) > 0 {
if criteriaParam[0] == criteriaName {
criteria_values := app.getSessionValue(w, r, criteriaName)
if criteria_values != nil {
criteria = criteria_values.(map[string]any)
}
}
} else {
app.removeSessionValue(w, r, criteriaName)
}
criteria["type_id"] = type_id
criteria["notebook_id"] = app.getCurrentNotebok_id(r)
rows, _, _ := itemModel.Find(criteria, offset)
for i, row := range rows {
rows[i].FieldsValuesMap = fieldModel.GetFieldsValuesAsMap(row.Id)
}
data["items"] = rows
if r.Header.Get("HX-Request") == "true" {
out := params.Get("out")
if out == "rows" {
err := response.HXFragment(fullBuf, []string{"items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list_rows", data)
if err != nil {
app.serverError(w, r, err)
}
} else {
err := response.HXFragment(fullBuf, []string{"items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteString("<div hx-swap-oob=\"true\" id=\"page-title\">")
err = response.HXFragmentOOB(fullBuf, []string{"items/list_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
}
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/index.tmpl", "items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl", "items/list_title.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
case http.MethodPost:
err := r.ParseForm()
if err != nil {
app.serverError(w, r, err)
return
}
category_id, _ := strconv.ParseInt(r.PostForm.Get("category_id"), 10, 64)
criteria := map[string]any{
"Title": r.PostForm.Get("Title"),
"Tags": r.PostForm.Get("Tags"),
"category_id": category_id,
"type_id": type_id,
}
criteriaName := fmt.Sprintf("itemsTypeSearch_%d", type_id)
data["criteriaName"] = criteriaName
app.saveSessionValue(w, r, criteriaName, criteria)
rows, _, _ := itemModel.Find(criteria, offset)
for i, row := range rows {
rows[i].FieldsValuesMap = fieldModel.GetFieldsValuesAsMap(row.Id)
}
data["items"] = &rows
err = response.HXFragment(fullBuf, []string{"items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
}
w.WriteHeader(http.StatusOK)
}
func (app *application) itemCreate(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
fieldModel := &models.FieldModel{DB: app.db}
categoryModel := &models.CategoryModel{DB: app.db}
typeModel := &models.TypeModel{DB: app.db}
type_id, _ := strconv.ParseInt(flow.Param(r.Context(), "type_id"), 10, 64)
aType, _, _ := typeModel.One(type_id)
data := app.newTemplateData(r)
data["type"] = aType
data["formAction"] = fmt.Sprint("/item/create/", type_id)
data["formTarget"] = "#page-content"
data["categories"] = categoryModel.AllAsOptions()
data["categoriesMap"] = categoryModel.AllAsMap()
criteria := map[string]any{
"notebook_id": app.getCurrentNotebok_id(r),
}
data["types"] = typeModel.FindAsOptions(criteria)
notebookModel := &models.NotebookModel{DB: app.db}
data["notebooks"] = notebookModel.AllAsOptions(false)
var fullBuf = new(bytes.Buffer)
var categories []string
fieldsSection, _, _ := fieldModel.ByTypeSection(int64(type_id))
fieldsValues := make(map[int64]map[int]string)
switch r.Method {
case http.MethodGet:
notebooks := []string{strconv.FormatInt(app.getCurrentNotebok_id(r), 10)}
data["item"] = itemForm{
Title: "",
Type_id: type_id,
Type_show_summary: aType.Show_summary,
Type_show_description: aType.Show_description,
Summary: "",
Description: "",
Tags: "",
FieldsSection: fieldsSection,
FieldsValues: fieldsValues,
Categories: categories,
Notebooks: notebooks,
Relations: nil,
}
if r.Header.Get("HX-Request") == "true" {
err := response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/create_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description"}`)
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/create_title.tmpl", "items/relations.tmpl", "items/form.tmpl", "items/fields.tmpl", "items/form_jscode.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
case http.MethodPost:
var itemFromForm itemForm
var item_id int64
err := request.DecodePostForm(r, &itemFromForm)
if err != nil {
app.serverError(w, r, err)
}
if !itemFromForm.Validate(w, r, app, data) {
return
}
item := &models.Item{
Type_id: itemFromForm.Type_id,
Title: itemFromForm.Title,
Summary: itemFromForm.Summary,
Description: itemFromForm.Description,
Tags: itemFromForm.Tags,
On_dashboard: itemFromForm.On_dashboard,
}
notebooks_str := strings.Join(itemFromForm.Notebooks, "|")
if len(notebooks_str) > 0 {
notebooks_str = "|" + notebooks_str + "|"
}
item.Notebooks = notebooks_str
categories_str := strings.Join(itemFromForm.Categories, "|")
if len(categories_str) > 0 {
categories_str = "|" + categories_str + "|"
}
item.Categories = categories_str
item_id, err = itemModel.Create(item)
item.Id = item_id
if err != nil {
app.badRequest(w, err)
return
}
fieldsValues := make(map[int64]map[int]string)
for name, values := range r.PostForm {
id_s, found := strings.CutPrefix(name, "FieldsValues-")
if found {
parts := strings.Split(id_s, "-")
type_field_id, _ := strconv.ParseInt(parts[0], 10, 64)
counter, _ := strconv.Atoi(parts[1])
_, found_key := fieldsValues[type_field_id]
if !found_key {
fieldsValues[type_field_id] = make(map[int]string)
}
fieldsValues[type_field_id][counter] = values[0]
}
related_id_s, found := strings.CutPrefix(name, "ItemRelation-New-")
if found && len(values) > 0 {
related_item_id, _ := strconv.ParseInt(related_id_s, 10, 64)
itemModel.AddRelation(item.Id, related_item_id, values[0])
}
}
for type_field_id, values := range fieldsValues {
fieldModel.SaveValues(item.Id, type_field_id, values)
}
item.Type_title = aType.Title
fields, _, _ := fieldModel.ByType(int64(type_id))
itemModel.SaveKeywords(item, &fields, fieldsValues)
data["formAction"] = fmt.Sprint("/item/update/", item_id)
data["formTarget"] = "#message"
notebooks := strings.Split(strings.Trim(item.Notebooks, "|"), "|")
categories := strings.Split(strings.Trim(item.Categories, "|"), "|")
relations, _, _ := itemModel.GetRelations(item.Id)
data["item"] = itemForm{
Id: item_id,
Type_id: type_id,
Type_show_summary: aType.Show_summary,
Type_show_description: aType.Show_description,
Title: item.Title,
Summary: item.Summary,
Description: item.Description,
Tags: item.Tags,
Type_icon: item.Type_icon,
Type_title: item.Type_title,
FieldsSection: fieldsSection,
FieldsValues: fieldsValues,
Notebooks: notebooks,
Categories: categories,
Relations: relations,
}
err = response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
dataMessage := make(map[string]string)
dataMessage["messageType"] = "success"
dataMessage["messageContent"] = "Item created successfully"
err = response.HXFragmentOOB(fullBuf, []string{"partials/message.tmpl"}, "message", dataMessage, "message")
if err != nil {
app.serverError(w, r, err)
}
w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description"}`)
w.Header().Add("HX-Replace-Url", fmt.Sprint("/item/update/", item_id))
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusUnprocessableEntity)
}
}
func (app *application) itemRead(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
itemModel := models.NewItemModel(app.db)
categoryModel := &models.CategoryModel{DB: app.db}
fieldModel := &models.FieldModel{DB: app.db}
relations, _, _ := itemModel.GetRelations(item_id)
item, _, _ := itemModel.One(item_id)
data["item"] = &item
data["relations"] = relations
data["categoriesMap"] = categoryModel.AllAsMap()
data["fields"] = fieldModel.GetFieldsValues(item_id)
if r.Header.Get("HX-Request") == "true" {
var fullBuf = new(bytes.Buffer)
err := response.HXFragment(fullBuf, []string{"items/read.tmpl", "items/relations_view.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/read_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/read_title.tmpl", "items/relations_view.tmpl", "items/read.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
w.WriteHeader(http.StatusOK)
}
func (app *application) itemView(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r)
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
itemModel := models.NewItemModel(app.db)
categoryModel := &models.CategoryModel{DB: app.db}
fieldModel := &models.FieldModel{DB: app.db}
item, _, _ := itemModel.One(item_id)
relations, _, _ := itemModel.GetRelations(item_id)
data["item"] = &item
data["relations"] = relations
data["categoriesMap"] = categoryModel.AllAsMap()
data["fields"] = fieldModel.GetFieldsValues(item_id)
if r.Header.Get("HX-Request") == "true" {
var fullBuf = new(bytes.Buffer)
err := response.HXFragment(fullBuf, []string{"items/view.tmpl", "items/relations_view.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/read_title.tmpl", "items/relations_view.tmpl", "items/read.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
w.WriteHeader(http.StatusOK)
}
func (app *application) itemUpdate(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
fieldModel := &models.FieldModel{DB: app.db}
item_id_str := flow.Param(r.Context(), "item_id")
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
item, _, _ := itemModel.One(item_id)
fieldsSection, _, _ := fieldModel.ByTypeSection(item.Type_id)
fieldsValues := fieldModel.GetFieldsValuesAsMap(item.Id)
data := app.newTemplateData(r)
data["formAction"] = "/item/update/" + item_id_str
data["formTarget"] = "#message"
notebookModel := &models.NotebookModel{DB: app.db}
data["notebooks"] = notebookModel.AllAsOptions(false)
categoryModel := &models.CategoryModel{DB: app.db}
data["categoriesMap"] = categoryModel.AllAsMap()
var fullBuf = new(bytes.Buffer)
switch r.Method {
case http.MethodGet:
notebooks := strings.Split(strings.Trim(item.Notebooks, "|"), "|")
categories := strings.Split(strings.Trim(item.Categories, "|"), "|")
relations, _, _ := itemModel.GetRelations(item.Id)
data["item"] = itemForm{
Id: item.Id,
Type_id: item.Type_id,
Type_show_summary: item.Type_show_summary,
Type_show_description: item.Type_show_description,
Title: item.Title,
Summary: item.Summary,
Description: item.Description,
Tags: item.Tags,
On_dashboard: item.On_dashboard,
Type_icon: item.Type_icon,
Type_title: item.Type_title,
FieldsSection: fieldsSection,
FieldsValues: fieldsValues,
Categories: categories,
Notebooks: notebooks,
Relations: relations,
}
data["categories"] = categoryModel.AllAsOptions()
typeModel := &models.TypeModel{DB: app.db}
criteria := map[string]any{
"notebook_id": app.getCurrentNotebok_id(r),
}
data["types"] = typeModel.FindAsOptions(criteria)
if r.Header.Get("HX-Request") == "true" {
err := response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description"}`)
fullBuf.WriteTo(w)
} else {
err := response.Page(w, http.StatusOK, data, []string{"items/update_title.tmpl", "items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl", "items/form_jscode.tmpl"})
if err != nil {
app.serverError(w, r, err)
}
}
case http.MethodPost:
var itemFromForm itemForm
err := request.DecodePostForm(r, &itemFromForm)
if err != nil {
app.serverError(w, r, err)
}
if !itemFromForm.Validate(w, r, app, data) {
return
}
item.Title = itemFromForm.Title
item.Type_id = itemFromForm.Type_id
item.Summary = itemFromForm.Summary
item.Description = itemFromForm.Description
item.Tags = itemFromForm.Tags
item.On_dashboard = itemFromForm.On_dashboard
notebooks_str := strings.Join(itemFromForm.Notebooks, "|")
if len(notebooks_str) > 0 {
notebooks_str = "|" + notebooks_str + "|"
}
item.Notebooks = notebooks_str
categories_str := strings.Join(itemFromForm.Categories, "|")
if len(categories_str) > 0 {
categories_str = "|" + categories_str + "|"
}
item.Categories = categories_str
err = itemModel.Update(item)
if err != nil {
app.badRequest(w, err)
return
}
fieldsValues := make(map[int64]map[int]string)
for name, values := range r.PostForm {
id_s, found := strings.CutPrefix(name, "FieldsValues-")
if found {
parts := strings.Split(id_s, "-")
type_field_id, _ := strconv.ParseInt(parts[0], 10, 64)
counter, _ := strconv.Atoi(parts[1])
valuesRemove, isMapContainsKey := r.PostForm["FieldsValuesToRemove-"+id_s]
if !isMapContainsKey || valuesRemove[0] == "0" {
_, found_key := fieldsValues[type_field_id]
if !found_key {
fieldsValues[type_field_id] = make(map[int]string)
}
fieldsValues[type_field_id][counter] = values[0]
}
}
related_id_s, found := strings.CutPrefix(name, "ItemRelation-New-")
if found && len(values) > 0 {
related_item_id, _ := strconv.ParseInt(related_id_s, 10, 64)
itemModel.AddRelation(item.Id, related_item_id, values[0])
}
id_s, found = strings.CutPrefix(name, "ItemRelation-")
if found && len(values) > 0 {
ids := strings.Split(id_s, "-")
item_id, _ := strconv.ParseInt(ids[0], 10, 64)
related_item_id, _ := strconv.ParseInt(ids[1], 10, 64)
valuesRemove, isMapContainsKey := r.PostForm["ItemRelation-ToRemove-"+id_s]
if isMapContainsKey && valuesRemove[0] == "1" {
itemModel.DeleteRelation(item_id, related_item_id)
} else {
itemModel.UpdateRelation(item_id, related_item_id, values[0])
}
}
}
for type_field_id, values := range fieldsValues {
fieldModel.SaveValues(item.Id, type_field_id, values)
}
fields, _, _ := fieldModel.ByType(item.Type_id)
itemModel.SaveKeywords(item, &fields, fieldsValues)
relations, _, _ := itemModel.GetRelations(item.Id)
data["item"] = itemForm{
Title: item.Title,
Type_icon: item.Type_icon,
Type_title: item.Type_title,
Relations: relations,
}
data["FieldsSection"] = fieldsSection
data["FieldsValues"] = fieldsValues
dataMessage := make(map[string]string)
dataMessage["messageType"] = "success"
dataMessage["messageContent"] = "Item saved successfully"
err = response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", dataMessage)
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
if err != nil {
app.serverError(w, r, err)
}
err = response.HXFragmentOOB(fullBuf, []string{"items/relations.tmpl"}, "item:relations", data, "relations")
if err != nil {
app.serverError(w, r, err)
}
data["uisection"] = "general"
err = response.HXFragmentOOB(fullBuf, []string{"items/fields.tmpl"}, "items:fields", data, "fields-general")
if err != nil {
app.serverError(w, r, err)
}
_, found := fieldsSection["fields"]
if found {
data["uisection"] = "fields"
err = response.HXFragmentOOB(fullBuf, []string{"items/fields.tmpl"}, "items:fields", data, "fields-fields")
if err != nil {
app.serverError(w, r, err)
}
}
fullBuf.WriteTo(w)
w.WriteHeader(http.StatusUnprocessableEntity)
}
}
func (app *application) itemDelete(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
item_id_str := flow.Param(r.Context(), "item_id")
item_id, _ := strconv.Atoi(item_id_str)
_, err := itemModel.Delete(item_id)
if err != nil {
app.serverError(w, r, err)
}
}
func (app *application) itemShare(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
item, _, _ := itemModel.One(item_id)
data := app.newTemplateData(r)
data["item"] = item
data["baseUrl"] = app.config.baseURL
switch r.Method {
case http.MethodGet:
var fullBuf = new(bytes.Buffer)
data["shareToken"] = app.generateSecureToken(18)
err := response.HXFragment(fullBuf, []string{"items/share.tmpl"}, "page:content", data)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
case http.MethodPost:
var itemShareFromForm itemShareForm
err := request.DecodePostForm(r, &itemShareFromForm)
if err != nil {
app.serverError(w, r, err)
}
if !itemShareFromForm.Validate(w, r, app, data) {
return
}
itemShare := &models.ItemShare{
Item_id: item_id,
Token: itemShareFromForm.Token,
}
itemshareModel := &models.ItemShareModel{DB: app.db}
_, err = itemshareModel.Create(itemShare)
if err != nil {
app.badRequest(w, err)
return
}
}
}
func (app *application) itemAddToDashboard(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
err := itemModel.AddToDashboard(item_id)
if err != nil {
app.serverError(w, r, err)
}
var fullBuf = new(bytes.Buffer)
err = response.HXFragment(fullBuf, []string{"items/remove_from_dashboard.tmpl"}, "item:remove_from_dashboard", item_id)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
}
func (app *application) itemRemoveFromDashboard(w http.ResponseWriter, r *http.Request) {
itemModel := models.NewItemModel(app.db)
item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
err := itemModel.RemoveFromDashboard(item_id)
if err != nil {
app.serverError(w, r, err)
}
if r.Method == http.MethodPost {
var fullBuf = new(bytes.Buffer)
err := response.HXFragment(fullBuf, []string{"items/add_to_dashboard.tmpl"}, "item:add_to_dashboard", item_id)
if err != nil {
app.serverError(w, r, err)
}
fullBuf.WriteTo(w)
}
}