Move langauge detection to separate module to be more reusable Add option to disable vendored file exclusion from file search Allways show all language stats for search
		
			
				
	
	
		
			401 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | |
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | |
| // Use of this source code is governed by a MIT-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package routers
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"strings"
 | |
| 
 | |
| 	"code.gitea.io/gitea/models"
 | |
| 	"code.gitea.io/gitea/modules/base"
 | |
| 	"code.gitea.io/gitea/modules/context"
 | |
| 	code_indexer "code.gitea.io/gitea/modules/indexer/code"
 | |
| 	"code.gitea.io/gitea/modules/log"
 | |
| 	"code.gitea.io/gitea/modules/setting"
 | |
| 	"code.gitea.io/gitea/modules/structs"
 | |
| 	"code.gitea.io/gitea/modules/util"
 | |
| 	"code.gitea.io/gitea/routers/user"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// tplHome home page template
 | |
| 	tplHome base.TplName = "home"
 | |
| 	// tplExploreRepos explore repositories page template
 | |
| 	tplExploreRepos base.TplName = "explore/repos"
 | |
| 	// tplExploreUsers explore users page template
 | |
| 	tplExploreUsers base.TplName = "explore/users"
 | |
| 	// tplExploreOrganizations explore organizations page template
 | |
| 	tplExploreOrganizations base.TplName = "explore/organizations"
 | |
| 	// tplExploreCode explore code page template
 | |
| 	tplExploreCode base.TplName = "explore/code"
 | |
| )
 | |
| 
 | |
| // Home render home page
 | |
| func Home(ctx *context.Context) {
 | |
| 	if ctx.IsSigned {
 | |
| 		if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
 | |
| 			ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
 | |
| 			ctx.HTML(200, user.TplActivate)
 | |
| 		} else if !ctx.User.IsActive || ctx.User.ProhibitLogin {
 | |
| 			log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr())
 | |
| 			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
 | |
| 			ctx.HTML(200, "user/auth/prohibit_login")
 | |
| 		} else if ctx.User.MustChangePassword {
 | |
| 			ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
 | |
| 			ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
 | |
| 			ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
 | |
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
 | |
| 		} else {
 | |
| 			user.Dashboard(ctx)
 | |
| 		}
 | |
| 		return
 | |
| 		// Check non-logged users landing page.
 | |
