diff --git a/database/user.go b/database/user.go
index aa8af93..421bf90 100644
--- a/database/user.go
+++ b/database/user.go
@@ -641,6 +641,7 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
return user, nil
}
+// internalGetProp is a helper used by the property functions.
func internalGetProp(uid int32, ndx int32) (*UserProperties, error) {
var err error = nil
key := fmt.Sprintf("%d:%d", uid, ndx)
diff --git a/ui/images.go b/ui/images.go
index d27117b..8c8e34b 100644
--- a/ui/images.go
+++ b/ui/images.go
@@ -13,7 +13,7 @@ package ui
import (
"bytes"
"embed"
- "fmt"
+ "errors"
"image"
"image/gif"
"image/jpeg"
@@ -32,6 +32,15 @@ import (
//go:embed static_images/*
var static_images embed.FS
+// Constants for default photo sizes.
+const (
+ UserPhotoWidth = 100
+ UserPhotoHeight = 100
+ UserPhotoMaxBytes = 2097152 // 2 Mb
+ CommunityLogoWidth = 110
+ CommunityLogoHeight = 60
+)
+
/* mimeTypeFromFilenane returns the MIME type of a file, given its filename.
* Parameters:
* filaname - The name of the file to be tested.
@@ -76,11 +85,26 @@ func AmServeImage(ctxt AmContext) (string, any, error) {
}
}
ctxt.SetRC(http.StatusNotFound)
- // TODO: improve this error reporting
- return "string", fmt.Sprintf("File not found: %s", ctxt.URLPath()), err
+ return ErrorPage(ctxt, err)
}
-func AmProcessUploadedImage(fileheader *multipart.FileHeader, width, height int) ([]byte, string, error) {
+/* AmProcessUploadedImage takes an image and resizes it to a specified size, returning its data.
+ * Parameters:
+ * fileheader - The multipart file header from the uploaded file.
+ * width - New image width in pizels.
+ * height - New image height in pixels.
+ * maxbytes - The maximum size of the user photo.
+ * Returns:
+ * Image data as a byte array.
+ * The MIME type of the image data.
+ * Standard Go error status.
+ */
+func AmProcessUploadedImage(fileheader *multipart.FileHeader, width, height, maxbytes int) ([]byte, string, error) {
+ // test size
+ if fileheader.Size > int64(maxbytes) {
+ return nil, "", errors.New("file is too large; please try again")
+ }
+
// open the file
file, err := fileheader.Open()
if err != nil {
diff --git a/ui/views/dialog.jet b/ui/views/dialog.jet
index a50cc14..9ce8064 100644
--- a/ui/views/dialog.jet
+++ b/ui/views/dialog.jet
@@ -188,8 +188,7 @@
-
+
{{ else if .Type == "header" }}
diff --git a/userdata.go b/userdata.go
index bd972d6..38cc1cc 100644
--- a/userdata.go
+++ b/userdata.go
@@ -22,6 +22,14 @@ import (
log "github.com/sirupsen/logrus"
)
+// userPhotoURL returns the photo URL from the contact info, or a default.
+func userPhotoURL(ci *database.ContactInfo) string {
+ if ci.PhotoURL != nil && *ci.PhotoURL != "" {
+ return *ci.PhotoURL
+ }
+ return "/img/builtin/no-user.png"
+}
+
/* EditProfileForm renders the Amsterdam profile editing form.
* Parameters:
* ctxt - The AmContext for the request.
@@ -74,7 +82,7 @@ func EditProfileForm(ctxt ui.AmContext) (string, any, error) {
dlg.Field("url").SetVal(ci.URL)
dlg.Field("dob").SetDate(u.DOB)
dlg.Field("descr").SetVal(u.Description)
- // TODO: do something for user photo
+ dlg.Field("photo").Value = userPhotoURL(ci)
dlg.Field("pic_in_post").SetChecked(u.FlagValue(database.UserFlagPicturesInPosts))
dlg.Field("no_mass_mail").SetChecked(u.FlagValue(database.UserFlagMassMailOptOut))
dlg.Field("locale").Value = prefs.ReadLocale()
@@ -86,6 +94,14 @@ func EditProfileForm(ctxt ui.AmContext) (string, any, error) {
return ui.ErrorPage(ctxt, err)
}
+/* EditProfile handles profile editing.
+ * Parameters:
+ * ctxt - The AmContext for the request.
+ * Returns:
+ * Command string dictating what to be rendered.
+ * Data as a parameter for the command string.
+ * Standard Go error status.
+ */
func EditProfile(ctxt ui.AmContext) (string, any, error) {
u := ctxt.CurrentUser()
if u.IsAnon {
@@ -201,15 +217,22 @@ func ProfilePhotoForm(ctxt ui.AmContext) (string, any, error) {
}
ci, err := u.ContactInfo()
if err == nil {
- _ = ci
ctxt.VarMap().Set("target", target)
- ctxt.VarMap().Set("photo_url", "/img/builtin/no-user.png")
+ ctxt.VarMap().Set("photo_url", userPhotoURL(ci))
ctxt.VarMap().Set("amsterdam_pageTitle", "Upload User Photo")
return "framed_template", "photo_upload.jet", nil
}
return ui.ErrorPage(ctxt, err)
}
+/* ProfilePhoto handles processing the uploaded user photo..
+ * Parameters:
+ * ctxt - The AmContext for the request.
+ * Returns:
+ * Command string dictating what to be rendered.
+ * Data as a parameter for the command string.
+ * Standard Go error status.
+ */
func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
u := ctxt.CurrentUser()
if u.IsAnon {
@@ -229,7 +252,10 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
if ctxt.FormFieldIsSet("upload") {
file, err := ctxt.FormFile("thepic")
if err == nil {
- imageData, mimeType, err := ui.AmProcessUploadedImage(file, 200, 200)
+ var imageData []byte
+ var mimeType string
+ imageData, mimeType, err = ui.AmProcessUploadedImage(file, ui.UserPhotoWidth, ui.UserPhotoHeight,
+ ui.UserPhotoMaxBytes)
if err == nil {
var img *database.ImageStore
img, err = database.AmStoreImage(database.ImageTypeUserPhoto, u.Uid, mimeType, imageData)
@@ -245,12 +271,13 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
}
ctxt.VarMap().Set("errorMessage", err.Error())
ctxt.VarMap().Set("target", target)
- ctxt.VarMap().Set("photo_url", "/img/builtin/no-user.png")
+ ctxt.VarMap().Set("photo_url", userPhotoURL(ci))
ctxt.VarMap().Set("amsterdam_pageTitle", "Upload User Photo")
return "framed_template", "photo_upload.jet", nil
}
if ctxt.FormFieldIsSet("remove") {
purl := ci.PhotoURL
+ happy := false
if purl == nil || *purl == "" {
// this is a no-op
return "redirect", "/profile?tgt=" + url.QueryEscape(target), nil
@@ -261,12 +288,14 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
return ui.ErrorPage(ctxt, err)
}
defer func() {
- go func() {
- err := database.AmDeleteImage(int32(id))
- if err != nil {
- log.Errorf("unable to delete image ID %d: %v", id, err)
- }
- }()
+ if happy {
+ go func() {
+ err := database.AmDeleteImage(int32(id))
+ if err != nil {
+ log.Errorf("unable to delete image ID %d: %v", id, err)
+ }
+ }()
+ }
}()
}
ci.PhotoURL = nil
@@ -274,6 +303,7 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
if err != nil {
return ui.ErrorPage(ctxt, err)
}
+ happy = true
return "redirect", "/profile?tgt=" + url.QueryEscape(target), nil
}
return ui.ErrorPage(ctxt, errors.New("invalid button detected in photo upload"))