diff --git a/app/config_register.go b/app/config_register.go
index 22d5f8c..01765a3 100644
--- a/app/config_register.go
+++ b/app/config_register.go
@@ -1,19 +1,26 @@
package app
+import "owl-blogs/domain/model"
+
+type AppConfig interface {
+ Form(binSvc model.BinaryStorageInterface) string
+ ParseFormData(data model.HttpFormData, binSvc model.BinaryStorageInterface) (AppConfig, error)
+}
+
type ConfigRegister struct {
- configs map[string]interface{}
+ configs map[string]AppConfig
}
type RegisteredConfig struct {
Name string
- Config interface{}
+ Config AppConfig
}
func NewConfigRegister() *ConfigRegister {
- return &ConfigRegister{configs: map[string]interface{}{}}
+ return &ConfigRegister{configs: map[string]AppConfig{}}
}
-func (r *ConfigRegister) Register(name string, config interface{}) {
+func (r *ConfigRegister) Register(name string, config AppConfig) {
r.configs[name] = config
}
@@ -28,6 +35,6 @@ func (r *ConfigRegister) Configs() []RegisteredConfig {
return configs
}
-func (r *ConfigRegister) GetConfig(name string) interface{} {
+func (r *ConfigRegister) GetConfig(name string) AppConfig {
return r.configs[name]
}
diff --git a/cmd/owl/editor_test.go b/cmd/owl/editor_test.go
index d7e4a56..7d570b6 100644
--- a/cmd/owl/editor_test.go
+++ b/cmd/owl/editor_test.go
@@ -77,9 +77,9 @@ func TestEditorFormPost(t *testing.T) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
- part, _ := writer.CreateFormFile("ImageId", filepath.Base(file.Name()))
+ part, _ := writer.CreateFormFile("image", filepath.Base(file.Name()))
io.Copy(part, file)
- part, _ = writer.CreateFormField("Content")
+ part, _ = writer.CreateFormField("content")
io.WriteString(part, "test content")
writer.Close()
diff --git a/plugings/instagram.go b/plugings/instagram.go
index 2e52dba..c95611f 100644
--- a/plugings/instagram.go
+++ b/plugings/instagram.go
@@ -6,6 +6,7 @@ import (
"owl-blogs/app/repository"
"owl-blogs/domain/model"
entrytypes "owl-blogs/entry_types"
+ "owl-blogs/render"
"github.com/Davincible/goinsta/v3"
)
@@ -20,6 +21,20 @@ type InstagramConfig struct {
Password string `owl:"widget=password"`
}
+// Form implements app.AppConfig.
+func (cfg *InstagramConfig) Form(binSvc model.BinaryStorageInterface) string {
+ f, _ := render.RenderTemplateToString("forms/InstagramConfig", cfg)
+ return f
+}
+
+// ParseFormData implements app.AppConfig.
+func (*InstagramConfig) ParseFormData(data model.HttpFormData, binSvc model.BinaryStorageInterface) (app.AppConfig, error) {
+ return &InstagramConfig{
+ User: data.FormValue("User"),
+ Password: data.FormValue("Password"),
+ }, nil
+}
+
func RegisterInstagram(
configRepo repository.ConfigRepository,
configRegister *app.ConfigRegister,
diff --git a/render/templates/forms/ActivityPubConfig.tmpl b/render/templates/forms/ActivityPubConfig.tmpl
new file mode 100644
index 0000000..596bebc
--- /dev/null
+++ b/render/templates/forms/ActivityPubConfig.tmpl
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/render/templates/forms/InstagramConfig.tmpl b/render/templates/forms/InstagramConfig.tmpl
new file mode 100644
index 0000000..c131350
--- /dev/null
+++ b/render/templates/forms/InstagramConfig.tmpl
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/web/activity_pub_handler.go b/web/activity_pub_handler.go
index d5d5eec..da9ae92 100644
--- a/web/activity_pub_handler.go
+++ b/web/activity_pub_handler.go
@@ -6,6 +6,7 @@ import (
"owl-blogs/app/repository"
"owl-blogs/config"
"owl-blogs/domain/model"
+ "owl-blogs/render"
vocab "github.com/go-ap/activitypub"
@@ -25,6 +26,21 @@ type ActivityPubConfig struct {
PrivateKeyPem string `owl:"inputType=text widget=textarea"`
}
+// Form implements app.AppConfig.
+func (cfg *ActivityPubConfig) Form(binSvc model.BinaryStorageInterface) string {
+ f, _ := render.RenderTemplateToString("forms/ActivityPubConfig", cfg)
+ return f
+}
+
+// ParseFormData implements app.AppConfig.
+func (*ActivityPubConfig) ParseFormData(data model.HttpFormData, binSvc model.BinaryStorageInterface) (app.AppConfig, error) {
+ return &ActivityPubConfig{
+ PreferredUsername: data.FormValue("PreferredUsername"),
+ PublicKeyPem: data.FormValue("PublicKeyPem"),
+ PrivateKeyPem: data.FormValue("PrivateKeyPem"),
+ }, nil
+}
+
type WebfingerResponse struct {
Subject string `json:"subject"`
Links []WebfingerLink `json:"links"`
diff --git a/web/admin_handler.go b/web/admin_handler.go
index 126a750..eb249f5 100644
--- a/web/admin_handler.go
+++ b/web/admin_handler.go
@@ -4,7 +4,6 @@ import (
"owl-blogs/app"
"owl-blogs/app/repository"
"owl-blogs/render"
- "owl-blogs/web/forms"
"sort"
"github.com/gofiber/fiber/v2"
@@ -14,6 +13,7 @@ type adminHandler struct {
configRepo repository.ConfigRepository
configRegister *app.ConfigRegister
typeRegistry *app.EntryTypeRegistry
+ binSvc *app.BinaryService
}
type adminContet struct {
@@ -75,8 +75,7 @@ func (h *adminHandler) HandleConfigGet(c *fiber.Ctx) error {
}
siteConfig := getSiteConfig(h.configRepo)
- form := forms.NewForm(config, nil)
- htmlForm, err := form.HtmlForm()
+ htmlForm := config.Form(h.binSvc)
if err != nil {
return err
}
@@ -93,9 +92,7 @@ func (h *adminHandler) HandleConfigPost(c *fiber.Ctx) error {
return c.SendStatus(404)
}
- form := forms.NewForm(config, nil)
-
- newConfig, err := form.Parse(c)
+ newConfig, err := config.ParseFormData(c, h.binSvc)
if err != nil {
return err
}
diff --git a/web/forms/form.go b/web/forms/form.go
deleted file mode 100644
index 2d2f5f3..0000000
--- a/web/forms/form.go
+++ /dev/null
@@ -1,185 +0,0 @@
-package forms
-
-import (
- "fmt"
- "owl-blogs/domain/model"
- "reflect"
- "strings"
-)
-
-type Form[T interface{}] struct {
- data T
- binSvc model.BinaryStorageInterface
-}
-
-type FormFieldParams struct {
- InputType string
- Widget string
-}
-
-type FormField struct {
- Name string
- Value reflect.Value
- Params FormFieldParams
-}
-
-func NewForm[T interface{}](data T, binaryService model.BinaryStorageInterface) *Form[T] {
- return &Form[T]{
- data: data,
- binSvc: binaryService,
- }
-}
-
-func (s *FormFieldParams) ApplyTag(tagKey string, tagValue string) error {
- switch tagKey {
- case "inputType":
- s.InputType = tagValue
- case "widget":
- s.Widget = tagValue
- default:
- return fmt.Errorf("unknown tag key: %v", tagKey)
- }
- return nil
-}
-
-func (s *FormField) ToWidget() Widget {
- switch s.Params.Widget {
- case "textarea":
- return &TextareaWidget{*s}
- case "textlist":
- return &TextListWidget{*s}
- case "password":
- return &PasswordWidget{*s}
- case "text":
- return &TextWidget{*s}
- default:
- return &OmitWidget{*s}
- }
-}
-
-func (s *FormField) Html() string {
- html := ""
- html += fmt.Sprintf("\n", s.Name, s.Name)
- if s.Params.InputType == "file" {
- html += fmt.Sprintf("\n", s.Params.InputType, s.Name, s.Name, s.Value)
- } else {
- html += s.ToWidget().Html()
- html += "\n"
- }
- return html
-}
-
-func FieldToFormField(field reflect.StructField, value reflect.Value) (FormField, error) {
- formField := FormField{
- Name: field.Name,
- Value: value,
- Params: FormFieldParams{},
- }
- tag := field.Tag.Get("owl")
- for _, param := range strings.Split(tag, " ") {
- parts := strings.Split(param, "=")
- if len(parts) != 2 {
- continue
- }
- err := formField.Params.ApplyTag(parts[0], parts[1])
- if err != nil {
- return FormField{}, err
- }
- }
- return formField, nil
-}
-
-func StructToFormFields(data interface{}) ([]FormField, error) {
- dataValue := reflect.Indirect(reflect.ValueOf(data))
- dataType := reflect.TypeOf(data).Elem()
- numFields := dataType.NumField()
- fields := []FormField{}
- for i := 0; i < numFields; i++ {
- field, err := FieldToFormField(
- dataType.Field(i),
- dataValue.FieldByIndex([]int{i}),
- )
- if err != nil {
- return nil, err
- }
- fields = append(fields, field)
- }
- return fields, nil
-}
-
-func (s *Form[T]) HtmlForm() (string, error) {
- fields, err := StructToFormFields(s.data)
- if err != nil {
- return "", err
- }
-
- html := ""
- for _, field := range fields {
- html += field.Html()
- }
-
- return html, nil
-}
-
-func (s *Form[T]) Parse(ctx model.HttpFormData) (T, error) {
- var empty T
-
- if ctx == nil {
- return empty, fmt.Errorf("nil context")
- }
- dataVal := reflect.ValueOf(s.data)
- if dataVal.Kind() != reflect.Ptr {
- return empty, fmt.Errorf("meta data is not a pointer")
- }
- fields, err := StructToFormFields(s.data)
- if err != nil {
- return empty, err
- }
- for _, field := range fields {
- fieldName := field.Name
-
- if field.Params.InputType == "file" {
- file, err := ctx.FormFile(fieldName)
- if err != nil {
- // If field already has a value, we can ignore the error
- if field.Value != reflect.Zero(field.Value.Type()) {
- metaField := dataVal.Elem().FieldByName(fieldName)
- if metaField.IsValid() {
- metaField.SetString(field.Value.String())
- }
- continue
- }
- return empty, err
- }
- fileData, err := file.Open()
- if err != nil {
- return empty, err
- }
- defer fileData.Close()
- fileBytes := make([]byte, file.Size)
- _, err = fileData.Read(fileBytes)
- if err != nil {
- return empty, err
- }
-
- binaryFile, err := s.binSvc.Create(file.Filename, fileBytes)
- if err != nil {
- return empty, err
- }
-
- metaField := dataVal.Elem().FieldByName(fieldName)
- if metaField.IsValid() {
- metaField.SetString(binaryFile.Id)
- }
- } else {
- formValue := ctx.FormValue(fieldName)
- metaField := dataVal.Elem().FieldByName(fieldName)
- if metaField.IsValid() {
- field.ToWidget().ParseValue(formValue, metaField)
- }
- }
-
- }
-
- return s.data, nil
-}
diff --git a/web/forms/form_test.go b/web/forms/form_test.go
deleted file mode 100644
index eb011ce..0000000
--- a/web/forms/form_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package forms_test
-
-import (
- "bytes"
- "io"
- "mime/multipart"
- "os"
- "owl-blogs/app"
- "owl-blogs/infra"
- "owl-blogs/test"
- "owl-blogs/web/forms"
- "path"
- "path/filepath"
- "reflect"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-type MockData struct {
- Image string `owl:"inputType=file"`
- Content string `owl:"inputType=text"`
-}
-
-type MockFormData struct {
- fileHeader *multipart.FileHeader
-}
-
-func NewMockFormData() *MockFormData {
- fileDir, _ := os.Getwd()
- fileName := "../../test/fixtures/test.png"
- filePath := path.Join(fileDir, fileName)
-
- file, err := os.Open(filePath)
- if err != nil {
- panic(err)
- }
- defer file.Close()
-
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
- part, err := writer.CreateFormFile("ImagePath", filepath.Base(file.Name()))
- if err != nil {
- panic(err)
- }
- io.Copy(part, file)
- writer.Close()
-
- multipartForm := multipart.NewReader(body, writer.Boundary())
- formData, err := multipartForm.ReadForm(0)
- if err != nil {
- panic(err)
- }
- fileHeader := formData.File["ImagePath"][0]
-
- return &MockFormData{fileHeader: fileHeader}
-}
-
-func (f *MockFormData) FormFile(key string) (*multipart.FileHeader, error) {
- return f.fileHeader, nil
-}
-
-func (f *MockFormData) FormValue(key string, defaultValue ...string) string {
- return key
-}
-
-func TestFieldToFormField(t *testing.T) {
- field := reflect.TypeOf(&MockData{}).Elem().Field(0)
- formField, err := forms.FieldToFormField(field, reflect.Value{})
- require.NoError(t, err)
- require.Equal(t, "Image", formField.Name)
- require.Equal(t, "file", formField.Params.InputType)
-}
-
-func TestStructToFields(t *testing.T) {
- fields, err := forms.StructToFormFields(&MockData{})
- require.NoError(t, err)
- require.Len(t, fields, 2)
- require.Equal(t, "Image", fields[0].Name)
- require.Equal(t, "file", fields[0].Params.InputType)
- require.Equal(t, "Content", fields[1].Name)
- require.Equal(t, "text", fields[1].Params.InputType)
-}
-
-func TestForm_HtmlForm(t *testing.T) {
- form := forms.NewForm(&MockData{}, nil)
- html, err := form.HtmlForm()
- require.NoError(t, err)
- require.Contains(t, html, "\n", s.Name, s.Value.String())
- return html
-}
-
-func (s *TextWidget) ParseValue(value string, output reflect.Value) error {
- output.SetString(value)
- return nil
-}
-
-type OmitWidget struct {
- FormField
-}
-
-func (s *OmitWidget) Html() string {
- html := ""
- return html
-}
-
-func (s *OmitWidget) ParseValue(value string, output reflect.Value) error {
- return nil
-}
-
-type PasswordWidget struct {
- FormField
-}
-
-func (s *PasswordWidget) Html() string {
- html := ""
- html += fmt.Sprintf("\n", s.Name, s.Value.String())
- return html
-}
-
-func (s *PasswordWidget) ParseValue(value string, output reflect.Value) error {
- output.SetString(value)
- return nil
-}
-
-type TextareaWidget struct {
- FormField
-}
-
-func (s *TextareaWidget) Html() string {
- html := ""
- html += fmt.Sprintf("\n", s.Name, s.Value.String())
- return html
-}
-
-func (s *TextareaWidget) ParseValue(value string, output reflect.Value) error {
- output.SetString(value)
- return nil
-}
-
-type TextListWidget struct {
- FormField
-}
-
-func (s *TextListWidget) Html() string {
- valueList := s.Value.Interface().([]string)
- value := strings.Join(valueList, "\n")
-
- html := ""
- html += fmt.Sprintf("\n", s.Name, value)
- return html
-}
-
-func (s *TextListWidget) ParseValue(value string, output reflect.Value) error {
- list := strings.Split(value, "\n")
- // trim entries
- for i, item := range list {
- list[i] = strings.TrimSpace(item)
- }
- // remove empty entries
- for i := len(list) - 1; i >= 0; i-- {
- if list[i] == "" {
- list = append(list[:i], list[i+1:]...)
- }
- }
-
- output.Set(reflect.ValueOf(list))
- return nil
-}