Skip to content
30 changes: 0 additions & 30 deletions models/issues/issue_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,36 +64,6 @@ func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID i
return result, nil
}

// LoadIssuesFromColumn load issues assigned to this column
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *IssuesOptions) (IssueList, error) {
issueList, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = b.ID
o.ProjectID = b.ProjectID
o.SortType = "project-column-sorting"
}))
if err != nil {
return nil, err
}

if b.Default {
issues, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = db.NoConditionID
o.ProjectID = b.ProjectID
o.SortType = "project-column-sorting"
}))
if err != nil {
return nil, err
}
issueList = append(issueList, issues...)
}

if err := issueList.LoadComments(ctx); err != nil {
return nil, err
}

return issueList, nil
}

// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
Expand Down
39 changes: 22 additions & 17 deletions models/project/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,12 @@ func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) {
return columns, nil
}

// getDefaultColumn return default column and ensure only one exists
func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
// getDefaultColumnWithFallback return default column if one exists
// otherwise return the first column by sorting and set it as default column
func (p *Project) getDefaultColumnWithFallback(ctx context.Context) (*Column, error) {
var column Column

// try to find a column "default=true"
has, err := db.GetEngine(ctx).
Where("project_id=? AND `default` = ?", p.ID, true).
Desc("id").Get(&column)
Expand All @@ -270,36 +273,38 @@ func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
if has {
return &column, nil
}

// try to find the first column by sorting
has, err = db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
if err != nil {
return nil, err
}
if has {
column.Default = true
if _, err := db.GetEngine(ctx).ID(column.ID).Cols("`default`").Update(&column); err != nil {
return nil, err
}
return &column, nil
}

return nil, ErrProjectColumnNotExist{ColumnID: 0}
}

// MustDefaultColumn returns the default column for a project.
// If one exists, it is returned
// If none exists, the first column will be elevated to the default column of this project
// If there is no column, it creates a default column and returns it
func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
c, err := p.getDefaultColumn(ctx)
c, err := p.getDefaultColumnWithFallback(ctx)
if err != nil && !IsErrProjectColumnNotExist(err) {
return nil, err
}
if c != nil {
return c, nil
}

var column Column
has, err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
if err != nil {
return nil, err
}
if has {
column.Default = true
if _, err := db.GetEngine(ctx).ID(column.ID).Cols("`default`").Update(&column); err != nil {
return nil, err
}
return &column, nil
}

