Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preview images for Issue cards in Project Board view #22112

Merged
merged 28 commits into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e06acf4
Preview images for Issue cards in Project Board view
gnat Dec 12, 2022
0afe784
Preview images for Issue cards in Project Board view
gnat Dec 12, 2022
a3fcf65
More specific selector for cleaned up card CSS.
gnat Dec 15, 2022
3745fc3
Added Project option to change Card Type (Image Previews vs Text Only)
gnat Dec 15, 2022
5f57f97
gofmt
gnat Dec 15, 2022
e07d534
Merge remote-tracking branch 'origin/master' into project_issue_image…
gnat Dec 23, 2022
9397770
Added migration.
gnat Dec 23, 2022
4acf5ba
gofmt
gnat Dec 23, 2022
0489cdb
Only fill issuesAttachmentMap if appropriate CardType is set.
gnat Jan 8, 2023
d901ec7
Removed migration.
gnat Jan 8, 2023
326c5b8
Re-added migration as v238
gnat Jan 8, 2023
3e77920
Merge remote-tracking branch 'origin/master' into project_issue_image…
gnat Jan 8, 2023
5936b56
Update models/migrations/v1_19/v238.go
gnat Feb 8, 2023
3aaf557
Merge branch 'master' into project_issue_image_preview
gnat Feb 8, 2023
5e05bec
Update templates/repo/projects/view.tmpl
gnat Feb 8, 2023
853adca
Update models/repo/attachment.go
gnat Feb 8, 2023
84fe0da
Update models/project/board.go
gnat Feb 8, 2023
8fd107a
Added migration.
gnat Feb 8, 2023
d734698
Cleanup from merge with master.
gnat Feb 8, 2023
a3b449a
Bring new org functionality up to date.
gnat Feb 8, 2023
a5e6e22
Cleanup from merge with master.
gnat Feb 8, 2023
5f52fc7
Merge branch 'main' into project_issue_image_preview
lunny Feb 8, 2023
c93b600
Update templates/repo/projects/view.tmpl
gnat Feb 8, 2023
eac2221
Update models/migrations/v1_19/v241.go
gnat Feb 8, 2023
4ce0594
Fixed linter.
gnat Feb 8, 2023
58ef8b1
Images and Text type was updated.
gnat Feb 8, 2023
1c71eab
Merge branch 'main' into project_issue_image_preview
lunny Feb 11, 2023
1f3c4d3
Merge branch 'main' into project_issue_image_preview
lunny Feb 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ var migrations = []Migration{
NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens),
// v240 -> v241
NewMigration("Add actions tables", v1_19.AddActionsTables),
// v241 -> v242
NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
22 changes: 22 additions & 0 deletions models/migrations/v1_19/v241.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_19 //nolint

import (
"fmt"

"xorm.io/xorm"
)

// AddCardTypeToProjectTable: add CardType column, setting existing rows to CardTypeTextOnly
func AddCardTypeToProjectTable(x *xorm.Engine) error {
type Project struct {
CardType int `xorm:"NOT NULL"`
}

if err := x.Sync(new(Project)); err != nil {
return fmt.Errorf("Sync: %w", err)
}
return nil
gnat marked this conversation as resolved.
Show resolved Hide resolved
}
21 changes: 21 additions & 0 deletions models/project/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type (
// BoardType is used to represent a project board type
BoardType uint8

// CardType is used to represent a project board card type
CardType uint8

// BoardList is a list of all project boards in a repository
BoardList []*Board
)
Expand All @@ -34,6 +37,14 @@ const (
BoardTypeBugTriage
)

const (
// CardTypeTextOnly is a project board card type that is text only
CardTypeTextOnly CardType = iota

// CardTypeImagesAndText is a project board card type that has images and text
CardTypeImagesAndText
)

// BoardColorPattern is a regexp witch can validate BoardColor
var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")

Expand Down Expand Up @@ -85,6 +96,16 @@ func IsBoardTypeValid(p BoardType) bool {
}
}

// IsCardTypeValid checks if the project board card type is valid
func IsCardTypeValid(p CardType) bool {
switch p {
case CardTypeTextOnly, CardTypeImagesAndText:
return true
default:
return false
}
}

