code.gitea.io/gitea@v1.21.7/models/repo/watch.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package repo
     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/setting"
    12  	"code.gitea.io/gitea/modules/timeutil"
    13  )
    14  
    15  // WatchMode specifies what kind of watch the user has on a repository
    16  type WatchMode int8
    17  
    18  const (
    19  	// WatchModeNone don't watch
    20  	WatchModeNone WatchMode = iota // 0
    21  	// WatchModeNormal watch repository (from other sources)
    22  	WatchModeNormal // 1
    23  	// WatchModeDont explicit don't auto-watch
    24  	WatchModeDont // 2
    25  	// WatchModeAuto watch repository (from AutoWatchOnChanges)
    26  	WatchModeAuto // 3
    27  )
    28  
    29  // Watch is connection request for receiving repository notification.
    30  type Watch struct {
    31  	ID          int64              `xorm:"pk autoincr"`
    32  	UserID      int64              `xorm:"UNIQUE(watch)"`
    33  	RepoID      int64              `xorm:"UNIQUE(watch)"`
    34  	Mode        WatchMode          `xorm:"SMALLINT NOT NULL DEFAULT 1"`
    35  	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
    36  	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
    37  }
    38  
    39  func init() {
    40  	db.RegisterModel(new(Watch))
    41  }
    42  
    43  // GetWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
    44  func GetWatch(ctx context.Context, userID, repoID int64) (Watch, error) {
    45  	watch := Watch{UserID: userID, RepoID: repoID}
    46  	has, err := db.GetEngine(ctx).Get(&watch)
    47  	if err != nil {
    48  		return watch, err
    49  	}
    50  	if !has {
    51  		watch.Mode = WatchModeNone
    52  	}
    53  	return watch, nil
    54  }
    55  
    56  // IsWatchMode Decodes watchability of WatchMode
    57  func IsWatchMode(mode WatchMode) bool {
    58  	return mode != WatchModeNone && mode != WatchModeDont
    59  }
    60  
    61  // IsWatching checks if user has watched given repository.
    62  func IsWatching(ctx context.Context, userID, repoID int64) bool {
    63  	watch, err := GetWatch(ctx, userID, repoID)
    64  	return err == nil && IsWatchMode(watch.Mode)
    65  }
    66  
    67  func watchRepoMode(ctx context.Context, watch Watch, mode WatchMode) (err error) {
    68  	if watch.Mode == mode {
    69  		return nil
    70  	}
    71  	if mode == WatchModeAuto && (watch.Mode == WatchModeDont || IsWatchMode(watch.Mode)) {
    72  		// Don't auto watch if already watching or deliberately not watching
    73  		return nil
    74  	}
    75  
    76  	hadrec := watch.Mode != WatchModeNone
    77  	needsrec := mode != WatchModeNone
    78  	repodiff := 0
    79  
    80  	if IsWatchMode(mode) && !IsWatchMode(watch.Mode) {
    81  		repodiff = 1
    82  	} else if !IsWatchMode(mode) && IsWatchMode(watch.Mode) {
    83  		repodiff = -1
    84  	}
    85  
    86  	watch.Mode = mode
    87  
    88  	e := db.GetEngine(ctx)
    89  
    90  	if !hadrec && needsrec {
    91  		watch.Mode = mode
    92  		if _, err = e.Insert(watch); err != nil {
    93  			return err
    94  		}
    95  	} else if needsrec {
    96  		watch.Mode = mode
    97  		if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil {
    98  			return err
    99  		}
   100  	} else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil {
   101  		return err
   102  	}
   103  	if repodiff != 0 {
   104  		_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID)
   105  	}
   106  	return err
   107  }
   108  
   109  // WatchRepoMode watch repository in specific mode.
   110  func WatchRepoMode(userID, repoID int64, mode WatchMode) (err error) {
   111  	var watch Watch
   112  	if watch, err = GetWatch(db.DefaultContext, userID, repoID); err != nil {
   113  		return err
   114  	}
   115  	return watchRepoMode(db.DefaultContext, watch, mode)
   116  }
   117  
   118  // WatchRepo watch or unwatch repository.
   119  func WatchRepo(ctx context.Context, userID, repoID int64, doWatch bool) (err error) {
   120  	var watch Watch
   121  	if watch, err = GetWatch(ctx, userID, repoID); err != nil {
   122  		return err
   123  	}
   124  	if !doWatch && watch.Mode == WatchModeAuto {
   125  		err = watchRepoMode(ctx, watch, WatchModeDont)
   126  	} else if !doWatch {
   127  		err = watchRepoMode(ctx, watch, WatchModeNone)
   128  	} else {
   129  		err = watchRepoMode(ctx, watch, WatchModeNormal)
   130  	}
   131  	return err
   132  }
   133  
   134  // GetWatchers returns all watchers of given repository.
   135  func GetWatchers(ctx context.Context, repoID int64) ([]*Watch, error) {
   136  	watches := make([]*Watch, 0, 10)
   137  	return watches, db.GetEngine(ctx).Where("`watch`.repo_id=?", repoID).
   138  		And("`watch`.mode<>?", WatchModeDont).
   139  		And("`user`.is_active=?", true).
   140  		And("`user`.prohibit_login=?", false).
   141  		Join("INNER", "`user`", "`user`.id = `watch`.user_id").
   142  		Find(&watches)
   143  }
   144  
   145  // GetRepoWatchersIDs returns IDs of watchers for a given repo ID
   146  // but avoids joining with `user` for performance reasons
   147  // User permissions must be verified elsewhere if required
   148  func GetRepoWatchersIDs(ctx context.Context, repoID int64) ([]int64, error) {
   149  	ids := make([]int64, 0, 64)
   150  	return ids, db.GetEngine(ctx).Table("watch").
   151  		Where("watch.repo_id=?", repoID).
   152  		And("watch.mode<>?", WatchModeDont).
   153  		Select("user_id").
   154  		Find(&ids)
   155  }
   156  
   157  // GetRepoWatchers returns range of users watching given repository.
   158  func GetRepoWatchers(ctx context.Context, repoID int64, opts db.ListOptions) ([]*user_model.User, error) {
   159  	sess := db.GetEngine(ctx).Where("watch.repo_id=?", repoID).
   160  		Join("LEFT", "watch", "`user`.id=`watch`.user_id").
   161  		And("`watch`.mode<>?", WatchModeDont)
   162  	if opts.Page > 0 {
   163  		sess = db.SetSessionPagination(sess, &opts)
   164  		users := make([]*user_model.User, 0, opts.PageSize)
   165  
   166  		return users, sess.Find(&users)
   167  	}
   168  
   169  	users := make([]*user_model.User, 0, 8)
   170  	return users, sess.Find(&users)
   171  }
   172  
   173  // WatchIfAuto subscribes to repo if AutoWatchOnChanges is set
   174  func WatchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error {
   175  	if !isWrite || !setting.Service.AutoWatchOnChanges {
   176  		return nil
   177  	}
   178  	watch, err := GetWatch(ctx, userID, repoID)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	if watch.Mode != WatchModeNone {
   183  		return nil
   184  	}
   185  	return watchRepoMode(ctx, watch, WatchModeAuto)
   186  }