code.gitea.io/gitea@v1.21.7/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  	// Find all reviews by ReviewID
    78  	reviews := make(map[int64]*Review)
    79  	ids := make([]int64, 0, len(comments))
    80  	for _, comment := range comments {
    81  		if comment.ReviewID != 0 {
    82  			ids = append(ids, comment.ReviewID)
    83  		}
    84  	}
    85  	if err := e.In("id", ids).Find(&reviews); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	n := 0
    90  	for _, comment := range comments {
    91  		if re, ok := reviews[comment.ReviewID]; ok && re != nil {
    92  			// If the review is pending only the author can see the comments (except if the review is set)
    93  			if review.ID == 0 && re.Type == ReviewTypePending &&
    94  				(currentUser == nil || currentUser.ID != re.ReviewerID) {
    95  				continue
    96  			}
    97  			comment.Review = re
    98  		}
    99  		comments[n] = comment
   100  		n++
   101  
   102  		if err := comment.LoadResolveDoer(ctx); err != nil {
   103  			return nil, err
   104  		}
   105  
   106  		if err := comment.LoadReactions(ctx, issue.Repo); err != nil {
   107  			return nil, err
   108  		}
   109  
   110  		var err error
   111  		if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
   112  			Ctx: ctx,
   113  			Links: markup.Links{
   114  				Base: issue.Repo.Link(),
   115  			},
   116  			Metas: issue.Repo.ComposeMetas(),
   117  		}, comment.Content); err != nil {
   118  			return nil, err
   119  		}
   120  	}
   121  	return comments[:n], nil
   122  }
   123  
   124  // FetchCodeCommentsByLine fetches the code comments for a given treePath and line number
   125  func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) ([]*Comment, error) {
   126  	opts := FindCommentsOptions{
   127  		Type:     CommentTypeCode,
   128  		IssueID:  issue.ID,
   129  		TreePath: treePath,
   130  		Line:     line,
   131  	}
   132  	return findCodeComments(ctx, opts, issue, currentUser, nil, showOutdatedComments)
   133  }