func createBoardsForProjectsType(ctx context.Context, project *Project) error {
var items []string

Expand Down
34 changes: 29 additions & 5 deletions models/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ import (
)

type (
// ProjectsConfig is used to identify the type of board that is being created
ProjectsConfig struct {
// BoardConfig is used to identify the type of board that is being created
BoardConfig struct {
BoardType BoardType
Translation string
}

// CardConfig is used to identify the type of board card that is being used
CardConfig struct {
CardType CardType
Translation string
}

// Type is used to identify the type of project in question and ownership
Type uint8
)
Expand Down Expand Up @@ -91,6 +97,7 @@ type Project struct {
CreatorID int64 `xorm:"NOT NULL"`
IsClosed bool `xorm:"INDEX"`
BoardType BoardType
CardType CardType
Type Type

RenderedContent string `xorm:"-"`
Expand Down Expand Up @@ -145,15 +152,23 @@ func init() {
db.RegisterModel(new(Project))
}

// GetProjectsConfig retrieves the types of configurations projects could have
func GetProjectsConfig() []ProjectsConfig {
return []ProjectsConfig{
// GetBoardConfig retrieves the types of configurations project boards could have
func GetBoardConfig() []BoardConfig {
return []BoardConfig{
{BoardTypeNone, "repo.projects.type.none"},
{BoardTypeBasicKanban, "repo.projects.type.basic_kanban"},
{BoardTypeBugTriage, "repo.projects.type.bug_triage"},
}
}

// GetCardConfig retrieves the types of configurations project board cards could have
func GetCardConfig() []CardConfig {
return []CardConfig{
{CardTypeTextOnly, "repo.projects.card_type.text_only"},
{CardTypeImagesAndText, "repo.projects.card_type.images_and_text"},
}
}

// IsTypeValid checks if a project type is valid
func IsTypeValid(p Type) bool {
switch p {
Expand Down Expand Up @@ -237,6 +252,10 @@ func NewProject(p *Project) error {
p.BoardType = BoardTypeNone
}

if !IsCardTypeValid(p.CardType) {
p.CardType = CardTypeTextOnly
}

if !IsTypeValid(p.Type) {
return util.NewInvalidArgumentErrorf("project type is not valid")
}
Expand Down Expand Up @@ -280,9 +299,14 @@ func GetProjectByID(ctx context.Context, id int64) (*Project, error) {

// UpdateProject updates project properties
func UpdateProject(ctx context.Context, p *Project) error {
if !IsCardTypeValid(p.CardType) {
p.CardType = CardTypeTextOnly
}

_, err := db.GetEngine(ctx).ID(p.ID).Cols(
"title",
"description",
"card_type",
).Update(p)
return err
}
Expand Down
1 change: 1 addition & 0 deletions models/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func TestProject(t *testing.T) {
project := &Project{
Type: TypeRepository,
BoardType: BoardTypeBasicKanban,
CardType: CardTypeTextOnly,
Title: "New Project",
RepoID: 1,
CreatedUnix: timeutil.TimeStampNow(),
Expand Down
15 changes: 15 additions & 0 deletions models/repo/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ func GetAttachmentsByIssueID(ctx context.Context, issueID int64) ([]*Attachment,
return attachments, db.GetEngine(ctx).Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
}

// GetAttachmentsByIssueIDImagesLatest returns the latest image attachments of an issue.
func GetAttachmentsByIssueIDImagesLatest(ctx context.Context, issueID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 5)
return attachments, db.GetEngine(ctx).Where(`issue_id = ? AND (name like '%.apng'
OR name like '%.avif'
OR name like '%.bmp'
OR name like '%.gif'
OR name like '%.jpg'
OR name like '%.jpeg'
OR name like '%.jxl'
OR name like '%.png'
OR name like '%.svg'
OR name like '%.webp')`, issueID).Desc("comment_id").Limit(5).Find(&attachments)
delvh marked this conversation as resolved.
Show resolved Hide resolved
}

// GetAttachmentsByCommentID returns all attachments if comment by given ID.
func GetAttachmentsByCommentID(ctx context.Context, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
Expand Down
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,9 @@ projects.board.color = "Color"
projects.open = Open
projects.close = Close
projects.board.assigned_to = Assigned to
projects.card_type.desc = "Card Previews"
projects.card_type.images_and_text = "Images and Text"
projects.card_type.text_only = "Text Only"
delvh marked this conversation as resolved.
Show resolved Hide resolved

issues.desc = Organize bug reports, tasks and milestones.
issues.filter_assignees = Filter Assignee
Expand Down
4 changes: 2 additions & 2 deletions routers/web/org/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func canWriteUnit(ctx *context.Context) bool {
// NewProject render creating a project page
func NewProject(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
shared_user.RenderUserHeader(ctx)
Expand All @@ -137,7 +137,7 @@ func NewProjectPost(ctx *context.Context) {
if ctx.HasError() {
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
ctx.Data["PageIsViewProjects"] = true
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
ctx.HTML(http.StatusOK, tplProjectsNew)
return
}
Expand Down
24 changes: 22 additions & 2 deletions routers/web/repo/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
project_model "code.gitea.io/gitea/models/project"
attachment_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
Expand Down Expand Up @@ -123,7 +124,8 @@ func Projects(ctx *context.Context) {
// NewProject render creating a project page
func NewProject(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.HTML(http.StatusOK, tplProjectsNew)
}
Expand All @@ -135,7 +137,8 @@ func NewProjectPost(ctx *context.Context) {

if ctx.HasError() {
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.HTML(http.StatusOK, tplProjectsNew)
return
}
Expand All @@ -146,6 +149,7 @@ func NewProjectPost(ctx *context.Context) {
Description: form.Content,
CreatorID: ctx.Doer.ID,
BoardType: form.BoardType,
CardType: form.CardType,
Type: project_model.TypeRepository,
}); err != nil {
ctx.ServerError("NewProject", err)
Expand Down Expand Up @@ -212,6 +216,7 @@ func EditProject(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
ctx.Data["PageIsEditProjects"] = true
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()

p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
if err != nil {
Expand All @@ -229,6 +234,7 @@ func EditProject(ctx *context.Context) {

ctx.Data["title"] = p.Title
ctx.Data["content"] = p.Description
ctx.Data["card_type"] = p.CardType

ctx.HTML(http.StatusOK, tplProjectsNew)
}
Expand All @@ -239,6 +245,7 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
ctx.Data["PageIsEditProjects"] = true
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()

if ctx.HasError() {
ctx.HTML(http.StatusOK, tplProjectsNew)
Expand All @@ -261,6 +268,7 @@ func EditProjectPost(ctx *context.Context) {

p.Title = form.Title
p.Description = form.Content
p.CardType = form.CardType
if err = project_model.UpdateProject(ctx, p); err != nil {
ctx.ServerError("UpdateProjects", err)
return
Expand Down Expand Up @@ -302,6 +310,18 @@ func ViewProject(ctx *context.Context) {
return
}

if project.CardType != project_model.CardTypeTextOnly {
issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment)
for _, issuesList := range issuesMap {
for _, issue := range issuesList {
if issueAttachment, err := attachment_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil {
issuesAttachmentMap[issue.ID] = issueAttachment
}
}
}
ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap
}

linkedPrsMap := make(map[int64][]*issues_model.Issue)
for _, issuesList := range issuesMap {
for _, issue := range issuesList {
Expand Down
2 changes: 2 additions & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ type CreateProjectForm struct {
Title string `binding:"Required;MaxSize(100)"`
Content string
BoardType project_model.BoardType
CardType project_model.CardType
}

// UserCreateProjectForm is a from for creating an individual or organization
Expand All @@ -520,6 +521,7 @@ type UserCreateProjectForm struct {
Title string `binding:"Required;MaxSize(100)"`
Content string
BoardType project_model.BoardType
CardType project_model.CardType
UID int64 `binding:"Required"`
}

Expand Down
2 changes: 1 addition & 1 deletion templates/projects/new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<input type="hidden" name="board_type" value="{{.type}}">
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
<div class="menu">
{{range $element := .ProjectTypes}}
{{range $element := .BoardTypes}}
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
{{end}}
</div>
Expand Down
33 changes: 27 additions & 6 deletions templates/repo/projects/new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,38 @@
</div>

{{if not .PageIsEditProjects}}
<label>{{.locale.Tr "repo.projects.template.desc"}}</label>
<div class="field">
<label>{{.locale.Tr "repo.projects.template.desc"}}</label>
<div class="ui selection dropdown">
<input type="hidden" name="board_type" value="{{.type}}">
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
<div class="menu">
{{range $element := .BoardTypes}}
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
{{end}}
</div>
</div>
</div>
{{end}}

<div class="field">
<label>{{.locale.Tr "repo.projects.card_type.desc"}}</label>
<div class="ui selection dropdown">
<input type="hidden" name="board_type" value="{{.type}}">
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
{{range $element := .CardTypes}}
{{if or (eq $.card_type $element.CardType) (and (not $.card_type) (eq $element.CardType 2))}}
<input type="hidden" name="card_type" value="{{$element.CardType}}">
<div class="default text">{{$.locale.Tr $element.Translation}}</div>
{{end}}
{{end}}
<div class="menu">
{{range $element := .ProjectTypes}}
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
{{range $element := .CardTypes}}
<div class="item" data-id="{{$element.CardType}}" data-value="{{$element.CardType}}">{{$.locale.Tr $element.Translation}}</div>
{{end}}
</div>
</div>
{{end}}
</div>

</div>
<div class="ui container">
<div class="ui divider"></div>
Expand Down
7 changes: 7 additions & 0 deletions templates/repo/projects/view.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@

<!-- start issue card -->
<div class="card board-card" data-issue="{{.ID}}">
{{if eq $.Project.CardType 2}}
gnat marked this conversation as resolved.
Show resolved Hide resolved
<div class="card-attachment-images">
{{range (index $.issuesAttachmentMap .ID)}}
<img src="{{.DownloadURL}}" alt="{{.Name}}" />
{{end}}
</div>
{{end}}
<div class="content p-0">
<div class="header">
<span class="dif ac vm {{if .IsClosed}}red{{else}}green{{end}}">
Expand Down
2 changes: 1 addition & 1 deletion templates/user/project.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<input type="hidden" name="board_type" value="{{.type}}">
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
<div class="menu">
{{range $element := .ProjectTypes}}
{{range $element := .BoardTypes}}
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
{{end}}
</div>
Expand Down
Loading