| 	} else if setting.LandingPageURL != setting.LandingPageHome {
 | |
| 		ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Check auto-login.
 | |
| 	uname := ctx.GetCookie(setting.CookieUserName)
 | |
| 	if len(uname) != 0 {
 | |
| 		ctx.Redirect(setting.AppSubURL + "/user/login")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	ctx.Data["PageIsHome"] = true
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 	ctx.HTML(200, tplHome)
 | |
| }
 | |
| 
 | |
| // RepoSearchOptions when calling search repositories
 | |
| type RepoSearchOptions struct {
 | |
| 	OwnerID    int64
 | |
| 	Private    bool
 | |
| 	Restricted bool
 | |
| 	PageSize   int
 | |
| 	TplName    base.TplName
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	nullByte = []byte{0x00}
 | |
| )
 | |
| 
 | |
| func isKeywordValid(keyword string) bool {
 | |
| 	return !bytes.Contains([]byte(keyword), nullByte)
 | |
| }
 | |
| 
 | |
| // RenderRepoSearch render repositories search page
 | |
| func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 | |
| 	page := ctx.QueryInt("page")
 | |
| 	if page <= 0 {
 | |
| 		page = 1
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		repos   []*models.Repository
 | |
| 		count   int64
 | |
| 		err     error
 | |
| 		orderBy models.SearchOrderBy
 | |
| 	)
 | |
| 
 | |
| 	ctx.Data["SortType"] = ctx.Query("sort")
 | |
| 	switch ctx.Query("sort") {
 | |
| 	case "newest":
 | |
| 		orderBy = models.SearchOrderByNewest
 | |
| 	case "oldest":
 | |
| 		orderBy = models.SearchOrderByOldest
 | |
| 	case "recentupdate":
 | |
| 		orderBy = models.SearchOrderByRecentUpdated
 | |
| 	case "leastupdate":
 | |
| 		orderBy = models.SearchOrderByLeastUpdated
 | |
| 	case "reversealphabetically":
 | |
| 		orderBy = models.SearchOrderByAlphabeticallyReverse
 | |
| 	case "alphabetically":
 | |
| 		orderBy = models.SearchOrderByAlphabetically
 | |
| 	case "reversesize":
 | |
| 		orderBy = models.SearchOrderBySizeReverse
 | |
| 	case "size":
 | |
| 		orderBy = models.SearchOrderBySize
 | |
| 	case "moststars":
 | |
| 		orderBy = models.SearchOrderByStarsReverse
 | |
| 	case "feweststars":
 | |
| 		orderBy = models.SearchOrderByStars
 | |
| 	case "mostforks":
 | |
| 		orderBy = models.SearchOrderByForksReverse
 | |
| 	case "fewestforks":
 | |
| 		orderBy = models.SearchOrderByForks
 | |
| 	default:
 | |
| 		ctx.Data["SortType"] = "recentupdate"
 | |
| 		orderBy = models.SearchOrderByRecentUpdated
 | |
| 	}
 | |
| 
 | |
| 	keyword := strings.Trim(ctx.Query("q"), " ")
 | |
| 	topicOnly := ctx.QueryBool("topic")
 | |
| 	ctx.Data["TopicOnly"] = topicOnly
 | |
| 
 | |
| 	repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
 | |
| 		ListOptions: models.ListOptions{
 | |
| 			Page:     page,
 | |
| 			PageSize: opts.PageSize,
 | |
| 		},
 | |
| 		Actor:              ctx.User,
 | |
| 		OrderBy:            orderBy,
 | |
| 		Private:            opts.Private,
 | |
| 		Keyword:            keyword,
 | |
| 		OwnerID:            opts.OwnerID,
 | |
| 		AllPublic:          true,
 | |
| 		AllLimited:         true,
 | |
| 		TopicOnly:          topicOnly,
 | |
| 		IncludeDescription: setting.UI.SearchRepoDescription,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		ctx.ServerError("SearchRepository", err)
 | |
| 		return
 | |
| 	}
 | |
| 	ctx.Data["Keyword"] = keyword
 | |
| 	ctx.Data["Total"] = count
 | |
| 	ctx.Data["Repos"] = repos
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 
 | |
| 	pager := context.NewPagination(int(count), opts.PageSize, page, 5)
 | |
| 	pager.SetDefaultParams(ctx)
 | |
| 	pager.AddParam(ctx, "topic", "TopicOnly")
 | |
| 	ctx.Data["Page"] = pager
 | |
| 
 | |
| 	ctx.HTML(200, opts.TplName)
 | |
| }
 | |
| 
 | |
| // ExploreRepos render explore repositories page
 | |
