// Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package explore import ( "fmt" "net/http" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/services/context" ) const ( // tplExploreRepos explore repositories page template tplExploreRepos base.TplName = "explore/repos" relevantReposOnlyParam string = "only_show_relevant" ) // RepoSearchOptions when calling search repositories type RepoSearchOptions struct { OwnerID int64 Private bool Restricted bool PageSize int OnlyShowRelevant bool TplName base.TplName } // RenderRepoSearch render repositories search page // This function is also used to render the Admin Repository Management page. func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { // Sitemap index for sitemap paths page := int(ctx.ParamsInt64("idx")) isSitemap := ctx.Params("idx") != "" if page <= 1 { page = ctx.FormInt("page") } if page <= 0 { page = 1 } if isSitemap { opts.PageSize = setting.UI.SitemapPagingNum } var ( repos []*repo_model.Repository count int64 err error orderBy db.SearchOrderBy ) sortOrder := ctx.FormString("sort") if sortOrder == "" { sortOrder = setting.UI.ExploreDefaultSort } ctx.Data["SortType"] = sortOrder switch sortOrder { case "newest": orderBy = db.SearchOrderByNewest case "oldest": orderBy = db.SearchOrderByOldest case "leastupdate": orderBy = db.SearchOrderByLeastUpdated case "reversealphabetically": orderBy = "owner_name DESC, name DESC" case "alphabetically": orderBy = "owner_name ASC, name ASC" case "reversesize": orderBy = db.SearchOrderBySizeReverse case "size": orderBy = db.SearchOrderBySize case "reversegitsize": orderBy = db.SearchOrderByGitSizeReverse case "gitsize": orderBy = db.SearchOrderByGitSize case "reverselfssize": orderBy = db.SearchOrderByLFSSizeReverse case "lfssize": orderBy = db.SearchOrderByLFSSize case "moststars": orderBy = db.SearchOrderByStarsReverse case "feweststars": orderBy = db.SearchOrderByStars case "mostforks": orderBy = db.SearchOrderByForksReverse case "fewestforks": orderBy = db.SearchOrderByForks default: ctx.Data["SortType"] = "recentupdate" orderBy = db.SearchOrderByRecentUpdated } keyword := ctx.FormTrim("q") ctx.Data["OnlyShowRelevant"] = opts.OnlyShowRelevant topicOnly := ctx.FormBool("topic") ctx.Data["TopicOnly"] = topicOnly language := ctx.FormTrim("language") ctx.Data["Language"] = language archived := ctx.FormOptionalBool("archived") ctx.Data["IsArchived"] = archived fork := ctx.FormOptionalBool("fork") ctx.Data["IsFork"] = fork mirror := ctx.FormOptionalBool("mirror") ctx.Data["IsMirror"] = mirror template := ctx.FormOptionalBool("template") ctx.Data["IsTemplate"] = template private := ctx.FormOptionalBool("private") ctx.Data["IsPrivate"] = private repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ Page: page, PageSize: opts.PageSize, }, Actor: ctx.Doer, OrderBy: orderBy, Private: opts.Private, Keyword: keyword, OwnerID: opts.OwnerID, AllPublic: true, AllLimited: true, TopicOnly: topicOnly, Language: language, IncludeDescription: setting.UI.SearchRepoDescription, OnlyShowRelevant: opts.OnlyShowRelevant, Archived: archived, Fork: fork, Mirror: mirror, Template: template, IsPrivate: private, }) if err != nil { ctx.ServerError("SearchRepository", err) return } if isSitemap { m := sitemap.NewSitemap() for _, item := range repos { m.Add(sitemap.URL{URL: item.HTMLURL(), LastMod: item.UpdatedUnix.AsTimePtr()}) } ctx.Resp.Header().Set("Content-Type", "text/xml") if _, err := m.WriteTo(ctx.Resp); err != nil { log.Error("Failed writing sitemap: %v", 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.AddParamString("topic", fmt.Sprint(topicOnly)) pager.AddParamString("language", language) pager.AddParamString(relevantReposOnlyParam, fmt.Sprint(opts.OnlyShowRelevant)) if archived.Has() { pager.AddParamString("archived", fmt.Sprint(archived.Value())) } if fork.Has() { pager.AddParamString("fork", fmt.Sprint(fork.Value())) } if mirror.Has() { pager.AddParamString("mirror", fmt.Sprint(mirror.Value())) } if template.Has() { pager.AddParamString("template", fmt.Sprint(template.Value())) } if private.Has() { pager.AddParamString("private", fmt.Sprint(private.Value())) } ctx.Data["Page"] = pager ctx.HTML(http.StatusOK, opts.TplName) } // Repos render explore repositories page func Repos(ctx *context.Context) { ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage 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.Doer != nil && !ctx.Doer.IsAdmin { ownerID = ctx.Doer.ID } onlyShowRelevant := setting.UI.OnlyShowRelevantRepos _ = ctx.Req.ParseForm() // parse the form first, to prepare the ctx.Req.Form field if len(ctx.Req.Form[relevantReposOnlyParam]) != 0 { onlyShowRelevant = ctx.FormBool(relevantReposOnlyParam) } RenderRepoSearch(ctx, &RepoSearchOptions{ PageSize: setting.UI.ExplorePagingNum, OwnerID: ownerID, Private: ctx.Doer != nil, TplName: tplExploreRepos, OnlyShowRelevant: onlyShowRelevant, }) }