| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- // Copyright 2014 The Gogs Authors. All rights reserved.
- // Copyright 2018 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package repo
-
- import (
- "errors"
- "fmt"
- "html/template"
- "net/http"
- "strconv"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
- "code.gitea.io/gitea/models/renderhelper"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- issue_service "code.gitea.io/gitea/services/issue"
- )
-
- const (
- tplAttachment templates.TplName = "repo/issue/view_content/attachments"
-
- tplIssues templates.TplName = "repo/issue/list"
- tplIssueNew templates.TplName = "repo/issue/new"
- tplIssueChoose templates.TplName = "repo/issue/choose"
- tplIssueView templates.TplName = "repo/issue/view"
-
- tplPullMergeBox templates.TplName = "repo/issue/view_content/pull_merge_box"
- tplReactions templates.TplName = "repo/issue/view_content/reactions"
-
- issueTemplateKey = "IssueTemplate"
- issueTemplateTitleKey = "IssueTemplateTitle"
- )
-
- // IssueTemplateCandidates issue templates
- var IssueTemplateCandidates = []string{
- "ISSUE_TEMPLATE.md",
- "ISSUE_TEMPLATE.yaml",
- "ISSUE_TEMPLATE.yml",
- "issue_template.md",
- "issue_template.yaml",
- "issue_template.yml",
- ".gitea/ISSUE_TEMPLATE.md",
- ".gitea/ISSUE_TEMPLATE.yaml",
- ".gitea/ISSUE_TEMPLATE.yml",
- ".gitea/issue_template.md",
- ".gitea/issue_template.yaml",
- ".gitea/issue_template.yml",
- ".github/ISSUE_TEMPLATE.md",
- ".github/ISSUE_TEMPLATE.yaml",
- ".github/ISSUE_TEMPLATE.yml",
- ".github/issue_template.md",
- ".github/issue_template.yaml",
- ".github/issue_template.yml",
- }
-
- // MustAllowUserComment checks to make sure if an issue is locked.
- // If locked and user has permissions to write to the repository,
- // then the comment is allowed, else it is blocked
- func MustAllowUserComment(ctx *context.Context) {
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
-
- if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
- ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
- ctx.Redirect(issue.Link())
- return
- }
- }
-
- // MustEnableIssues check if repository enable internal issues
- func MustEnableIssues(ctx *context.Context) {
- if !ctx.Repo.CanRead(unit.TypeIssues) &&
- !ctx.Repo.CanRead(unit.TypeExternalTracker) {
- ctx.NotFound(nil)
- return
- }
-
- unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker)
- if err == nil {
- ctx.Redirect(unit.ExternalTrackerConfig().ExternalTrackerURL)
- return
- }
- }
-
- // MustAllowPulls check if repository enable pull requests and user have right to do that
- func MustAllowPulls(ctx *context.Context) {
- if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
- ctx.NotFound(nil)
- return
- }
-
- // User can send pull request if owns a forked repository.
- if ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) {
- ctx.Repo.PullRequest.Allowed = true
- }
- }
-
- func retrieveProjectsInternal(ctx *context.Context, repo *repo_model.Repository) (open, closed []*project_model.Project) {
- // Distinguish whether the owner of the repository
- // is an individual or an organization
- repoOwnerType := project_model.TypeIndividual
- if repo.Owner.IsOrganization() {
- repoOwnerType = project_model.TypeOrganization
- }
-
- projectsUnit := repo.MustGetUnit(ctx, unit.TypeProjects)
-
- var openProjects []*project_model.Project
- var closedProjects []*project_model.Project
- var err error
-
- if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) {
- openProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
- ListOptions: db.ListOptionsAll,
- RepoID: repo.ID,
- IsClosed: optional.Some(false),
- Type: project_model.TypeRepository,
- })
- if err != nil {
- ctx.ServerError("GetProjects", err)
- return nil, nil
- }
- closedProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
- ListOptions: db.ListOptionsAll,
- RepoID: repo.ID,
- IsClosed: optional.Some(true),
- Type: project_model.TypeRepository,
- })
- if err != nil {
- ctx.ServerError("GetProjects", err)
- return nil, nil
- }
- }
-
- if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeOwner) {
- openProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
- ListOptions: db.ListOptionsAll,
- OwnerID: repo.OwnerID,
- IsClosed: optional.Some(false),
- Type: repoOwnerType,
- })
- if err != nil {
- ctx.ServerError("GetProjects", err)
- return nil, nil
- }
- openProjects = append(openProjects, openProjects2...)
- closedProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
- ListOptions: db.ListOptionsAll,
- OwnerID: repo.OwnerID,
- IsClosed: optional.Some(true),
- Type: repoOwnerType,
- })
- if err != nil {
- ctx.ServerError("GetProjects", err)
- return nil, nil
- }
- closedProjects = append(closedProjects, closedProjects2...)
- }
- return openProjects, closedProjects
- }
-
- // GetActionIssue will return the issue which is used in the context.
- func GetActionIssue(ctx *context.Context) *issues_model.Issue {
- issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
- if err != nil {
- ctx.NotFoundOrServerError("GetIssueByIndex", issues_model.IsErrIssueNotExist, err)
- return nil
- }
- issue.Repo = ctx.Repo.Repository
- checkIssueRights(ctx, issue)
- if ctx.Written() {
- return nil
- }
- if err = issue.LoadAttributes(ctx); err != nil {
- ctx.ServerError("LoadAttributes", err)
- return nil
- }
- return issue
- }
-
- func checkIssueRights(ctx *context.Context, issue *issues_model.Issue) {
- if issue.IsPull && !ctx.Repo.CanRead(unit.TypePullRequests) ||
- !issue.IsPull && !ctx.Repo.CanRead(unit.TypeIssues) {
- ctx.NotFound(nil)
- }
- }
-
- func getActionIssues(ctx *context.Context) issues_model.IssueList {
- commaSeparatedIssueIDs := ctx.FormString("issue_ids")
- if len(commaSeparatedIssueIDs) == 0 {
- return nil
- }
- issueIDs := make([]int64, 0, 10)
- for stringIssueID := range strings.SplitSeq(commaSeparatedIssueIDs, ",") {
- issueID, err := strconv.ParseInt(stringIssueID, 10, 64)
- if err != nil {
- ctx.ServerError("ParseInt", err)
- return nil
- }
- issueIDs = append(issueIDs, issueID)
- }
- issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
- if err != nil {
- ctx.ServerError("GetIssuesByIDs", err)
- return nil
- }
- // Check access rights for all issues
- issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
- prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
- for _, issue := range issues {
- if issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound(errors.New("some issue's RepoID is incorrect"))
- return nil
- }
- if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
- ctx.NotFound(nil)
- return nil
- }
- if err = issue.LoadAttributes(ctx); err != nil {
- ctx.ServerError("LoadAttributes", err)
- return nil
- }
- }
- return issues
- }
-
- // GetIssueInfo get an issue of a repository
- func GetIssueInfo(ctx *context.Context) {
- issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
- if err != nil {
- if issues_model.IsErrIssueNotExist(err) {
- ctx.HTTPError(http.StatusNotFound)
- } else {
- ctx.HTTPError(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
- }
- return
- }
-
- if issue.IsPull {
- // Need to check if Pulls are enabled and we can read Pulls
- if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
- ctx.HTTPError(http.StatusNotFound)
- return
- }
- } else {
- // Need to check if Issues are enabled and we can read Issues
- if !ctx.Repo.CanRead(unit.TypeIssues) {
- ctx.HTTPError(http.StatusNotFound)
- return
- }
- }
-
- ctx.JSON(http.StatusOK, map[string]any{
- "convertedIssue": convert.ToIssue(ctx, ctx.Doer, issue),
- "renderedLabels": templates.NewRenderUtils(ctx).RenderLabels(issue.Labels, ctx.Repo.RepoLink, issue),
- })
- }
-
- // UpdateIssueTitle change issue's title
- func UpdateIssueTitle(ctx *context.Context) {
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
-
- if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.HTTPError(http.StatusForbidden)
- return
- }
-
- title := ctx.FormTrim("title")
- if len(title) == 0 {
- ctx.HTTPError(http.StatusNoContent)
- return
- }
-
- if err := issue_service.ChangeTitle(ctx, issue, ctx.Doer, title); err != nil {
- ctx.ServerError("ChangeTitle", err)
- return
- }
-
- ctx.JSON(http.StatusOK, map[string]any{
- "title": issue.Title,
- })
- }
-
- // UpdateIssueRef change issue's ref (branch)
- func UpdateIssueRef(ctx *context.Context) {
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
-
- if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) || issue.IsPull {
- ctx.HTTPError(http.StatusForbidden)
- return
- }
-
- ref := ctx.FormTrim("ref")
-
- if err := issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, ref); err != nil {
- ctx.ServerError("ChangeRef", err)
- return
- }
-
- ctx.JSON(http.StatusOK, map[string]any{
- "ref": ref,
- })
- }
-
- // UpdateIssueContent change issue's content
- func UpdateIssueContent(ctx *context.Context) {
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
-
- if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.HTTPError(http.StatusForbidden)
- return
- }
-
- if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, ctx.Req.FormValue("content"), ctx.FormInt("content_version")); err != nil {
- if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.JSONError(ctx.Tr("repo.issues.edit.blocked_user"))
- } else if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
- if issue.IsPull {
- ctx.JSONError(ctx.Tr("repo.pulls.edit.already_changed"))
- } else {
- ctx.JSONError(ctx.Tr("repo.issues.edit.already_changed"))
- }
- } else {
- ctx.ServerError("ChangeContent", err)
- }
- return
- }
-
- // when update the request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates
- if !ctx.FormBool("ignore_attachments") {
- if err := updateAttachments(ctx, issue, ctx.FormStrings("files[]")); err != nil {
- ctx.ServerError("UpdateAttachments", err)
- return
- }
- }
-
- rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{
- FootnoteContextID: "0",
- })
- content, err := markdown.RenderString(rctx, issue.Content)
- if err != nil {
- ctx.ServerError("RenderString", err)
- return
- }
-
- ctx.JSON(http.StatusOK, map[string]any{
- "content": content,
- "contentVersion": issue.ContentVersion,
- "attachments": attachmentsHTML(ctx, issue.Attachments, issue.Content),
- })
- }
-
- // UpdateIssueDeadline updates an issue deadline
- func UpdateIssueDeadline(ctx *context.Context) {
- issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
- if err != nil {
- if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.HTTPError(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
- }
- return
- }
-
- if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
- ctx.HTTPError(http.StatusForbidden, "", "Not repo writer")
- return
- }
-
- deadlineUnix, _ := common.ParseDeadlineDateToEndOfDay(ctx.FormString("deadline"))
- if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
- ctx.HTTPError(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
- return
- }
-
- ctx.JSONRedirect("")
- }
-
- // UpdateIssueMilestone change issue's milestone
- func UpdateIssueMilestone(ctx *context.Context) {
- issues := getActionIssues(ctx)
- if ctx.Written() {
- return
- }
-
- milestoneID := ctx.FormInt64("id")
- for _, issue := range issues {
- oldMilestoneID := issue.MilestoneID
- if oldMilestoneID == milestoneID {
- continue
- }
- issue.MilestoneID = milestoneID
- if milestoneID > 0 {
- var err error
- issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, milestoneID)
- if err != nil {
- ctx.ServerError("GetMilestoneByRepoID", err)
- return
- }
- } else {
- issue.Milestone = nil
- }
- if err := issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
- ctx.ServerError("ChangeMilestoneAssign", err)
- return
- }
- }
-
- ctx.JSONOK()
- }
-
- // UpdateIssueAssignee change issue's or pull's assignee
- func UpdateIssueAssignee(ctx *context.Context) {
- issues := getActionIssues(ctx)
- if ctx.Written() {
- return
- }
-
- assigneeID := ctx.FormInt64("id")
- action := ctx.FormString("action")
-
- for _, issue := range issues {
- switch action {
- case "clear":
- if err := issue_service.DeleteNotPassedAssignee(ctx, issue, ctx.Doer, []*user_model.User{}); err != nil {
- ctx.ServerError("ClearAssignees", err)
- return
- }
- default:
- assignee, err := user_model.GetUserByID(ctx, assigneeID)
- if err != nil {
- ctx.ServerError("GetUserByID", err)
- return
- }
-
- valid, err := access_model.CanBeAssigned(ctx, assignee, issue.Repo, issue.IsPull)
- if err != nil {
- ctx.ServerError("canBeAssigned", err)
- return
- }
- if !valid {
- ctx.ServerError("canBeAssigned", repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name})
- return
- }
-
- _, _, err = issue_service.ToggleAssigneeWithNotify(ctx, issue, ctx.Doer, assigneeID)
- if err != nil {
- ctx.ServerError("ToggleAssignee", err)
- return
- }
- }
- }
- ctx.JSONOK()
- }
-
- // ChangeIssueReaction create a reaction for issue
- func ChangeIssueReaction(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.ReactionForm)
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
-
- if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
- if log.IsTrace() {
- if ctx.IsSigned {
- issueType := "issues"
- if issue.IsPull {
- issueType = "pulls"
- }
- log.Trace("Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n"+
- "User in Repo has Permissions: %-+v",
- ctx.Doer,
- issue.PosterID,
- issueType,
- ctx.Repo.Repository,
- ctx.Repo.Permission)
- } else {
- log.Trace("Permission Denied: Not logged in")
- }
- }
-
- ctx.HTTPError(http.StatusForbidden)
- return
- }
-
- if ctx.HasError() {
- ctx.ServerError("ChangeIssueReaction", errors.New(ctx.GetErrMsg()))
- return
- }
-
- switch ctx.PathParam("action") {
- case "react":
- reaction, err := issue_service.CreateIssueReaction(ctx, ctx.Doer, issue, form.Content)
- if err != nil {
- if issues_model.IsErrForbiddenIssueReaction(err) || errors.Is(err, user_model.ErrBlockedUser) {
- ctx.ServerError("ChangeIssueReaction", err)
- return
- }
- log.Info("CreateIssueReaction: %s", err)
- break
- }
- // Reload new reactions
- issue.Reactions = nil
- if err = issue.LoadAttributes(ctx); err != nil {
- log.Info("issue.LoadAttributes: %s", err)
- break
- }
-
- log.Trace("Reaction for issue created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, reaction.ID)
- case "unreact":
- if err := issues_model.DeleteIssueReaction(ctx, ctx.Doer.ID, issue.ID, form.Content); err != nil {
- ctx.ServerError("DeleteIssueReaction", err)
- return
- }
-
- // Reload new reactions
- issue.Reactions = nil
- if err := issue.LoadAttributes(ctx); err != nil {
- log.Info("issue.LoadAttributes: %s", err)
- break
- }
-
- log.Trace("Reaction for issue removed: %d/%d", ctx.Repo.Repository.ID, issue.ID)
- default:
- ctx.NotFound(nil)
- return
- }
-
- if len(issue.Reactions) == 0 {
- ctx.JSON(http.StatusOK, map[string]any{
- "empty": true,
- "html": "",
- })
- return
- }
-
- html, err := ctx.RenderToHTML(tplReactions, map[string]any{
- "ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index),
- "Reactions": issue.Reactions.GroupByType(),
- })
- if err != nil {
- ctx.ServerError("ChangeIssueReaction.HTMLString", err)
- return
- }
- ctx.JSON(http.StatusOK, map[string]any{
- "html": html,
- })
- }
-
- // GetIssueAttachments returns attachments for the issue
- func GetIssueAttachments(ctx *context.Context) {
- issue := GetActionIssue(ctx)
- if ctx.Written() {
- return
- }
- attachments := make([]*api.Attachment, len(issue.Attachments))
- for i := 0; i < len(issue.Attachments); i++ {
- attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i])
- }
- ctx.JSON(http.StatusOK, attachments)
- }
-
- func updateAttachments(ctx *context.Context, item any, files []string) error {
- var attachments []*repo_model.Attachment
- switch content := item.(type) {
- case *issues_model.Issue:
- attachments = content.Attachments
- case *issues_model.Comment:
- attachments = content.Attachments
- default:
- return fmt.Errorf("unknown Type: %T", content)
- }
- for i := 0; i < len(attachments); i++ {
- if util.SliceContainsString(files, attachments[i].UUID) {
- continue
- }
- if err := repo_model.DeleteAttachment(ctx, attachments[i], true); err != nil {
- return err
- }
- }
- var err error
- if len(files) > 0 {
- switch content := item.(type) {
- case *issues_model.Issue:
- err = issues_model.UpdateIssueAttachments(ctx, content.ID, files)
- case *issues_model.Comment:
- err = issues_model.UpdateCommentAttachments(ctx, content, files)
- default:
- return fmt.Errorf("unknown Type: %T", content)
- }
- if err != nil {
- return err
- }
- }
- switch content := item.(type) {
- case *issues_model.Issue:
- content.Attachments, err = repo_model.GetAttachmentsByIssueID(ctx, content.ID)
- case *issues_model.Comment:
- content.Attachments, err = repo_model.GetAttachmentsByCommentID(ctx, content.ID)
- default:
- return fmt.Errorf("unknown Type: %T", content)
- }
- return err
- }
-
- func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) template.HTML {
- attachHTML, err := ctx.RenderToHTML(tplAttachment, map[string]any{
- "ctxData": ctx.Data,
- "Attachments": attachments,
- "Content": content,
- })
- if err != nil {
- ctx.ServerError("attachmentsHTML.HTMLString", err)
- return ""
- }
- return attachHTML
- }
-
- // handleMentionableAssigneesAndTeams gets all teams that current user can mention, and fills the assignee users to the context data
- func handleMentionableAssigneesAndTeams(ctx *context.Context, assignees []*user_model.User) {
- // TODO: need to figure out how many places this is really used, and rename it to "MentionableAssignees"
- // at the moment it is used on the issue list page, for the markdown editor mention
- ctx.Data["Assignees"] = assignees
-
- if ctx.Doer == nil || !ctx.Repo.Owner.IsOrganization() {
- return
- }
-
- var isAdmin bool
- var err error
- var teams []*organization.Team
- org := organization.OrgFromUser(ctx.Repo.Owner)
- // Admin has super access.
- if ctx.Doer.IsAdmin {
- isAdmin = true
- } else {
- isAdmin, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("IsOwnedBy", err)
- return
- }
- }
-
- if isAdmin {
- teams, err = org.LoadTeams(ctx)
- if err != nil {
- ctx.ServerError("LoadTeams", err)
- return
- }
- } else {
- teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("GetUserTeams", err)
- return
- }
- }
-
- ctx.Data["MentionableTeams"] = teams
- ctx.Data["MentionableTeamsOrg"] = ctx.Repo.Owner.Name
- ctx.Data["MentionableTeamsOrgAvatar"] = ctx.Repo.Owner.AvatarLink(ctx)
- }
|