| func ExploreRepos(ctx *context.Context) {
 | |
| 	ctx.Data["Title"] = ctx.Tr("explore")
 | |
| 	ctx.Data["PageIsExplore"] = true
 | |
| 	ctx.Data["PageIsExploreRepositories"] = true
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 
 | |
| 	var ownerID int64
 | |
| 	if ctx.User != nil && !ctx.User.IsAdmin {
 | |
| 		ownerID = ctx.User.ID
 | |
| 	}
 | |
| 
 | |
| 	RenderRepoSearch(ctx, &RepoSearchOptions{
 | |
| 		PageSize: setting.UI.ExplorePagingNum,
 | |
| 		OwnerID:  ownerID,
 | |
| 		Private:  ctx.User != nil,
 | |
| 		TplName:  tplExploreRepos,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // RenderUserSearch render user search page
 | |
| func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) {
 | |
| 	opts.Page = ctx.QueryInt("page")
 | |
| 	if opts.Page <= 1 {
 | |
| 		opts.Page = 1
 | |
| 	}
 | |
| 	opts.Actor = ctx.User
 | |
| 
 | |
| 	var (
 | |
| 		users   []*models.User
 | |
| 		count   int64
 | |
| 		err     error
 | |
| 		orderBy models.SearchOrderBy
 | |
| 	)
 | |
| 
 | |
| 	ctx.Data["SortType"] = ctx.Query("sort")
 | |
| 	switch ctx.Query("sort") {
 | |
| 	case "newest":
 | |
| 		orderBy = models.SearchOrderByIDReverse
 | |
| 	case "oldest":
 | |
| 		orderBy = models.SearchOrderByID
 | |
| 	case "recentupdate":
 | |
| 		orderBy = models.SearchOrderByRecentUpdated
 | |
| 	case "leastupdate":
 | |
| 		orderBy = models.SearchOrderByLeastUpdated
 | |
| 	case "reversealphabetically":
 | |
| 		orderBy = models.SearchOrderByAlphabeticallyReverse
 | |
| 	case "alphabetically":
 | |
| 		orderBy = models.SearchOrderByAlphabetically
 | |
| 	default:
 | |
| 		ctx.Data["SortType"] = "alphabetically"
 | |
| 		orderBy = models.SearchOrderByAlphabetically
 | |
| 	}
 | |
| 
 | |
| 	opts.Keyword = strings.Trim(ctx.Query("q"), " ")
 | |
| 	opts.OrderBy = orderBy
 | |
| 	if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
 | |
| 		users, count, err = models.SearchUsers(opts)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchUsers", err)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	ctx.Data["Keyword"] = opts.Keyword
 | |
| 	ctx.Data["Total"] = count
 | |
| 	ctx.Data["Users"] = users
 | |
| 	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 
 | |
| 	pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
 | |
| 	pager.SetDefaultParams(ctx)
 | |
| 	ctx.Data["Page"] = pager
 | |
| 
 | |
| 	ctx.HTML(200, tplName)
 | |
| }
 | |
| 
 | |
| // ExploreUsers render explore users page
 | |
| func ExploreUsers(ctx *context.Context) {
 | |
| 	ctx.Data["Title"] = ctx.Tr("explore")
 | |
| 	ctx.Data["PageIsExplore"] = true
 | |
| 	ctx.Data["PageIsExploreUsers"] = true
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 
 | |
| 	RenderUserSearch(ctx, &models.SearchUserOptions{
 | |
| 		Type:        models.UserTypeIndividual,
 | |
| 		ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum},
 | |
| 		IsActive:    util.OptionalBoolTrue,
 | |
| 		Visible:     []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
 | |
| 	}, tplExploreUsers)
 | |
| }
 | |
| 
 | |
| // ExploreOrganizations render explore organizations page
 | |
| func ExploreOrganizations(ctx *context.Context) {
 | |
| 	ctx.Data["Title"] = ctx.Tr("explore")
 | |
| 	ctx.Data["PageIsExplore"] = true
 | |
| 	ctx.Data["PageIsExploreOrganizations"] = true
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 
 | |
| 	visibleTypes := []structs.VisibleType{structs.VisibleTypePublic}
 | |
| 	if ctx.User != nil {
 | |
| 		visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate)
 | |
| 	}
 | |
| 
 | |
| 	RenderUserSearch(ctx, &models.SearchUserOptions{
 | |
| 		Type:        models.UserTypeOrganization,
 | |
| 		ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum},
 | |
| 		Visible:     visibleTypes,
 | |
| 	}, tplExploreOrganizations)
 | |
| }
 | |
| 
 | |
| // ExploreCode render explore code page
 | |
