From 95f2e2b57beedcdeb2b9623dc86e26f252fdd7bd Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 9 May 2018 18:29:04 +0200 Subject: [PATCH] Multiple assignees (#3705) --- models/error.go | 16 ++ models/fixtures/issue.yml | 2 - models/fixtures/issue_assignees.yml | 8 + models/fixtures/issue_user.yml | 3 - models/issue.go | 159 ++++------- models/issue_assignees.go | 263 ++++++++++++++++++ models/issue_assignees_test.go | 71 +++++ models/issue_comment.go | 112 ++++---- models/issue_list.go | 46 +-- models/issue_mail.go | 13 +- models/issue_user.go | 52 ++-- models/issue_user_test.go | 17 -- models/migrations/migrations.go | 13 +- models/migrations/v56.go | 12 +- models/migrations/v64.go | 129 +++++++++ models/models.go | 1 + models/pull.go | 7 +- models/repo.go | 6 +- models/user.go | 6 +- models/webhook_dingtalk.go | 6 +- models/webhook_discord.go | 6 +- models/webhook_slack.go | 6 +- modules/auth/repo_form.go | 1 + modules/context/repo.go | 3 +- options/locale/locale_en-US.ini | 6 +- public/css/index.css | 2 +- public/js/index.js | 154 ++++++---- public/less/_repository.less | 9 +- routers/api/v1/repo/issue.go | 65 +++-- routers/api/v1/repo/pull.go | 73 +++-- routers/repo/issue.go | 70 +++-- routers/repo/pull.go | 10 +- templates/repo/issue/list.tmpl | 8 +- templates/repo/issue/new_form.tmpl | 41 ++- .../repo/issue/view_content/comments.tmpl | 32 ++- .../repo/issue/view_content/sidebar.tmpl | 35 ++- 36 files changed, 1012 insertions(+), 451 deletions(-) create mode 100644 models/fixtures/issue_assignees.yml create mode 100644 models/issue_assignees.go create mode 100644 models/issue_assignees_test.go create mode 100644 models/migrations/v64.go diff --git a/models/error.go b/models/error.go index d24da2642..cdb18d23c 100644 --- a/models/error.go +++ b/models/error.go @@ -733,6 +733,22 @@ func (err ErrRepoFileAlreadyExist) Error() string { return fmt.Sprintf("repository file already exists [file_name: %s]", err.FileName) } +// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo +type ErrUserDoesNotHaveAccessToRepo struct { + UserID int64 + RepoName string +} + +// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExist. +func IsErrUserDoesNotHaveAccessToRepo(err error) bool { + _, ok := err.(ErrUserDoesNotHaveAccessToRepo) + return ok +} + +func (err ErrUserDoesNotHaveAccessToRepo) Error() string { + return fmt.Sprintf("user doesn't have acces to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName) +} + // __________ .__ // \______ \____________ ____ ____ | |__ // | | _/\_ __ \__ \ / \_/ ___\| | \ diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml index ff514e706..4de8c4fa7 100644 --- a/models/fixtures/issue.yml +++ b/models/fixtures/issue.yml @@ -3,7 +3,6 @@ repo_id: 1 index: 1 poster_id: 1 - assignee_id: 1 name: issue1 content: content for the first issue is_closed: false @@ -67,7 +66,6 @@ repo_id: 3 index: 1 poster_id: 1 - assignee_id: 1 name: issue6 content: content6 is_closed: false diff --git a/models/fixtures/issue_assignees.yml b/models/fixtures/issue_assignees.yml new file mode 100644 index 000000000..1be07df7d --- /dev/null +++ b/models/fixtures/issue_assignees.yml @@ -0,0 +1,8 @@ +- + id: 1 + assignee_id: 1 + issue_id: 1 +- + id: 2 + assignee_id: 1 + issue_id: 6 diff --git a/models/fixtures/issue_user.yml b/models/fixtures/issue_user.yml index b3f98a71d..8039b1e40 100644 --- a/models/fixtures/issue_user.yml +++ b/models/fixtures/issue_user.yml @@ -3,7 +3,6 @@ uid: 1 issue_id: 1 is_read: true - is_assigned: true is_mentioned: false - @@ -11,7 +10,6 @@ uid: 2 issue_id: 1 is_read: true - is_assigned: false is_mentioned: false - @@ -19,5 +17,4 @@ uid: 4 issue_id: 1 is_read: false - is_assigned: false is_mentioned: false diff --git a/models/issue.go b/models/issue.go index 7f83d5984..ad354cc34 100644 --- a/models/issue.go +++ b/models/issue.go @@ -37,7 +37,7 @@ type Issue struct { MilestoneID int64 `xorm:"INDEX"` Milestone *Milestone `xorm:"-"` Priority int - AssigneeID int64 `xorm:"INDEX"` + AssigneeID int64 `xorm:"-"` Assignee *User `xorm:"-"` IsClosed bool `xorm:"INDEX"` IsRead bool `xorm:"-"` @@ -56,6 +56,7 @@ type Issue struct { Comments []*Comment `xorm:"-"` Reactions ReactionList `xorm:"-"` TotalTrackedTime int64 `xorm:"-"` + Assignees []*User `xorm:"-"` } var ( @@ -140,22 +141,6 @@ func (issue *Issue) loadPoster(e Engine) (err error) { return } -func (issue *Issue) loadAssignee(e Engine) (err error) { - if issue.Assignee == nil && issue.AssigneeID > 0 { - issue.Assignee, err = getUserByID(e, issue.AssigneeID) - if err != nil { - issue.AssigneeID = -1 - issue.Assignee = NewGhostUser() - if !IsErrUserNotExist(err) { - return fmt.Errorf("getUserByID.(assignee) [%d]: %v", issue.AssigneeID, err) - } - err = nil - return - } - } - return -} - func (issue *Issue) loadPullRequest(e Engine) (err error) { if issue.IsPull && issue.PullRequest == nil { issue.PullRequest, err = getPullRequestByIssueID(e, issue.ID) @@ -231,7 +216,7 @@ func (issue *Issue) loadAttributes(e Engine) (err error) { } } - if err = issue.loadAssignee(e); err != nil { + if err = issue.loadAssignees(e); err != nil { return } @@ -343,8 +328,11 @@ func (issue *Issue) APIFormat() *api.Issue { if issue.Milestone != nil { apiIssue.Milestone = issue.Milestone.APIFormat() } - if issue.Assignee != nil { - apiIssue.Assignee = issue.Assignee.APIFormat() + if len(issue.Assignees) > 0 { + for _, assignee := range issue.Assignees { + apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat()) + } + apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee` } if issue.IsPull { apiIssue.PullRequest = &api.PullRequestMeta{ @@ -605,19 +593,6 @@ func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) { return sess.Commit() } -// GetAssignee sets the Assignee attribute of this issue. -func (issue *Issue) GetAssignee() (err error) { - if issue.AssigneeID == 0 || issue.Assignee != nil { - return nil - } - - issue.Assignee, err = GetUserByID(issue.AssigneeID) - if IsErrUserNotExist(err) { - return nil - } - return err -} - // ReadBy sets issue to be read by given user. func (issue *Issue) ReadBy(userID int64) error { if err := UpdateIssueUserByRead(userID, issue.ID); err != nil { @@ -823,55 +798,6 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) { return nil } -// ChangeAssignee changes the Assignee field of this issue. -func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) { - var oldAssigneeID = issue.AssigneeID - issue.AssigneeID = assigneeID - if err = UpdateIssueUserByAssignee(issue); err != nil { - return fmt.Errorf("UpdateIssueUserByAssignee: %v", err) - } - - sess := x.NewSession() - defer sess.Close() - - if err = issue.loadRepo(sess); err != nil { - return fmt.Errorf("loadRepo: %v", err) - } - - if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, oldAssigneeID, assigneeID); err != nil { - return fmt.Errorf("createAssigneeComment: %v", err) - } - - issue.Assignee, err = GetUserByID(issue.AssigneeID) - if err != nil && !IsErrUserNotExist(err) { - log.Error(4, "GetUserByID [assignee_id: %v]: %v", issue.AssigneeID, err) - return nil - } - - // Error not nil here means user does not exist, which is remove assignee. - isRemoveAssignee := err != nil - if issue.IsPull { - issue.PullRequest.Issue = issue - apiPullRequest := &api.PullRequestPayload{ - Index: issue.Index, - PullRequest: issue.PullRequest.APIFormat(), - Repository: issue.Repo.APIFormat(AccessModeNone), - Sender: doer.APIFormat(), - } - if isRemoveAssignee { - apiPullRequest.Action = api.HookIssueUnassigned - } else { - apiPullRequest.Action = api.HookIssueAssigned - } - if err := PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest); err != nil { - log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err) - return nil - } - } - go HookQueue.Add(issue.RepoID) - return nil -} - // GetTasks returns the amount of tasks in the issues content func (issue *Issue) GetTasks() int { return len(issueTasksPat.FindAllStringIndex(issue.Content, -1)) @@ -887,6 +813,7 @@ type NewIssueOptions struct { Repo *Repository Issue *Issue LabelIDs []int64 + AssigneeIDs []int64 Attachments []string // In UUID format. IsPull bool } @@ -909,14 +836,32 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { } } - if assigneeID := opts.Issue.AssigneeID; assigneeID > 0 { - valid, err := hasAccess(e, assigneeID, opts.Repo, AccessModeWrite) - if err != nil { - return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err) + // Keep the old assignee id thingy for compatibility reasons + if opts.Issue.AssigneeID > 0 { + isAdded := false + // Check if the user has already been passed to issue.AssigneeIDs, if not, add it + for _, aID := range opts.AssigneeIDs { + if aID == opts.Issue.AssigneeID { + isAdded = true + break + } } - if !valid { - opts.Issue.AssigneeID = 0 - opts.Issue.Assignee = nil + + if !isAdded { + opts.AssigneeIDs = append(opts.AssigneeIDs, opts.Issue.AssigneeID) + } + } + + // Check for and validate assignees + if len(opts.AssigneeIDs) > 0 { + for _, assigneeID := range opts.AssigneeIDs { + valid, err := hasAccess(e, assigneeID, opts.Repo, AccessModeWrite) + if err != nil { + return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err) + } + if !valid { + return ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: opts.Repo.Name} + } } } @@ -931,11 +876,10 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { } } - if opts.Issue.AssigneeID > 0 { - if err = opts.Issue.loadRepo(e); err != nil { - return err - } - if _, err = createAssigneeComment(e, doer, opts.Issue.Repo, opts.Issue, -1, opts.Issue.AssigneeID); err != nil { + // Insert the assignees + for _, assigneeID := range opts.AssigneeIDs { + err = opts.Issue.changeAssignee(e, doer, assigneeID) + if err != nil { return err } } @@ -995,7 +939,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { } // NewIssue creates new issue with labels for repository. -func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { +func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { @@ -1007,7 +951,11 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) Issue: issue, LabelIDs: labelIDs, Attachments: uuids, + AssigneeIDs: assigneeIDs, }); err != nil { + if IsErrUserDoesNotHaveAccessToRepo(err) { + return err + } return fmt.Errorf("newIssue: %v", err) } @@ -1150,7 +1098,8 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error { } if opts.AssigneeID > 0 { - sess.And("issue.assignee_id=?", opts.AssigneeID) + sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", opts.AssigneeID) } if opts.PosterID > 0 { @@ -1372,7 +1321,8 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { } if opts.AssigneeID > 0 { - sess.And("issue.assignee_id = ?", opts.AssigneeID) + sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", opts.AssigneeID) } if opts.PosterID > 0 { @@ -1438,13 +1388,15 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) { } case FilterModeAssign: stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false). - And("assignee_id = ?", opts.UserID). + Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", opts.UserID). Count(new(Issue)) if err != nil { return nil, err } stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true). - And("assignee_id = ?", opts.UserID). + Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", opts.UserID). Count(new(Issue)) if err != nil { return nil, err @@ -1466,7 +1418,8 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) { cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed}) stats.AssignCount, err = x.Where(cond). - And("assignee_id = ?", opts.UserID). + Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", opts.UserID). Count(new(Issue)) if err != nil { return nil, err @@ -1505,8 +1458,10 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen switch filterMode { case FilterModeAssign: - openCountSession.And("assignee_id = ?", uid) - closedCountSession.And("assignee_id = ?", uid) + openCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", uid) + closedCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", uid) case FilterModeCreate: openCountSession.And("poster_id = ?", uid) closedCountSession.And("poster_id = ?", uid) diff --git a/models/issue_assignees.go b/models/issue_assignees.go new file mode 100644 index 000000000..3e68126a6 --- /dev/null +++ b/models/issue_assignees.go @@ -0,0 +1,263 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "fmt" + + "code.gitea.io/gitea/modules/log" + + api "code.gitea.io/sdk/gitea" + "github.com/go-xorm/xorm" +) + +// IssueAssignees saves all issue assignees +type IssueAssignees struct { + ID int64 `xorm:"pk autoincr"` + AssigneeID int64 `xorm:"INDEX"` + IssueID int64 `xorm:"INDEX"` +} + +// This loads all assignees of an issue +func (issue *Issue) loadAssignees(e Engine) (err error) { + // Reset maybe preexisting assignees + issue.Assignees = []*User{} + + err = e.Table("`user`"). + Join("INNER", "issue_assignees", "assignee_id = `user`.id"). + Where("issue_assignees.issue_id = ?", issue.ID). + Find(&issue.Assignees) + + if err != nil { + return err + } + + // Check if we have at least one assignee and if yes put it in as `Assignee` + if len(issue.Assignees) > 0 { + issue.Assignee = issue.Assignees[0] + } + + return +} + +// GetAssigneesByIssue returns everyone assigned to that issue +func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) { + err = issue.loadAssignees(x) + if err != nil { + return assignees, err + } + + return issue.Assignees, nil +} + +// IsUserAssignedToIssue returns true when the user is assigned to the issue +func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) { + isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID}) + return +} + +// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array +func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err error) { + var found bool + + for _, assignee := range issue.Assignees { + + found = false + for _, alreadyAssignee := range assignees { + if assignee.ID == alreadyAssignee.ID { + found = true + break + } + } + + if !found { + // This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here + if err := UpdateAssignee(issue, doer, assignee.ID); err != nil { + return err + } + } + } + + return nil +} + +// MakeAssigneeList concats a string with all names of the assignees. Useful for logs. +func MakeAssigneeList(issue *Issue) (assigneeList string, err error) { + err = issue.loadAssignees(x) + if err != nil { + return "", err + } + + for in, assignee := range issue.Assignees { + assigneeList += assignee.Name + + if len(issue.Assignees) > (in + 1) { + assigneeList += ", " + } + } + return +} + +// ClearAssigneeByUserID deletes all assignments of an user +func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) { + _, err = sess.Delete(&IssueAssignees{AssigneeID: userID}) + return +} + +// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue +func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) { + // Check if the user is already assigned + isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID}) + if err != nil { + return err + } + + if !isAssigned { + return issue.ChangeAssignee(doer, assigneeID) + } + return nil +} + +// UpdateAssignee deletes or adds an assignee to an issue +func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) { + return issue.ChangeAssignee(doer, assigneeID) +} + +// ChangeAssignee changes the Assignee of this issue. +func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) { + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + if err := issue.changeAssignee(sess, doer, assigneeID); err != nil { + return err + } + + return sess.Commit() +} + +func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64) (err error) { + + // Update the assignee + removed, err := updateIssueAssignee(sess, issue, assigneeID) + if err != nil { + return fmt.Errorf("UpdateIssueUserByAssignee: %v", err) + } + + // Repo infos + if err = issue.loadRepo(sess); err != nil { + return fmt.Errorf("loadRepo: %v", err) + } + + // Comment + if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil { + return fmt.Errorf("createAssigneeComment: %v", err) + } + + if issue.IsPull { + issue.PullRequest = &PullRequest{Issue: issue} + apiPullRequest := &api.PullRequestPayload{ + Index: issue.Index, + PullRequest: issue.PullRequest.APIFormat(), + Repository: issue.Repo.APIFormat(AccessModeNone), + Sender: doer.APIFormat(), + } + if removed { + apiPullRequest.Action = api.HookIssueUnassigned + } else { + apiPullRequest.Action = api.HookIssueAssigned + } + if err := PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest); err != nil { + log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) + return nil + } + } + go HookQueue.Add(issue.RepoID) + return nil +} + +// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s) +// Deleting is done the Github way (quote from their api documentation): +// https://developer.github.com/v3/issues/#edit-an-issue +// "assignees" (array): Logins for Users to assign to this issue. +// Pass one or more user logins to replace the set of assignees on this Issue. +// Send an empty array ([]) to clear all assignees from the Issue. +func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) { + var allNewAssignees []*User + + // Keep the old assignee thingy for compatibility reasons + if oneAssignee != "" { + // Prevent double adding assignees + var isDouble bool + for _, assignee := range multipleAssignees { + if assignee == oneAssignee { + isDouble = true + break + } + } + + if !isDouble { + multipleAssignees = append(multipleAssignees, oneAssignee) + } + } + + // Loop through all assignees to add them + for _, assigneeName := range multipleAssignees { + assignee, err := GetUserByName(assigneeName) + if err != nil { + return err + } + + allNewAssignees = append(allNewAssignees, assignee) + } + + // Delete all old assignees not passed + if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil { + return err + } + + // Add all new assignees + // Update the assignee. The function will check if the user exists, is already + // assigned (which he shouldn't as we deleted all assignees before) and + // has access to the repo. + for _, assignee := range allNewAssignees { + // Extra method to prevent double adding (which would result in removing) + err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID) + if err != nil { + return err + } + } + + return +} + +// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs +func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) { + + // Keeping the old assigning method for compatibility reasons + if oneAssignee != "" { + + // Prevent double adding assignees + var isDouble bool + for _, assignee := range multipleAssignees { + if assignee == oneAssignee { + isDouble = true + break + } + } + + if !isDouble { + multipleAssignees = append(multipleAssignees, oneAssignee) + } + } + + // Get the IDs of all assignees + assigneeIDs = GetUserIDsByNames(multipleAssignees) + + return +} diff --git a/models/issue_assignees_test.go b/models/issue_assignees_test.go new file mode 100644 index 000000000..324781219 --- /dev/null +++ b/models/issue_assignees_test.go @@ -0,0 +1,71 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUpdateAssignee(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + // Fake issue with assignees + issue, err := GetIssueByID(1) + assert.NoError(t, err) + + // Assign multiple users + user2, err := GetUserByID(2) + assert.NoError(t, err) + err = UpdateAssignee(issue, &User{ID: 1}, user2.ID) + assert.NoError(t, err) + + user3, err := GetUserByID(3) + assert.NoError(t, err) + err = UpdateAssignee(issue, &User{ID: 1}, user3.ID) + assert.NoError(t, err) + + user1, err := GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him + assert.NoError(t, err) + err = UpdateAssignee(issue, &User{ID: 1}, user1.ID) + assert.NoError(t, err) + + // Check if he got removed + isAssigned, err := IsUserAssignedToIssue(issue, user1) + assert.NoError(t, err) + assert.False(t, isAssigned) + + // Check if they're all there + assignees, err := GetAssigneesByIssue(issue) + assert.NoError(t, err) + + var expectedAssignees []*User + expectedAssignees = append(expectedAssignees, user2) + expectedAssignees = append(expectedAssignees, user3) + + for in, assignee := range assignees { + assert.Equal(t, assignee.ID, expectedAssignees[in].ID) + } + + // Check if the user is assigned + isAssigned, err = IsUserAssignedToIssue(issue, user2) + assert.NoError(t, err) + assert.True(t, isAssigned) + + // This user should not be assigned + isAssigned, err = IsUserAssignedToIssue(issue, &User{ID: 4}) + assert.NoError(t, err) + assert.False(t, isAssigned) + + // Clean everyone + err = DeleteNotPassedAssignee(issue, user1, []*User{}) + assert.NoError(t, err) + + // Check they're gone + assignees, err = GetAssigneesByIssue(issue) + assert.NoError(t, err) + assert.Equal(t, 0, len(assignees)) +} diff --git a/models/issue_comment.go b/models/issue_comment.go index aedb12486..2c5875c29 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -81,23 +81,22 @@ const ( // Comment represents a comment in commit and issue page. type Comment struct { - ID int64 `xorm:"pk autoincr"` - Type CommentType - PosterID int64 `xorm:"INDEX"` - Poster *User `xorm:"-"` - IssueID int64 `xorm:"INDEX"` - LabelID int64 - Label *Label `xorm:"-"` - OldMilestoneID int64 - MilestoneID int64 - OldMilestone *Milestone `xorm:"-"` - Milestone *Milestone `xorm:"-"` - OldAssigneeID int64 - AssigneeID int64 - Assignee *User `xorm:"-"` - OldAssignee *User `xorm:"-"` - OldTitle string - NewTitle string + ID int64 `xorm:"pk autoincr"` + Type CommentType + PosterID int64 `xorm:"INDEX"` + Poster *User `xorm:"-"` + IssueID int64 `xorm:"INDEX"` + LabelID int64 + Label *Label `xorm:"-"` + OldMilestoneID int64 + MilestoneID int64 + OldMilestone *Milestone `xorm:"-"` + Milestone *Milestone `xorm:"-"` + AssigneeID int64 + RemovedAssignee bool + Assignee *User `xorm:"-"` + OldTitle string + NewTitle string CommitID int64 Line int64 @@ -247,18 +246,9 @@ func (c *Comment) LoadMilestone() error { return nil } -// LoadAssignees if comment.Type is CommentTypeAssignees, then load assignees -func (c *Comment) LoadAssignees() error { +// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees +func (c *Comment) LoadAssigneeUser() error { var err error - if c.OldAssigneeID > 0 { - c.OldAssignee, err = getUserByID(x, c.OldAssigneeID) - if err != nil { - if !IsErrUserNotExist(err) { - return err - } - c.OldAssignee = NewGhostUser() - } - } if c.AssigneeID > 0 { c.Assignee, err = getUserByID(x, c.AssigneeID) @@ -324,21 +314,21 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err LabelID = opts.Label.ID } comment := &Comment{ - Type: opts.Type, - PosterID: opts.Doer.ID, - Poster: opts.Doer, - IssueID: opts.Issue.ID, - LabelID: LabelID, - OldMilestoneID: opts.OldMilestoneID, - MilestoneID: opts.MilestoneID, - OldAssigneeID: opts.OldAssigneeID, - AssigneeID: opts.AssigneeID, - CommitID: opts.CommitID, - CommitSHA: opts.CommitSHA, - Line: opts.LineNum, - Content: opts.Content, - OldTitle: opts.OldTitle, - NewTitle: opts.NewTitle, + Type: opts.Type, + PosterID: opts.Doer.ID, + Poster: opts.Doer, + IssueID: opts.Issue.ID, + LabelID: LabelID, + OldMilestoneID: opts.OldMilestoneID, + MilestoneID: opts.MilestoneID, + RemovedAssignee: opts.RemovedAssignee, + AssigneeID: opts.AssigneeID, + CommitID: opts.CommitID, + CommitSHA: opts.CommitSHA, + Line: opts.LineNum, + Content: opts.Content, + OldTitle: opts.OldTitle, + NewTitle: opts.NewTitle, } if _, err = e.Insert(comment); err != nil { return nil, err @@ -480,14 +470,14 @@ func createMilestoneComment(e *xorm.Session, doer *User, repo *Repository, issue }) } -func createAssigneeComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, oldAssigneeID, assigneeID int64) (*Comment, error) { +func createAssigneeComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, assigneeID int64, removedAssignee bool) (*Comment, error) { return createComment(e, &CreateCommentOptions{ - Type: CommentTypeAssignees, - Doer: doer, - Repo: repo, - Issue: issue, - OldAssigneeID: oldAssigneeID, - AssigneeID: assigneeID, + Type: CommentTypeAssignees, + Doer: doer, + Repo: repo, + Issue: issue, + RemovedAssignee: removedAssignee, + AssigneeID: assigneeID, }) } @@ -548,17 +538,17 @@ type CreateCommentOptions struct { Issue *Issue Label *Label - OldMilestoneID int64 - MilestoneID int64 - OldAssigneeID int64 - AssigneeID int64 - OldTitle string - NewTitle string - CommitID int64 - CommitSHA string - LineNum int64 - Content string - Attachments []string // UUIDs of attachments + OldMilestoneID int64 + MilestoneID int64 + AssigneeID int64 + RemovedAssignee bool + OldTitle string + NewTitle string + CommitID int64 + CommitSHA string + LineNum int64 + Content string + Attachments []string // UUIDs of attachments } // CreateComment creates comment of issue or commit. diff --git a/models/issue_list.go b/models/issue_list.go index 01a1a15f4..05130a6ee 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -154,38 +154,38 @@ func (issues IssueList) loadMilestones(e Engine) error { return nil } -func (issues IssueList) getAssigneeIDs() []int64 { - var ids = make(map[int64]struct{}, len(issues)) - for _, issue := range issues { - if _, ok := ids[issue.AssigneeID]; !ok { - ids[issue.AssigneeID] = struct{}{} - } - } - return keysInt64(ids) -} - func (issues IssueList) loadAssignees(e Engine) error { - assigneeIDs := issues.getAssigneeIDs() - if len(assigneeIDs) == 0 { + if len(issues) == 0 { return nil } - assigneeMaps := make(map[int64]*User, len(assigneeIDs)) - err := e. - In("id", assigneeIDs). - Find(&assigneeMaps) + type AssigneeIssue struct { + IssueAssignee *IssueAssignees `xorm:"extends"` + Assignee *User `xorm:"extends"` + } + + var assignees = make(map[int64][]*User, len(issues)) + rows, err := e.Table("issue_assignees"). + Join("INNER", "user", "`user`.id = `issue_assignees`.assignee_id"). + In("`issue_assignees`.issue_id", issues.getIssueIDs()). + Rows(new(AssigneeIssue)) if err != nil { return err } + defer rows.Close() + + for rows.Next() { + var assigneeIssue AssigneeIssue + err = rows.Scan(&assigneeIssue) + if err != nil { + return err + } + + assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee) + } for _, issue := range issues { - if issue.AssigneeID <= 0 { - continue - } - var ok bool - if issue.Assignee, ok = assigneeMaps[issue.AssigneeID]; !ok { - issue.Assignee = NewGhostUser() - } + issue.Assignees = assignees[issue.ID] } return nil } diff --git a/models/issue_mail.go b/models/issue_mail.go index 08e4eed58..179bb6527 100644 --- a/models/issue_mail.go +++ b/models/issue_mail.go @@ -46,9 +46,16 @@ func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content participants = append(participants, issue.Poster) } - // Assignee must receive any communications - if issue.Assignee != nil && issue.AssigneeID > 0 && issue.AssigneeID != doer.ID { - participants = append(participants, issue.Assignee) + // Assignees must receive any communications + assignees, err := GetAssigneesByIssue(issue) + if err != nil { + return err + } + + for _, assignee := range assignees { + if assignee.ID != doer.ID { + participants = append(participants, assignee) + } } tos := make([]string, 0, len(watchers)) // List of email addresses. diff --git a/models/issue_user.go b/models/issue_user.go index 1aded0b44..de5a185ae 100644 --- a/models/issue_user.go +++ b/models/issue_user.go @@ -6,6 +6,8 @@ package models import ( "fmt" + + "github.com/go-xorm/xorm" ) // IssueUser represents an issue-user relation. @@ -14,7 +16,6 @@ type IssueUser struct { UID int64 `xorm:"INDEX"` // User ID. IssueID int64 IsRead bool - IsAssigned bool IsMentioned bool } @@ -32,9 +33,8 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error { issueUsers := make([]*IssueUser, 0, len(assignees)+1) for _, assignee := range assignees { issueUsers = append(issueUsers, &IssueUser{ - IssueID: issue.ID, - UID: assignee.ID, - IsAssigned: assignee.ID == issue.AssigneeID, + IssueID: issue.ID, + UID: assignee.ID, }) isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID } @@ -51,34 +51,38 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error { return nil } -func updateIssueUserByAssignee(e Engine, issue *Issue) (err error) { - if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?", false, issue.ID); err != nil { - return err +func updateIssueAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) { + + // Check if the user exists + _, err = GetUserByID(assigneeID) + if err != nil { + return false, err } - // Assignee ID equals to 0 means clear assignee. - if issue.AssigneeID > 0 { - if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?", true, issue.AssigneeID, issue.ID); err != nil { - return err + // Check if the submitted user is already assigne, if yes delete him otherwise add him + var toBeDeleted bool + for _, assignee := range issue.Assignees { + if assignee.ID == assigneeID { + toBeDeleted = true + break } } - return updateIssue(e, issue) -} + assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID} -// UpdateIssueUserByAssignee updates issue-user relation for assignee. -func UpdateIssueUserByAssignee(issue *Issue) (err error) { - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err + if toBeDeleted { + _, err = e.Delete(assigneeIn) + if err != nil { + return toBeDeleted, err + } + } else { + _, err = e.Insert(assigneeIn) + if err != nil { + return toBeDeleted, err + } } - if err = updateIssueUserByAssignee(sess, issue); err != nil { - return err - } - - return sess.Commit() + return toBeDeleted, nil } // UpdateIssueUserByRead updates issue-user relation for reading. diff --git a/models/issue_user_test.go b/models/issue_user_test.go index e3ebfa8fa..a333bc861 100644 --- a/models/issue_user_test.go +++ b/models/issue_user_test.go @@ -32,23 +32,6 @@ func Test_newIssueUsers(t *testing.T) { AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID}) } -func TestUpdateIssueUserByAssignee(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) - - // artificially change assignee in issue_user table - AssertSuccessfulInsert(t, &IssueUser{IssueID: issue.ID, UID: 5, IsAssigned: true}) - _, err := x.Cols("is_assigned"). - Update(&IssueUser{IsAssigned: false}, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID}) - assert.NoError(t, err) - - assert.NoError(t, UpdateIssueUserByAssignee(issue)) - - // issue_user table should now be correct again - AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID}, "is_assigned=1") - AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 5}, "is_assigned=0") -} - func TestUpdateIssueUserByRead(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index aa9dd1310..e85da8de7 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -180,6 +180,8 @@ var migrations = []Migration{ NewMigration("add last used passcode column for TOTP", addLastUsedPasscodeTOTP), // v63 -> v64 NewMigration("add language column for user setting", addLanguageSetting), + // v64 -> v65 + NewMigration("add multiple assignees", addMultipleAssignees), } // Migrate database to current version @@ -229,7 +231,7 @@ Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to curr return nil } -func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) (err error) { +func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...string) (err error) { if tableName == "" || len(columnNames) == 0 { return nil } @@ -245,17 +247,10 @@ func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) ( } cols += "DROP COLUMN `" + col + "`" } - if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { + if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) } case setting.UseMSSQL: - sess := x.NewSession() - defer sess.Close() - - if err = sess.Begin(); err != nil { - return err - } - cols := "" for _, col := range columnNames { if cols != "" { diff --git a/models/migrations/v56.go b/models/migrations/v56.go index 1f96cc543..79f8ce0ba 100644 --- a/models/migrations/v56.go +++ b/models/migrations/v56.go @@ -9,5 +9,15 @@ import ( ) func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) { - return dropTableColumns(x, "org_user", "is_owner", "num_teams") + sess := x.NewSession() + defer sess.Close() + + if err = sess.Begin(); err != nil { + return err + } + + if err := dropTableColumns(sess, "org_user", "is_owner", "num_teams"); err != nil { + return err + } + return sess.Commit() } diff --git a/models/migrations/v64.go b/models/migrations/v64.go new file mode 100644 index 000000000..a281ac67e --- /dev/null +++ b/models/migrations/v64.go @@ -0,0 +1,129 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "code.gitea.io/gitea/modules/util" + + "github.com/go-xorm/xorm" +) + +func addMultipleAssignees(x *xorm.Engine) error { + + // Redeclare issue struct + type Issue struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"` + Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository. + PosterID int64 `xorm:"INDEX"` + Title string `xorm:"name"` + Content string `xorm:"TEXT"` + MilestoneID int64 `xorm:"INDEX"` + Priority int + AssigneeID int64 `xorm:"INDEX"` + IsClosed bool `xorm:"INDEX"` + IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. + NumComments int + Ref string + + DeadlineUnix util.TimeStamp `xorm:"INDEX"` + CreatedUnix util.TimeStamp `xorm:"INDEX created"` + UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` + ClosedUnix util.TimeStamp `xorm:"INDEX"` + } + + allIssues := []Issue{} + err := x.Find(&allIssues) + if err != nil { + return err + } + + // Create the table + type IssueAssignees struct { + ID int64 `xorm:"pk autoincr"` + AssigneeID int64 `xorm:"INDEX"` + IssueID int64 `xorm:"INDEX"` + } + err = x.Sync2(IssueAssignees{}) + if err != nil { + return err + } + + // Range over all issues and insert a new entry for each issue/assignee + sess := x.NewSession() + defer sess.Close() + + err = sess.Begin() + if err != nil { + return err + } + + for _, issue := range allIssues { + if issue.AssigneeID != 0 { + _, err := sess.Insert(IssueAssignees{IssueID: issue.ID, AssigneeID: issue.AssigneeID}) + if err != nil { + sess.Rollback() + return err + } + } + } + + // Updated the comment table + type Comment struct { + ID int64 `xorm:"pk autoincr"` + Type int + PosterID int64 `xorm:"INDEX"` + IssueID int64 `xorm:"INDEX"` + LabelID int64 + OldMilestoneID int64 + MilestoneID int64 + OldAssigneeID int64 + AssigneeID int64 + RemovedAssignee bool + OldTitle string + NewTitle string + + CommitID int64 + Line int64 + Content string `xorm:"TEXT"` + RenderedContent string `xorm:"-"` + + CreatedUnix util.TimeStamp `xorm:"INDEX created"` + UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` + + // Reference issue in commit message + CommitSHA string `xorm:"VARCHAR(40)"` + } + if err := x.Sync2(Comment{}); err != nil { + return err + } + + // Migrate comments + // First update everything to not have nulls in db + if _, err := sess.Where("type = ?", 9).Cols("removed_assignee").Update(Comment{RemovedAssignee: false}); err != nil { + return err + } + + allAssignementComments := []Comment{} + if err := sess.Where("type = ?", 9).Find(&allAssignementComments); err != nil { + return err + } + + for _, comment := range allAssignementComments { + // Everytime where OldAssigneeID is > 0, the assignement was removed. + if comment.OldAssigneeID > 0 { + _, err = sess.ID(comment.ID).Update(Comment{RemovedAssignee: true}) + } + } + + if err := dropTableColumns(sess, "issue", "assignee_id"); err != nil { + return err + } + + if err := dropTableColumns(sess, "issue_user", "is_assigned"); err != nil { + return err + } + return sess.Commit() +} diff --git a/models/models.go b/models/models.go index 549d2eadc..9213cd3b7 100644 --- a/models/models.go +++ b/models/models.go @@ -119,6 +119,7 @@ func init() { new(RepoIndexerStatus), new(LFSLock), new(Reaction), + new(IssueAssignees), ) gonicNames := []string{"SSL", "UID"} diff --git a/models/pull.go b/models/pull.go index 5f4a6e205..d23d885b6 100644 --- a/models/pull.go +++ b/models/pull.go @@ -198,6 +198,7 @@ func (pr *PullRequest) APIFormat() *api.PullRequest { Labels: apiIssue.Labels, Milestone: apiIssue.Milestone, Assignee: apiIssue.Assignee, + Assignees: apiIssue.Assignees, State: apiIssue.State, Comments: apiIssue.Comments, HTMLURL: pr.Issue.HTMLURL(), @@ -719,7 +720,7 @@ func (pr *PullRequest) testPatch() (err error) { } // NewPullRequest creates new pull request with labels for repository. -func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) { +func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte, assigneeIDs []int64) (err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { @@ -732,7 +733,11 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str LabelIDs: labelIDs, Attachments: uuids, IsPull: true, + AssigneeIDs: assigneeIDs, }); err != nil { + if IsErrUserDoesNotHaveAccessToRepo(err) { + return err + } return fmt.Errorf("newIssue: %v", err) } diff --git a/models/repo.go b/models/repo.go index 43fb1e0c7..4a7eb859c 100644 --- a/models/repo.go +++ b/models/repo.go @@ -600,9 +600,9 @@ func (repo *Repository) GetAssignees() (_ []*User, err error) { return repo.getAssignees(x) } -// GetAssigneeByID returns the user that has write access of repository by given ID. -func (repo *Repository) GetAssigneeByID(userID int64) (*User, error) { - return GetAssigneeByID(repo, userID) +// GetUserIfHasWriteAccess returns the user that has write access of repository by given ID. +func (repo *Repository) GetUserIfHasWriteAccess(userID int64) (*User, error) { + return GetUserIfHasWriteAccess(repo, userID) } // GetMilestoneByID returns the milestone belongs to repository by given ID. diff --git a/models/user.go b/models/user.go index 106d79ffc..8f5ee6e5a 100644 --- a/models/user.go +++ b/models/user.go @@ -993,7 +993,7 @@ func deleteUser(e *xorm.Session, u *User) error { // ***** END: PublicKey ***** // Clear assignee. - if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.ID); err != nil { + if err = clearAssigneeByUserID(e, u.ID); err != nil { return fmt.Errorf("clear assignee: %v", err) } @@ -1110,8 +1110,8 @@ func GetUserByID(id int64) (*User, error) { return getUserByID(x, id) } -// GetAssigneeByID returns the user with write access of repository by given ID. -func GetAssigneeByID(repo *Repository, userID int64) (*User, error) { +// GetUserIfHasWriteAccess returns the user with write access of repository by given ID. +func GetUserIfHasWriteAccess(repo *Repository, userID int64) (*User, error) { has, err := HasAccess(userID, repo, AccessModeWrite) if err != nil { return nil, err diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go index e25e98908..719ffcae7 100644 --- a/models/webhook_dingtalk.go +++ b/models/webhook_dingtalk.go @@ -118,8 +118,12 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) text = p.PullRequest.Body case api.HookIssueAssigned: + list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID}) + if err != nil { + return &DingtalkPayload{}, err + } title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName, - p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title) + list, p.Index, p.PullRequest.Title) text = p.PullRequest.Body case api.HookIssueUnassigned: title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) diff --git a/models/webhook_discord.go b/models/webhook_discord.go index 6d39d8b99..40d9d5899 100644 --- a/models/webhook_discord.go +++ b/models/webhook_discord.go @@ -191,8 +191,12 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) text = p.PullRequest.Body color = warnColor case api.HookIssueAssigned: + list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID}) + if err != nil { + return &DiscordPayload{}, err + } title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName, - p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title) + list, p.Index, p.PullRequest.Title) text = p.PullRequest.Body color = successColor case api.HookIssueUnassigned: diff --git a/models/webhook_slack.go b/models/webhook_slack.go index dd25a8d7d..256819adc 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -172,8 +172,12 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink) attachmentText = SlackTextFormatter(p.PullRequest.Body) case api.HookIssueAssigned: + list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID}) + if err != nil { + return &SlackPayload{}, err + } text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName, - SlackLinkFormatter(setting.AppURL+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName), + SlackLinkFormatter(setting.AppURL+list, list), titleLink, senderLink) case api.HookIssueUnassigned: text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 4aa9ce5f5..431ab7f13 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -254,6 +254,7 @@ func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors type CreateIssueForm struct { Title string `binding:"Required;MaxSize(255)"` LabelIDs string `form:"label_ids"` + AssigneeIDs string `form:"assignee_ids"` Ref string `form:"ref"` MilestoneID int64 AssigneeID int64 diff --git a/modules/context/repo.go b/modules/context/repo.go index f3ae33cb5..1fe5482ce 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -99,8 +99,9 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b // Checking for following: // 1. Is timetracker enabled // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? + isAssigned, _ := models.IsUserAssignedToIssue(issue, user) return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() || - r.IsWriter() || issue.IsPoster(user.ID) || issue.AssigneeID == user.ID) + r.IsWriter() || issue.IsPoster(user.ID) || isAssigned) } // GetCommitsCount returns cached commit count for current view diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b6f2ddfcb..d0f858829 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -624,9 +624,9 @@ issues.new.no_milestone = No Milestone issues.new.clear_milestone = Clear milestone issues.new.open_milestone = Open Milestones issues.new.closed_milestone = Closed Milestones -issues.new.assignee = Assignee -issues.new.clear_assignee = Clear assignee -issues.new.no_assignee = No assignee +issues.new.assignees = Assignees +issues.new.clear_assignees = Clear assignees +issues.new.no_assignees = Nobody assigned issues.no_ref = No Branch/Tag Specified issues.create = Create Issue issues.new_label = New Label diff --git a/public/css/index.css b/public/css/index.css index 377e8d1be..95a2df21d 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1 +1 @@ -.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}.overflow.menu .items{max-height:300px;overflow-y:auto}.overflow.menu .items .item{position:relative;cursor:pointer;display:block;border:none;height:auto;border-top:none;line-height:1em;color:rgba(0,0,0,.8);padding:.71428571em 1.14285714em!important;font-size:1rem;text-transform:none;font-weight:400;box-shadow:none;-webkit-touch-callout:none}.overflow.menu .items .item.active{font-weight:700}.overflow.menu .items .item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.8);z-index:13}.scrolling.menu .item.selected{font-weight:700!important}footer{height:40px;background-color:#fff;border-top:1px solid #d6d6d6;clear:both;width:100%;color:#888}footer .container{padding-top:10px}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}.hide{display:none}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}.markdown:not(code){overflow:hidden;font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home{padding-bottom:80px}.home .logo{max-width:220px}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',sans-serif,'Microsoft YaHei'}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px;padding-bottom:80px}.install{padding-top:45px;padding-bottom:80px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px!important}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:monospace}.repository{padding-top:15px;padding-bottom:80px}.repository .header-grid{padding-top:5px;padding-bottom:5px}.repository .header-grid .ui.compact.menu{margin-left:1rem}.repository .header-grid .ui.header{margin-top:0}.repository .header-grid .mega-octicon{width:30px;font-size:30px}.repository .header-grid .ui.huge.breadcrumb{font-weight:400;font-size:1.7rem}.repository .header-grid .fork-flag{margin-left:38px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .header-grid .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .header-grid .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-top:-2px;margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin-left:-5px;margin-right:-7px}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}.repository #clone-panel input{border-radius:0;padding:5px 10px}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead th:first-child{display:block;position:relative;width:325%}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:Consolas,monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:Consolas,monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:#21BA4518}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21BA4580}.repository .diff-detail-box{margin:15px 0;line-height:30px}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .ui.right{margin-bottom:15px}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:Consolas,monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-actions{display:none}.issue.list{list-style:none;padding-top:15px}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45!important}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{tab-size:1!important;-moz-tab-size:1!important}.tab-size-2{tab-size:2!important;-moz-tab-size:2!important}.tab-size-3{tab-size:3!important;-moz-tab-size:3!important}.tab-size-4{tab-size:4!important;-moz-tab-size:4!important}.tab-size-5{tab-size:5!important;-moz-tab-size:5!important}.tab-size-6{tab-size:6!important;-moz-tab-size:6!important}.tab-size-7{tab-size:7!important;-moz-tab-size:7!important}.tab-size-8{tab-size:8!important;-moz-tab-size:8!important}.tab-size-9{tab-size:9!important;-moz-tab-size:9!important}.tab-size-10{tab-size:10!important;-moz-tab-size:10!important}.tab-size-11{tab-size:11!important;-moz-tab-size:11!important}.tab-size-12{tab-size:12!important;-moz-tab-size:12!important}.tab-size-13{tab-size:13!important;-moz-tab-size:13!important}.tab-size-14{tab-size:14!important;-moz-tab-size:14!important}.tab-size-15{tab-size:15!important;-moz-tab-size:15!important}.tab-size-16{tab-size:16!important;-moz-tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px;display:none}#repo-topic{margin-top:5px}.CodeMirror{font:14px Consolas,"Liberation Mono",Menlo,Courier,monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px;padding-bottom:80px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px;padding-bottom:80px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}.user.profile .ui.repository.list{margin-top:25px}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px;padding-bottom:80px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news p{line-height:1em}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:Consolas,monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px;padding-bottom:80px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px;padding-bottom:80px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline} \ No newline at end of file +.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}.overflow.menu .items{max-height:300px;overflow-y:auto}.overflow.menu .items .item{position:relative;cursor:pointer;display:block;border:none;height:auto;border-top:none;line-height:1em;color:rgba(0,0,0,.8);padding:.71428571em 1.14285714em!important;font-size:1rem;text-transform:none;font-weight:400;box-shadow:none;-webkit-touch-callout:none}.overflow.menu .items .item.active{font-weight:700}.overflow.menu .items .item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.8);z-index:13}.scrolling.menu .item.selected{font-weight:700!important}footer{height:40px;background-color:#fff;border-top:1px solid #d6d6d6;clear:both;width:100%;color:#888}footer .container{padding-top:10px}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}.hide{display:none}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}.markdown:not(code){overflow:hidden;font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home{padding-bottom:80px}.home .logo{max-width:220px}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',sans-serif,'Microsoft YaHei'}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px;padding-bottom:80px}.install{padding-top:45px;padding-bottom:80px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px!important}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:monospace}.repository{padding-top:15px;padding-bottom:80px}.repository .header-grid{padding-top:5px;padding-bottom:5px}.repository .header-grid .ui.compact.menu{margin-left:1rem}.repository .header-grid .ui.header{margin-top:0}.repository .header-grid .mega-octicon{width:30px;font-size:30px}.repository .header-grid .ui.huge.breadcrumb{font-weight:400;font-size:1.7rem}.repository .header-grid .fork-flag{margin-left:38px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .header-grid .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .header-grid .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-top:-2px;margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}.repository .filter.menu .text{margin-left:.9em}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}.repository #clone-panel input{border-radius:0;padding:5px 10px}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead th:first-child{display:block;position:relative;width:325%}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:Consolas,monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:Consolas,monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:#21BA4518}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21BA4580}.repository .diff-detail-box{margin:15px 0;line-height:30px}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .ui.right{margin-bottom:15px}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:Consolas,monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-actions{display:none}.issue.list{list-style:none;padding-top:15px}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45!important}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{tab-size:1!important;-moz-tab-size:1!important}.tab-size-2{tab-size:2!important;-moz-tab-size:2!important}.tab-size-3{tab-size:3!important;-moz-tab-size:3!important}.tab-size-4{tab-size:4!important;-moz-tab-size:4!important}.tab-size-5{tab-size:5!important;-moz-tab-size:5!important}.tab-size-6{tab-size:6!important;-moz-tab-size:6!important}.tab-size-7{tab-size:7!important;-moz-tab-size:7!important}.tab-size-8{tab-size:8!important;-moz-tab-size:8!important}.tab-size-9{tab-size:9!important;-moz-tab-size:9!important}.tab-size-10{tab-size:10!important;-moz-tab-size:10!important}.tab-size-11{tab-size:11!important;-moz-tab-size:11!important}.tab-size-12{tab-size:12!important;-moz-tab-size:12!important}.tab-size-13{tab-size:13!important;-moz-tab-size:13!important}.tab-size-14{tab-size:14!important;-moz-tab-size:14!important}.tab-size-15{tab-size:15!important;-moz-tab-size:15!important}.tab-size-16{tab-size:16!important;-moz-tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px;display:none}#repo-topic{margin-top:5px}.CodeMirror{font:14px Consolas,"Liberation Mono",Menlo,Courier,monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px;padding-bottom:80px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px;padding-bottom:80px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}.user.profile .ui.repository.list{margin-top:25px}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px;padding-bottom:80px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news p{line-height:1em}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:Consolas,monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px;padding-bottom:80px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px;padding-bottom:80px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline} \ No newline at end of file diff --git a/public/js/index.js b/public/js/index.js index dc473b6f3..000229dbc 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -179,81 +179,115 @@ function initCommentForm() { initBranchSelector(); initCommentPreviewTab($('.comment.form')); - // Labels - var $list = $('.ui.labels.list'); - var $noSelect = $list.find('.no-select'); - var $labelMenu = $('.select-label .menu'); - var hasLabelUpdateAction = $labelMenu.data('action') == 'update'; + // Listsubmit + function initListSubmits(selector, outerSelector) { + var $list = $('.ui.' + outerSelector + '.list'); + var $noSelect = $list.find('.no-select'); + var $listMenu = $('.' + selector + ' .menu'); + var hasLabelUpdateAction = $listMenu.data('action') == 'update'; - $('.select-label').dropdown('setting', 'onHide', function(){ - if (hasLabelUpdateAction) { - location.reload(); - } - }); - - $labelMenu.find('.item:not(.no-select)').click(function () { - if ($(this).hasClass('checked')) { - $(this).removeClass('checked'); - $(this).find('.octicon').removeClass('octicon-check'); + $('.' + selector).dropdown('setting', 'onHide', function(){ + hasLabelUpdateAction = $listMenu.data('action') == 'update'; // Update the var if (hasLabelUpdateAction) { + location.reload(); + } + }); + + $listMenu.find('.item:not(.no-select)').click(function () { + + // we don't need the action attribute when updating assignees + if (selector == 'select-assignees-modify') { + + // UI magic. We need to do this here, otherwise it would destroy the functionality of + // adding/removing labels + if ($(this).hasClass('checked')) { + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + } else { + $(this).addClass('checked'); + $(this).find('.octicon').addClass('octicon-check'); + } + updateIssuesMeta( - $labelMenu.data('update-url'), - "detach", - $labelMenu.data('issue-id'), + $listMenu.data('update-url'), + "", + $listMenu.data('issue-id'), $(this).data('id') ); + $listMenu.data('action', 'update'); // Update to reload the page when we updated items + return false; } - } else { - $(this).addClass('checked'); - $(this).find('.octicon').addClass('octicon-check'); - if (hasLabelUpdateAction) { - updateIssuesMeta( - $labelMenu.data('update-url'), - "attach", - $labelMenu.data('issue-id'), - $(this).data('id') - ); - } - } - var labelIds = []; - $(this).parent().find('.item').each(function () { if ($(this).hasClass('checked')) { - labelIds.push($(this).data('id')); - $($(this).data('id-selector')).removeClass('hide'); + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + if (hasLabelUpdateAction) { + updateIssuesMeta( + $listMenu.data('update-url'), + "detach", + $listMenu.data('issue-id'), + $(this).data('id') + ); + } } else { - $($(this).data('id-selector')).addClass('hide'); + $(this).addClass('checked'); + $(this).find('.octicon').addClass('octicon-check'); + if (hasLabelUpdateAction) { + updateIssuesMeta( + $listMenu.data('update-url'), + "attach", + $listMenu.data('issue-id'), + $(this).data('id') + ); + } } + + var listIds = []; + $(this).parent().find('.item').each(function () { + if ($(this).hasClass('checked')) { + listIds.push($(this).data('id')); + $($(this).data('id-selector')).removeClass('hide'); + } else { + $($(this).data('id-selector')).addClass('hide'); + } + }); + if (listIds.length == 0) { + $noSelect.removeClass('hide'); + } else { + $noSelect.addClass('hide'); + } + $($(this).parent().data('id')).val(listIds.join(",")); + return false; }); - if (labelIds.length == 0) { + $listMenu.find('.no-select.item').click(function () { + if (hasLabelUpdateAction || selector == 'select-assignees-modify') { + updateIssuesMeta( + $listMenu.data('update-url'), + "clear", + $listMenu.data('issue-id'), + "" + ); + $listMenu.data('action', 'update'); // Update to reload the page when we updated items + } + + $(this).parent().find('.item').each(function () { + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + }); + + $list.find('.item').each(function () { + $(this).addClass('hide'); + }); $noSelect.removeClass('hide'); - } else { - $noSelect.addClass('hide'); - } - $($(this).parent().data('id')).val(labelIds.join(",")); - return false; - }); - $labelMenu.find('.no-select.item').click(function () { - if (hasLabelUpdateAction) { - updateIssuesMeta( - $labelMenu.data('update-url'), - "clear", - $labelMenu.data('issue-id'), - "" - ); - } + $($(this).parent().data('id')).val(''); - $(this).parent().find('.item').each(function () { - $(this).removeClass('checked'); - $(this).find('.octicon').removeClass('octicon-check'); }); + } - $list.find('.item').each(function () { - $(this).addClass('hide'); - }); - $noSelect.removeClass('hide'); - $($(this).parent().data('id')).val(''); - }); + // Init labels and assignees + initListSubmits('select-label', 'labels'); + initListSubmits('select-assignees', 'assignees'); + initListSubmits('select-assignees-modify', 'assignees'); function selectItem(select_id, input_id) { var $menu = $(select_id + ' .menu'); diff --git a/public/less/_repository.less b/public/less/_repository.less index b99e2a934..54024d790 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -119,8 +119,11 @@ } .octicon { float: left; - margin-left: -5px; - margin-right: -7px; + margin: 5px -7px 0 -5px; + width: 16px; + } + .text{ + margin-left: 0.9em; } .menu { max-height: 300px; @@ -1745,4 +1748,4 @@ tbody.commit-list { #repo-topic { margin-top: 5px; -} \ No newline at end of file +} diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index cc033554f..211d8045a 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -178,25 +178,22 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { DeadlineUnix: deadlineUnix, } - if ctx.Repo.IsWriter() { - if len(form.Assignee) > 0 { - assignee, err := models.GetUserByName(form.Assignee) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", form.Assignee)) - } else { - ctx.Error(500, "GetUserByName", err) - } - return - } - issue.AssigneeID = assignee.ID + // Get all assignee IDs + assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) + } else { + ctx.Error(500, "AddAssigneeByName", err) } - issue.MilestoneID = form.Milestone - } else { - form.Labels = nil + return } - if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil { + if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil { + if models.IsErrUserDoesNotHaveAccessToRepo(err) { + ctx.Error(400, "UserDoesNotHaveAccessToRepo", err) + return + } ctx.Error(500, "NewIssue", err) return } @@ -209,7 +206,6 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { } // Refetch from database to assign some automatic values - var err error issue, err = models.GetIssueByID(issue.ID) if err != nil { ctx.Error(500, "GetIssueByID", err) @@ -272,6 +268,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { issue.Content = *form.Body } + // Update the deadline var deadlineUnix util.TimeStamp if form.Deadline != nil && !form.Deadline.IsZero() { deadlineUnix = util.TimeStamp(form.Deadline.Unix()) @@ -282,28 +279,28 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { return } - if ctx.Repo.IsWriter() && form.Assignee != nil && - (issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(*form.Assignee)) { - if len(*form.Assignee) == 0 { - issue.AssigneeID = 0 - } else { - assignee, err := models.GetUserByName(*form.Assignee) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee)) - } else { - ctx.Error(500, "GetUserByName", err) - } - return - } - issue.AssigneeID = assignee.ID + // Add/delete assignees + + // Deleting is done the Github way (quote from their api documentation): + // https://developer.github.com/v3/issues/#edit-an-issue + // "assignees" (array): Logins for Users to assign to this issue. + // Pass one or more user logins to replace the set of assignees on this Issue. + // Send an empty array ([]) to clear all assignees from the Issue. + + if ctx.Repo.IsWriter() && (form.Assignees != nil || form.Assignee != nil) { + + oneAssignee := "" + if form.Assignee != nil { + oneAssignee = *form.Assignee } - if err = models.UpdateIssueUserByAssignee(issue); err != nil { - ctx.Error(500, "UpdateIssueUserByAssignee", err) + err = models.UpdateAPIAssignee(issue, oneAssignee, form.Assignees, ctx.User) + if err != nil { + ctx.Error(500, "UpdateAPIAssignee", err) return } } + if ctx.Repo.IsWriter() && form.Milestone != nil && issue.MilestoneID != *form.Milestone { oldMilestoneID := issue.MilestoneID diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 3ea4b8d32..78d96c647 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -211,26 +211,6 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption milestoneID = milestone.ID } - if len(form.Assignee) > 0 { - assigneeUser, err := models.GetUserByName(form.Assignee) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", form.Assignee)) - } else { - ctx.Error(500, "GetUserByName", err) - } - return - } - - assignee, err := repo.GetAssigneeByID(assigneeUser.ID) - if err != nil { - ctx.Error(500, "GetAssigneeByID", err) - return - } - - assigneeID = assignee.ID - } - patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch) if err != nil { ctx.Error(500, "GetPatch", err) @@ -266,7 +246,22 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption Type: models.PullRequestGitea, } - if err := models.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch); err != nil { + // Get all assignee IDs + assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) + } else { + ctx.Error(500, "AddAssigneeByName", err) + } + return + } + + if err := models.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch, assigneeIDs); err != nil { + if models.IsErrUserDoesNotHaveAccessToRepo(err) { + ctx.Error(400, "UserDoesNotHaveAccessToRepo", err) + return + } ctx.Error(500, "NewPullRequest", err) return } else if err := pr.PushToBaseRepo(); err != nil { @@ -335,6 +330,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { issue.Content = form.Body } + // Update Deadline var deadlineUnix util.TimeStamp if form.Deadline != nil && !form.Deadline.IsZero() { deadlineUnix = util.TimeStamp(form.Deadline.Unix()) @@ -345,28 +341,27 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { return } - if ctx.Repo.IsWriter() && len(form.Assignee) > 0 && - (issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(form.Assignee)) { - if len(form.Assignee) == 0 { - issue.AssigneeID = 0 - } else { - assignee, err := models.GetUserByName(form.Assignee) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", form.Assignee)) - } else { - ctx.Error(500, "GetUserByName", err) - } - return - } - issue.AssigneeID = assignee.ID - } + // Add/delete assignees - if err = models.UpdateIssueUserByAssignee(issue); err != nil { - ctx.Error(500, "UpdateIssueUserByAssignee", err) + // Deleting is done the Github way (quote from their api documentation): + // https://developer.github.com/v3/issues/#edit-an-issue + // "assignees" (array): Logins for Users to assign to this issue. + // Pass one or more user logins to replace the set of assignees on this Issue. + // Send an empty array ([]) to clear all assignees from the Issue. + + if ctx.Repo.IsWriter() && (form.Assignees != nil || len(form.Assignee) > 0) { + + err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err)) + } else { + ctx.Error(500, "UpdateAPIAssignee", err) + } return } } + if ctx.Repo.IsWriter() && form.Milestone != 0 && issue.MilestoneID != form.Milestone { oldMilestoneID := issue.MilestoneID diff --git a/routers/repo/issue.go b/routers/repo/issue.go index c7aef2559..33ba3e0d6 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -364,7 +364,7 @@ func NewIssue(ctx *context.Context) { } // ValidateRepoMetas check and returns repository's meta informations -func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, int64, int64) { +func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, []int64, int64) { var ( repo = ctx.Repo.Repository err error @@ -372,11 +372,11 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository) if ctx.Written() { - return nil, 0, 0 + return nil, nil, 0 } if !ctx.Repo.IsWriter() { - return nil, 0, 0 + return nil, nil, 0 } var labelIDs []int64 @@ -385,7 +385,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 if len(form.LabelIDs) > 0 { labelIDs, err = base.StringsToInt64s(strings.Split(form.LabelIDs, ",")) if err != nil { - return nil, 0, 0 + return nil, nil, 0 } labelIDMark := base.Int64sToMap(labelIDs) @@ -407,23 +407,35 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 ctx.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID) if err != nil { ctx.ServerError("GetMilestoneByID", err) - return nil, 0, 0 + return nil, nil, 0 } ctx.Data["milestone_id"] = milestoneID } - // Check assignee. - assigneeID := form.AssigneeID - if assigneeID > 0 { - ctx.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID) + // Check assignees + var assigneeIDs []int64 + if len(form.AssigneeIDs) > 0 { + assigneeIDs, err = base.StringsToInt64s(strings.Split(form.AssigneeIDs, ",")) if err != nil { - ctx.ServerError("GetAssigneeByID", err) - return nil, 0, 0 + return nil, nil, 0 + } + + // Check if the passed assignees actually exists and has write access to the repo + for _, aID := range assigneeIDs { + _, err = repo.GetUserIfHasWriteAccess(aID) + if err != nil { + ctx.ServerError("GetUserIfHasWriteAccess", err) + return nil, nil, 0 + } } - ctx.Data["assignee_id"] = assigneeID } - return labelIDs, milestoneID, assigneeID + // Keep the old assignee id thingy for compatibility reasons + if form.AssigneeID > 0 { + assigneeIDs = append(assigneeIDs, form.AssigneeID) + } + + return labelIDs, assigneeIDs, milestoneID } // NewIssuePost response for creating new issue @@ -440,7 +452,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { attachments []string ) - labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form) + labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form) if ctx.Written() { return } @@ -460,11 +472,14 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { PosterID: ctx.User.ID, Poster: ctx.User, MilestoneID: milestoneID, - AssigneeID: assigneeID, Content: form.Content, Ref: form.Ref, } - if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil { + if err := models.NewIssue(repo, issue, labelIDs, assigneeIDs, attachments); err != nil { + if models.IsErrUserDoesNotHaveAccessToRepo(err) { + ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error()) + return + } ctx.ServerError("NewIssue", err) return } @@ -702,8 +717,8 @@ func ViewIssue(ctx *context.Context) { comment.Milestone = ghostMilestone } } else if comment.Type == models.CommentTypeAssignees { - if err = comment.LoadAssignees(); err != nil { - ctx.ServerError("LoadAssignees", err) + if err = comment.LoadAssigneeUser(); err != nil { + ctx.ServerError("LoadAssigneeUser", err) return } } @@ -912,13 +927,20 @@ func UpdateIssueAssignee(ctx *context.Context) { } assigneeID := ctx.QueryInt64("id") + action := ctx.Query("action") + for _, issue := range issues { - if issue.AssigneeID == assigneeID { - continue - } - if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil { - ctx.ServerError("ChangeAssignee", err) - return + switch action { + case "clear": + if err := models.DeleteNotPassedAssignee(issue, ctx.User, []*models.User{}); err != nil { + ctx.ServerError("ClearAssignees", err) + return + } + default: + if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil { + ctx.ServerError("ChangeAssignee", err) + return + } } } ctx.JSON(200, map[string]interface{}{ diff --git a/routers/repo/pull.go b/routers/repo/pull.go index a852cee1f..dfe07ca44 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -775,7 +775,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) return } - labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form) + labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form) if ctx.Written() { return } @@ -811,7 +811,6 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) PosterID: ctx.User.ID, Poster: ctx.User, MilestoneID: milestoneID, - AssigneeID: assigneeID, IsPull: true, Content: form.Content, } @@ -828,7 +827,12 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) } // FIXME: check error in the case two people send pull request at almost same time, give nice error prompt // instead of 500. - if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil { + + if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch, assigneeIDs); err != nil { + if models.IsErrUserDoesNotHaveAccessToRepo(err) { + ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error()) + return + } ctx.ServerError("NewPullRequest", err) return } else if err := pullRequest.PushToBaseRepo(); err != nil { diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index d8e42df12..40a56b1b3 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -156,7 +156,7 @@ - + diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index b6d2c96bb..dc266218b 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -118,15 +118,29 @@ {{else if eq .Type 9}}
- {{if gt .AssigneeID 0}}{{if eq .Poster.ID .AssigneeID}} - - {{.Poster.Name}} {{$.i18n.Tr "repo.issues.self_assign_at" $createdStr | Safe}} - {{else}} - - {{.Assignee.Name}} {{$.i18n.Tr "repo.issues.add_assignee_at" .Poster.Name $createdStr | Safe}} {{end}}{{else if gt .OldAssigneeID 0}} - - - {{.Poster.Name}} {{$.i18n.Tr "repo.issues.remove_assignee_at" $createdStr | Safe}} {{end}} + {{if gt .AssigneeID 0}} + {{if .RemovedAssignee}} + + + + + {{.Assignee.Name}} + {{$.i18n.Tr "repo.issues.remove_assignee_at" $createdStr | Safe}} + + {{else}} + + + + + {{.Assignee.Name}} + {{if eq .Poster.ID .AssigneeID}} + {{$.i18n.Tr "repo.issues.self_assign_at" $createdStr | Safe}} + {{else}} + {{$.i18n.Tr "repo.issues.add_assignee_at" .Poster.Name $createdStr | Safe}} + {{end}} + + {{end}} + {{end}}
{{else if eq .Type 10}}
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 648533fca..f403fa7cc 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -68,23 +68,40 @@
-