Add member, collaborator, contributor, and first-time contributor roles and tooltips (#26658)
				
					
				
			GitHub like role descriptor    --------- 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue