mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-13 00:01:02 -04:00
Compare commits
6 Commits
8bba7e30b2
...
d5e417a33d
Author | SHA1 | Date | |
---|---|---|---|
|
d5e417a33d | ||
|
d32af84a10 | ||
|
6221a6fd54 | ||
|
feed1ff38f | ||
|
bd66fa586a | ||
|
007d181bb5 |
@ -149,7 +149,7 @@ rules:
|
||||
jquery/no-global-eval: [2]
|
||||
jquery/no-grep: [2]
|
||||
jquery/no-has: [2]
|
||||
jquery/no-hide: [0]
|
||||
jquery/no-hide: [2]
|
||||
jquery/no-html: [0]
|
||||
jquery/no-in-array: [2]
|
||||
jquery/no-is-array: [2]
|
||||
@ -166,13 +166,13 @@ rules:
|
||||
jquery/no-proxy: [2]
|
||||
jquery/no-ready: [0]
|
||||
jquery/no-serialize: [2]
|
||||
jquery/no-show: [0]
|
||||
jquery/no-show: [2]
|
||||
jquery/no-size: [2]
|
||||
jquery/no-sizzle: [0]
|
||||
jquery/no-slide: [0]
|
||||
jquery/no-submit: [0]
|
||||
jquery/no-text: [0]
|
||||
jquery/no-toggle: [0]
|
||||
jquery/no-toggle: [2]
|
||||
jquery/no-trigger: [0]
|
||||
jquery/no-trim: [2]
|
||||
jquery/no-val: [0]
|
||||
|
@ -93,6 +93,11 @@ However, there are still some special cases, so the current guideline is:
|
||||
* `node.dataset` should not be used, use `node.getAttribute` instead.
|
||||
* never bind any user data to a DOM node, use a suitable design pattern to describe the relation between node and data.
|
||||
|
||||
### Show/Hide Elements
|
||||
|
||||
* Vue components are recommended to use `v-if` and `v-show` to show/hide elements.
|
||||
* Go template code should use Gitea's `.gt-hidden` and `showElem()/hideElem()/toggleElem()`, see more details in `.gt-hidden`'s comment.
|
||||
|
||||
### Legacy Code
|
||||
|
||||
A lot of legacy code already existed before this document's written. It's recommended to refactor legacy code to follow the guidelines.
|
||||
|
@ -106,7 +106,7 @@ You can try it out using [the online demo](https://try.gitea.io/).
|
||||
- Permission to create organizations
|
||||
- Permission to import repositories
|
||||
- Organization management
|
||||
- People
|
||||
- Members
|
||||
- Teams
|
||||
- Avatar
|
||||
- Hooks
|
||||
|
@ -534,7 +534,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
|
||||
repo = act.Repo
|
||||
|
||||
// check repo owner exist.
|
||||
if err := act.Repo.GetOwner(ctx); err != nil {
|
||||
if err := act.Repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("can't get repo owner: %w", err)
|
||||
}
|
||||
} else if act.Repo == nil {
|
||||
|
@ -287,3 +287,20 @@
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 18
|
||||
repo_id: 55
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue for scoped labels
|
||||
content: content
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
@ -4,6 +4,7 @@
|
||||
org_id: 0
|
||||
name: label1
|
||||
color: '#abcdef'
|
||||
exclusive: false
|
||||
num_issues: 2
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -13,6 +14,7 @@
|
||||
org_id: 0
|
||||
name: label2
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 1
|
||||
num_closed_issues: 1
|
||||
|
||||
@ -22,6 +24,7 @@
|
||||
org_id: 3
|
||||
name: orglabel3
|
||||
color: '#abcdef'
|
||||
exclusive: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -31,6 +34,7 @@
|
||||
org_id: 3
|
||||
name: orglabel4
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 1
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -40,5 +44,46 @@
|
||||
org_id: 0
|
||||
name: pull-test-label
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 6
|
||||
repo_id: 55
|
||||
org_id: 0
|
||||
name: unscoped_label
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 7
|
||||
repo_id: 55
|
||||
org_id: 0
|
||||
name: scope/label1
|
||||
color: '#000000'
|
||||
exclusive: true
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 8
|
||||
repo_id: 55
|
||||
org_id: 0
|
||||
name: scope/label2
|
||||
color: '#000000'
|
||||
exclusive: true
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 9
|
||||
repo_id: 55
|
||||
org_id: 0
|
||||
name: scope/subscope/label2
|
||||
color: '#000000'
|
||||
exclusive: true
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
@ -1622,3 +1622,15 @@
|
||||
is_archived: false
|
||||
is_private: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 55
|
||||
owner_id: 2
|
||||
owner_name: user2
|
||||
lower_name: scoped_label
|
||||
name: scoped_label
|
||||
is_empty: false
|
||||
is_archived: false
|
||||
is_private: true
|
||||
num_issues: 1
|
||||
status: 0
|
||||
|
@ -66,7 +66,7 @@
|
||||
num_followers: 2
|
||||
num_following: 1
|
||||
num_stars: 2
|
||||
num_repos: 10
|
||||
num_repos: 11
|
||||
num_teams: 0
|
||||
num_members: 0
|
||||
visibility: 0
|
||||
|
@ -314,8 +314,8 @@ type WhitelistOptions struct {
|
||||
// This function also performs check if whitelist user and team's IDs have been changed
|
||||
// to avoid unnecessary whitelist delete and regenerate.
|
||||
func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, protectBranch *ProtectedBranch, opts WhitelistOptions) (err error) {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
return fmt.Errorf("GetOwner: %v", err)
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("LoadOwner: %v", err)
|
||||
}
|
||||
|
||||
whitelist, err := updateUserWhitelist(ctx, repo, protectBranch.WhitelistUserIDs, opts.UserIDs)
|
||||
|
@ -620,7 +620,7 @@ func (c *Comment) LoadAssigneeUserAndTeam() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.Issue.Repo.GetOwner(db.DefaultContext); err != nil {
|
||||
if err = c.Issue.Repo.LoadOwner(db.DefaultContext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -824,7 +824,7 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = opts.Repo.GetOwner(ctx); err != nil {
|
||||
if err = opts.Repo.LoadOwner(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -538,6 +538,31 @@ func (ts labelSorter) Swap(i, j int) {
|
||||
[]*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i]
|
||||
}
|
||||
|
||||
// Ensure only one label of a given scope exists, with labels at the end of the
|
||||
// array getting preference over earlier ones.
|
||||
func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label {
|
||||
validLabels := make([]*Label, 0, len(labels))
|
||||
|
||||
for i, label := range labels {
|
||||
scope := label.ExclusiveScope()
|
||||
if scope != "" {
|
||||
foundOther := false
|
||||
for _, otherLabel := range labels[i+1:] {
|
||||
if otherLabel.ExclusiveScope() == scope {
|
||||
foundOther = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if foundOther {
|
||||
continue
|
||||
}
|
||||
}
|
||||
validLabels = append(validLabels, label)
|
||||
}
|
||||
|
||||
return validLabels
|
||||
}
|
||||
|
||||
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
||||
// Triggers appropriate WebHooks, if any.
|
||||
func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
||||
@ -555,6 +580,8 @@ func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (e
|
||||
return err
|
||||
}
|
||||
|
||||
labels = RemoveDuplicateExclusiveLabels(labels)
|
||||
|
||||
sort.Sort(labelSorter(labels))
|
||||
sort.Sort(labelSorter(issue.Labels))
|
||||
|
||||
@ -2099,7 +2126,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
|
||||
resolved := make(map[string]bool, 10)
|
||||
var mentionTeams []string
|
||||
|
||||
if err := issue.Repo.GetOwner(ctx); err != nil {
|
||||
if err := issue.Repo.LoadOwner(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
func TestIssue_ReplaceLabels(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
testSuccess := func(issueID int64, labelIDs []int64) {
|
||||
testSuccess := func(issueID int64, labelIDs, expectedLabelIDs []int64) {
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
@ -35,15 +35,20 @@ func TestIssue_ReplaceLabels(t *testing.T) {
|
||||
labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID})
|
||||
}
|
||||
assert.NoError(t, issues_model.ReplaceIssueLabels(issue, labels, doer))
|
||||
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(labelIDs))
|
||||
for _, labelID := range labelIDs {
|
||||
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(expectedLabelIDs))
|
||||
for _, labelID := range expectedLabelIDs {
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID})
|
||||
}
|
||||
}
|
||||
|
||||
testSuccess(1, []int64{2})
|
||||
testSuccess(1, []int64{1, 2})
|
||||
testSuccess(1, []int64{})
|
||||
testSuccess(1, []int64{2}, []int64{2})
|
||||
testSuccess(1, []int64{1, 2}, []int64{1, 2})
|
||||
testSuccess(1, []int64{}, []int64{})
|
||||
|
||||
// mutually exclusive scoped labels 7 and 8
|
||||
testSuccess(18, []int64{6, 7}, []int64{6, 7})
|
||||
testSuccess(18, []int64{7, 8}, []int64{8})
|
||||
testSuccess(18, []int64{6, 8, 7}, []int64{6, 7})
|
||||
}
|
||||
|
||||
func Test_GetIssueIDsByRepoID(t *testing.T) {
|
||||
@ -523,5 +528,5 @@ func TestCountIssues(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
count, err := issues_model.CountIssues(db.DefaultContext, &issues_model.IssuesOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 17, count)
|
||||
assert.EqualValues(t, 18, count)
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ package issues
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -89,6 +87,7 @@ type Label struct {
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Exclusive bool
|
||||
Description string
|
||||
Color string `xorm:"VARCHAR(7)"`
|
||||
NumIssues int
|
||||
@ -128,18 +127,22 @@ func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64)
|
||||
}
|
||||
|
||||
// LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked
|
||||
func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64) {
|
||||
func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) {
|
||||
var labelQuerySlice []string
|
||||
labelSelected := false
|
||||
labelID := strconv.FormatInt(label.ID, 10)
|
||||
for _, s := range currentSelectedLabels {
|
||||
labelScope := label.ExclusiveScope()
|
||||
for i, s := range currentSelectedLabels {
|
||||
if s == label.ID {
|
||||
labelSelected = true
|
||||
} else if -s == label.ID {
|
||||
labelSelected = true
|
||||
label.IsExcluded = true
|
||||
} else if s != 0 {
|
||||
labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10))
|
||||
// Exclude other labels in the same scope from selection
|
||||
if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] {
|
||||
labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
if !labelSelected {
|
||||
@ -159,49 +162,43 @@ func (label *Label) BelongsToRepo() bool {
|
||||
return label.RepoID > 0
|
||||
}
|
||||
|
||||
// SrgbToLinear converts a component of an sRGB color to its linear intensity
|
||||
// See: https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ)
|
||||
func SrgbToLinear(color uint8) float64 {
|
||||
flt := float64(color) / 255
|
||||
if flt <= 0.04045 {
|
||||
return flt / 12.92
|
||||
// Get color as RGB values in 0..255 range
|
||||
func (label *Label) ColorRGB() (float64, float64, float64, error) {
|
||||
color, err := strconv.ParseUint(label.Color[1:], 16, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
return math.Pow((flt+0.055)/1.055, 2.4)
|
||||
|
||||
r := float64(uint8(0xFF & (uint32(color) >> 16)))
|
||||
g := float64(uint8(0xFF & (uint32(color) >> 8)))
|
||||
b := float64(uint8(0xFF & uint32(color)))
|
||||
return r, g, b, nil
|
||||
}
|
||||
|
||||
// Luminance returns the luminance of an sRGB color
|
||||
func Luminance(color uint32) float64 {
|
||||
r := SrgbToLinear(uint8(0xFF & (color >> 16)))
|
||||
g := SrgbToLinear(uint8(0xFF & (color >> 8)))
|
||||
b := SrgbToLinear(uint8(0xFF & color))
|
||||
|
||||
// luminance ratios for sRGB
|
||||
return 0.2126*r + 0.7152*g + 0.0722*b
|
||||
}
|
||||
|
||||
// LuminanceThreshold is the luminance at which white and black appear to have the same contrast
|
||||
// i.e. x such that 1.05 / (x + 0.05) = (x + 0.05) / 0.05
|
||||
// i.e. math.Sqrt(1.05*0.05) - 0.05
|
||||
const LuminanceThreshold float64 = 0.179
|
||||
|
||||
// ForegroundColor calculates the text color for labels based
|
||||
// on their background color.
|
||||
func (label *Label) ForegroundColor() template.CSS {
|
||||
// Determine if label text should be light or dark to be readable on background color
|
||||
func (label *Label) UseLightTextColor() bool {
|
||||
if strings.HasPrefix(label.Color, "#") {
|
||||
if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil {
|
||||
// NOTE: see web_src/js/components/ContextPopup.vue for similar implementation
|
||||
luminance := Luminance(uint32(color))
|
||||
|
||||
// prefer white or black based upon contrast
|
||||
if luminance < LuminanceThreshold {
|
||||
return template.CSS("#fff")
|
||||
}
|
||||
return template.CSS("#000")
|
||||
if r, g, b, err := label.ColorRGB(); err == nil {
|
||||
// Perceived brightness from: https://www.w3.org/TR/AERT/#color-contrast
|
||||
// In the future WCAG 3 APCA may be a better solution
|
||||
brightness := (0.299*r + 0.587*g + 0.114*b) / 255
|
||||
return brightness < 0.35
|
||||
}
|
||||
}
|
||||
|
||||
// default to black
|
||||
return template.CSS("#000")
|
||||
return false
|
||||
}
|
||||
|
||||
// Return scope substring of label name, or empty string if none exists
|
||||
func (label *Label) ExclusiveScope() string {
|
||||
if !label.Exclusive {
|
||||
return ""
|
||||
}
|
||||
lastIndex := strings.LastIndex(label.Name, "/")
|
||||
if lastIndex == -1 || lastIndex == 0 || lastIndex == len(label.Name)-1 {
|
||||
return ""
|
||||
}
|
||||
return label.Name[:lastIndex]
|
||||
}
|
||||
|
||||
// NewLabel creates a new label
|
||||
@ -253,7 +250,7 @@ func UpdateLabel(l *Label) error {
|
||||
if !LabelColorPattern.MatchString(l.Color) {
|
||||
return fmt.Errorf("bad color code: %s", l.Color)
|
||||
}
|
||||
return updateLabelCols(db.DefaultContext, l, "name", "description", "color")
|
||||
return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive")
|
||||
}
|
||||
|
||||
// DeleteLabel delete a label
|
||||
@ -620,6 +617,29 @@ func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m
|
||||
return updateLabelCols(ctx, label, "num_issues", "num_closed_issue")
|
||||
}
|
||||
|
||||
// Remove all issue labels in the given exclusive scope
|
||||
func RemoveDuplicateExclusiveIssueLabels(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
|
||||
scope := label.ExclusiveScope()
|
||||
if scope == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var toRemove []*Label
|
||||
for _, issueLabel := range issue.Labels {
|
||||
if label.ID != issueLabel.ID && issueLabel.ExclusiveScope() == scope {
|
||||
toRemove = append(toRemove, issueLabel)
|
||||
}
|
||||
}
|
||||
|
||||
for _, issueLabel := range toRemove {
|
||||
if err = deleteIssueLabel(ctx, issue, issueLabel, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewIssueLabel creates a new issue-label relation.
|
||||
func NewIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error) {
|
||||
if HasIssueLabel(db.DefaultContext, issue.ID, label.ID) {
|
||||
@ -641,6 +661,10 @@ func NewIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
package issues_test
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@ -25,13 +24,22 @@ func TestLabel_CalOpenIssues(t *testing.T) {
|
||||
assert.EqualValues(t, 2, label.NumOpenIssues)
|
||||
}
|
||||
|
||||
func TestLabel_ForegroundColor(t *testing.T) {
|
||||
func TestLabel_TextColor(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
|
||||
assert.Equal(t, template.CSS("#000"), label.ForegroundColor())
|
||||
assert.False(t, label.UseLightTextColor())
|
||||
|
||||
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
|
||||
assert.Equal(t, template.CSS("#fff"), label.ForegroundColor())
|
||||
assert.True(t, label.UseLightTextColor())
|
||||
}
|
||||
|
||||
func TestLabel_ExclusiveScope(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7})
|
||||
assert.Equal(t, "scope", label.ExclusiveScope())
|
||||
|
||||
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 9})
|
||||
assert.Equal(t, "scope/subscope", label.ExclusiveScope())
|
||||
}
|
||||
|
||||
func TestNewLabels(t *testing.T) {
|
||||
@ -266,6 +274,7 @@ func TestUpdateLabel(t *testing.T) {
|
||||
Color: "#ffff00",
|
||||
Name: "newLabelName",
|
||||
Description: label.Description,
|
||||
Exclusive: false,
|
||||
}
|
||||
label.Color = update.Color
|
||||
label.Name = update.Name
|
||||
@ -323,6 +332,34 @@ func TestNewIssueLabel(t *testing.T) {
|
||||
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{})
|
||||
}
|
||||
|
||||
func TestNewIssueExclusiveLabel(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 18})
|
||||
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
otherLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 6})
|
||||
exclusiveLabelA := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7})
|
||||
exclusiveLabelB := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 8})
|
||||
|
||||
// coexisting regular and exclusive label
|
||||
assert.NoError(t, issues_model.NewIssueLabel(issue, otherLabel, doer))
|
||||
assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelA, doer))
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID})
|
||||
|
||||
// exclusive label replaces existing one
|
||||
assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelB, doer))
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelB.ID})
|
||||
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID})
|
||||
|
||||
// exclusive label replaces existing one again
|
||||
assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelA, doer))
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID})
|
||||
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelB.ID})
|
||||
}
|
||||
|
||||
func TestNewIssueLabels(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
label1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
|
||||
|
@ -498,7 +498,7 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := pr.Issue.Repo.GetOwner(ctx); err != nil {
|
||||
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
org_id: 0
|
||||
name: label1
|
||||
color: '#abcdef'
|
||||
exclusive: false
|
||||
num_issues: 2
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -13,6 +14,7 @@
|
||||
org_id: 0
|
||||
name: label2
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 1
|
||||
num_closed_issues: 1
|
||||
-
|
||||
@ -21,6 +23,7 @@
|
||||
org_id: 3
|
||||
name: orglabel3
|
||||
color: '#abcdef'
|
||||
exclusive: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -30,6 +33,7 @@
|
||||
org_id: 3
|
||||
name: orglabel4
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 1
|
||||
num_closed_issues: 0
|
||||
|
||||
@ -39,5 +43,6 @@
|
||||
org_id: 0
|
||||
name: pull-test-label
|
||||
color: '#000000'
|
||||
exclusive: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
@ -459,6 +459,8 @@ var migrations = []Migration{
|
||||
NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
|
||||
// v242 -> v243
|
||||
NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
|
||||
// v243 -> v244
|
||||
NewMigration("Add exclusive label", v1_19.AddExclusiveLabel),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
16
models/migrations/v1_19/v244.go
Normal file
16
models/migrations/v1_19/v244.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_19 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddExclusiveLabel(x *xorm.Engine) error {
|
||||
type Label struct {
|
||||
Exclusive bool
|
||||
}
|
||||
|
||||
return x.Sync(new(Label))
|
||||
}
|
@ -85,8 +85,8 @@ func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mo
|
||||
// FIXME: do cross-comparison so reduce deletions and additions to the minimum?
|
||||
func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) {
|
||||
minMode := perm.AccessModeRead
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
return fmt.Errorf("GetOwner: %w", err)
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("LoadOwner: %w", err)
|
||||
}
|
||||
|
||||
// If the repo isn't private and isn't owned by a organization,
|
||||
@ -143,7 +143,7 @@ func refreshCollaboratorAccesses(ctx context.Context, repoID int64, accessMap ma
|
||||
func RecalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, ignTeamID int64) (err error) {
|
||||
accessMap := make(map[int64]*userAccess, 20)
|
||||
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
} else if !repo.Owner.IsOrganization() {
|
||||
return fmt.Errorf("owner is not an organization: %d", repo.OwnerID)
|
||||
@ -199,7 +199,7 @@ func RecalculateUserAccess(ctx context.Context, repo *repo_model.Repository, uid
|
||||
accessMode = collaborator.Mode
|
||||
}
|
||||
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
} else if repo.Owner.IsOrganization() {
|
||||
var teams []organization.Team
|
||||
|
@ -97,7 +97,7 @@ func TestRepository_RecalculateAccesses(t *testing.T) {
|
||||
// test with organization repo
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
assert.NoError(t, repo1.GetOwner(db.DefaultContext))
|
||||
assert.NoError(t, repo1.LoadOwner(db.DefaultContext))
|
||||
|
||||
_, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 2, RepoID: 3})
|
||||
assert.NoError(t, err)
|
||||
@ -114,7 +114,7 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {
|
||||
// test with non-organization repo
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo1.GetOwner(db.DefaultContext))
|
||||
assert.NoError(t, repo1.LoadOwner(db.DefaultContext))
|
||||
|
||||
_, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 4, RepoID: 4})
|
||||
assert.NoError(t, err)
|
||||
|
@ -175,7 +175,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
||||
}
|
||||
}
|
||||
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -210,7 +210,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
||||
return
|
||||
}
|
||||
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if !repo.Owner.IsOrganization() {
|
||||
@ -281,7 +281,7 @@ func IsUserRealRepoAdmin(repo *repo_model.Repository, user *user_model.User) (bo
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if err := repo.GetOwner(db.DefaultContext); err != nil {
|
||||
if err := repo.LoadOwner(db.DefaultContext); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -378,7 +378,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
|
||||
|
||||
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
|
||||
func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode) (_ []*user_model.User, err error) {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ func (repo *Repository) AfterLoad() {
|
||||
// LoadAttributes loads attributes of the repository.
|
||||
func (repo *Repository) LoadAttributes(ctx context.Context) error {
|
||||
// Load owner
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("load owner: %w", err)
|
||||
}
|
||||
|
||||
@ -373,8 +373,8 @@ func (repo *Repository) GetUnit(ctx context.Context, tp unit.Type) (*RepoUnit, e
|
||||
return nil, ErrUnitTypeNotExist{tp}
|
||||
}
|
||||
|
||||
// GetOwner returns the repository owner
|
||||
func (repo *Repository) GetOwner(ctx context.Context) (err error) {
|
||||
// LoadOwner loads owner user
|
||||
func (repo *Repository) LoadOwner(ctx context.Context) (err error) {
|
||||
if repo.Owner != nil {
|
||||
return nil
|
||||
}
|
||||
@ -388,7 +388,7 @@ func (repo *Repository) GetOwner(ctx context.Context) (err error) {
|
||||
// It creates a fake object that contains error details
|
||||
// when error occurs.
|
||||
func (repo *Repository) MustOwner(ctx context.Context) *user_model.User {
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return &user_model.User{
|
||||
Name: "error",
|
||||
FullName: err.Error(),
|
||||
|
@ -143,7 +143,7 @@ func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName s
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repo.GetOwner(db.DefaultContext); err != nil {
|
||||
if err := repo.LoadOwner(db.DefaultContext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ func GetWatchedRepos(ctx context.Context, userID int64, private bool, listOption
|
||||
// GetRepoAssignees returns all users that have write access and can be assigned to issues
|
||||
// of the repository,
|
||||
func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.User, err error) {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
|
||||
// TODO: may be we should have a busy choice for users to block review request to them.
|
||||
func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) {
|
||||
// Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ func TestRepository_DeleteCollaboration(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo.GetOwner(db.DefaultContext))
|
||||
assert.NoError(t, repo.LoadOwner(db.DefaultContext))
|
||||
assert.NoError(t, DeleteCollaboration(repo, 4))
|
||||
unittest.AssertNotExistsBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4})
|
||||
|
||||
|
@ -273,8 +273,8 @@ func RetrieveBaseRepo(ctx *Context, repo *repo_model.Repository) {
|
||||
}
|
||||
ctx.ServerError("GetBaseRepo", err)
|
||||
return
|
||||
} else if err = repo.BaseRepo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("BaseRepo.GetOwner", err)
|
||||
} else if err = repo.BaseRepo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("BaseRepo.LoadOwner", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -290,8 +290,8 @@ func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
|
||||
}
|
||||
ctx.ServerError("GetTemplateRepo", err)
|
||||
return
|
||||
} else if err = templateRepo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("TemplateRepo.GetOwner", err)
|
||||
} else if err = templateRepo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("TemplateRepo.LoadOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -356,8 +356,8 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
|
||||
|
||||
func repoAssignment(ctx *Context, repo *repo_model.Repository) {
|
||||
var err error
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("GetOwner", err)
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("LoadOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err
|
||||
if owner, has := cache.Get(repo.OwnerID); has {
|
||||
repo.Owner = owner.(*user_model.User)
|
||||
} else {
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
cache.Add(repo.OwnerID, repo.Owner)
|
||||
|
@ -9,4 +9,5 @@ type Label struct {
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
Description string `json:"description"`
|
||||
Exclusive bool `json:"exclusive"`
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func TestRepository_AddCollaborator(t *testing.T) {
|
||||
|
||||
testSuccess := func(repoID, userID int64) {
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
assert.NoError(t, repo.GetOwner(db.DefaultContext))
|
||||
assert.NoError(t, repo.LoadOwner(db.DefaultContext))
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
|
||||
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
|
||||
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
|
||||
|
@ -335,7 +335,7 @@ func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
|
||||
|
||||
// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
|
||||
func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -379,8 +379,8 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
|
||||
}
|
||||
|
||||
if visibilityChanged {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
return fmt.Errorf("getOwner: %w", err)
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("LoadOwner: %w", err)
|
||||
}
|
||||
if repo.Owner.IsOrganization() {
|
||||
// Organization repository need to recalculate access table when visibility is changed.
|
||||
|
@ -16,7 +16,7 @@ func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, er
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if err := repo.GetOwner(db.DefaultContext); err != nil {
|
||||
if err := repo.LoadOwner(db.DefaultContext); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ package structs
|
||||
type Label struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
// example: false
|
||||
Exclusive bool `json:"exclusive"`
|
||||
// example: 00aabb
|
||||
Color string `json:"color"`
|
||||
Description string `json:"description"`
|
||||
@ -19,6 +21,8 @@ type Label struct {
|
||||
type CreateLabelOption struct {
|
||||
// required:true
|
||||
Name string `json:"name" binding:"Required"`
|
||||
// example: false
|
||||
Exclusive bool `json:"exclusive"`
|
||||
// required:true
|
||||
// example: #00aabb
|
||||
Color string `json:"color" binding:"Required"`
|
||||
@ -27,7 +31,10 @@ type CreateLabelOption struct {
|
||||
|
||||
// EditLabelOption options for editing a label
|
||||
type EditLabelOption struct {
|
||||
Name *string `json:"name"`
|
||||
Name *string `json:"name"`
|
||||
// example: false
|
||||
Exclusive *bool `json:"exclusive"`
|
||||
// example: #00aabb
|
||||
Color *string `json:"color"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
@ -7,10 +7,12 @@ package templates
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"math"
|
||||
"mime"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
@ -382,6 +384,9 @@ func NewFuncMap() []template.FuncMap {
|
||||
// the table is NOT sorted with this header
|
||||
return ""
|
||||
},
|
||||
"RenderLabel": func(label *issues_model.Label) template.HTML {
|
||||
return template.HTML(RenderLabel(label))
|
||||
},
|
||||
"RenderLabels": func(labels []*issues_model.Label, repoLink string) template.HTML {
|
||||
htmlCode := `<span class="labels-list">`
|
||||
for _, label := range labels {
|
||||
@ -389,8 +394,8 @@ func NewFuncMap() []template.FuncMap {
|
||||
if label == nil {
|
||||
continue
|
||||
}
|
||||
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d' class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</a> ",
|
||||
repoLink, label.ID, label.ForegroundColor(), label.Color, html.EscapeString(label.Description), RenderEmoji(label.Name))
|
||||
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ",
|
||||
repoLink, label.ID, RenderLabel(label))
|
||||
}
|
||||
htmlCode += "</span>"
|
||||
return template.HTML(htmlCode)
|
||||
@ -801,6 +806,67 @@ func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[str
|
||||
return template.HTML(renderedText)
|
||||
}
|
||||
|
||||
// RenderLabel renders a label
|
||||
func RenderLabel(label *issues_model.Label) string {
|
||||
labelScope := label.ExclusiveScope()
|
||||
|
||||
textColor := "#111"
|
||||
if label.UseLightTextColor() {
|
||||
textColor = "#eee"
|
||||
}
|
||||
|
||||
description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
|
||||
|
||||
if labelScope == "" {
|
||||
// Regular label
|
||||
return fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</div>",
|
||||
textColor, label.Color, description, RenderEmoji(label.Name))
|
||||
}
|
||||
|
||||
// Scoped label
|
||||
scopeText := RenderEmoji(labelScope)
|
||||
itemText := RenderEmoji(label.Name[len(labelScope)+1:])
|
||||
|
||||
itemColor := label.Color
|
||||
scopeColor := label.Color
|
||||
if r, g, b, err := label.ColorRGB(); err == nil {
|
||||
// Make scope and item background colors slightly darker and lighter respectively.
|
||||
// More contrast needed with higher luminance, empirically tweaked.
|
||||
luminance := (0.299*r + 0.587*g + 0.114*b) / 255
|
||||
contrast := 0.01 + luminance*0.06
|
||||
// Ensure we add the same amount of contrast also near 0 and 1.
|
||||
darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
|
||||
lighten := contrast + math.Max(contrast-luminance, 0.0)
|
||||
// Compute factor to keep RGB values proportional.
|
||||
darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
|
||||
lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
|
||||
|
||||
scopeBytes := []byte{
|
||||
uint8(math.Min(math.Round(r*darkenFactor), 255)),
|
||||
uint8(math.Min(math.Round(g*darkenFactor), 255)),
|
||||
uint8(math.Min(math.Round(b*darkenFactor), 255)),
|
||||
}
|
||||
itemBytes := []byte{
|
||||
uint8(math.Min(math.Round(r*lightenFactor), 255)),
|
||||
uint8(math.Min(math.Round(g*lightenFactor), 255)),
|
||||
uint8(math.Min(math.Round(b*lightenFactor), 255)),
|
||||
}
|
||||
|
||||
itemColor = "#" + hex.EncodeToString(itemBytes)
|
||||
scopeColor = "#" + hex.EncodeToString(scopeBytes)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+
|
||||
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
||||
"<div class='ui label scope-middle' style='background: linear-gradient(-80deg, %s 48%%, %s 52%% 0%%);'> </div>"+
|
||||
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important''>%s</div>"+
|
||||
"</span>",
|
||||
description,
|
||||
textColor, scopeColor, scopeText,
|
||||
itemColor, scopeColor,
|
||||
textColor, itemColor, itemText)
|
||||
}
|
||||
|
||||
// RenderEmoji renders html text with emoji post processors
|
||||
func RenderEmoji(text string) template.HTML {
|
||||
renderedText, err := markup.RenderEmoji(template.HTMLEscapeString(text))
|
||||
|
@ -86,7 +86,7 @@ func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
|
||||
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
|
||||
// already been populated.
|
||||
func LoadGitRepo(t *testing.T, ctx *context.Context) {
|
||||
assert.NoError(t, ctx.Repo.Repository.GetOwner(ctx))
|
||||
assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))
|
||||
var err error
|
||||
ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
|
||||
assert.NoError(t, err)
|
||||
|
@ -1395,9 +1395,12 @@ issues.sign_in_require_desc = <a href="%s">Sign in</a> to join this conversation
|
||||
issues.edit = Edit
|
||||
issues.cancel = Cancel
|
||||
issues.save = Save
|
||||
issues.label_title = Label name
|
||||
issues.label_description = Label description
|
||||
issues.label_color = Label color
|
||||
issues.label_title = Name
|
||||
issues.label_description = Description
|
||||
issues.label_color = Color
|
||||
issues.label_exclusive = Exclusive
|
||||
issues.label_exclusive_desc = Name the label <code>scope/item</code> to make it mutually exclusive with other <code>scope/</code> labels.
|
||||
issues.label_exclusive_warning = Any conflicting scoped labels will be removed when editing the labels of an issue or pull request.
|
||||
issues.label_count = %d labels
|
||||
issues.label_open_issues = %d open issues/pull requests
|
||||
issues.label_edit = Edit
|
||||
@ -2364,7 +2367,7 @@ org_full_name_holder = Organization Full Name
|
||||
org_name_helper = Organization names should be short and memorable.
|
||||
create_org = Create Organization
|
||||
repo_updated = Updated
|
||||
people = People
|
||||
members = Members
|
||||
teams = Teams
|
||||
code = Code
|
||||
lower_members = members
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
container_module "code.gitea.io/gitea/modules/packages/container"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@ -71,11 +72,9 @@ func processManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffe
|
||||
}
|
||||
|
||||
if isImageManifestMediaType(mci.MediaType) {
|
||||
d, err := processImageManifest(mci, buf)
|
||||
return d, err
|
||||
return processImageManifest(mci, buf)
|
||||
} else if isImageIndexMediaType(mci.MediaType) {
|
||||
d, err := processImageManifestIndex(mci, buf)
|
||||
return d, err
|
||||
return processImageManifestIndex(mci, buf)
|
||||
}
|
||||
return "", errManifestInvalid
|
||||
}
|
||||
@ -182,6 +181,10 @@ func processImageManifest(mci *manifestCreationInfo, buf *packages_module.Hashed
|
||||
return err
|
||||
}
|
||||
|
||||
if err := notifyPackageCreate(mci.Creator, pv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestDigest = digest
|
||||
|
||||
return nil
|
||||
@ -271,6 +274,10 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
|
||||
return err
|
||||
}
|
||||
|
||||
if err := notifyPackageCreate(mci.Creator, pv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestDigest = digest
|
||||
|
||||
return nil
|
||||
@ -282,6 +289,17 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
|
||||
return manifestDigest, nil
|
||||
}
|
||||
|
||||
func notifyPackageCreate(doer *user_model.User, pv *packages_model.PackageVersion) error {
|
||||
pd, err := packages_model.GetPackageDescriptor(db.DefaultContext, pv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notification.NotifyPackageCreate(db.DefaultContext, doer, pd)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
|
||||
created := true
|
||||
p := &packages_model.Package{
|
||||
|
@ -94,6 +94,7 @@ func CreateLabel(ctx *context.APIContext) {
|
||||
|
||||
label := &issues_model.Label{
|
||||
Name: form.Name,
|
||||
Exclusive: form.Exclusive,
|
||||
Color: form.Color,
|
||||
OrgID: ctx.Org.Organization.ID,
|
||||
Description: form.Description,
|
||||
@ -195,6 +196,9 @@ func EditLabel(ctx *context.APIContext) {
|
||||
if form.Name != nil {
|
||||
label.Name = *form.Name
|
||||
}
|
||||
if form.Exclusive != nil {
|
||||
label.Exclusive = *form.Exclusive
|
||||
}
|
||||
if form.Color != nil {
|
||||
label.Color = strings.Trim(*form.Color, " ")
|
||||
if len(label.Color) == 6 {
|
||||
|
@ -156,6 +156,7 @@ func CreateLabel(ctx *context.APIContext) {
|
||||
|
||||
label := &issues_model.Label{
|
||||
Name: form.Name,
|
||||
Exclusive: form.Exclusive,
|
||||
Color: form.Color,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Description: form.Description,
|
||||
@ -218,6 +219,9 @@ func EditLabel(ctx *context.APIContext) {
|
||||
if form.Name != nil {
|
||||
label.Name = *form.Name
|
||||
}
|
||||
if form.Exclusive != nil {
|
||||
label.Exclusive = *form.Exclusive
|
||||
}
|
||||
if form.Color != nil {
|
||||
label.Color = strings.Trim(*form.Color, " ")
|
||||
if len(label.Color) == 6 {
|
||||
|
@ -201,7 +201,7 @@ func Search(ctx *context.APIContext) {
|
||||
|
||||
results := make([]*api.Repository, len(repos))
|
||||
for i, repo := range repos {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
|
@ -119,8 +119,8 @@ func ListMyRepos(ctx *context.APIContext) {
|
||||
|
||||
results := make([]*api.Repository, len(repos))
|
||||
for i, repo := range repos {
|
||||
if err = repo.GetOwner(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetOwner", err)
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadOwner", err)
|
||||
return
|
||||
}
|
||||
accessMode, err := access_model.AccessLevel(ctx, ctx.Doer, repo)
|
||||
|
@ -45,6 +45,7 @@ func NewLabel(ctx *context.Context) {
|
||||
l := &issues_model.Label{
|
||||
OrgID: ctx.Org.Organization.ID,
|
||||
Name: form.Title,
|
||||
Exclusive: form.Exclusive,
|
||||
Description: form.Description,
|
||||
Color: form.Color,
|
||||
}
|
||||
@ -70,6 +71,7 @@ func UpdateLabel(ctx *context.Context) {
|
||||
}
|
||||
|
||||
l.Name = form.Title
|
||||
l.Exclusive = form.Exclusive
|
||||
l.Description = form.Description
|
||||
l.Color = form.Color
|
||||
if err := issues_model.UpdateLabel(l); err != nil {
|
||||
|
@ -279,7 +279,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := ci.HeadRepo.GetOwner(ctx); err != nil {
|
||||
if err := ci.HeadRepo.LoadOwner(ctx); err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", nil)
|
||||
} else {
|
||||
|
@ -146,8 +146,8 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
|
||||
|
||||
// don't allow anonymous pulls if organization is not public
|
||||
if isPublicPull {
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("GetOwner", err)
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("LoadOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -332,8 +332,24 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
||||
labels = append(labels, orgLabels...)
|
||||
}
|
||||
|
||||
// Get the exclusive scope for every label ID
|
||||
labelExclusiveScopes := make([]string, 0, len(labelIDs))
|
||||
for _, labelID := range labelIDs {
|
||||
foundExclusiveScope := false
|
||||
for _, label := range labels {
|
||||
if label.ID == labelID || label.ID == -labelID {
|
||||
labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope())
|
||||
foundExclusiveScope = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundExclusiveScope {
|
||||
labelExclusiveScopes = append(labelExclusiveScopes, "")
|
||||
}
|
||||
}
|
||||
|
||||
for _, l := range labels {
|
||||
l.LoadSelectedLabelsAfterClick(labelIDs)
|
||||
l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
|
||||
}
|
||||
ctx.Data["Labels"] = labels
|
||||
ctx.Data["NumLabels"] = len(labels)
|
||||
@ -2157,8 +2173,8 @@ func UpdatePullReviewRequest(ctx *context.Context) {
|
||||
}
|
||||
if reviewID < 0 {
|
||||
// negative reviewIDs represent team requests
|
||||
if err := issue.Repo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("issue.Repo.GetOwner", err)
|
||||
if err := issue.Repo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("issue.Repo.LoadOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ func NewLabel(ctx *context.Context) {
|
||||
l := &issues_model.Label{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: form.Title,
|
||||
Exclusive: form.Exclusive,
|
||||
Description: form.Description,
|
||||
Color: form.Color,
|
||||
}
|
||||
@ -138,6 +139,7 @@ func UpdateLabel(ctx *context.Context) {
|
||||
}
|
||||
|
||||
l.Name = form.Title
|
||||
l.Exclusive = form.Exclusive
|
||||
l.Description = form.Description
|
||||
l.Color = form.Color
|
||||
if err := issues_model.UpdateLabel(l); err != nil {
|
||||
@ -175,7 +177,7 @@ func UpdateIssueLabel(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
case "attach", "detach", "toggle":
|
||||
case "attach", "detach", "toggle", "toggle-alt":
|
||||
label, err := issues_model.GetLabelByID(ctx, ctx.FormInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrRepoLabelNotExist(err) {
|
||||
@ -189,12 +191,18 @@ func UpdateIssueLabel(ctx *context.Context) {
|
||||
if action == "toggle" {
|
||||
// detach if any issues already have label, otherwise attach
|
||||
action = "attach"
|
||||
for _, issue := range issues {
|
||||
if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) {
|
||||
action = "detach"
|
||||
break
|
||||
if label.ExclusiveScope() == "" {
|
||||
for _, issue := range issues {
|
||||
if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) {
|
||||
action = "detach"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if action == "toggle-alt" {
|
||||
// always detach with alt key pressed, to be able to remove
|
||||
// scoped labels
|
||||
action = "detach"
|
||||
}
|
||||
|
||||
if action == "attach" {
|
||||
|
@ -119,8 +119,8 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := forkRepo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("GetOwner", err)
|
||||
if err := forkRepo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("LoadOwner", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1315,8 +1315,8 @@ func CleanUpPullRequest(ctx *context.Context) {
|
||||
} else if err = pr.LoadBaseRepo(ctx); err != nil {
|
||||
ctx.ServerError("LoadBaseRepo", err)
|
||||
return
|
||||
} else if err = pr.HeadRepo.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("HeadRepo.GetOwner", err)
|
||||
} else if err = pr.HeadRepo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("HeadRepo.LoadOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -650,7 +650,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("Convert Fork", err)
|
||||
return
|
||||
}
|
||||
|
@ -1033,8 +1033,8 @@ func Forks(ctx *context.Context) {
|
||||
}
|
||||
|
||||
for _, fork := range forks {
|
||||
if err = fork.GetOwner(ctx); err != nil {
|
||||
ctx.ServerError("GetOwner", err)
|
||||
if err = fork.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("LoadOwner", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
if err := issue.LoadRepo(ctx); err != nil {
|
||||
return &api.Issue{}
|
||||
}
|
||||
if err := issue.Repo.GetOwner(ctx); err != nil {
|
||||
if err := issue.Repo.LoadOwner(ctx); err != nil {
|
||||
return &api.Issue{}
|
||||
}
|
||||
|
||||
@ -182,6 +182,7 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m
|
||||
result := &api.Label{
|
||||
ID: label.ID,
|
||||
Name: label.Name,
|
||||
Exclusive: label.Exclusive,
|
||||
Color: strings.TrimLeft(label.Color, "#"),
|
||||
Description: label.Description,
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc
|
||||
hasProjects = true
|
||||
}
|
||||
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -564,6 +564,7 @@ func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) b
|
||||
type CreateLabelForm struct {
|
||||
ID int64
|
||||
Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
|
||||
Exclusive bool `form:"exclusive"`
|
||||
Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
|
||||
Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"`
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ func assertCommentsEqual(t *testing.T, expected, actual []*base.Comment) {
|
||||
|
||||
func assertLabelEqual(t *testing.T, expected, actual *base.Label) {
|
||||
assert.Equal(t, expected.Name, actual.Name)
|
||||
assert.Equal(t, expected.Exclusive, actual.Exclusive)
|
||||
assert.Equal(t, expected.Color, actual.Color)
|
||||
assert.Equal(t, expected.Description, actual.Description)
|
||||
}
|
||||
|
@ -261,8 +261,8 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool {
|
||||
// When the commit author is unknown set the BaseRepo owner as merger
|
||||
if merger == nil {
|
||||
if pr.BaseRepo.Owner == nil {
|
||||
if err = pr.BaseRepo.GetOwner(ctx); err != nil {
|
||||
log.Error("%-v BaseRepo.GetOwner: %v", pr, err)
|
||||
if err = pr.BaseRepo.LoadOwner(ctx); err != nil {
|
||||
log.Error("%-v BaseRepo.LoadOwner: %v", pr, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -199,8 +199,8 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
|
||||
if err := pr.Issue.LoadRepo(hammerCtx); err != nil {
|
||||
log.Error("LoadRepo for issue [%d]: %v", pr.ID, err)
|
||||
}
|
||||
if err := pr.Issue.Repo.GetOwner(hammerCtx); err != nil {
|
||||
log.Error("GetOwner for PR [%d]: %v", pr.ID, err)
|
||||
if err := pr.Issue.Repo.LoadOwner(hammerCtx); err != nil {
|
||||
log.Error("LoadOwner for PR [%d]: %v", pr.ID, err)
|
||||
}
|
||||
|
||||
if wasAutoMerged {
|
||||
@ -573,7 +573,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode
|
||||
}
|
||||
|
||||
var headUser *user_model.User
|
||||
err = pr.HeadRepo.GetOwner(ctx)
|
||||
err = pr.HeadRepo.LoadOwner(ctx)
|
||||
if err != nil {
|
||||
if !user_model.IsErrUserNotExist(err) {
|
||||
log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err)
|
||||
|
@ -38,12 +38,12 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str
|
||||
return "", &repo_model.ErrRepoNotExist{
|
||||
ID: pr.BaseRepoID,
|
||||
}
|
||||
} else if err := pr.HeadRepo.GetOwner(ctx); err != nil {
|
||||
log.Error("HeadRepo.GetOwner: %v", err)
|
||||
return "", fmt.Errorf("HeadRepo.GetOwner: %w", err)
|
||||
} else if err := pr.BaseRepo.GetOwner(ctx); err != nil {
|
||||
log.Error("BaseRepo.GetOwner: %v", err)
|
||||
return "", fmt.Errorf("BaseRepo.GetOwner: %w", err)
|
||||
} else if err := pr.HeadRepo.LoadOwner(ctx); err != nil {
|
||||
log.Error("HeadRepo.LoadOwner: %v", err)
|
||||
return "", fmt.Errorf("HeadRepo.LoadOwner: %w", err)
|
||||
} else if err := pr.BaseRepo.LoadOwner(ctx); err != nil {
|
||||
log.Error("BaseRepo.LoadOwner: %v", err)
|
||||
return "", fmt.Errorf("BaseRepo.LoadOwner: %w", err)
|
||||
}
|
||||
|
||||
// Clone base repo.
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
// GetReviewerTeams get all teams can be requested to review
|
||||
func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) {
|
||||
if err := repo.GetOwner(db.DefaultContext); err != nil {
|
||||
if err := repo.LoadOwner(db.DefaultContext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !repo.Owner.IsOrganization() {
|
||||
|
@ -31,6 +31,7 @@ func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_m
|
||||
newLabels = append(newLabels, &issues_model.Label{
|
||||
RepoID: generateRepo.ID,
|
||||
Name: templateLabel.Name,
|
||||
Exclusive: templateLabel.Exclusive,
|
||||
Description: templateLabel.Description,
|
||||
Color: templateLabel.Color,
|
||||
})
|
||||
|
@ -26,7 +26,7 @@ var repoWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
// TransferOwnership transfers all corresponding setting from old user to new one.
|
||||
func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
|
||||
if err := repo.GetOwner(ctx); err != nil {
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, team := range teams {
|
||||
|
@ -45,7 +45,7 @@
|
||||
<label for="port">{{.locale.Tr "admin.auths.port"}}</label>
|
||||
<input id="port" name="port" value="{{$cfg.Port}}" placeholder="e.g. 636" required>
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}hide{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if .Source.SkipVerify}}checked{{end}}>
|
||||
@ -152,7 +152,7 @@
|
||||
<input id="use_paged_search" name="use_paged_search" type="checkbox" {{if $cfg.UsePagedSearch}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field required search-page-size{{if not $cfg.UsePagedSearch}} hide{{end}}">
|
||||
<div class="field required search-page-size{{if not $cfg.UsePagedSearch}} gt-hidden{{end}}">
|
||||
<label for="search_page_size">{{.locale.Tr "admin.auths.search_page_size"}}</label>
|
||||
<input id="search_page_size" name="search_page_size" value="{{if $cfg.UsePagedSearch}}{{$cfg.SearchPageSize}}{{end}}">
|
||||
</div>
|
||||
@ -209,7 +209,7 @@
|
||||
</div>
|
||||
<p class="help">{{.locale.Tr "admin.auths.force_smtps_helper"}}</p>
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}hide{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if .Source.SkipVerify}}checked{{end}}>
|
||||
|
@ -36,13 +36,13 @@
|
||||
{{template "admin/auth/source/smtp" .}}
|
||||
|
||||
<!-- PAM -->
|
||||
<div class="pam required field {{if not (eq .type 4)}}hide{{end}}">
|
||||
<div class="pam required field {{if not (eq .type 4)}}gt-hidden{{end}}">
|
||||
<label for="pam_service_name">{{.locale.Tr "admin.auths.pam_service_name"}}</label>
|
||||
<input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" />
|
||||
<label for="pam_email_domain">{{.locale.Tr "admin.auths.pam_email_domain"}}</label>
|
||||
<input id="pam_email_domain" name="pam_email_domain" value="{{.pam_email_domain}}">
|
||||
</div>
|
||||
<div class="pam optional field {{if not (eq .type 4)}}hide{{end}}">
|
||||
<div class="pam optional field {{if not (eq .type 4)}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label for="skip_local_two_fa"><strong>{{.locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
|
||||
<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if .skip_local_two_fa}}checked{{end}}>
|
||||
@ -62,7 +62,7 @@
|
||||
<input name="attributes_in_bind" type="checkbox" {{if .attributes_in_bind}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}">
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{.locale.Tr "admin.auths.syncenabled"}}</strong></label>
|
||||
<input name="is_sync_enabled" type="checkbox" {{if .is_sync_enabled}}checked{{end}}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}hide{{end}}">
|
||||
<div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}gt-hidden{{end}}">
|
||||
<div class="inline required field {{if .Err_SecurityProtocol}}error{{end}}">
|
||||
<label>{{.locale.Tr "admin.auths.security_protocol"}}</label>
|
||||
<div class="ui selection security-protocol dropdown">
|
||||
@ -20,17 +20,17 @@
|
||||
<label for="port">{{.locale.Tr "admin.auths.port"}}</label>
|
||||
<input id="port" name="port" value="{{.port}}" placeholder="e.g. 636">
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}hide{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if .skip_verify}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap field {{if not (eq .type 2)}}hide{{end}}">
|
||||
<div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<label for="bind_dn">{{.locale.Tr "admin.auths.bind_dn"}}</label>
|
||||
<input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com">
|
||||
</div>
|
||||
<div class="ldap field {{if not (eq .type 2)}}hide{{end}}">
|
||||
<div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<label for="bind_password">{{.locale.Tr "admin.auths.bind_password"}}</label>
|
||||
<input id="bind_password" name="bind_password" type="password" autocomplete="off" value="{{.bind_password}}">
|
||||
</div>
|
||||
@ -38,7 +38,7 @@
|
||||
<label for="user_base">{{.locale.Tr "admin.auths.user_base"}}</label>
|
||||
<input id="user_base" name="user_base" value="{{.user_base}}" placeholder="e.g. ou=Users,dc=mydomain,dc=com">
|
||||
</div>
|
||||
<div class="dldap required field {{if not (eq .type 5)}}hide{{end}}">
|
||||
<div class="dldap required field {{if not (eq .type 5)}}gt-hidden{{end}}">
|
||||
<label for="user_dn">{{.locale.Tr "admin.auths.user_dn"}}</label>
|
||||
<input id="user_dn" name="user_dn" value="{{.user_dn}}" placeholder="e.g. uid=%s,ou=Users,dc=mydomain,dc=com">
|
||||
</div>
|
||||
@ -115,13 +115,13 @@
|
||||
</div>
|
||||
<!-- ldap group end -->
|
||||
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}">
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label for="use_paged_search"><strong>{{.locale.Tr "admin.auths.use_paged_search"}}</strong></label>
|
||||
<input id="use_paged_search" name="use_paged_search" class="use-paged-search" type="checkbox" {{if .use_paged_search}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}hide{{end}}">
|
||||
<div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}gt-hidden{{end}}">
|
||||
<label for="search_page_size">{{.locale.Tr "admin.auths.search_page_size"}}</label>
|
||||
<input id="search_page_size" name="search_page_size" value="{{.search_page_size}}">
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="oauth2 field {{if not (eq .type 6)}}hide{{end}}">
|
||||
<div class="oauth2 field {{if not (eq .type 6)}}gt-hidden{{end}}">
|
||||
<div class="inline required field">
|
||||
<label>{{.locale.Tr "admin.auths.oauth2_provider"}}</label>
|
||||
<div class="ui selection type dropdown">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="smtp field {{if not (eq .type 3)}}hide{{end}}">
|
||||
<div class="smtp field {{if not (eq .type 3)}}gt-hidden{{end}}">
|
||||
<div class="inline required field">
|
||||
<label>{{.locale.Tr "admin.auths.smtp_auth"}}</label>
|
||||
<div class="ui selection type dropdown">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="sspi field {{if not (eq .type 7)}}hide{{end}}">
|
||||
<div class="sspi field {{if not (eq .type 7)}}gt-hidden{{end}}">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<label for="sspi_auto_create_users"><strong>{{.locale.Tr "admin.auths.sspi_auto_create_users"}}</strong></label>
|
||||
|
@ -56,7 +56,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}hide{{end}}">
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}gt-hidden{{end}}">
|
||||
<label for="login_name">{{.locale.Tr "admin.users.auth_login_name"}}</label>
|
||||
<input id="login_name" name="login_name" value="{{.User.LoginName}}" autofocus>
|
||||
</div>
|
||||
@ -68,7 +68,7 @@
|
||||
<label for="email">{{.locale.Tr "email"}}</label>
|
||||
<input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required>
|
||||
</div>
|
||||
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}hide{{end}}">
|
||||
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}gt-hidden{{end}}">
|
||||
<label for="password">{{.locale.Tr "password"}}</label>
|
||||
<input id="password" name="password" type="password" autocomplete="new-password">
|
||||
<p class="help">{{.locale.Tr "admin.users.password_helper"}}</p>
|
||||
@ -116,7 +116,7 @@
|
||||
<input name="restricted" type="checkbox" {{if .User.IsRestricted}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline field"{{if DisableGitHooks}} hidden{{end}}>
|
||||
<div class="inline field {{if DisableGitHooks}}gt-hidden{{end}}">
|
||||
<div class="ui checkbox tooltip" data-content="{{.locale.Tr "admin.users.allow_git_hook_tooltip"}}">
|
||||
<label><strong>{{.locale.Tr "admin.users.allow_git_hook"}}</strong></label>
|
||||
<input name="allow_git_hook" type="checkbox" {{if .User.CanEditGitHook}}checked{{end}} {{if DisableGitHooks}}disabled{{end}}>
|
||||
|
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hide" data-db-setting-for="common-host">
|
||||
<div class="gt-hidden" data-db-setting-for="common-host">
|
||||
<div class="inline required field {{if .Err_DbSetting}}error{{end}}">
|
||||
<label for="db_host">{{.locale.Tr "install.host"}}</label>
|
||||
<input id="db_host" name="db_host" value="{{.db_host}}">
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hide" data-db-setting-for="postgres">
|
||||
<div class="gt-hidden" data-db-setting-for="postgres">
|
||||
<div class="inline required field">
|
||||
<label>{{.locale.Tr "install.ssl_mode"}}</label>
|
||||
<div class="ui selection database type dropdown">
|
||||
@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hide" data-db-setting-for="mysql">
|
||||
<div class="gt-hidden" data-db-setting-for="mysql">
|
||||
<div class="inline required field">
|
||||
<label>{{.locale.Tr "install.charset"}}</label>
|
||||
<div class="ui selection database type dropdown">
|
||||
@ -83,7 +83,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hide" data-db-setting-for="sqlite3">
|
||||
<div class="gt-hidden" data-db-setting-for="sqlite3">
|
||||
<div class="inline required field {{if or .Err_DbPath .Err_DbSetting}}error{{end}}">
|
||||
<label for="db_path">{{.locale.Tr "install.path"}}</label>
|
||||
<input id="db_path" name="db_path" value="{{.db_path}}">
|
||||
@ -346,5 +346,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img style="display: none" src="{{AssetUrlPrefix}}/img/loading.png"/>
|
||||
<img class="gt-hidden" src="{{AssetUrlPrefix}}/img/loading.png"/>
|
||||
{{template "base/footer" .}}
|
||||
|
@ -42,7 +42,7 @@
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
<h4 class="ui top attached header gt-df">
|
||||
<strong class="gt-f1">{{.locale.Tr "org.people"}}</strong>
|
||||
<strong class="gt-f1">{{.locale.Tr "org.members"}}</strong>
|
||||
<div class="ui">
|
||||
<a class="text grey gt-dif gt-ac" href="{{.OrgLink}}/members"><span>{{.MembersTotal}}</span> {{svg "octicon-chevron-right"}}</a>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
{{end}}
|
||||
{{if .IsOrganizationMember}}
|
||||
<a class="{{if $.PageIsOrgMembers}}active {{end}}item" href="{{$.OrgLink}}/members">
|
||||
{{svg "octicon-organization"}} {{$.locale.Tr "org.people"}}
|
||||
{{svg "octicon-person"}} {{$.locale.Tr "org.members"}}
|
||||
{{if .NumMembers}}
|
||||
<div class="ui primary label">{{.NumMembers}}</div>
|
||||
{{end}}
|
||||
|
@ -14,8 +14,8 @@
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="required field {{if .Err_Name}}error{{end}}">
|
||||
<label for="org_name">{{.locale.Tr "org.org_name_holder"}}
|
||||
<span class="text red hide" id="org-name-change-prompt"> {{.locale.Tr "org.settings.change_orgname_prompt"}}</span>
|
||||
<span class="text red hide" id="org-name-change-redirect-prompt"> {{.locale.Tr "org.settings.change_orgname_redirect_prompt"}}</span>
|
||||
<span class="text red gt-hidden" id="org-name-change-prompt"> {{.locale.Tr "org.settings.change_orgname_prompt"}}</span>
|
||||
<span class="text red gt-hidden" id="org-name-change-redirect-prompt"> {{.locale.Tr "org.settings.change_orgname_redirect_prompt"}}</span>
|
||||
</label>
|
||||
<input id="org_name" name="name" value="{{.Org.Name}}" data-org-name="{{.Org.Name}}" autofocus required>
|
||||
</div>
|
||||
|
@ -71,7 +71,7 @@
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<div class="team-units required grouped field"{{if eq .Team.AccessMode 3}} style="display: none"{{end}}>
|
||||
<div class="team-units required grouped field {{if eq .Team.AccessMode 3}}gt-hidden{{end}}">
|
||||
<label>{{.locale.Tr "org.team_unit_desc"}}</label>
|
||||
<table class="ui celled table">
|
||||
<thead>
|
||||
|
@ -234,7 +234,7 @@
|
||||
{{if or .Labels .Assignees}}
|
||||
<div class="extra content labels-list gt-p-0 gt-pt-2">
|
||||
{{range .Labels}}
|
||||
<a class="ui label" target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}};" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
|
||||
<a target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{RenderLabel .}}</a>
|
||||
{{end}}
|
||||
<div class="right floated">
|
||||
{{range .Assignees}}
|
||||
|
@ -12,7 +12,7 @@
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.locale.Tr "repo.normal_view"}}</a>
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.locale.Tr "repo.file_history"}}</a>
|
||||
<a class="ui tiny button unescape-button">{{.locale.Tr "repo.unescape_control_characters"}}</a>
|
||||
<a class="ui tiny button escape-button" style="display: none;">{{.locale.Tr "repo.escape_control_characters"}}</a>
|
||||
<a class="ui tiny button escape-button gt-hidden">{{.locale.Tr "repo.escape_control_characters"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</h4>
|
||||
|
@ -27,7 +27,7 @@
|
||||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</button>
|
||||
<div class="data" style="display: none" data-mode="{{if or .root.IsViewTag .isTag}}tags{{else}}branches{{end}}">
|
||||
<div class="data gt-hidden" data-mode="{{if or .root.IsViewTag .isTag}}tags{{else}}branches{{end}}">
|
||||
{{if $showBranchesInDropdown}}
|
||||
{{range .root.Branches}}
|
||||
<div class="item branch {{if eq $defaultBranch .}}selected{{end}}" data-url="{{PathEscapeSegments .}}">{{.}}</div>
|
||||
|
@ -72,7 +72,7 @@
|
||||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body" style="display: none;">{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}</pre>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}</pre>
|
||||
{{end}}
|
||||
</td>
|
||||
{{if .Committer}}
|
||||
|
@ -52,7 +52,7 @@
|
||||
<button class="ui button ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body" style="display: none;">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -73,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="template_units" style="display: none;">
|
||||
<div id="template_units" class="gt-hidden">
|
||||
<div class="inline field">
|
||||
<label>{{.locale.Tr "repo.template.items"}}</label>
|
||||
<div class="ui checkbox">
|
||||
|
@ -67,7 +67,7 @@
|
||||
</script>
|
||||
<div id="diff-file-list"></div>
|
||||
<div id="diff-container">
|
||||
<div id="diff-file-tree" class="hide"></div>
|
||||
<div id="diff-file-tree" class="gt-hidden"></div>
|
||||
<div id="diff-file-boxes" class="sixteen wide column">
|
||||
{{range $i, $file := .Diff.Files}}
|
||||
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
|
||||
@ -116,7 +116,7 @@
|
||||
{{end}}
|
||||
{{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}}
|
||||
<a class="ui basic tiny button unescape-button">{{$.locale.Tr "repo.unescape_control_characters"}}</a>
|
||||
<a class="ui basic tiny button escape-button" style="display: none;">{{$.locale.Tr "repo.escape_control_characters"}}</a>
|
||||
<a class="ui basic tiny button escape-button gt-hidden">{{$.locale.Tr "repo.escape_control_characters"}}</a>
|
||||
{{end}}
|
||||
{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
|
||||
{{if $file.IsDeleted}}
|
||||
@ -136,7 +136,7 @@
|
||||
</div>
|
||||
</h4>
|
||||
<div class="diff-file-body ui attached unstackable table segment" {{if $file.IsViewed}}data-folded="true"{{end}}>
|
||||
<div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}">
|
||||
<div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} gt-hidden{{end}}">
|
||||
{{if or $file.IsIncomplete $file.IsBin}}
|
||||
<div class="diff-file-body binary" style="padding: 5px 10px;">
|
||||
{{if $file.IsIncomplete}}
|
||||
@ -187,7 +187,7 @@
|
||||
</div>
|
||||
|
||||
{{if not $.Repository.IsArchived}}
|
||||
<div class="hide" id="edit-content-form">
|
||||
<div class="gt-hidden" id="edit-content-form">
|
||||
<div class="ui comment form">
|
||||
<div class="ui top attached tabular menu">
|
||||
<a class="active write item">{{$.locale.Tr "write"}}</a>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{if and $.root.SignedUserID (not $.Repository.IsArchived)}}
|
||||
<form class="ui form {{if $.hidden}}hide comment-form comment-form-reply{{end}}" action="{{$.root.Issue.Link}}/files/reviews/comments" method="post">
|
||||
<form class="ui form {{if $.hidden}}gt-hidden comment-form comment-form-reply{{end}}" action="{{$.root.Issue.Link}}/files/reviews/comments" method="post">
|
||||
{{$.root.CsrfTokenHtml}}
|
||||
<input type="hidden" name="origin" value="{{if $.root.PageIsPullFiles}}diff{{else}}timeline{{end}}">
|
||||
<input type="hidden" name="latest_commit_id" value="{{$.root.AfterCommitID}}"/>
|
||||
|
@ -54,8 +54,8 @@
|
||||
<span class="no-content">{{$.root.locale.Tr "repo.issues.no_content"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-context="{{$.root.RepoLink}}"></div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div>
|
||||
<div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-context="{{$.root.RepoLink}}"></div>
|
||||
</div>
|
||||
{{$reactions := .Reactions.GroupByType}}
|
||||
{{if $reactions}}
|
||||
|
@ -88,7 +88,7 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="scrolling menu reference-list-menu base-tag-list" style="display: none">
|
||||
<div class="scrolling menu reference-list-menu base-tag-list gt-hidden">
|
||||
{{range .Tags}}
|
||||
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{end}}{{PathEscapeSegments $.HeadBranch}}">{{$BaseCompareName}}:{{.}}</div>
|
||||
{{end}}
|
||||
@ -157,7 +157,7 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="scrolling menu reference-list-menu head-tag-list" style="display: none">
|
||||
<div class="scrolling menu reference-list-menu head-tag-list gt-hidden">
|
||||
{{range .HeadTags}}
|
||||
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{PathEscapeSegments $.BaseBranch}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{end}}{{PathEscapeSegments .}}">{{$HeadCompareName}}:{{.}}</div>
|
||||
{{end}}
|
||||
@ -184,10 +184,10 @@
|
||||
{{if .IsNothingToCompare}}
|
||||
{{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived)}}
|
||||
<div class="ui segment">{{.locale.Tr "repo.pulls.nothing_to_compare_and_allow_empty_pr"}}</div>
|
||||
<div class="ui info message show-form-container" {{if .Flash}}style="display: none"{{end}}>
|
||||
<div class="ui info message show-form-container {{if .Flash}}gt-hidden{{end}}">
|
||||
<button class="ui button green show-form">{{.locale.Tr "repo.pulls.new"}}</button>
|
||||
</div>
|
||||
<div class="pullrequest-form" {{if not .Flash}}style="display: none"{{end}}>
|
||||
<div class="pullrequest-form {{if not .Flash}}gt-hidden{{end}}">
|
||||
{{template "repo/issue/new_form" .}}
|
||||
</div>
|
||||
{{else}}
|
||||
@ -214,7 +214,7 @@
|
||||
</div>
|
||||
{{else}}
|
||||
{{if and $.IsSigned (not .Repository.IsArchived)}}
|
||||
<div class="ui info message show-form-container" {{if .Flash}}style="display: none"{{end}}>
|
||||
<div class="ui info message show-form-container {{if .Flash}}gt-hidden{{end}}">
|
||||
<button class="ui button green show-form">{{.locale.Tr "repo.pulls.new"}}</button>
|
||||
</div>
|
||||
{{else if .Repository.IsArchived}}
|
||||
@ -223,7 +223,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $.IsSigned}}
|
||||
<div class="pullrequest-form" {{if not .Flash}}style="display: none"{{end}}>
|
||||
<div class="pullrequest-form {{if not .Flash}}gt-hidden{{end}}">
|
||||
{{template "repo/issue/new_form" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -13,14 +13,14 @@
|
||||
{{svg "octicon-unfold" 16 "gt-mr-3"}}
|
||||
{{$.locale.Tr "repo.issues.review.show_resolved"}}
|
||||
</button>
|
||||
<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="hide ui tiny right labeled button hide-outdated gt-df gt-ac">
|
||||
<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button hide-outdated gt-df gt-ac gt-hidden">
|
||||
{{svg "octicon-fold" 16 "gt-mr-3"}}
|
||||
{{$.locale.Tr "repo.issues.review.hide_resolved"}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div id="code-comments-{{(index .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}">
|
||||
<div id="code-comments-{{(index .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}gt-hidden{{end}}">
|
||||
<div class="comment-list">
|
||||
<ui class="ui comments">
|
||||
{{template "repo/diff/comments" dict "root" $ "comments" .comments}}
|
||||
|
@ -11,7 +11,7 @@
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide">
|
||||
<div class="gt-hidden">
|
||||
<div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side-{{.file.Index}}">
|
||||
<div class="diff-side-by-side">
|
||||
{{if .blobBase}}
|
||||
|
@ -36,7 +36,7 @@
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="ui bottom attached active tab segment" data-tab="write">
|
||||
<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
|
||||
<textarea id="edit_area" name="content" class="gt-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
|
||||
data-url="{{.Repository.Link}}/markdown"
|
||||
data-context="{{.RepoLink}}"
|
||||
data-markdown-file-exts="{{.MarkdownFileExts}}"
|
||||
|
@ -25,7 +25,7 @@
|
||||
<a class="active item" data-tab="write">{{svg "octicon-code" 16 "gt-mr-2"}}{{.locale.Tr "repo.editor.new_patch"}}</a>
|
||||
</div>
|
||||
<div class="ui bottom attached active tab segment" data-tab="write">
|
||||
<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-patch"
|
||||
<textarea id="edit_area" name="content" class="gt-hidden" data-id="repo-{{.Repository.Name}}-patch"
|
||||
data-context="{{.RepoLink}}"
|
||||
data-line-wrap-extensions="{{.LineWrapExtensions}}">
|
||||
{{.FileContent}}</textarea>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="repo-find-file-no-result" class="ui row center gt-mt-5" hidden>
|
||||
<div id="repo-find-file-no-result" class="ui row center gt-mt-5 gt-hidden">
|
||||
<h3>{{.locale.Tr "repo.find_file.no_matching"}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<div class="ui dividing"></div>
|
||||
<div class="ui segment loading hide" id="loading-indicator"></div>
|
||||
<div class="ui segment loading gt-hidden" id="loading-indicator"></div>
|
||||
{{template "repo/graph/svgcontainer" .}}
|
||||
{{template "repo/graph/commits" .}}
|
||||
</div>
|
||||
|
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
|
||||
<div class="ui repo-topic-edit grid form" id="topic_edit" style="display:none">
|
||||
<div class="ui repo-topic-edit grid form gt-hidden" id="topic_edit">
|
||||
<div class="fourteen wide column">
|
||||
<div class="field">
|
||||
<div class="ui fluid multiple search selection dropdown">
|
||||
@ -52,7 +52,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="hide" id="validate_prompt">
|
||||
<div class="gt-hidden" id="validate_prompt">
|
||||
<span id="count_prompt">{{.locale.Tr "repo.topic.count_prompt"}}</span>
|
||||
<span id="format_prompt">{{.locale.Tr "repo.topic.format_prompt"}}</span>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}}" style="display: none">
|
||||
<div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}} gt-hidden">
|
||||
{{if .Reference}}
|
||||
<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{.locale.Tr "repo.clear_ref"}}</a></strong></div>
|
||||
{{end}}
|
||||
|
@ -26,31 +26,45 @@
|
||||
<form class="ui edit-label form ignore-dirty" action="{{$.Link}}/edit" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input id="label-modal-id" name="id" type="hidden">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">
|
||||
<div class="ui small input">
|
||||
<input class="new-label-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label for="name">{{.locale.Tr "repo.issues.label_title"}}</label>
|
||||
<div class="ui small input">
|
||||
<input class="label-name-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<div class="ui small fluid input">
|
||||
<input class="new-label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field label-exclusive-input-field">
|
||||
<div class="ui checkbox">
|
||||
<input class="label-exclusive-input" name="exclusive" type="checkbox">
|
||||
<label>{{.locale.Tr "repo.issues.label_exclusive"}}</label>
|
||||
</div>
|
||||
<br/>
|
||||
<small class="desc">{{.locale.Tr "repo.issues.label_exclusive_desc" | Safe}}</small>
|
||||
<div class="desc gt-ml-2 gt-mt-3 gt-hidden label-exclusive-warning">
|
||||
{{svg "octicon-alert"}} {{.locale.Tr "repo.issues.label_exclusive_warning" | Safe}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="description">{{.locale.Tr "repo.issues.label_description"}}</label>
|
||||
<div class="ui small fluid input">
|
||||
<input class="label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field color-field">
|
||||
<label for="color">{{$.locale.Tr "repo.issues.label_color"}}</label>
|
||||
<div class="color picker column">
|
||||
<input class="color-picker" name="color" value="#70c24a" required maxlength="7">
|
||||
</div>
|
||||
<div class="column precolors">
|
||||
{{template "repo/issue/label_precolors"}}
|
||||
<div class="column precolors">
|
||||
{{template "repo/issue/label_precolors"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui negative button">
|
||||
<div class="ui secondary small basic cancel button">
|
||||
{{.locale.Tr "cancel"}}
|
||||
</div>
|
||||
<div class="ui positive button">
|
||||
<div class="ui primary small approve button">
|
||||
{{.locale.Tr "save"}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,7 @@
|
||||
<a
|
||||
class="ui label item {{if not .label.IsChecked}}hide{{end}}"
|
||||
class="item {{if not .label.IsChecked}}gt-hidden{{end}}"
|
||||
id="label_{{.label.ID}}"
|
||||
href="{{.root.RepoLink}}/{{if or .root.IsPull .root.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.label.ID}}"{{/* FIXME: use .root.Issue.Link or create .root.Link */}}
|
||||
style="color: {{.label.ForegroundColor}}; background-color: {{.label.Color}}"
|
||||
title="{{.label.Description | RenderEmojiPlain}}"
|
||||
>
|
||||
{{.label.Name | RenderEmoji}}
|
||||
{{RenderLabel .label}}
|
||||
</a>
|
||||
|
@ -30,28 +30,24 @@
|
||||
{{range .Labels}}
|
||||
<li class="item">
|
||||
<div class="ui grid middle aligned">
|
||||
<div class="nine wide column">
|
||||
{{RenderLabel .}}
|
||||
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}
|
||||
</div>
|
||||
<div class="four wide column">
|
||||
<div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{svg "octicon-tag"}} {{.Name | RenderEmoji}}</div>
|
||||
</div>
|
||||
<div class="six wide column">
|
||||
<div class="ui">
|
||||
{{.Description | RenderEmoji}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
{{if $.PageIsOrgSettingsLabels}}
|
||||
<a class="ui right open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
||||
<a class="ui left open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
||||
{{else}}
|
||||
<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
||||
<a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
{{if and (not $.PageIsOrgSettingsLabels ) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
|
||||
<a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
|
||||
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
|
||||
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
|
||||
{{else if $.PageIsOrgSettingsLabels}}
|
||||
<a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
|
||||
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
|
||||
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@ -73,16 +69,12 @@
|
||||
{{range .OrgLabels}}
|
||||
<li class="item">
|
||||
<div class="ui grid middle aligned">
|
||||
<div class="three wide column">
|
||||
<div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{svg "octicon-tag"}} {{.Name | RenderEmoji}}</div>
|
||||
<div class="nine wide column">
|
||||
{{RenderLabel .}}
|
||||
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}
|
||||
</div>
|
||||
<div class="seven wide column">
|
||||
<div class="ui">
|
||||
{{.Description | RenderEmoji}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a>
|
||||
<div class="four wide column">
|
||||
<a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
</div>
|
||||
|
@ -1,27 +1,47 @@
|
||||
<div class="ui new-label segment hide">
|
||||
<form class="ui form" action="{{$.Link}}/new" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">
|
||||
<div class="ui small new-label modal">
|
||||
<div class="header">
|
||||
{{.locale.Tr "repo.issues.new_label"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<form class="ui new-label form ignore-dirty" action="{{$.Link}}/new" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="required field">
|
||||
<label for="name">{{.locale.Tr "repo.issues.label_title"}}</label>
|
||||
<div class="ui small input">
|
||||
<input class="new-label-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
|
||||
<input class="label-name-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
<div class="field label-exclusive-input-field">
|
||||
<div class="ui checkbox">
|
||||
<input class="label-exclusive-input" name="exclusive" type="checkbox">
|
||||
<label>{{.locale.Tr "repo.issues.label_exclusive"}}</label>
|
||||
</div>
|
||||
<br/>
|
||||
<small class="desc">{{.locale.Tr "repo.issues.label_exclusive_desc" | Safe}}</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="description">{{.locale.Tr "repo.issues.label_description"}}</label>
|
||||
<div class="ui small fluid input">
|
||||
<input class="new-label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
|
||||
<input class="label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<div class="color picker column">
|
||||
<input class="color-picker" name="color" value="#70c24a" required maxlength="7">
|
||||
</div>
|
||||
<div class="column precolors">
|
||||
{{template "repo/issue/label_precolors"}}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="ui secondary small basic cancel button">{{.locale.Tr "repo.milestones.cancel"}}</div>
|
||||
<button class="ui primary small button">{{.locale.Tr "repo.issues.create_label"}}</button>
|
||||
<div class="field color-field">
|
||||
<label for="color">{{$.locale.Tr "repo.issues.label_color"}}</label>
|
||||
<div class="color picker column">
|
||||
<input class="color-picker" name="color" value="#70c24a" required maxlength="7">
|
||||
<div class="column precolors">
|
||||
{{template "repo/issue/label_precolors"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui secondary small basic cancel button">
|
||||
{{.locale.Tr "cancel"}}
|
||||
</div>
|
||||
</form>
|
||||
<div class="ui primary small approve button">
|
||||
{{.locale.Tr "repo.issues.create_label"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="ui labels list">
|
||||
<span class="no-select item {{if .ctx.HasSelectedLabel}}hide{{end}}">{{.ctx.locale.Tr "repo.issues.new.no_label"}}</span>
|
||||
<span class="no-select item {{if .ctx.HasSelectedLabel}}gt-hidden{{end}}">{{.ctx.locale.Tr "repo.issues.new.no_label"}}</span>
|
||||
<span class="labels-list">
|
||||
{{range .ctx.Labels}}
|
||||
{{template "repo/issue/labels/label" dict "root" $.root "label" .}}
|
||||
|
@ -50,8 +50,14 @@
|
||||
</div>
|
||||
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
|
||||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||
{{$previousExclusiveScope := "_no_scope"}}
|
||||
{{range .Labels}}
|
||||
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
|
||||
{{$exclusiveScope := .ExclusiveScope}}
|
||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
{{$previousExclusiveScope = $exclusiveScope}}
|
||||
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@ -193,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="issue-actions" class="ui stackable grid hide">
|
||||
<div id="issue-actions" class="ui stackable grid gt-hidden">
|
||||
<div class="six wide column">
|
||||
{{template "repo/issue/openclose" .}}
|
||||
</div>
|
||||
@ -217,9 +223,15 @@
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</span>
|
||||
<div class="menu">
|
||||
{{$previousExclusiveScope := "_no_scope"}}
|
||||
{{range .Labels}}
|
||||
{{$exclusiveScope := .ExclusiveScope}}
|
||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
{{$previousExclusiveScope = $exclusiveScope}}
|
||||
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
||||
{{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
|
||||
{{if contain $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -58,7 +58,7 @@
|
||||
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
|
||||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||
{{range .Labels}}
|
||||
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
|
||||
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{RenderLabel .}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@ -135,7 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="issue-actions" class="ui stackable grid hide">
|
||||
<div id="issue-actions" class="ui stackable grid gt-hidden">
|
||||
<div class="six wide column">
|
||||
{{template "repo/issue/openclose" .}}
|
||||
</div>
|
||||
@ -161,7 +161,7 @@
|
||||
<div class="menu">
|
||||
{{range .Labels}}
|
||||
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
||||
{{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
|
||||
{{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{RenderLabel .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -53,14 +53,26 @@
|
||||
{{end}}
|
||||
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div>
|
||||
{{if or .Labels .OrgLabels}}
|
||||
{{$previousExclusiveScope := "_no_scope"}}
|
||||
{{range .Labels}}
|
||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
|
||||
{{$exclusiveScope := .ExclusiveScope}}
|
||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
{{$previousExclusiveScope = $exclusiveScope}}
|
||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}}
|
||||
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a>
|
||||
{{end}}
|
||||
|
||||
<div class="ui divider"></div>
|
||||
{{$previousExclusiveScope := "_no_scope"}}
|
||||
{{range .OrgLabels}}
|
||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
|
||||
{{$exclusiveScope := .ExclusiveScope}}
|
||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
{{$previousExclusiveScope = $exclusiveScope}}
|
||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}}
|
||||
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a>
|
||||
{{end}}
|
||||
{{else}}
|
||||
@ -122,7 +134,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui select-milestone list">
|
||||
<span class="no-select item {{if .Milestone}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span>
|
||||
<span class="no-select item {{if .Milestone}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span>
|
||||
<div class="selected">
|
||||
{{if .Milestone}}
|
||||
<a class="item muted sidebar-item-link" href="{{.RepoLink}}/issues?milestone={{.Milestone.ID}}">
|
||||
@ -186,7 +198,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui select-project list">
|
||||
<span class="no-select item {{if .Project}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span>
|
||||
<span class="no-select item {{if .Project}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span>
|
||||
<div class="selected">
|
||||
{{if .Project}}
|
||||
<a class="item muted sidebar-item-link" href="{{.RepoLink}}/projects/{{.Project.ID}}">
|
||||
@ -224,11 +236,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui assignees list">
|
||||
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">
|
||||
<span class="no-select item {{if .HasSelectedLabel}}gt-hidden{{end}}">
|
||||
{{.locale.Tr "repo.issues.new.no_assignees"}}
|
||||
</span>
|
||||
{{range .Assignees}}
|
||||
<a class="hide item gt-p-2 muted" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
|
||||
<a class="item gt-p-2 muted gt-hidden" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
|
||||
{{avatar $.Context . 28 "gt-mr-3 gt-vm"}}{{.GetDisplayName}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -77,8 +77,8 @@
|
||||
<span class="no-content">{{.locale.Tr "repo.issues.no_content"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="issue-{{.Issue.ID}}-raw" class="raw-content hide">{{.Issue.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
|
||||
<div id="issue-{{.Issue.ID}}-raw" class="raw-content gt-hidden">{{.Issue.Content}}</div>
|
||||
<div class="edit-content-zone gt-hidden" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
|
||||
{{if .Issue.Attachments}}
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments "Content" .Issue.RenderedContent}}
|
||||
{{end}}
|
||||
@ -194,7 +194,7 @@
|
||||
{{template "repo/issue/view_content/sidebar" .}}
|
||||
</div>
|
||||
|
||||
<div class="hide" id="edit-content-form">
|
||||
<div class="gt-hidden" id="edit-content-form">
|
||||
<div class="ui comment form">
|
||||
<div class="ui top tabular menu">
|
||||
<a class="active write item">{{$.locale.Tr "write"}}</a>
|
||||
@ -224,7 +224,7 @@
|
||||
|
||||
{{template "repo/issue/view_content/reference_issue_dialog" .}}
|
||||
|
||||
<div class="hide" id="no-content">
|
||||
<div class="gt-hidden" id="no-content">
|
||||
<span class="no-content">{{.locale.Tr "repo.issues.no_content"}}</span>
|
||||
</div>
|
||||
|
||||
|
@ -77,8 +77,8 @@
|
||||
<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div>
|
||||
<div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
{{if .Attachments}}
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments "Content" .RenderedContent}}
|
||||
{{end}}
|
||||
@ -449,8 +449,8 @@
|
||||
<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div>
|
||||
<div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
{{if .Attachments}}
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments "Content" .RenderedContent}}
|
||||
{{end}}
|
||||
@ -576,8 +576,8 @@
|
||||
<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
<div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div>
|
||||
<div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
</div>
|
||||
{{$reactions := .Reactions.GroupByType}}
|
||||
{{if $reactions}}
|
||||
|
@ -456,7 +456,7 @@
|
||||
|
||||
{{if $.StillCanManualMerge}}
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui form manually-merged-fields" style="display: none">
|
||||
<div class="ui form manually-merged-fields gt-hidden">
|
||||
<form action="{{.Link}}/merge" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="field">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="ui divider"></div>
|
||||
<div class="instruct-toggle"> {{$.locale.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div>
|
||||
<div class="instruct-content gt-mt-3" style="display:none">
|
||||
<div class="instruct-content gt-mt-3 gt-hidden">
|
||||
<div><h3 class="gt-di">{{$.locale.Tr "step1"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step1_desc"}}</div>
|
||||
<div class="ui secondary segment">
|
||||
{{if eq $.Issue.PullRequest.Flow 0}}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user