Added project links to project edit page
This commit is contained in:
		
							parent
							
								
									80f0e3b176
								
							
						
					
					
						commit
						eb32b04437
					
				| 
						 | 
					@ -137,6 +137,19 @@
 | 
				
			||||||
						<div class="c--dim f7">Plaintext only. No links or markdown.</div>
 | 
											<div class="c--dim f7">Plaintext only. No links or markdown.</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="edit-form-row">
 | 
				
			||||||
 | 
										<div class="pt-input-ns">Project links:</div>
 | 
				
			||||||
 | 
										<div>
 | 
				
			||||||
 | 
											<textarea class="links" name="links" id="links" maxlength="2048" data-max-chars="2048">
 | 
				
			||||||
 | 
												{{- .ProjectSettings.LinksText -}}
 | 
				
			||||||
 | 
											</textarea>
 | 
				
			||||||
 | 
											<div class="c--dim f7">
 | 
				
			||||||
 | 
												<div>Relevant links to put on the project page.</div>
 | 
				
			||||||
 | 
												<div>Format: url [Title] (e.g. <code>http://example.com/ Example Site</code>)</div>
 | 
				
			||||||
 | 
												<div>(1 per line, 10 max)</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
				<div class="edit-form-row">
 | 
									<div class="edit-form-row">
 | 
				
			||||||
					<div class="pt-input-ns">Full description:</div>
 | 
										<div class="pt-input-ns">Full description:</div>
 | 
				
			||||||
					<div>
 | 
										<div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,6 +146,7 @@ type ProjectSettings struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Blurb       string
 | 
						Blurb       string
 | 
				
			||||||
	Description string
 | 
						Description string
 | 
				
			||||||
 | 
						LinksText   string
 | 
				
			||||||
	Owners      []User
 | 
						Owners      []User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LightLogo string
 | 
						LightLogo string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -496,6 +496,24 @@ func ProjectEdit(c *RequestContext) ResponseData {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return c.ErrorResponse(http.StatusInternalServerError, err)
 | 
							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(
 | 
						projectSettings := templates.ProjectToProjectSettings(
 | 
				
			||||||
		&p.Project,
 | 
							&p.Project,
 | 
				
			||||||
		p.Owners,
 | 
							p.Owners,
 | 
				
			||||||
| 
						 | 
					@ -504,6 +522,8 @@ func ProjectEdit(c *RequestContext) ResponseData {
 | 
				
			||||||
		c.Theme,
 | 
							c.Theme,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						projectSettings.LinksText = LinksToText(projectLinkResult)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var res ResponseData
 | 
						var res ResponseData
 | 
				
			||||||
	res.MustWriteTemplate("project_edit.html", ProjectEditData{
 | 
						res.MustWriteTemplate("project_edit.html", ProjectEditData{
 | 
				
			||||||
		BaseData:        getBaseDataAutocrumb(c, "Edit Project"),
 | 
							BaseData:        getBaseDataAutocrumb(c, "Edit Project"),
 | 
				
			||||||
| 
						 | 
					@ -558,6 +578,7 @@ type ProjectPayload struct {
 | 
				
			||||||
	ProjectID         int
 | 
						ProjectID         int
 | 
				
			||||||
	Name              string
 | 
						Name              string
 | 
				
			||||||
	Blurb             string
 | 
						Blurb             string
 | 
				
			||||||
 | 
						Links             []ParsedLink
 | 
				
			||||||
	Description       string
 | 
						Description       string
 | 
				
			||||||
	ParsedDescription string
 | 
						ParsedDescription string
 | 
				
			||||||
	Lifecycle         models.ProjectLifecycle
 | 
						Lifecycle         models.ProjectLifecycle
 | 
				
			||||||
| 
						 | 
					@ -600,6 +621,7 @@ func ParseProjectEditForm(c *RequestContext) ProjectEditFormResult {
 | 
				
			||||||
		res.RejectionReason = "Projects must have a short description"
 | 
							res.RejectionReason = "Projects must have a short description"
 | 
				
			||||||
		return res
 | 
							return res
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						links := ParseLinks(c.Req.Form.Get("links"))
 | 
				
			||||||
	description := c.Req.Form.Get("description")
 | 
						description := c.Req.Form.Get("description")
 | 
				
			||||||
	parsedDescription := parsing.ParseMarkdown(description, parsing.ForumRealMarkdown)
 | 
						parsedDescription := parsing.ParseMarkdown(description, parsing.ForumRealMarkdown)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -650,6 +672,7 @@ func ParseProjectEditForm(c *RequestContext) ProjectEditFormResult {
 | 
				
			||||||
	res.Payload = ProjectPayload{
 | 
						res.Payload = ProjectPayload{
 | 
				
			||||||
		Name:              projectName,
 | 
							Name:              projectName,
 | 
				
			||||||
		Blurb:             shortDesc,
 | 
							Blurb:             shortDesc,
 | 
				
			||||||
 | 
							Links:             links,
 | 
				
			||||||
		Description:       description,
 | 
							Description:       description,
 | 
				
			||||||
		ParsedDescription: parsedDescription,
 | 
							ParsedDescription: parsedDescription,
 | 
				
			||||||
		Lifecycle:         lifecycle,
 | 
							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)
 | 
							payload.OwnerUsernames = append(payload.OwnerUsernames, selfUsername)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var qb db.QueryBuilder
 | 
						_, err := tx.Exec(ctx,
 | 
				
			||||||
	qb.Add(
 | 
					 | 
				
			||||||
		`
 | 
							`
 | 
				
			||||||
		UPDATE handmade_project SET
 | 
							UPDATE handmade_project SET
 | 
				
			||||||
			name = $?,
 | 
								name = $2,
 | 
				
			||||||
			blurb = $?,
 | 
								blurb = $3,
 | 
				
			||||||
			description = $?,
 | 
								description = $4,
 | 
				
			||||||
			descparsed = $?,
 | 
								descparsed = $5,
 | 
				
			||||||
			lifecycle = $?
 | 
								lifecycle = $6
 | 
				
			||||||
 | 
							WHERE id = $1
 | 
				
			||||||
		`,
 | 
							`,
 | 
				
			||||||
 | 
							payload.ProjectID,
 | 
				
			||||||
		payload.Name,
 | 
							payload.Name,
 | 
				
			||||||
		payload.Blurb,
 | 
							payload.Blurb,
 | 
				
			||||||
		payload.Description,
 | 
							payload.Description,
 | 
				
			||||||
		payload.ParsedDescription,
 | 
							payload.ParsedDescription,
 | 
				
			||||||
		payload.Lifecycle,
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		return oops.New(err, "Failed to update project")
 | 
							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
 | 
								UPDATE handmade_project SET
 | 
				
			||||||
				slug = $2,
 | 
									slug = $2,
 | 
				
			||||||
				featured = $3,
 | 
									featured = $3,
 | 
				
			||||||
				personal = $4
 | 
									personal = $4,
 | 
				
			||||||
 | 
									hidden = $5
 | 
				
			||||||
			WHERE
 | 
								WHERE
 | 
				
			||||||
				id = $1
 | 
									id = $1
 | 
				
			||||||
			`,
 | 
								`,
 | 
				
			||||||
| 
						 | 
					@ -756,6 +775,7 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P
 | 
				
			||||||
			payload.Slug,
 | 
								payload.Slug,
 | 
				
			||||||
			payload.Featured,
 | 
								payload.Featured,
 | 
				
			||||||
			payload.Personal,
 | 
								payload.Personal,
 | 
				
			||||||
 | 
								payload.Hidden,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return oops.New(err, "Failed to update project with admin fields")
 | 
								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
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -235,11 +235,7 @@ func UserSettings(c *RequestContext) ResponseData {
 | 
				
			||||||
		return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
 | 
							return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	linksText := ""
 | 
						linksText := LinksToText(links)
 | 
				
			||||||
	for _, ilink := range links {
 | 
					 | 
				
			||||||
		link := ilink.(*models.Link)
 | 
					 | 
				
			||||||
		linksText += fmt.Sprintf("%s %s\n", link.URL, link.Name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var tduser *templates.DiscordUser
 | 
						var tduser *templates.DiscordUser
 | 
				
			||||||
	var numUnsavedMessages int
 | 
						var numUnsavedMessages int
 | 
				
			||||||
| 
						 | 
					@ -365,31 +361,19 @@ func UserSettingsSave(c *RequestContext) ResponseData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Process links
 | 
						// Process links
 | 
				
			||||||
	linksText := form.Get("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)
 | 
						_, err = tx.Exec(c.Context(), `DELETE FROM handmade_links WHERE user_id = $1`, c.CurrentUser.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		c.Logger.Warn().Err(err).Msg("failed to delete old links")
 | 
							c.Logger.Warn().Err(err).Msg("failed to delete old links")
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		for i, link := range links {
 | 
							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(),
 | 
								_, err := tx.Exec(c.Context(),
 | 
				
			||||||
				`
 | 
									`
 | 
				
			||||||
				INSERT INTO handmade_links (name, url, ordering, user_id)
 | 
									INSERT INTO handmade_links (name, url, ordering, user_id)
 | 
				
			||||||
				VALUES ($1, $2, $3, $4)
 | 
									VALUES ($1, $2, $3, $4)
 | 
				
			||||||
				`,
 | 
									`,
 | 
				
			||||||
				name,
 | 
									link.Name,
 | 
				
			||||||
				url,
 | 
									link.Url,
 | 
				
			||||||
				i,
 | 
									i,
 | 
				
			||||||
				c.CurrentUser.ID,
 | 
									c.CurrentUser.ID,
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue