code.gitea.io/gitea@v1.21.7/models/issues/issue_watch.go (about)

     1  // Copyright 2017 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  	repo_model "code.gitea.io/gitea/models/repo"
    11  	user_model "code.gitea.io/gitea/models/user"
    12  	"code.gitea.io/gitea/modules/timeutil"
    13  )
    14  
    15  // IssueWatch is connection request for receiving issue notification.
    16  type IssueWatch struct {
    17  	ID          int64              `xorm:"pk autoincr"`
    18  	UserID      int64              `xorm:"UNIQUE(watch) NOT NULL"`
    19  	IssueID     int64              `xorm:"UNIQUE(watch) NOT NULL"`
    20  	IsWatching  bool               `xorm:"NOT NULL"`
    21  	CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
    22  	UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
    23  }
    24  
    25  func init() {
    26  	db.RegisterModel(new(IssueWatch))
    27  }
    28  
    29  // IssueWatchList contains IssueWatch
    30  type IssueWatchList []*IssueWatch
    31  
    32  // CreateOrUpdateIssueWatch set watching for a user and issue
    33  func CreateOrUpdateIssueWatch(ctx context.Context, userID, issueID int64, isWatching bool) error {
    34  	iw, exists, err := GetIssueWatch(ctx, userID, issueID)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	if !exists {
    40  		iw = &IssueWatch{
    41  			UserID:     userID,
    42  			IssueID:    issueID,
    43  			IsWatching: isWatching,
    44  		}
    45  
    46  		if _, err := db.GetEngine(ctx).Insert(iw); err != nil {
    47  			return err
    48  		}
    49  	} else {
    50  		iw.IsWatching = isWatching
    51  
    52  		if _, err := db.GetEngine(ctx).ID(iw.ID).Cols("is_watching", "updated_unix").Update(iw); err != nil {
    53  			return err
    54  		}
    55  	}
    56  	return nil
    57  }
    58  
    59  // GetIssueWatch returns all IssueWatch objects from db by user and issue
    60  // the current Web-UI need iw object for watchers AND explicit non-watchers
    61  func GetIssueWatch(ctx context.Context, userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
    62  	iw = new(IssueWatch)
    63  	exists, err = db.GetEngine(ctx).
    64  		Where("user_id = ?", userID).
    65  		And("issue_id = ?", issueID).
    66  		Get(iw)
    67  	return iw, exists, err
    68  }
    69  
    70  // CheckIssueWatch check if an user is watching an issue
    71  // it takes participants and repo watch into account
    72  func CheckIssueWatch(ctx context.Context, user *user_model.User, issue *Issue) (bool, error) {
    73  	iw, exist, err := GetIssueWatch(ctx, user.ID, issue.ID)
    74  	if err != nil {
    75  		return false, err
    76  	}
    77  	if exist {
    78  		return iw.IsWatching, nil
    79  	}
    80  	w, err := repo_model.GetWatch(ctx, user.ID, issue.RepoID)
    81  	if err != nil {
    82  		return false, err
    83  	}
    84  	return repo_model.IsWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil
    85  }
    86  
    87  // GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id
    88  // but avoids joining with `user` for performance reasons
    89  // User permissions must be verified elsewhere if required
    90  func GetIssueWatchersIDs(ctx context.Context, issueID int64, watching bool) ([]int64, error) {
    91  	ids := make([]int64, 0, 64)
    92  	return ids, db.GetEngine(ctx).Table("issue_watch").
    93  		Where("issue_id=?", issueID).
    94  		And("is_watching = ?", watching).
    95  		Select("user_id").
    96  		Find(&ids)
    97  }
    98  
    99  // GetIssueWatchers returns watchers/unwatchers of a given issue
   100  func GetIssueWatchers(ctx context.Context, issueID int64, listOptions db.ListOptions) (IssueWatchList, error) {
   101  	sess := db.GetEngine(ctx).
   102  		Where("`issue_watch`.issue_id = ?", issueID).
   103  		And("`issue_watch`.is_watching = ?", true).
   104  		And("`user`.is_active = ?", true).
   105  		And("`user`.prohibit_login = ?", false).
   106  		Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id")
   107  
   108  	if listOptions.Page != 0 {
   109  		sess = db.SetSessionPagination(sess, &listOptions)
   110  		watches := make([]*IssueWatch, 0, listOptions.PageSize)
   111  		return watches, sess.Find(&watches)
   112  	}
   113  	watches := make([]*IssueWatch, 0, 8)
   114  	return watches, sess.Find(&watches)
   115  }
   116  
   117  // CountIssueWatchers count watchers/unwatchers of a given issue
   118  func CountIssueWatchers(ctx context.Context, issueID int64) (int64, error) {
   119  	return db.GetEngine(ctx).
   120  		Where("`issue_watch`.issue_id = ?", issueID).
   121  		And("`issue_watch`.is_watching = ?", true).
   122  		And("`user`.is_active = ?", true).
   123  		And("`user`.prohibit_login = ?", false).
   124  		Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id").Count(new(IssueWatch))
   125  }
   126  
   127  // RemoveIssueWatchersByRepoID remove issue watchers by repoID
   128  func RemoveIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error {
   129  	_, err := db.GetEngine(ctx).
   130  		Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
   131  		Where("`issue_watch`.user_id = ?", userID).
   132  		Delete(new(IssueWatch))
   133  	return err
   134  }