mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Improve utils of slices (#22379)
- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
  - It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
			
			
This commit is contained in:
		@@ -950,7 +950,7 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
 | 
			
		||||
	if c.IsSet("auth-type") {
 | 
			
		||||
		conf.Auth = c.String("auth-type")
 | 
			
		||||
		validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
 | 
			
		||||
		if !contains(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
 | 
			
		||||
		if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
 | 
			
		||||
			return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
 | 
			
		||||
		}
 | 
			
		||||
		conf.Auth = c.String("auth-type")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								cmd/dump.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								cmd/dump.go
									
									
									
									
									
								
							@@ -409,15 +409,6 @@ func runDump(ctx *cli.Context) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func contains(slice []string, s string) bool {
 | 
			
		||||
	for _, v := range slice {
 | 
			
		||||
		if v == s {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
 | 
			
		||||
func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
 | 
			
		||||
	absPath, err := filepath.Abs(absPath)
 | 
			
		||||
@@ -438,7 +429,7 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
 | 
			
		||||
		currentAbsPath := path.Join(absPath, file.Name())
 | 
			
		||||
		currentInsidePath := path.Join(insidePath, file.Name())
 | 
			
		||||
		if file.IsDir() {
 | 
			
		||||
			if !contains(excludeAbsPath, currentAbsPath) {
 | 
			
		||||
			if !util.SliceContainsString(excludeAbsPath, currentAbsPath) {
 | 
			
		||||
				if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -409,14 +409,14 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
 | 
			
		||||
		sshKeySplit := strings.Split(v, " ")
 | 
			
		||||
		if len(sshKeySplit) > 1 {
 | 
			
		||||
			key := strings.Join(sshKeySplit[:2], " ")
 | 
			
		||||
			if !util.ExistsInSlice(key, providedKeys) {
 | 
			
		||||
			if !util.SliceContainsString(providedKeys, key) {
 | 
			
		||||
				providedKeys = append(providedKeys, key)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if Public Key sync is needed
 | 
			
		||||
	if util.IsEqualSlice(giteaKeys, providedKeys) {
 | 
			
		||||
	if util.SliceSortedEqual(giteaKeys, providedKeys) {
 | 
			
		||||
		log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
@@ -425,7 +425,7 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
 | 
			
		||||
	// Add new Public SSH Keys that doesn't already exist in DB
 | 
			
		||||
	var newKeys []string
 | 
			
		||||
	for _, key := range providedKeys {
 | 
			
		||||
		if !util.ExistsInSlice(key, giteaKeys) {
 | 
			
		||||
		if !util.SliceContainsString(giteaKeys, key) {
 | 
			
		||||
			newKeys = append(newKeys, key)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -436,7 +436,7 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
 | 
			
		||||
	// Mark keys from DB that no longer exist in the source for deletion
 | 
			
		||||
	var giteaKeysToDelete []string
 | 
			
		||||
	for _, giteaKey := range giteaKeys {
 | 
			
		||||
		if !util.ExistsInSlice(giteaKey, providedKeys) {
 | 
			
		||||
		if !util.SliceContainsString(providedKeys, giteaKey) {
 | 
			
		||||
			log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
 | 
			
		||||
			giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,13 +69,13 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
 | 
			
		||||
			if ip != nil && ip.IsLoopback() {
 | 
			
		||||
				// strip port
 | 
			
		||||
				uri.Host = uri.Hostname()
 | 
			
		||||
				if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) {
 | 
			
		||||
				if util.SliceContainsString(app.RedirectURIs, uri.String(), true) {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
 | 
			
		||||
	return util.SliceContainsString(app.RedirectURIs, redirectURI, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base32 characters, but lowercased.
 | 
			
		||||
 
 | 
			
		||||
@@ -342,7 +342,7 @@ func IsProtectedBranch(ctx context.Context, repoID int64, branchName string) (bo
 | 
			
		||||
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
 | 
			
		||||
// the users from newWhitelist which have explicit read or write access to the repo.
 | 
			
		||||
func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
 | 
			
		||||
	hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
 | 
			
		||||
	hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
 | 
			
		||||
	if !hasUsersChanged {
 | 
			
		||||
		return currentWhitelist, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -363,7 +363,7 @@ func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, c
 | 
			
		||||
// updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
 | 
			
		||||
// the users from newWhitelist which have write access to the repo.
 | 
			
		||||
func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
 | 
			
		||||
	hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
 | 
			
		||||
	hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
 | 
			
		||||
	if !hasUsersChanged {
 | 
			
		||||
		return currentWhitelist, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -392,7 +392,7 @@ func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, curre
 | 
			
		||||
// updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with
 | 
			
		||||
// the teams from newWhitelist which have write access to the repo.
 | 
			
		||||
func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
 | 
			
		||||
	hasTeamsChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
 | 
			
		||||
	hasTeamsChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
 | 
			
		||||
	if !hasTeamsChanged {
 | 
			
		||||
		return currentWhitelist, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -404,7 +404,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre
 | 
			
		||||
 | 
			
		||||
	whitelist = make([]int64, 0, len(teams))
 | 
			
		||||
	for i := range teams {
 | 
			
		||||
		if util.IsInt64InSlice(teams[i].ID, newWhitelist) {
 | 
			
		||||
		if util.SliceContains(newWhitelist, teams[i].ID) {
 | 
			
		||||
			whitelist = append(whitelist, teams[i].ID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,7 @@ func MakeIDsFromAPIAssigneesToAdd(ctx context.Context, oneAssignee string, multi
 | 
			
		||||
	var requestAssignees []string
 | 
			
		||||
 | 
			
		||||
	// Keeping the old assigning method for compatibility reasons
 | 
			
		||||
	if oneAssignee != "" && !util.IsStringInSlice(oneAssignee, multipleAssignees) {
 | 
			
		||||
	if oneAssignee != "" && !util.SliceContainsString(multipleAssignees, oneAssignee) {
 | 
			
		||||
		requestAssignees = append(requestAssignees, oneAssignee)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1529,7 +1529,7 @@ func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool {
 | 
			
		||||
		log.Error(err.Error())
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return util.IsInt64InSlice(user.ID, userIDs)
 | 
			
		||||
	return util.SliceContains(userIDs, user.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateIssueMentions updates issue-user relations for mentioned users.
 | 
			
		||||
@@ -2023,7 +2023,7 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro
 | 
			
		||||
		Find(&userIDs); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("get poster IDs: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !util.IsInt64InSlice(issue.PosterID, userIDs) {
 | 
			
		||||
	if !util.SliceContains(userIDs, issue.PosterID) {
 | 
			
		||||
		return append(userIDs, issue.PosterID), nil
 | 
			
		||||
	}
 | 
			
		||||
	return userIDs, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -398,20 +398,13 @@ func DeleteTeam(t *organization.Team) error {
 | 
			
		||||
			return fmt.Errorf("findProtectedBranches: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, p := range protections {
 | 
			
		||||
			var matched1, matched2, matched3 bool
 | 
			
		||||
			if len(p.WhitelistTeamIDs) != 0 {
 | 
			
		||||
				p.WhitelistTeamIDs, matched1 = util.RemoveIDFromList(
 | 
			
		||||
					p.WhitelistTeamIDs, t.ID)
 | 
			
		||||
			}
 | 
			
		||||
			if len(p.ApprovalsWhitelistTeamIDs) != 0 {
 | 
			
		||||
				p.ApprovalsWhitelistTeamIDs, matched2 = util.RemoveIDFromList(
 | 
			
		||||
					p.ApprovalsWhitelistTeamIDs, t.ID)
 | 
			
		||||
			}
 | 
			
		||||
			if len(p.MergeWhitelistTeamIDs) != 0 {
 | 
			
		||||
				p.MergeWhitelistTeamIDs, matched3 = util.RemoveIDFromList(
 | 
			
		||||
					p.MergeWhitelistTeamIDs, t.ID)
 | 
			
		||||
			}
 | 
			
		||||
			if matched1 || matched2 || matched3 {
 | 
			
		||||
			lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistTeamIDs), len(p.ApprovalsWhitelistTeamIDs), len(p.MergeWhitelistTeamIDs)
 | 
			
		||||
			p.WhitelistTeamIDs = util.SliceRemoveAll(p.WhitelistTeamIDs, t.ID)
 | 
			
		||||
			p.ApprovalsWhitelistTeamIDs = util.SliceRemoveAll(p.ApprovalsWhitelistTeamIDs, t.ID)
 | 
			
		||||
			p.MergeWhitelistTeamIDs = util.SliceRemoveAll(p.MergeWhitelistTeamIDs, t.ID)
 | 
			
		||||
			if lenIDs != len(p.WhitelistTeamIDs) ||
 | 
			
		||||
				lenApprovalIDs != len(p.ApprovalsWhitelistTeamIDs) ||
 | 
			
		||||
				lenMergeIDs != len(p.MergeWhitelistTeamIDs) {
 | 
			
		||||
				if _, err = sess.ID(p.ID).Cols(
 | 
			
		||||
					"whitelist_team_i_ds",
 | 
			
		||||
					"merge_whitelist_team_i_ds",
 | 
			
		||||
 
 | 
			
		||||
@@ -141,20 +141,13 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			for _, p := range protections {
 | 
			
		||||
				var matched1, matched2, matched3 bool
 | 
			
		||||
				if len(p.WhitelistUserIDs) != 0 {
 | 
			
		||||
					p.WhitelistUserIDs, matched1 = util.RemoveIDFromList(
 | 
			
		||||
						p.WhitelistUserIDs, u.ID)
 | 
			
		||||
				}
 | 
			
		||||
				if len(p.ApprovalsWhitelistUserIDs) != 0 {
 | 
			
		||||
					p.ApprovalsWhitelistUserIDs, matched2 = util.RemoveIDFromList(
 | 
			
		||||
						p.ApprovalsWhitelistUserIDs, u.ID)
 | 
			
		||||
				}
 | 
			
		||||
				if len(p.MergeWhitelistUserIDs) != 0 {
 | 
			
		||||
					p.MergeWhitelistUserIDs, matched3 = util.RemoveIDFromList(
 | 
			
		||||
						p.MergeWhitelistUserIDs, u.ID)
 | 
			
		||||
				}
 | 
			
		||||
				if matched1 || matched2 || matched3 {
 | 
			
		||||
				lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistUserIDs), len(p.ApprovalsWhitelistUserIDs), len(p.MergeWhitelistUserIDs)
 | 
			
		||||
				p.WhitelistUserIDs = util.SliceRemoveAll(p.WhitelistUserIDs, u.ID)
 | 
			
		||||
				p.ApprovalsWhitelistUserIDs = util.SliceRemoveAll(p.ApprovalsWhitelistUserIDs, u.ID)
 | 
			
		||||
				p.MergeWhitelistUserIDs = util.SliceRemoveAll(p.MergeWhitelistUserIDs, u.ID)
 | 
			
		||||
				if lenIDs != len(p.WhitelistUserIDs) ||
 | 
			
		||||
					lenApprovalIDs != len(p.ApprovalsWhitelistUserIDs) ||
 | 
			
		||||
					lenMergeIDs != len(p.MergeWhitelistUserIDs) {
 | 
			
		||||
					if _, err = e.ID(p.ID).Cols(
 | 
			
		||||
						"whitelist_user_i_ds",
 | 
			
		||||
						"merge_whitelist_user_i_ds",
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ func LoadRepoConfig() {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, f := range customFiles {
 | 
			
		||||
				if !util.IsStringInSlice(f, files, true) {
 | 
			
		||||
				if !util.SliceContainsString(files, f, true) {
 | 
			
		||||
					files = append(files, f)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -200,12 +200,12 @@ func LoadRepoConfig() {
 | 
			
		||||
	// Filter out invalid names and promote preferred licenses.
 | 
			
		||||
	sortedLicenses := make([]string, 0, len(Licenses))
 | 
			
		||||
	for _, name := range setting.Repository.PreferredLicenses {
 | 
			
		||||
		if util.IsStringInSlice(name, Licenses, true) {
 | 
			
		||||
		if util.SliceContainsString(Licenses, name, true) {
 | 
			
		||||
			sortedLicenses = append(sortedLicenses, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, name := range Licenses {
 | 
			
		||||
		if !util.IsStringInSlice(name, setting.Repository.PreferredLicenses, true) {
 | 
			
		||||
		if !util.SliceContainsString(setting.Repository.PreferredLicenses, name, true) {
 | 
			
		||||
			sortedLicenses = append(sortedLicenses, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Int64Slice attaches the methods of Interface to []int64, sorting in increasing order.
 | 
			
		||||
type Int64Slice []int64
 | 
			
		||||
 | 
			
		||||
func (p Int64Slice) Len() int           { return len(p) }
 | 
			
		||||
func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] }
 | 
			
		||||
func (p Int64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 | 
			
		||||
 | 
			
		||||
// IsSliceInt64Eq returns if the two slice has the same elements but different sequences.
 | 
			
		||||
func IsSliceInt64Eq(a, b []int64) bool {
 | 
			
		||||
	if len(a) != len(b) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(Int64Slice(a))
 | 
			
		||||
	sort.Sort(Int64Slice(b))
 | 
			
		||||
	for i := 0; i < len(a); i++ {
 | 
			
		||||
		if a[i] != b[i] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExistsInSlice returns true if string exists in slice.
 | 
			
		||||
func ExistsInSlice(target string, slice []string) bool {
 | 
			
		||||
	i := sort.Search(len(slice),
 | 
			
		||||
		func(i int) bool { return slice[i] == target })
 | 
			
		||||
	return i < len(slice)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsStringInSlice sequential searches if string exists in slice.
 | 
			
		||||
func IsStringInSlice(target string, slice []string, insensitive ...bool) bool {
 | 
			
		||||
	caseInsensitive := false
 | 
			
		||||
	if len(insensitive) != 0 && insensitive[0] {
 | 
			
		||||
		caseInsensitive = true
 | 
			
		||||
		target = strings.ToLower(target)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(slice); i++ {
 | 
			
		||||
		if caseInsensitive {
 | 
			
		||||
			if strings.ToLower(slice[i]) == target {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if slice[i] == target {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsInt64InSlice sequential searches if int64 exists in slice.
 | 
			
		||||
func IsInt64InSlice(target int64, slice []int64) bool {
 | 
			
		||||
	for i := 0; i < len(slice); i++ {
 | 
			
		||||
		if slice[i] == target {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEqualSlice returns true if slices are equal.
 | 
			
		||||
func IsEqualSlice(target, source []string) bool {
 | 
			
		||||
	if len(target) != len(source) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (target == nil) != (source == nil) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(target)
 | 
			
		||||
	sort.Strings(source)
 | 
			
		||||
 | 
			
		||||
	for i, v := range target {
 | 
			
		||||
		if v != source[i] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,90 @@
 | 
			
		||||
// Copyright 2022 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
// Most of the functions in this file can have better implementations with "golang.org/x/exp/slices".
 | 
			
		||||
// However, "golang.org/x/exp" is experimental and unreliable, we shouldn't use it.
 | 
			
		||||
// So lets waiting for the "slices" has be promoted to the main repository one day.
 | 
			
		||||
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
// RemoveIDFromList removes the given ID from the slice, if found.
 | 
			
		||||
// It does not preserve order, and assumes the ID is unique.
 | 
			
		||||
func RemoveIDFromList(list []int64, id int64) ([]int64, bool) {
 | 
			
		||||
	n := len(list) - 1
 | 
			
		||||
	for i, item := range list {
 | 
			
		||||
		if item == id {
 | 
			
		||||
			list[i] = list[n]
 | 
			
		||||
			return list[:n], true
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
// SliceContains returns true if the target exists in the slice.
 | 
			
		||||
func SliceContains[T comparable](slice []T, target T) bool {
 | 
			
		||||
	return SliceContainsFunc(slice, func(t T) bool { return t == target })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceContainsFunc returns true if any element in the slice satisfies the targetFunc.
 | 
			
		||||
func SliceContainsFunc[T any](slice []T, targetFunc func(T) bool) bool {
 | 
			
		||||
	for _, v := range slice {
 | 
			
		||||
		if targetFunc(v) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return list, false
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceContainsString sequential searches if string exists in slice.
 | 
			
		||||
func SliceContainsString(slice []string, target string, insensitive ...bool) bool {
 | 
			
		||||
	if len(insensitive) != 0 && insensitive[0] {
 | 
			
		||||
		target = strings.ToLower(target)
 | 
			
		||||
		return SliceContainsFunc(slice, func(t string) bool { return strings.ToLower(t) == target })
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return SliceContains(slice, target)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceSortedEqual returns true if the two slices will be equal when they get sorted.
 | 
			
		||||
// It doesn't require that the slices have been sorted, and it doesn't sort them either.
 | 
			
		||||
func SliceSortedEqual[T comparable](s1, s2 []T) bool {
 | 
			
		||||
	if len(s1) != len(s2) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	counts := make(map[T]int, len(s1))
 | 
			
		||||
	for _, v := range s1 {
 | 
			
		||||
		counts[v]++
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range s2 {
 | 
			
		||||
		counts[v]--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, v := range counts {
 | 
			
		||||
		if v != 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceEqual returns true if the two slices are equal.
 | 
			
		||||
func SliceEqual[T comparable](s1, s2 []T) bool {
 | 
			
		||||
	if len(s1) != len(s2) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, v := range s1 {
 | 
			
		||||
		if s2[i] != v {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceRemoveAll removes all the target elements from the slice.
 | 
			
		||||
func SliceRemoveAll[T comparable](slice []T, target T) []T {
 | 
			
		||||
	return SliceRemoveAllFunc(slice, func(t T) bool { return t == target })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SliceRemoveAllFunc removes all elements which satisfy the targetFunc from the slice.
 | 
			
		||||
func SliceRemoveAllFunc[T comparable](slice []T, targetFunc func(T) bool) []T {
 | 
			
		||||
	idx := 0
 | 
			
		||||
	for _, v := range slice {
 | 
			
		||||
		if targetFunc(v) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		slice[idx] = v
 | 
			
		||||
		idx++
 | 
			
		||||
	}
 | 
			
		||||
	return slice[:idx]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										88
									
								
								modules/util/slice_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								modules/util/slice_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSliceContains(t *testing.T) {
 | 
			
		||||
	assert.True(t, SliceContains([]int{2, 0, 2, 3}, 2))
 | 
			
		||||
	assert.True(t, SliceContains([]int{2, 0, 2, 3}, 0))
 | 
			
		||||
	assert.True(t, SliceContains([]int{2, 0, 2, 3}, 3))
 | 
			
		||||
 | 
			
		||||
	assert.True(t, SliceContains([]string{"2", "0", "2", "3"}, "0"))
 | 
			
		||||
	assert.True(t, SliceContains([]float64{2, 0, 2, 3}, 0))
 | 
			
		||||
	assert.True(t, SliceContains([]bool{false, true, false}, true))
 | 
			
		||||
 | 
			
		||||
	assert.False(t, SliceContains([]int{2, 0, 2, 3}, 4))
 | 
			
		||||
	assert.False(t, SliceContains([]int{}, 4))
 | 
			
		||||
	assert.False(t, SliceContains(nil, 4))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSliceContainsString(t *testing.T) {
 | 
			
		||||
	assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "a"))
 | 
			
		||||
	assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "b"))
 | 
			
		||||
	assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "A", true))
 | 
			
		||||
	assert.True(t, SliceContainsString([]string{"C", "B", "A", "B"}, "a", true))
 | 
			
		||||
 | 
			
		||||
	assert.False(t, SliceContainsString([]string{"c", "b", "a", "b"}, "z"))
 | 
			
		||||
	assert.False(t, SliceContainsString([]string{"c", "b", "a", "b"}, "A"))
 | 
			
		||||
	assert.False(t, SliceContainsString([]string{}, "a"))
 | 
			
		||||
	assert.False(t, SliceContainsString(nil, "a"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSliceSortedEqual(t *testing.T) {
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int{2, 0, 2, 3}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int{3, 0, 2, 2}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int{}, []int{}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int(nil), nil))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int(nil), []int{}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]int{}, []int{}))
 | 
			
		||||
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]string{"2", "0", "2", "3"}, []string{"2", "0", "2", "3"}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]float64{2, 0, 2, 3}, []float64{2, 0, 2, 3}))
 | 
			
		||||
	assert.True(t, SliceSortedEqual([]bool{false, true, false}, []bool{false, true, false}))
 | 
			
		||||
 | 
			
		||||
	assert.False(t, SliceSortedEqual([]int{2, 0, 2}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceSortedEqual([]int{}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceSortedEqual(nil, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceSortedEqual([]int{2, 0, 2, 4}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceSortedEqual([]int{2, 0, 0, 3}, []int{2, 0, 2, 3}))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSliceEqual(t *testing.T) {
 | 
			
		||||
	assert.True(t, SliceEqual([]int{2, 0, 2, 3}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.True(t, SliceEqual([]int{}, []int{}))
 | 
			
		||||
	assert.True(t, SliceEqual([]int(nil), nil))
 | 
			
		||||
	assert.True(t, SliceEqual([]int(nil), []int{}))
 | 
			
		||||
	assert.True(t, SliceEqual([]int{}, []int{}))
 | 
			
		||||
 | 
			
		||||
	assert.True(t, SliceEqual([]string{"2", "0", "2", "3"}, []string{"2", "0", "2", "3"}))
 | 
			
		||||
	assert.True(t, SliceEqual([]float64{2, 0, 2, 3}, []float64{2, 0, 2, 3}))
 | 
			
		||||
	assert.True(t, SliceEqual([]bool{false, true, false}, []bool{false, true, false}))
 | 
			
		||||
 | 
			
		||||
	assert.False(t, SliceEqual([]int{3, 0, 2, 2}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceEqual([]int{2, 0, 2}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceEqual([]int{}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceEqual(nil, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceEqual([]int{2, 0, 2, 4}, []int{2, 0, 2, 3}))
 | 
			
		||||
	assert.False(t, SliceEqual([]int{2, 0, 0, 3}, []int{2, 0, 2, 3}))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSliceRemoveAll(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 0), []int{2, 2, 3})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 2), []int{0, 3})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{0, 0, 0, 0}, 0), []int{})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 4), []int{2, 0, 2, 3})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{}, 0), []int{})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int(nil), 0), []int(nil))
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]int{}, 0), []int{})
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]string{"2", "0", "2", "3"}, "0"), []string{"2", "2", "3"})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]float64{2, 0, 2, 3}, 0), []float64{2, 2, 3})
 | 
			
		||||
	assert.Equal(t, SliceRemoveAll([]bool{false, true, false}, true), []bool{false, false})
 | 
			
		||||
}
 | 
			
		||||
@@ -107,11 +107,11 @@ func toAPIHook(ctx *context.APIContext, repoLink string, hook *webhook.Webhook)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func issuesHook(events []string, event string) bool {
 | 
			
		||||
	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook_module.HookEventIssues), events, true)
 | 
			
		||||
	return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventIssues), true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pullHook(events []string, event string) bool {
 | 
			
		||||
	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook_module.HookEventPullRequest), events, true)
 | 
			
		||||
	return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addHook add the hook specified by `form`, `orgID` and `repoID`. If there is
 | 
			
		||||
@@ -130,15 +130,15 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 | 
			
		||||
		HookEvent: &webhook_module.HookEvent{
 | 
			
		||||
			ChooseEvents: true,
 | 
			
		||||
			HookEvents: webhook_module.HookEvents{
 | 
			
		||||
				Create:               util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true),
 | 
			
		||||
				Delete:               util.IsStringInSlice(string(webhook_module.HookEventDelete), form.Events, true),
 | 
			
		||||
				Fork:                 util.IsStringInSlice(string(webhook_module.HookEventFork), form.Events, true),
 | 
			
		||||
				Create:               util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true),
 | 
			
		||||
				Delete:               util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true),
 | 
			
		||||
				Fork:                 util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true),
 | 
			
		||||
				Issues:               issuesHook(form.Events, "issues_only"),
 | 
			
		||||
				IssueAssign:          issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)),
 | 
			
		||||
				IssueLabel:           issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)),
 | 
			
		||||
				IssueMilestone:       issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)),
 | 
			
		||||
				IssueComment:         issuesHook(form.Events, string(webhook_module.HookEventIssueComment)),
 | 
			
		||||
				Push:                 util.IsStringInSlice(string(webhook_module.HookEventPush), form.Events, true),
 | 
			
		||||
				Push:                 util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true),
 | 
			
		||||
				PullRequest:          pullHook(form.Events, "pull_request_only"),
 | 
			
		||||
				PullRequestAssign:    pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)),
 | 
			
		||||
				PullRequestLabel:     pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)),
 | 
			
		||||
@@ -146,9 +146,9 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 | 
			
		||||
				PullRequestComment:   pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)),
 | 
			
		||||
				PullRequestReview:    pullHook(form.Events, "pull_request_review"),
 | 
			
		||||
				PullRequestSync:      pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)),
 | 
			
		||||
				Wiki:                 util.IsStringInSlice(string(webhook_module.HookEventWiki), form.Events, true),
 | 
			
		||||
				Repository:           util.IsStringInSlice(string(webhook_module.HookEventRepository), form.Events, true),
 | 
			
		||||
				Release:              util.IsStringInSlice(string(webhook_module.HookEventRelease), form.Events, true),
 | 
			
		||||
				Wiki:                 util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true),
 | 
			
		||||
				Repository:           util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true),
 | 
			
		||||
				Release:              util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true),
 | 
			
		||||
			},
 | 
			
		||||
			BranchFilter: form.BranchFilter,
 | 
			
		||||
		},
 | 
			
		||||
@@ -277,14 +277,14 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
 | 
			
		||||
	w.PushOnly = false
 | 
			
		||||
	w.SendEverything = false
 | 
			
		||||
	w.ChooseEvents = true
 | 
			
		||||
	w.Create = util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true)
 | 
			
		||||
	w.Push = util.IsStringInSlice(string(webhook_module.HookEventPush), form.Events, true)
 | 
			
		||||
	w.Create = util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true)
 | 
			
		||||
	w.Delete = util.IsStringInSlice(string(webhook_module.HookEventDelete), form.Events, true)
 | 
			
		||||
	w.Fork = util.IsStringInSlice(string(webhook_module.HookEventFork), form.Events, true)
 | 
			
		||||
	w.Repository = util.IsStringInSlice(string(webhook_module.HookEventRepository), form.Events, true)
 | 
			
		||||
	w.Wiki = util.IsStringInSlice(string(webhook_module.HookEventWiki), form.Events, true)
 | 
			
		||||
	w.Release = util.IsStringInSlice(string(webhook_module.HookEventRelease), form.Events, true)
 | 
			
		||||
	w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
 | 
			
		||||
	w.Push = util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true)
 | 
			
		||||
	w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
 | 
			
		||||
	w.Delete = util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true)
 | 
			
		||||
	w.Fork = util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true)
 | 
			
		||||
	w.Repository = util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true)
 | 
			
		||||
	w.Wiki = util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true)
 | 
			
		||||
	w.Release = util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true)
 | 
			
		||||
	w.BranchFilter = form.BranchFilter
 | 
			
		||||
 | 
			
		||||
	err := w.SetHeaderAuthorization(form.AuthorizationHeader)
 | 
			
		||||
 
 | 
			
		||||
@@ -139,7 +139,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
 | 
			
		||||
	viewType := ctx.FormString("type")
 | 
			
		||||
	sortType := ctx.FormString("sort")
 | 
			
		||||
	types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested"}
 | 
			
		||||
	if !util.IsStringInSlice(viewType, types, true) {
 | 
			
		||||
	if !util.SliceContainsString(types, viewType, true) {
 | 
			
		||||
		viewType = "all"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -3087,7 +3087,7 @@ func updateAttachments(ctx *context.Context, item interface{}, files []string) e
 | 
			
		||||
		return fmt.Errorf("unknown Type: %T", content)
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < len(attachments); i++ {
 | 
			
		||||
		if util.IsStringInSlice(attachments[i].UUID, files) {
 | 
			
		||||
		if util.SliceContainsString(files, attachments[i].UUID) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err := repo_model.DeleteAttachment(attachments[i], true); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
 | 
			
		||||
 | 
			
		||||
func checkHookType(ctx *context.Context) string {
 | 
			
		||||
	hookType := strings.ToLower(ctx.Params(":type"))
 | 
			
		||||
	if !util.IsStringInSlice(hookType, setting.Webhook.Types, true) {
 | 
			
		||||
	if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
 | 
			
		||||
		ctx.NotFound("checkHookType", nil)
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -214,7 +214,7 @@ func NotificationSubscriptions(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["SortType"] = sortType
 | 
			
		||||
 | 
			
		||||
	state := ctx.FormString("state")
 | 
			
		||||
	if !util.IsStringInSlice(state, []string{"all", "open", "closed"}, true) {
 | 
			
		||||
	if !util.SliceContainsString([]string{"all", "open", "closed"}, state, true) {
 | 
			
		||||
		state = "all"
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data["State"] = state
 | 
			
		||||
 
 | 
			
		||||
@@ -413,7 +413,7 @@ func UpdateUserLang(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["PageIsSettingsAppearance"] = true
 | 
			
		||||
 | 
			
		||||
	if len(form.Language) != 0 {
 | 
			
		||||
		if !util.IsStringInSlice(form.Language, setting.Langs) {
 | 
			
		||||
		if !util.SliceContainsString(setting.Langs, form.Language) {
 | 
			
		||||
			ctx.Flash.Error(ctx.Tr("settings.update_language_not_found", form.Language))
 | 
			
		||||
			ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
 | 
			
		||||
			return
 | 
			
		||||
 
 | 
			
		||||
@@ -246,7 +246,7 @@ func (source *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string
 | 
			
		||||
	membershipsToAdd := map[string][]string{}
 | 
			
		||||
	membershipsToRemove := map[string][]string{}
 | 
			
		||||
	for group, memberships := range ldapGroupsToTeams {
 | 
			
		||||
		isUserInGroup := util.IsStringInSlice(group, usersLdapGroups)
 | 
			
		||||
		isUserInGroup := util.SliceContainsString(usersLdapGroups, group)
 | 
			
		||||
		if isUserInGroup {
 | 
			
		||||
			for org, teams := range memberships {
 | 
			
		||||
				membershipsToAdd[org] = teams
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
 | 
			
		||||
		idx := strings.Index(userName, "@")
 | 
			
		||||
		if idx == -1 {
 | 
			
		||||
			return nil, user_model.ErrUserNotExist{Name: userName}
 | 
			
		||||
		} else if !util.IsStringInSlice(userName[idx+1:], strings.Split(source.AllowedDomains, ","), true) {
 | 
			
		||||
		} else if !util.SliceContainsString(strings.Split(source.AllowedDomains, ","), userName[idx+1:], true) {
 | 
			
		||||
			return nil, user_model.ErrUserNotExist{Name: userName}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func nameAllowed(name string) error {
 | 
			
		||||
	if util.IsStringInSlice(name, reservedWikiNames) {
 | 
			
		||||
	if util.SliceContainsString(reservedWikiNames, name) {
 | 
			
		||||
		return repo_model.ErrWikiReservedName{
 | 
			
		||||
			Title: name,
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ func TestAPIRepoTeams(t *testing.T) {
 | 
			
		||||
	if assert.Len(t, teams, 2) {
 | 
			
		||||
		assert.EqualValues(t, "Owners", teams[0].Name)
 | 
			
		||||
		assert.True(t, teams[0].CanCreateOrgRepo)
 | 
			
		||||
		assert.True(t, util.IsEqualSlice(unit.AllUnitKeyNames(), teams[0].Units), fmt.Sprintf("%v == %v", unit.AllUnitKeyNames(), teams[0].Units))
 | 
			
		||||
		assert.True(t, util.SliceSortedEqual(unit.AllUnitKeyNames(), teams[0].Units), fmt.Sprintf("%v == %v", unit.AllUnitKeyNames(), teams[0].Units))
 | 
			
		||||
		assert.EqualValues(t, "owner", teams[0].Permission)
 | 
			
		||||
 | 
			
		||||
		assert.EqualValues(t, "test_team", teams[1].Name)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user