diff --git a/community.go b/community.go index e5fe217..87bce45 100644 --- a/community.go +++ b/community.go @@ -62,7 +62,11 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) { } ctxt.VarMap().Set("commName", comm.Name) - // TODO: set photo URL + if ci.PhotoURL != nil && *ci.PhotoURL != "" { + ctxt.VarMap().Set("logoURL", *ci.PhotoURL) + } else { + ctxt.VarMap().Set("logoURL", "/img/builtin/default-community.jpg") + } tz := prefs.Location() loc := prefs.Localizer() ctxt.VarMap().Set("dateCreated", loc.Strftime("%x %X", comm.CreateDate.In(tz))) diff --git a/communityadmin.go b/communityadmin.go index 10a6b66..4e35386 100644 --- a/communityadmin.go +++ b/communityadmin.go @@ -15,10 +15,12 @@ import ( "fmt" "net/http" "strconv" + "strings" "git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/ui" "git.erbosoft.com/amy/amsterdam/util" + log "github.com/sirupsen/logrus" ) /* CommunityAdminMenu renders the community administration menu. @@ -67,6 +69,22 @@ func setupCommunityProfileDialog(dlg *ui.Dialog, comm *database.Community) { } } +// communityLogoURL returns the logo URL from the contact info, or a default. +func communityLogoURL(ci *database.ContactInfo) string { + if ci.PhotoURL != nil && *ci.PhotoURL != "" { + return *ci.PhotoURL + } + return "/img/builtin/default-community.jpg" +} + +/* CommunityProfileForm displays the dfialog for editing the community profile. + * 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 CommunityProfileForm(ctxt ui.AmContext) (string, any, error) { err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) if err != nil { @@ -97,7 +115,7 @@ func CommunityProfileForm(ctxt ui.AmContext) (string, any, error) { dlg.Field("rules").SetVal(comm.Rules) dlg.Field("language").SetVal(comm.Language) dlg.Field("url").SetVal(ci.URL) - // TODO: set logo URL + dlg.Field("logo").Value = communityLogoURL(ci) dlg.Field("company").SetVal(ci.Company) dlg.Field("addr1").SetVal(ci.Addr1) dlg.Field("addr2").SetVal(ci.Addr2) @@ -222,3 +240,104 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) { } return ui.ErrorPage(ctxt, err) } + +func CommunityLogoForm(ctxt ui.AmContext) (string, any, error) { + err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) + if err != nil { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, err) + } + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) + } + ci, err := comm.ContactInfo() + if err == nil { + ctxt.VarMap().Set("commName", comm.Name) + ctxt.VarMap().Set("commAlias", comm.Alias) + ctxt.VarMap().Set("logo_url", communityLogoURL(ci)) + ctxt.VarMap().Set("amsterdam_pageTitle", "Upload Community Logo: "+comm.Name) + return "framed_template", "logo_upload.jet", nil + } + return ui.ErrorPage(ctxt, err) +} + +func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) { + err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) + if err != nil { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, err) + } + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) + } + ci, err := comm.ContactInfo() + if err != nil { + return ui.ErrorPage(ctxt, err) + } + if ctxt.FormFieldIsSet("cancel") { + return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil + } + if ctxt.FormFieldIsSet("upload") { + file, err := ctxt.FormFile("thepic") + if err == nil { + var imageData []byte + var mimeType string + imageData, mimeType, err = ui.AmProcessUploadedImage(file, ui.CommunityLogoWidth, ui.CommunityLogoHeight, + ui.CommunityLogoMaxBytes) + if err == nil { + var img *database.ImageStore + img, err = database.AmStoreImage(database.ImageTypeCommunityLogo, comm.Id, mimeType, imageData) + if err == nil { + photourl := fmt.Sprintf("/img/store/%d", img.ImgId) + ci.PhotoURL = &photourl + _, err = ci.Save() + if err == nil { + return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil + } + } + } + } + ctxt.VarMap().Set("errorMessage", err.Error()) + ctxt.VarMap().Set("commName", comm.Name) + ctxt.VarMap().Set("commAlias", comm.Alias) + ctxt.VarMap().Set("logo_url", communityLogoURL(ci)) + ctxt.VarMap().Set("amsterdam_pageTitle", "Upload Community Logo: "+comm.Name) + return "framed_template", "logo_upload.jet", nil + } + if ctxt.FormFieldIsSet("remove") { + purl := ci.PhotoURL + happy := false + if purl == nil || *purl == "" { + // this is a no-op + return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil + } + if strings.HasPrefix(*purl, "/img/store/") { + id, err := strconv.Atoi((*purl)[11:]) + if err != nil { + return ui.ErrorPage(ctxt, err) + } + defer func() { + 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 + _, err := ci.Save() + if err != nil { + return ui.ErrorPage(ctxt, err) + } + happy = true + return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil + } + return ui.ErrorPage(ctxt, errors.New("invalid button detected in logo upload")) +} diff --git a/main.go b/main.go index 84440f1..b4cbc77 100644 --- a/main.go +++ b/main.go @@ -68,6 +68,8 @@ func setupEcho() *echo.Echo { e.GET("/comm/:cid/admin", ui.AmWrap(CommunityAdminMenu)) e.GET("/comm/:cid/admin/profile", ui.AmWrap(CommunityProfileForm)) e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile)) + e.GET("/comm/:cid/admin/logo", ui.AmWrap(CommunityLogoForm)) + e.POST("/comm/:cid/admin/logo", ui.AmWrap(EditCommunityLogo)) return e } diff --git a/ui/dialogs/commprofile.yaml b/ui/dialogs/commprofile.yaml index 9c0b38b..a1d4ca8 100644 --- a/ui/dialogs/commprofile.yaml +++ b/ui/dialogs/commprofile.yaml @@ -54,7 +54,7 @@ fields: - type: "communitylogo" name: "logo" caption: "Community logo" - param: "/TODO/comm/[CID]/admin/logo" + param: "/comm/[CID]/admin/logo" - type: "header" name: "header2" caption: "Location" diff --git a/ui/images.go b/ui/images.go index 9281bcb..7aa20d8 100644 --- a/ui/images.go +++ b/ui/images.go @@ -35,11 +35,12 @@ var static_images embed.FS // Constants for default photo sizes. const ( - UserPhotoWidth = 100 - UserPhotoHeight = 100 - UserPhotoMaxBytes = 2097152 // 2 Mb - CommunityLogoWidth = 110 - CommunityLogoHeight = 60 + UserPhotoWidth = 100 + UserPhotoHeight = 100 + UserPhotoMaxBytes = 2097152 // 2 Mb + CommunityLogoWidth = 110 + CommunityLogoHeight = 60 + CommunityLogoMaxBytes = 2097152 // 2 Mb ) /* mimeTypeFromFilenane returns the MIME type of a file, given its filename. diff --git a/ui/views/comprofile.jet b/ui/views/comprofile.jet index b4c532b..6aef5c9 100644 --- a/ui/views/comprofile.jet +++ b/ui/views/comprofile.jet @@ -19,8 +19,8 @@
- {{ commName}} community logo + {{ commName }} community logo
Community created:
{{ dateCreated }}
diff --git a/ui/views/dialog.jet b/ui/views/dialog.jet index 1650630..e957478 100644 --- a/ui/views/dialog.jet +++ b/ui/views/dialog.jet @@ -243,11 +243,11 @@ {{ if .Subcaption != "" }} {{ .Subcaption }}{{ end }} (click to change): {{ if .Disabled }} - + {{ else }} - Click to upload logo + Click to upload logo {{ end }}
diff --git a/ui/views/logo_upload.jet b/ui/views/logo_upload.jet new file mode 100644 index 0000000..5038ad9 --- /dev/null +++ b/ui/views/logo_upload.jet @@ -0,0 +1,89 @@ +{* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *} + +
+
+

Upload Community Logo:

+ {{ commName }} +
+
+ + {{ if isset(errorMessage) }} + +
+
+
+ ⚠️ +
+
+

{{ CapitalizeString(errorMessage) }}.

+
+
+
+ {{ end }} + + +
+
+
+ +
+
+ Current community logo +
+

Current Logo

+
+ + +
+
+ + +

+ Recommended: Maximum file size: 2MB. Supported formats: JPG, PNG, GIF. +

+
+ + +
+ + + +
+
+
+ + +
+

Logo Image Guidelines:

+
    +
  • Your logo will be visible to other users in the community
  • +
  • Photos should be appropriate for a professional community setting
  • +
  • The image will be automatically resized to fit the logo display
  • +
  • You can update or remove the communiity logo at any time
  • +
+
+
+
+