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("
") 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) } }