code.gitea.io/gitea@v1.22.3/models/issues/comment_code.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package issues
     5  
     6  import (
     7  	"context"
     8  
     9  	"code.gitea.io/gitea/models/db"
    10  	user_model "code.gitea.io/gitea/models/user"
    11  	"code.gitea.io/gitea/modules/markup"
    12  	"code.gitea.io/gitea/modules/markup/markdown"
    13  
    14  	"xorm.io/builder"
    15  )
    16  
    17  // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS
    18  type CodeComments map[string]map[int64][]*Comment
    19  
    20  // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
    21  func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) {
    22  	return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments)
    23  }
    24  
    25  func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) {
    26  	pathToLineToComment := make(CodeComments)
    27  	if review == nil {
    28  		review = &Review{ID: 0}
    29  	}
    30  	opts := FindCommentsOptions{
    31  		Type:     CommentTypeCode,
    32  		IssueID:  issue.ID,
    33  		ReviewID: review.ID,
    34  	}
    35  
    36  	comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	for _, comment := range comments {
    42  		if pathToLineToComment[comment.TreePath] == nil {
    43  			pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment)
    44  		}
    45  		pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment)
    46  	}
    47  	return pathToLineToComment, nil
    48  }
    49  
    50  func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) {
    51  	var comments CommentList
    52  	if review == nil {
    53  		review = &Review{ID: 0}
    54  	}
    55  	conds := opts.ToConds()
    56  
    57  	if !showOutdatedComments && review.ID == 0 {
    58  		conds = conds.And(builder.Eq{"invalidated": false})
    59  	}
    60  
    61  	e := db.GetEngine(ctx)
    62  	if err := e.Where(conds).
    63  		Asc("comment.created_unix").
    64  		Asc("comment.id").
    65  		Find(&comments); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	if err := issue.LoadRepo(ctx); err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	if err := comments.LoadPosters(ctx); err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	if err := comments.LoadAttachments(ctx); err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	// Find all reviews by ReviewID
    82  	reviews := make(map[int64]*Review)
    83  	ids := make([]int64, 0, len(comments))
    84  	for _, comment := range comments {
    85  		if comment.ReviewID != 0 {
    86  			ids = append(ids, comment.ReviewID)
    87  		}
    88  	}
    89  	if err := e.In("id", ids).Find(&reviews); err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	n := 0
    94  	for _, comment := range comments {
    95  		if re, ok := reviews[comment.ReviewID]; ok && re != nil {
    96  			// If the review is pending only the author can see the comments (except if the review is set)
    97  			if review.ID == 0 && re.Type == ReviewTypePending &&
    98  				(currentUser == nil || currentUser.ID != re.ReviewerID) {
    99  				continue
   100  			}
   101  			comment.Review = re
   102  		}
   103  		comments[n] = comment
   104  		n++
   105  
   106  		if err := comment.LoadResolveDoer(ctx); err != nil {
   107  			return nil, err
   108  		}
   109  
   110  		if err := comment.LoadReactions(ctx, issue.Repo); err != nil {
   111  			return nil, err
   112  		}
   113  
   114  		var err error
   115  		if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
   116  			Ctx: ctx,
   117  			Links: markup.Links{
   118  				Base: issue.Repo.Link(),
   119  			},
   120  			Metas: issue.Repo.ComposeMetas(ctx),
   121  		}, comment.Content); err != nil {
   122  			return nil, err
   123  		}
   124  	}
   125  	return comments[:n], nil
   126  }
   127  
   128  // FetchCodeCommentsByLine fetches the code comments for a given treePath and line number
   129  func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) (CommentList, error) {
   130  	opts := FindCommentsOptions{
   131  		Type:     CommentTypeCode,
   132  		IssueID:  issue.ID,
   133  		TreePath: treePath,
   134  		Line:     line,
   135  	}
   136  	return findCodeComments(ctx, opts, issue, currentUser, nil, showOutdatedComments)
   137  }