// create a default column if none is found
column = Column{
column := Column{
ProjectID: p.ID,
Default: true,
Title: "Uncategorized",
Expand Down
10 changes: 4 additions & 6 deletions routers/web/repo/issue_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,9 @@ func NewIssue(ctx *context.Context) {
}

pageMetaData.MilestonesData.SelectedMilestoneID = ctx.FormInt64("milestone")
pageMetaData.ProjectsData.SelectedProjectID = ctx.FormInt64("project")
if pageMetaData.ProjectsData.SelectedProjectID > 0 {
if len(ctx.Req.URL.Query().Get("project")) > 0 {
ctx.Data["redirect_after_creation"] = "project"
}
pageMetaData.ProjectsData.SelectedProjectIDs, _ = base.StringsToInt64s(strings.Split(ctx.FormString("project"), ","))
if len(pageMetaData.ProjectsData.SelectedProjectIDs) == 1 {
ctx.Data["redirect_after_creation"] = "project"
}

tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
Expand Down Expand Up @@ -273,7 +271,7 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
ctx.NotFound(nil)
return ret
}
pageMetaData.ProjectsData.SelectedProjectID = form.ProjectID
pageMetaData.ProjectsData.SelectedProjectIDs = util.Iif(form.ProjectID > 0, []int64{form.ProjectID}, nil)

// prepare assignees
candidateAssignees := toSet(pageMetaData.AssigneesData.CandidateAssignees, func(user *user_model.User) int64 { return user.ID })
Expand Down
42 changes: 38 additions & 4 deletions routers/web/repo/issue_page_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ type issueSidebarAssigneesData struct {
}

type issueSidebarProjectsData struct {
SelectedProjectID int64
OpenProjects []*project_model.Project
ClosedProjects []*project_model.Project
SelectedProjectIDs []int64 // TODO: support multiple projects in the future

// the "selected" fields are only valid when len(SelectedProjectIDs)==1
SelectedProjectColumns []*project_model.Column
SelectedProjectColumn *project_model.Column

OpenProjects []*project_model.Project
ClosedProjects []*project_model.Project
}

type IssuePageMetaData struct {
Expand Down Expand Up @@ -92,6 +97,11 @@ func retrieveRepoIssueMetaData(ctx *context.Context, repo *repo_model.Repository
return data
}

data.retrieveProjectData(ctx)
if ctx.Written() {
return data
}

// TODO: the issue/pull permissions are quite complex and unclear
// A reader could create an issue/PR with setting some meta (eg: assignees from issue template, reviewers, target branch)
// A reader(creator) could update some meta (eg: target branch), but can't change assignees anymore.
Expand Down Expand Up @@ -158,9 +168,33 @@ func (d *IssuePageMetaData) retrieveAssigneesData(ctx *context.Context) {
ctx.Data["Assignees"] = d.AssigneesData.CandidateAssignees
}

func (d *IssuePageMetaData) retrieveProjectData(ctx *context.Context) {
if d.Issue == nil || d.Issue.Project == nil {
return
}
d.ProjectsData.SelectedProjectIDs = []int64{d.Issue.Project.ID}
columns, err := d.Issue.Project.GetColumns(ctx)
if err != nil {
ctx.ServerError("GetProjectColumns", err)
return
}
d.ProjectsData.SelectedProjectColumns = columns
columnID, err := d.Issue.ProjectColumnID(ctx)
if err != nil {
ctx.ServerError("ProjectColumnID", err)
return
}
for _, col := range columns {
if col.ID == columnID {
d.ProjectsData.SelectedProjectColumn = col
break
}
}
}

func (d *IssuePageMetaData) retrieveProjectsDataForIssueWriter(ctx *context.Context) {
if d.Issue != nil && d.Issue.Project != nil {
d.ProjectsData.SelectedProjectID = d.Issue.Project.ID
d.ProjectsData.SelectedProjectIDs = []int64{d.Issue.Project.ID}
}
d.ProjectsData.OpenProjects, d.ProjectsData.ClosedProjects = retrieveProjectsInternal(ctx, ctx.Repo.Repository)
}
Expand Down
9 changes: 6 additions & 3 deletions services/projects/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,18 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
}

// LoadIssuesFromProject load issues assigned to each project column inside the given project
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (map[int64]issues_model.IssueList, error) {
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (results map[int64]issues_model.IssueList, _ error) {
issueList, err := issues_model.Issues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
o.ProjectID = project.ID
o.SortType = "project-column-sorting"
}))
if err != nil {
return nil, err
}

if len(issueList) == 0 {
// if no issue, return directly, then no need to create a default column for an empty project
return results, nil
}
if err := issueList.LoadComments(ctx); err != nil {
return nil, err
}
Expand All @@ -110,7 +113,7 @@ func LoadIssuesFromProject(ctx context.Context, project *project_model.Project,
return nil, err
}

results := make(map[int64]issues_model.IssueList)
results = make(map[int64]issues_model.IssueList)
for _, issue := range issueList {
projectColumnID, ok := issueColumnMap[issue.ID]
if !ok {
Expand Down
8 changes: 4 additions & 4 deletions templates/base/head_navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<span class="text">
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
<span class="only-mobile">{{.SignedUser.Name}}</span>
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
</span>
<div class="menu user-menu">
<div class="header">
Expand All @@ -64,9 +64,9 @@
{{else if .IsSigned}}
{{template "base/head_navbar_icons" dict "ItemExtraClass" "not-mobile" "PageGlobalData" .PageGlobalData}}
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
<span class="text">
<span class="flex-text-block">
{{svg "octicon-plus"}}
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
<span class="only-mobile">{{ctx.Locale.Tr "create_new"}}</span>
</span>
<div class="menu">
Expand All @@ -93,7 +93,7 @@
{{if .IsAdmin}}{{svg "octicon-shield-check" 16 "navbar-admin-badge"}}{{end}}
</span>
<span class="only-mobile">{{.SignedUser.Name}}</span>
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
</span>
<div class="menu user-menu">
<div class="header">
Expand Down
4 changes: 2 additions & 2 deletions templates/base/head_navbar_icons.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
{{- $notificationUnreadCount := call $data.GetNotificationUnreadCount -}}
{{if $activeStopwatch}}
<a class="item active-stopwatch {{$itemExtraClass}}" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}">
<div class="tw-relative">
<div class="tw-relative flex-text-block">
{{svg "octicon-stopwatch"}}
<span class="header-stopwatch-dot"></span>
</div>
</a>
{{end}}
<a class="item {{$itemExtraClass}}" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}">
<div class="tw-relative">
<div class="tw-relative flex-text-block">
{{svg "octicon-bell"}}
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/issue/new_form.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
</div>
</div>

<div class="issue-content-right ui segment">
<div class="issue-content-right ui segment" data-global-init="initRepoIssueSidebar">
{{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}

{{if .PageIsComparePull}}
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/issue/sidebar/project_list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="issue-sidebar-combo" data-selection-mode="single" data-update-algo="all"
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/projects?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="project_id" type="hidden" value="{{$data.SelectedProjectID}}">
<input class="combo-value" name="project_id" type="hidden" value="{{if and $pageMeta.CanModifyIssueOrPull $data.SelectedProjectIDs}}{{index $data.SelectedProjectIDs 0}}{{end}}">
<div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
<a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.projects"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
Expand Down
1 change: 1 addition & 0 deletions templates/repo/issue/view_content.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
</div>

{{template "repo/issue/view_content/comments" .}}
<div class="timeline-item tw-hidden" id="timeline-comments-end"></div>

{{if and .Issue.IsPull (not $.Repository.IsArchived)}}
{{template "repo/issue/view_content/pull_merge_box".}}
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/issue/view_content/sidebar.tmpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="issue-content-right ui segment">
<div class="issue-content-right ui segment" data-global-init="initRepoIssueSidebar">
{{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}

{{if .Issue.IsPull}}
Expand Down
4 changes: 2 additions & 2 deletions templates/repo/issue/view_content/watching.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<input type="hidden" name="watch" value="{{if $.IssueWatch.IsWatching}}0{{else}}1{{end}}">
<button class="fluid ui button">
{{if $.IssueWatch.IsWatching}}
{{svg "octicon-mute" 16 "tw-mr-2"}}
{{svg "octicon-mute" 16}}
{{ctx.Locale.Tr "repo.issues.unsubscribe"}}
{{else}}
{{svg "octicon-unmute" 16 "tw-mr-2"}}
{{svg "octicon-unmute" 16}}
{{ctx.Locale.Tr "repo.issues.subscribe"}}
{{end}}
</button>
Expand Down
2 changes: 1 addition & 1 deletion templates/shared/issuelist.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<div class="flex-item-leading">
{{/* using some tw helpers is the only way to align the checkbox */}}
<div class="flex-text-inline tw-mt-[2px]">
<div class="flex-text-inline tw-mt-[3px]">
{{if $.CanWriteIssuesOrPulls}}
<input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-[14px]" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} &quot;{{.Title}}&quot;">
{{end}}
Expand Down
1 change: 0 additions & 1 deletion web_src/css/modules/button.css
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@

.ui.fluid.button {
width: 100%;
display: block;
}

.ui.primary.button,
Expand Down
1 change: 1 addition & 0 deletions web_src/css/modules/label.css
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ If the labels-list itself needs some layouts, use extra classes or "tw" helpers.
}

.labels-list a {
display: inline-flex;
max-width: 100%; /* for ellipsis */
}

Expand Down
Loading