mirror of https://github.com/go-gitea/gitea.git
Add selecting tags on the compare page (#15723)
* Add selecting tags on the compare page * Remove unused condition and change indentation * Fix tag tab in dropdown to be black * Add compare tag integration test Co-authored-by: Jonathan Tran <jon@allspice.io>
This commit is contained in:
parent
4900881924
commit
9557b8603a
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompareTag(t *testing.T) {
|
||||||
|
defer prepareTestEnv(t)()
|
||||||
|
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/compare/v1.1...master")
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
selection := htmlDoc.doc.Find(".choose.branch .filter.dropdown")
|
||||||
|
// A dropdown for both base and head.
|
||||||
|
assert.Lenf(t, selection.Nodes, 2, "The template has changed")
|
||||||
|
}
|
|
@ -1286,6 +1286,9 @@ issues.review.resolved_by = marked this conversation as resolved
|
||||||
issues.assignee.error = Not all assignees was added due to an unexpected error.
|
issues.assignee.error = Not all assignees was added due to an unexpected error.
|
||||||
issues.reference_issue.body = Body
|
issues.reference_issue.body = Body
|
||||||
|
|
||||||
|
compare.compare_base = base
|
||||||
|
compare.compare_head = compare
|
||||||
|
|
||||||
pulls.desc = Enable pull requests and code reviews.
|
pulls.desc = Enable pull requests and code reviews.
|
||||||
pulls.new = New Pull Request
|
pulls.new = New Pull Request
|
||||||
pulls.compare_changes = New Pull Request
|
pulls.compare_changes = New Pull Request
|
||||||
|
|
|
@ -391,7 +391,7 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
|
||||||
if rootRepo != nil &&
|
if rootRepo != nil &&
|
||||||
rootRepo.ID != headRepo.ID &&
|
rootRepo.ID != headRepo.ID &&
|
||||||
rootRepo.ID != baseRepo.ID {
|
rootRepo.ID != baseRepo.ID {
|
||||||
perm, branches, err := getBranchesForRepo(ctx.User, rootRepo)
|
perm, branches, tags, err := getBranchesAndTagsForRepo(ctx.User, rootRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranchesForRepo", err)
|
ctx.ServerError("GetBranchesForRepo", err)
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
|
@ -399,19 +399,20 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
|
||||||
if perm {
|
if perm {
|
||||||
ctx.Data["RootRepo"] = rootRepo
|
ctx.Data["RootRepo"] = rootRepo
|
||||||
ctx.Data["RootRepoBranches"] = branches
|
ctx.Data["RootRepoBranches"] = branches
|
||||||
|
ctx.Data["RootRepoTags"] = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a ownForkRepo and it's different from:
|
// If we have a ownForkRepo and it's different from:
|
||||||
// 1. The computed base
|
// 1. The computed base
|
||||||
// 2. The computed hea
|
// 2. The computed head
|
||||||
// 3. The rootRepo (if we have one)
|
// 3. The rootRepo (if we have one)
|
||||||
// then get the branches from it.
|
// then get the branches from it.
|
||||||
if ownForkRepo != nil &&
|
if ownForkRepo != nil &&
|
||||||
ownForkRepo.ID != headRepo.ID &&
|
ownForkRepo.ID != headRepo.ID &&
|
||||||
ownForkRepo.ID != baseRepo.ID &&
|
ownForkRepo.ID != baseRepo.ID &&
|
||||||
(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
|
(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
|
||||||
perm, branches, err := getBranchesForRepo(ctx.User, ownForkRepo)
|
perm, branches, tags, err := getBranchesAndTagsForRepo(ctx.User, ownForkRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranchesForRepo", err)
|
ctx.ServerError("GetBranchesForRepo", err)
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
|
@ -419,6 +420,7 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
|
||||||
if perm {
|
if perm {
|
||||||
ctx.Data["OwnForkRepo"] = ownForkRepo
|
ctx.Data["OwnForkRepo"] = ownForkRepo
|
||||||
ctx.Data["OwnForkRepoBranches"] = branches
|
ctx.Data["OwnForkRepoBranches"] = branches
|
||||||
|
ctx.Data["OwnForkRepoTags"] = tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,25 +574,29 @@ func PrepareCompareDiff(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBranchesForRepo(user *models.User, repo *models.Repository) (bool, []string, error) {
|
func getBranchesAndTagsForRepo(user *models.User, repo *models.Repository) (bool, []string, []string, error) {
|
||||||
perm, err := models.GetUserRepoPermission(repo, user)
|
perm, err := models.GetUserRepoPermission(repo, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
if !perm.CanRead(models.UnitTypeCode) {
|
if !perm.CanRead(models.UnitTypeCode) {
|
||||||
return false, nil, nil
|
return false, nil, nil, nil
|
||||||
}
|
}
|
||||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
defer gitRepo.Close()
|
defer gitRepo.Close()
|
||||||
|
|
||||||
branches, _, err := gitRepo.GetBranches(0, 0)
|
branches, _, err := gitRepo.GetBranches(0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
return true, branches, nil
|
tags, err := gitRepo.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, nil, err
|
||||||
|
}
|
||||||
|
return true, branches, tags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompareDiff show different from one commit to another commit
|
// CompareDiff show different from one commit to another commit
|
||||||
|
@ -608,7 +614,14 @@ func CompareDiff(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Data["PageIsComparePull"] == true {
|
baseGitRepo := ctx.Repo.GitRepo
|
||||||
|
baseTags, err := baseGitRepo.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Tags"] = baseTags
|
||||||
|
|
||||||
headBranches, _, err := headGitRepo.GetBranches(0, 0)
|
headBranches, _, err := headGitRepo.GetBranches(0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranches", err)
|
ctx.ServerError("GetBranches", err)
|
||||||
|
@ -616,6 +629,14 @@ func CompareDiff(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
ctx.Data["HeadBranches"] = headBranches
|
ctx.Data["HeadBranches"] = headBranches
|
||||||
|
|
||||||
|
headTags, err := headGitRepo.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["HeadTags"] = headTags
|
||||||
|
|
||||||
|
if ctx.Data["PageIsComparePull"] == true {
|
||||||
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !models.IsErrPullRequestNotExist(err) {
|
if !models.IsErrPullRequestNotExist(err) {
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
|
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
|
||||||
|
|
||||||
{{if .PageIsComparePull}}
|
|
||||||
<h2 class="ui header">
|
<h2 class="ui header">
|
||||||
{{if and $.IsSigned (not .Repository.IsArchived)}}
|
{{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}}
|
||||||
{{.i18n.Tr "repo.pulls.compare_changes"}}
|
{{.i18n.Tr "repo.pulls.compare_changes"}}
|
||||||
<div class="sub header">{{.i18n.Tr "repo.pulls.compare_changes_desc"}}</div>
|
<div class="sub header">{{.i18n.Tr "repo.pulls.compare_changes_desc"}}</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
@ -37,15 +36,31 @@
|
||||||
{{svg "octicon-git-compare"}}
|
{{svg "octicon-git-compare"}}
|
||||||
<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
|
<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
|
||||||
<div class="ui basic small button">
|
<div class="ui basic small button">
|
||||||
<span class="text">{{.i18n.Tr "repo.pulls.compare_base"}}: {{$BaseCompareName}}:{{$.BaseBranch}}</span>
|
<span class="text">{{if $.PageIsComparePull}}{{.i18n.Tr "repo.pulls.compare_base"}}{{else}}{{.i18n.Tr "repo.compare.compare_base"}}{{end}}: {{$BaseCompareName}}:{{$.BaseBranch}}</span>
|
||||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="ui icon search input">
|
<div class="ui icon search input">
|
||||||
<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
|
<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
|
||||||
<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
|
<input name="search" placeholder="{{.i18n.Tr "repo.filter_branch_and_tag"}}...">
|
||||||
</div>
|
</div>
|
||||||
<div class="scrolling menu">
|
<div class="header">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="two column row">
|
||||||
|
<a class="reference column" href="#" data-target=".base-branch-list">
|
||||||
|
<span class="text black">
|
||||||
|
{{svg "octicon-git-branch" 16 "mr-2"}}{{.i18n.Tr "repo.branches"}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a class="reference column" href="#" data-target=".base-tag-list">
|
||||||
|
<span class="text black">
|
||||||
|
{{svg "octicon-tag" 16 "mr-2"}}{{.i18n.Tr "repo.tags"}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="scrolling menu reference-list-menu base-branch-list">
|
||||||
{{range .Branches}}
|
{{range .Branches}}
|
||||||
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$BaseCompareName}}:{{.}}</div>
|
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$BaseCompareName}}:{{.}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -65,20 +80,56 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="scrolling menu reference-list-menu base-tag-list" style="display: none">
|
||||||
|
{{range .Tags}}
|
||||||
|
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$BaseCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .PullRequestCtx.SameRepo}}
|
||||||
|
{{range .HeadTags}}
|
||||||
|
<div class="item" data-url="{{$.HeadRepo.Link}}/compare/{{EscapePound .}}...{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{EscapePound $.HeadBranch}}">{{$HeadCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if .OwnForkRepo}}
|
||||||
|
{{range .OwnForkRepoTags}}
|
||||||
|
<div class="item" data-url="{{$.OwnForkRepo.Link}}/compare/{{EscapePound .}}...{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{EscapePound $.HeadBranch}}">{{$OwnForkCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if .RootRepo}}
|
||||||
|
{{range .RootRepoTags}}
|
||||||
|
<div class="item" data-url="{{$.RootRepo.Link}}/compare/{{EscapePound .}}...{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{EscapePound $.HeadBranch}}">{{$RootRepoCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
...
|
...
|
||||||
<div class="ui floating filter dropdown">
|
<div class="ui floating filter dropdown">
|
||||||
<div class="ui basic small button">
|
<div class="ui basic small button">
|
||||||
<span class="text">{{.i18n.Tr "repo.pulls.compare_compare"}}: {{$HeadCompareName}}:{{$.HeadBranch}}</span>
|
<span class="text">{{if $.PageIsComparePull}}{{.i18n.Tr "repo.pulls.compare_compare"}}{{else}}{{.i18n.Tr "repo.compare.compare_head"}}{{end}}: {{$HeadCompareName}}:{{$.HeadBranch}}</span>
|
||||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="ui icon search input">
|
<div class="ui icon search input">
|
||||||
<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
|
<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
|
||||||
<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
|
<input name="search" placeholder="{{.i18n.Tr "repo.filter_branch_and_tag"}}...">
|
||||||
</div>
|
</div>
|
||||||
<div class="scrolling menu">
|
<div class="header">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="two column row">
|
||||||
|
<a class="reference column" href="#" data-target=".head-branch-list">
|
||||||
|
<span class="text black">
|
||||||
|
{{svg "octicon-git-branch" 16 "mr-2"}}{{.i18n.Tr "repo.branches"}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a class="reference column" href="#" data-target=".head-tag-list">
|
||||||
|
<span class="text black">
|
||||||
|
{{svg "octicon-tag" 16 "mr-2"}}{{.i18n.Tr "repo.tags"}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="scrolling menu reference-list-menu head-branch-list">
|
||||||
{{range .HeadBranches}}
|
{{range .HeadBranches}}
|
||||||
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound .}}">{{$HeadCompareName}}:{{.}}</div>
|
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound .}}">{{$HeadCompareName}}:{{.}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -98,10 +149,29 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="scrolling menu reference-list-menu head-tag-list" style="display: none">
|
||||||
</div>
|
{{range .HeadTags}}
|
||||||
</div>
|
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}/{{$.HeadRepo.Name}}:{{end}}{{EscapePound .}}">{{$HeadCompareName}}:{{.}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if not .PullRequestCtx.SameRepo}}
|
||||||
|
{{range .Tags}}
|
||||||
|
<div class="item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{$.BaseName}}/{{$.Repository.Name}}:{{EscapePound .}}">{{$BaseCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if .OwnForkRepo}}
|
||||||
|
{{range .OwnForkRepoTags}}
|
||||||
|
<div class="item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{$.OwnForkRepo.OwnerName}}/{{$.OwnForkRepo.Name}}:{{EscapePound .}}">{{$OwnForkCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if .RootRepo}}
|
||||||
|
{{range .RootRepoTags}}
|
||||||
|
<div class="item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{$.RootRepo.OwnerName}}/{{$.RootRepo.Name}}:{{EscapePound .}}">{{$RootRepoCompareName}}:{{.}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{if .IsNothingToCompare}}
|
{{if .IsNothingToCompare}}
|
||||||
{{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived) }}
|
{{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived) }}
|
||||||
|
|
|
@ -1241,10 +1241,16 @@ async function initRepository() {
|
||||||
$(this).select();
|
$(this).select();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Compare or pull request
|
||||||
|
const $repoDiff = $('.repository.diff');
|
||||||
|
if ($repoDiff.length) {
|
||||||
|
initBranchOrTagDropdown('.choose.branch .dropdown');
|
||||||
|
initFilterSearchDropdown('.choose.branch .dropdown');
|
||||||
|
}
|
||||||
|
|
||||||
// Pull request
|
// Pull request
|
||||||
const $repoComparePull = $('.repository.compare.pull');
|
const $repoComparePull = $('.repository.compare.pull');
|
||||||
if ($repoComparePull.length > 0) {
|
if ($repoComparePull.length > 0) {
|
||||||
initFilterSearchDropdown('.choose.branch .dropdown');
|
|
||||||
// show pull request form
|
// show pull request form
|
||||||
$repoComparePull.find('button.show-form').on('click', function (e) {
|
$repoComparePull.find('button.show-form').on('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -3447,6 +3453,17 @@ function initIssueTimetracking() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initBranchOrTagDropdown(selector) {
|
||||||
|
$(selector).each(function() {
|
||||||
|
const $dropdown = $(this);
|
||||||
|
$dropdown.find('.reference.column').on('click', function () {
|
||||||
|
$dropdown.find('.scrolling.reference-list-menu').hide();
|
||||||
|
$($(this).data('target')).show();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initFilterBranchTagDropdown(selector) {
|
function initFilterBranchTagDropdown(selector) {
|
||||||
$(selector).each(function () {
|
$(selector).each(function () {
|
||||||
const $dropdown = $(this);
|
const $dropdown = $(this);
|
||||||
|
|
Loading…
Reference in New Issue