forked from NYANDEV/forgejo
Add member
, collaborator
, contributor
, and first-time contributor
roles and tooltips (#26658)
GitHub like role descriptor ![image](https://github.com/go-gitea/gitea/assets/18380374/ceaed92c-6749-47b3-89e8-0e0e7ae65321) ![image](https://github.com/go-gitea/gitea/assets/18380374/8193ec34-cbf0-47f9-b0de-10dbddd66970) ![image](https://github.com/go-gitea/gitea/assets/18380374/56c7ed85-6177-425e-9f2f-926e99770782) --------- Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
0d55f64e6c
commit
d2e4039def
5 changed files with 106 additions and 73 deletions
|
@ -23,6 +23,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
|
@ -181,40 +182,32 @@ func (t CommentType) HasAttachmentSupport() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// RoleDescriptor defines comment tag type
|
||||
type RoleDescriptor int
|
||||
// RoleInRepo presents the user's participation in the repo
|
||||
type RoleInRepo string
|
||||
|
||||
// RoleDescriptor defines comment "role" tags
|
||||
type RoleDescriptor struct {
|
||||
IsPoster bool
|
||||
RoleInRepo RoleInRepo
|
||||
}
|
||||
|
||||
// Enumerate all the role tags.
|
||||
const (
|
||||
RoleDescriptorNone RoleDescriptor = iota
|
||||
RoleDescriptorPoster
|
||||
RoleDescriptorWriter
|
||||
RoleDescriptorOwner
|
||||
RoleRepoOwner RoleInRepo = "owner"
|
||||
RoleRepoMember RoleInRepo = "member"
|
||||
RoleRepoCollaborator RoleInRepo = "collaborator"
|
||||
RoleRepoFirstTimeContributor RoleInRepo = "first_time_contributor"
|
||||
RoleRepoContributor RoleInRepo = "contributor"
|
||||
)
|
||||
|
||||
// WithRole enable a specific tag on the RoleDescriptor.
|
||||
func (rd RoleDescriptor) WithRole(role RoleDescriptor) RoleDescriptor {
|
||||
return rd | (1 << role)
|
||||
// LocaleString returns the locale string name of the role
|
||||
func (r RoleInRepo) LocaleString(lang translation.Locale) string {
|
||||
return lang.Tr("repo.issues.role." + string(r))
|
||||
}
|
||||
|
||||
func stringToRoleDescriptor(role string) RoleDescriptor {
|
||||
switch role {
|
||||
case "Poster":
|
||||
return RoleDescriptorPoster
|
||||
case "Writer":
|
||||
return RoleDescriptorWriter
|
||||
case "Owner":
|
||||
return RoleDescriptorOwner
|
||||
default:
|
||||
return RoleDescriptorNone
|
||||
}
|
||||
}
|
||||
|
||||
// HasRole returns if a certain role is enabled on the RoleDescriptor.
|
||||
func (rd RoleDescriptor) HasRole(role string) bool {
|
||||
roleDescriptor := stringToRoleDescriptor(role)
|
||||
bitValue := rd & (1 << roleDescriptor)
|
||||
return (bitValue > 0)
|
||||
// LocaleHelper returns the locale tooltip of the role
|
||||
func (r RoleInRepo) LocaleHelper(lang translation.Locale) string {
|
||||
return lang.Tr("repo.issues.role." + string(r) + "_helper")
|
||||
}
|
||||
|
||||
// Comment represents a comment in commit and issue page.
|
||||
|
|
|
@ -199,3 +199,16 @@ func (prs PullRequestList) GetIssueIDs() []int64 {
|
|||
}
|
||||
return issueIDs
|
||||
}
|
||||
|
||||
// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo
|
||||
func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Join("INNER", "pull_request", "pull_request.issue_id = issue.id").
|
||||
Where("repo_id=?", repoID).
|
||||
And("poster_id=?", posterID).
|
||||
And("is_pull=?", true).
|
||||
And("pull_request.has_merged=?", true).
|
||||
Select("issue.id").
|
||||
Limit(1).
|
||||
Get(new(Issue))
|
||||
}
|
||||
|
|
|
@ -1480,9 +1480,18 @@ issues.ref_reopening_from = `<a href="%[3]s">referenced a pull request %[4]s tha
|
|||
issues.ref_closed_from = `<a href="%[3]s">closed this issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopened_from = `<a href="%[3]s">reopened this issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_from = `from %[1]s`
|
||||
issues.poster = Poster
|
||||
issues.collaborator = Collaborator
|
||||
issues.owner = Owner
|
||||
issues.author = Author
|
||||
issues.author_helper = This user is the author.
|
||||
issues.role.owner = Owner
|
||||
issues.role.owner_helper = This user is the owner of this repository.
|
||||
issues.role.member = Member
|
||||
issues.role.member_helper = This user is a member of the organization owning this repository.
|
||||
issues.role.collaborator = Collaborator
|
||||
issues.role.collaborator_helper = This user has been invited to collaborate on the repository.
|
||||
issues.role.first_time_contributor = First-time contributor
|
||||
issues.role.first_time_contributor_helper = This is the first contribution of this user to the repository.
|
||||
issues.role.contributor = Contributor
|
||||
issues.role.contributor_helper = This user has previously committed to the repository.
|
||||
issues.re_request_review=Re-request review
|
||||
issues.is_stale = There have been changes to this PR since this review
|
||||
issues.remove_request_review=Remove review request
|
||||
|
|
|
@ -1228,47 +1228,70 @@ func NewIssuePost(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// roleDescriptor returns the Role Descriptor for a comment in/with the given repo, poster and issue
|
||||
// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue
|
||||
func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) {
|
||||
roleDescriptor := issues_model.RoleDescriptor{}
|
||||
|
||||
if hasOriginalAuthor {
|
||||
return issues_model.RoleDescriptorNone, nil
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, poster)
|
||||
if err != nil {
|
||||
return issues_model.RoleDescriptorNone, err
|
||||
}
|
||||
|
||||
// By default the poster has no roles on the comment.
|
||||
roleDescriptor := issues_model.RoleDescriptorNone
|
||||
|
||||
// Check if the poster is owner of the repo.
|
||||
if perm.IsOwner() {
|
||||
// If the poster isn't a admin, enable the owner role.
|
||||
if !poster.IsAdmin {
|
||||
roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner)
|
||||
} else {
|
||||
|
||||
// Otherwise check if poster is the real repo admin.
|
||||
ok, err := access_model.IsUserRealRepoAdmin(repo, poster)
|
||||
if err != nil {
|
||||
return issues_model.RoleDescriptorNone, err
|
||||
}
|
||||
if ok {
|
||||
roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the poster can write issues or pulls to the repo, enable the Writer role.
|
||||
// Only enable this if the poster doesn't have the owner role already.
|
||||
if !roleDescriptor.HasRole("Owner") && perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorWriter)
|
||||
return roleDescriptor, err
|
||||
}
|
||||
|
||||
// If the poster is the actual poster of the issue, enable Poster role.
|
||||
if issue.IsPoster(poster.ID) {
|
||||
roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorPoster)
|
||||
roleDescriptor.IsPoster = issue.IsPoster(poster.ID)
|
||||
|
||||
// Check if the poster is owner of the repo.
|
||||
if perm.IsOwner() {
|
||||
// If the poster isn't an admin, enable the owner role.
|
||||
if !poster.IsAdmin {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
|
||||
// Otherwise check if poster is the real repo admin.
|
||||
ok, err := access_model.IsUserRealRepoAdmin(repo, poster)
|
||||
if err != nil {
|
||||
return roleDescriptor, err
|
||||
}
|
||||
if ok {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If repo is organization, check Member role
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return roleDescriptor, err
|
||||
}
|
||||
if repo.Owner.IsOrganization() {
|
||||
if isMember, err := organization.IsOrganizationMember(ctx, repo.Owner.ID, poster.ID); err != nil {
|
||||
return roleDescriptor, err
|
||||
} else if isMember {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoMember
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If the poster is the collaborator of the repo
|
||||
if isCollaborator, err := repo_model.IsCollaborator(ctx, repo.ID, poster.ID); err != nil {
|
||||
return roleDescriptor, err
|
||||
} else if isCollaborator {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoCollaborator
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
|
||||
hasMergedPR, err := issues_model.HasMergedPullRequestInRepo(ctx, repo.ID, poster.ID)
|
||||
if err != nil {
|
||||
return roleDescriptor, err
|
||||
} else if hasMergedPR {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor
|
||||
} else {
|
||||
// only display first time contributor in the first opening pull request
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor
|
||||
}
|
||||
|
||||
return roleDescriptor, nil
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
{{if and (.ShowRole.HasRole "Poster") (not .IgnorePoster)}}
|
||||
<div class="ui basic label role-label">
|
||||
{{ctx.Locale.Tr "repo.issues.poster"}}
|
||||
{{if and .ShowRole.IsPoster (not .IgnorePoster)}}
|
||||
<div class="ui basic label role-label" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.author_helper"}}">
|
||||
{{ctx.Locale.Tr "repo.issues.author"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if (.ShowRole.HasRole "Writer")}}
|
||||
<div class="ui basic label role-label">
|
||||
{{ctx.Locale.Tr "repo.issues.collaborator"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if (.ShowRole.HasRole "Owner")}}
|
||||
<div class="ui basic label role-label">
|
||||
{{ctx.Locale.Tr "repo.issues.owner"}}
|
||||
{{if .ShowRole.RoleInRepo}}
|
||||
<div class="ui basic label role-label" data-tooltip-content="{{.ShowRole.RoleInRepo.LocaleHelper ctx.Locale}}">
|
||||
{{.ShowRole.RoleInRepo.LocaleString ctx.Locale}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in a new issue