diff --git a/src/templates/src/project_edit.html b/src/templates/src/project_edit.html index 7f085759..3fb5d1cf 100644 --- a/src/templates/src/project_edit.html +++ b/src/templates/src/project_edit.html @@ -137,6 +137,19 @@
Plaintext only. No links or markdown.
+
+
Project links:
+
+ +
+
Relevant links to put on the project page.
+
Format: url [Title] (e.g. http://example.com/ Example Site)
+
(1 per line, 10 max)
+
+
+
Full description:
diff --git a/src/templates/types.go b/src/templates/types.go index 73bda64e..915a1e73 100644 --- a/src/templates/types.go +++ b/src/templates/types.go @@ -146,6 +146,7 @@ type ProjectSettings struct { Blurb string Description string + LinksText string Owners []User LightLogo string diff --git a/src/website/links_helper.go b/src/website/links_helper.go new file mode 100644 index 00000000..93eeef16 --- /dev/null +++ b/src/website/links_helper.go @@ -0,0 +1,40 @@ +package website + +import ( + "fmt" + "strings" + + "git.handmade.network/hmn/hmn/src/models" +) + +type ParsedLink struct { + Name string + Url string +} + +func ParseLinks(text string) []ParsedLink { + lines := strings.Split(text, "\n") + res := make([]ParsedLink, 0, len(lines)) + for _, line := range lines { + linkParts := strings.SplitN(line, " ", 2) + url := strings.TrimSpace(linkParts[0]) + name := "" + if len(linkParts) > 1 { + name = strings.TrimSpace(linkParts[1]) + } + if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { + continue + } + res = append(res, ParsedLink{Name: name, Url: url}) + } + return res +} + +func LinksToText(links []interface{}) string { + linksText := "" + for _, l := range links { + link := l.(*models.Link) + linksText += fmt.Sprintf("%s %s\n", link.URL, link.Name) + } + return linksText +} diff --git a/src/website/projects.go b/src/website/projects.go index a85ba98a..b86d6d13 100644 --- a/src/website/projects.go +++ b/src/website/projects.go @@ -496,6 +496,24 @@ func ProjectEdit(c *RequestContext) ResponseData { if err != nil { return c.ErrorResponse(http.StatusInternalServerError, err) } + + c.Perf.StartBlock("SQL", "Fetching project links") + projectLinkResult, err := db.Query(c.Context(), c.Conn, models.Link{}, + ` + SELECT $columns + FROM + handmade_links as link + WHERE + link.project_id = $1 + ORDER BY link.ordering ASC + `, + p.Project.ID, + ) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project links")) + } + c.Perf.EndBlock() + projectSettings := templates.ProjectToProjectSettings( &p.Project, p.Owners, @@ -504,6 +522,8 @@ func ProjectEdit(c *RequestContext) ResponseData { c.Theme, ) + projectSettings.LinksText = LinksToText(projectLinkResult) + var res ResponseData res.MustWriteTemplate("project_edit.html", ProjectEditData{ BaseData: getBaseDataAutocrumb(c, "Edit Project"), @@ -558,6 +578,7 @@ type ProjectPayload struct { ProjectID int Name string Blurb string + Links []ParsedLink Description string ParsedDescription string Lifecycle models.ProjectLifecycle @@ -600,6 +621,7 @@ func ParseProjectEditForm(c *RequestContext) ProjectEditFormResult { res.RejectionReason = "Projects must have a short description" return res } + links := ParseLinks(c.Req.Form.Get("links")) description := c.Req.Form.Get("description") parsedDescription := parsing.ParseMarkdown(description, parsing.ForumRealMarkdown) @@ -650,6 +672,7 @@ func ParseProjectEditForm(c *RequestContext) ProjectEditFormResult { res.Payload = ProjectPayload{ Name: projectName, Blurb: shortDesc, + Links: links, Description: description, ParsedDescription: parsedDescription, Lifecycle: lifecycle, @@ -714,28 +737,23 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P payload.OwnerUsernames = append(payload.OwnerUsernames, selfUsername) } - var qb db.QueryBuilder - qb.Add( + _, err := tx.Exec(ctx, ` UPDATE handmade_project SET - name = $?, - blurb = $?, - description = $?, - descparsed = $?, - lifecycle = $? + name = $2, + blurb = $3, + description = $4, + descparsed = $5, + lifecycle = $6 + WHERE id = $1 `, + payload.ProjectID, payload.Name, payload.Blurb, payload.Description, payload.ParsedDescription, payload.Lifecycle, ) - if user.IsStaff { - qb.Add(`, hidden = $?`, payload.Hidden) - } - qb.Add(`WHERE id = $?`, payload.ProjectID) - - _, err := tx.Exec(ctx, qb.String(), qb.Args()...) if err != nil { return oops.New(err, "Failed to update project") } @@ -748,7 +766,8 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P UPDATE handmade_project SET slug = $2, featured = $3, - personal = $4 + personal = $4, + hidden = $5 WHERE id = $1 `, @@ -756,6 +775,7 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P payload.Slug, payload.Featured, payload.Personal, + payload.Hidden, ) if err != nil { return oops.New(err, "Failed to update project with admin fields") @@ -835,6 +855,26 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P } } + _, err = tx.Exec(ctx, `DELETE FROM handmade_links WHERE project_id = $1`, payload.ProjectID) + if err != nil { + return oops.New(err, "Failed to delete project links") + } + for i, link := range payload.Links { + _, err = tx.Exec(ctx, + ` + INSERT INTO handmade_links (name, url, ordering, project_id) + VALUES ($1, $2, $3, $4) + `, + link.Name, + link.Url, + i, + payload.ProjectID, + ) + if err != nil { + return oops.New(err, "Failed to insert new project link") + } + } + return nil } diff --git a/src/website/user.go b/src/website/user.go index 2b6b9ad9..61f6b92b 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -235,11 +235,7 @@ func UserSettings(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links")) } - linksText := "" - for _, ilink := range links { - link := ilink.(*models.Link) - linksText += fmt.Sprintf("%s %s\n", link.URL, link.Name) - } + linksText := LinksToText(links) var tduser *templates.DiscordUser var numUnsavedMessages int @@ -365,31 +361,19 @@ func UserSettingsSave(c *RequestContext) ResponseData { // Process links linksText := form.Get("links") - links := strings.Split(linksText, "\n") + links := ParseLinks(linksText) _, err = tx.Exec(c.Context(), `DELETE FROM handmade_links WHERE user_id = $1`, c.CurrentUser.ID) if err != nil { c.Logger.Warn().Err(err).Msg("failed to delete old links") } else { for i, link := range links { - link = strings.TrimSpace(link) - linkParts := strings.SplitN(link, " ", 2) - url := strings.TrimSpace(linkParts[0]) - name := "" - if len(linkParts) > 1 { - name = strings.TrimSpace(linkParts[1]) - } - - if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { - continue - } - _, err := tx.Exec(c.Context(), ` INSERT INTO handmade_links (name, url, ordering, user_id) VALUES ($1, $2, $3, $4) `, - name, - url, + link.Name, + link.Url, i, c.CurrentUser.ID, )