| func ExploreCode(ctx *context.Context) {
 | |
| 	if !setting.Indexer.RepoIndexerEnabled {
 | |
| 		ctx.Redirect(setting.AppSubURL+"/explore", 302)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 | |
| 	ctx.Data["Title"] = ctx.Tr("explore")
 | |
| 	ctx.Data["PageIsExplore"] = true
 | |
| 	ctx.Data["PageIsExploreCode"] = true
 | |
| 
 | |
| 	language := strings.TrimSpace(ctx.Query("l"))
 | |
| 	keyword := strings.TrimSpace(ctx.Query("q"))
 | |
| 	page := ctx.QueryInt("page")
 | |
| 	if page <= 0 {
 | |
| 		page = 1
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		repoIDs []int64
 | |
| 		err     error
 | |
| 		isAdmin bool
 | |
| 		userID  int64
 | |
| 	)
 | |
| 	if ctx.User != nil {
 | |
| 		userID = ctx.User.ID
 | |
| 		isAdmin = ctx.User.IsAdmin
 | |
| 	}
 | |
| 
 | |
| 	// guest user or non-admin user
 | |
| 	if ctx.User == nil || !isAdmin {
 | |
| 		repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.User)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchResults", err)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		total                 int
 | |
| 		searchResults         []*code_indexer.Result
 | |
| 		searchResultLanguages []*code_indexer.SearchResultLanguages
 | |
| 	)
 | |
| 
 | |
| 	// if non-admin login user, we need check UnitTypeCode at first
 | |
| 	if ctx.User != nil && len(repoIDs) > 0 {
 | |
| 		repoMaps, err := models.GetRepositoriesMapByIDs(repoIDs)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchResults", err)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		var rightRepoMap = make(map[int64]*models.Repository, len(repoMaps))
 | |
| 		repoIDs = make([]int64, 0, len(repoMaps))
 | |
| 		for id, repo := range repoMaps {
 | |
| 			if repo.CheckUnitUser(userID, isAdmin, models.UnitTypeCode) {
 | |
| 				rightRepoMap[id] = repo
 | |
| 				repoIDs = append(repoIDs, id)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ctx.Data["RepoMaps"] = rightRepoMap
 | |
| 
 | |
| 		total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchResults", err)
 | |
| 			return
 | |
| 		}
 | |
| 		// if non-login user or isAdmin, no need to check UnitTypeCode
 | |
| 	} else if (ctx.User == nil && len(repoIDs) > 0) || isAdmin {
 | |
| 		total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchResults", err)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		var loadRepoIDs = make([]int64, 0, len(searchResults))
 | |
| 		for _, result := range searchResults {
 | |
| 			var find bool
 | |
| 			for _, id := range loadRepoIDs {
 | |
| 				if id == result.RepoID {
 | |
| 					find = true
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			if !find {
 | |
| 				loadRepoIDs = append(loadRepoIDs, result.RepoID)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		repoMaps, err := models.GetRepositoriesMapByIDs(loadRepoIDs)
 | |
| 		if err != nil {
 | |
| 			ctx.ServerError("SearchResults", err)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		ctx.Data["RepoMaps"] = repoMaps
 | |
| 	}
 | |
| 
 | |
| 	ctx.Data["Keyword"] = keyword
 | |
| 	ctx.Data["Language"] = language
 | |
| 	ctx.Data["SearchResults"] = searchResults
 | |
| 	ctx.Data["SearchResultLanguages"] = searchResultLanguages
 | |
| 	ctx.Data["RequireHighlightJS"] = true
 | |
| 	ctx.Data["PageIsViewCode"] = true
 | |
| 
 | |
| 	pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
 | |
| 	pager.SetDefaultParams(ctx)
 | |
| 	pager.AddParam(ctx, "l", "Language")
 | |
| 	ctx.Data["Page"] = pager
 | |
| 
 | |
| 	ctx.HTML(200, tplExploreCode)
 | |
| }
 | |
| 
 | |
| // NotFound render 404 page
 | |
| func NotFound(ctx *context.Context) {
 | |
| 	ctx.Data["Title"] = "Page Not Found"
 | |
| 	ctx.NotFound("home.NotFound", nil)
 | |
| }
 |