diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 80017a11764ee..5016f29cf9ffd 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1097,6 +1097,9 @@ PATH =
;; Number of items that are displayed in home feed
;FEED_PAGING_NUM = 20
;;
+;; Number of items that are displayed in a single subsitemap
+;SITEMAP_PAGING_NUM = 20
+;;
;; Number of maximum commits displayed in commit graph.
;GRAPH_MAX_COMMIT_NUM = 100
;;
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 15628a7def051..df659b6540119 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -174,6 +174,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `MEMBERS_PAGING_NUM`: **20**: Number of members that are shown in organization members.
- `FEED_MAX_COMMIT_NUM`: **5**: Number of maximum commits shown in one activity feed.
- `FEED_PAGING_NUM`: **20**: Number of items that are displayed in home feed.
+- `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
- `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
- `DEFAULT_THEME`: **auto**: \[auto, gitea, arc-green\]: Set the default theme for the Gitea install.
diff --git a/docs/content/doc/developers/oauth2-provider.md b/docs/content/doc/developers/oauth2-provider.md
index efe78eed97659..ce6e9aad8cbe5 100644
--- a/docs/content/doc/developers/oauth2-provider.md
+++ b/docs/content/doc/developers/oauth2-provider.md
@@ -41,13 +41,13 @@ To use the Authorization Code Grant as a third party application it is required
## Scopes
-Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and his/her organizations.
+Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and their organizations.
## Example
**Note:** This example does not use PKCE.
-1. Redirect to user to the authorization endpoint in order to get his/her consent for accessing the resources:
+1. Redirect to user to the authorization endpoint in order to get their consent for accessing the resources:
```curl
https://[YOUR-GITEA-URL]/login/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI& response_type=code&state=STATE
diff --git a/docs/content/doc/developers/oauth2-provider.zh-tw.md b/docs/content/doc/developers/oauth2-provider.zh-tw.md
index a2013feac4cf3..179c6342cdd6a 100644
--- a/docs/content/doc/developers/oauth2-provider.zh-tw.md
+++ b/docs/content/doc/developers/oauth2-provider.zh-tw.md
@@ -46,7 +46,7 @@ Gitea 支援作為 OAuth2 提供者,能讓第三方程式能在使用者同意
**備註:** 此範例未使用 PKCE。
1. 重新導向使用者到 authorization endpoint 以獲得他同意授權存取資源:
-
+
```curl
https://[YOUR-GITEA-URL]/login/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI& response_type=code&state=STATE
diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl
index da15e9ef69ee4..d4d1517011e2a 100644
--- a/integrations/mssql.ini.tmpl
+++ b/integrations/mssql.ini.tmpl
@@ -10,11 +10,13 @@ PASSWD = {{TEST_MSSQL_PASSWORD}}
SSL_MODE = disable
[indexer]
-ISSUE_INDEXER_PATH = integrations/gitea-integration-mssql/indexers/issues.bleve
-ISSUE_INDEXER_QUEUE_DIR = integrations/gitea-integration-mssql/indexers/issues.queue
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = integrations/gitea-integration-mssql/indexers/repos.bleve
+[queue.issue_indexer]
+PATH = integrations/gitea-integration-mssql/indexers/issues.bleve
+DATADIR = integrations/gitea-integration-mssql/indexers/issues.queue
+
[queue]
TYPE = immediate
diff --git a/integrations/mysql.ini.tmpl b/integrations/mysql.ini.tmpl
index 4df49336424a6..8f610326e245b 100644
--- a/integrations/mysql.ini.tmpl
+++ b/integrations/mysql.ini.tmpl
@@ -10,12 +10,14 @@ PASSWD = {{TEST_MYSQL_PASSWORD}}
SSL_MODE = disable
[indexer]
-ISSUE_INDEXER_TYPE = elasticsearch
-ISSUE_INDEXER_CONN_STR = http://elastic:changeme@elasticsearch:9200
-ISSUE_INDEXER_QUEUE_DIR = integrations/gitea-integration-mysql/indexers/issues.queue
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = integrations/gitea-integration-mysql/indexers/repos.bleve
+[queue.issue_indexer]
+TYPE = elasticsearch
+CONN_STR = http://elastic:changeme@elasticsearch:9200
+DATADIR = integrations/gitea-integration-mysql/indexers/issues.queue
+
[queue]
TYPE = immediate
diff --git a/integrations/mysql8.ini.tmpl b/integrations/mysql8.ini.tmpl
index 4b63dd51a1fdd..5133058237c20 100644
--- a/integrations/mysql8.ini.tmpl
+++ b/integrations/mysql8.ini.tmpl
@@ -10,11 +10,13 @@ PASSWD = {{TEST_MYSQL8_PASSWORD}}
SSL_MODE = disable
[indexer]
-ISSUE_INDEXER_PATH = integrations/gitea-integration-mysql8/indexers/issues.bleve
-ISSUE_INDEXER_QUEUE_DIR = integrations/gitea-integration-mysql8/indexers/issues.queue
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = integrations/gitea-integration-mysql8/indexers/repos.bleve
+[queue.issue_indexer]
+PATH = integrations/gitea-integration-mysql8/indexers/issues.bleve
+DATADIR = integrations/gitea-integration-mysql8/indexers/issues.queue
+
[queue]
TYPE = immediate
diff --git a/integrations/pgsql.ini.tmpl b/integrations/pgsql.ini.tmpl
index 5b54a02c9fac8..15cd6a057b2c9 100644
--- a/integrations/pgsql.ini.tmpl
+++ b/integrations/pgsql.ini.tmpl
@@ -11,11 +11,13 @@ SCHEMA = {{TEST_PGSQL_SCHEMA}}
SSL_MODE = disable
[indexer]
-ISSUE_INDEXER_PATH = integrations/gitea-integration-pgsql/indexers/issues.bleve
-ISSUE_INDEXER_QUEUE_DIR = integrations/gitea-integration-pgsql/indexers/issues.queue
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = integrations/gitea-integration-pgsql/indexers/repos.bleve
+[queue.issue_indexer]
+PATH = integrations/gitea-integration-pgsql/indexers/issues.bleve
+DATADIR = integrations/gitea-integration-pgsql/indexers/issues.queue
+
[queue]
TYPE = immediate
diff --git a/integrations/sqlite.ini.tmpl b/integrations/sqlite.ini.tmpl
index 2da7fd65d392b..fa57e1aa96b88 100644
--- a/integrations/sqlite.ini.tmpl
+++ b/integrations/sqlite.ini.tmpl
@@ -6,11 +6,13 @@ DB_TYPE = sqlite3
PATH = integrations/gitea-integration-sqlite/gitea.db
[indexer]
-ISSUE_INDEXER_PATH = integrations/gitea-integration-sqlite/indexers/issues.bleve
-ISSUE_INDEXER_QUEUE_DIR = integrations/gitea-integration-sqlite/indexers/issues.queue
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = integrations/gitea-integration-sqlite/indexers/repos.bleve
+[queue.issue_indexer]
+PATH = integrations/gitea-integration-sqlite/indexers/issues.bleve
+DATADIR = integrations/gitea-integration-sqlite/indexers/issues.queue
+
[queue]
TYPE = immediate
diff --git a/models/migrations/v154.go b/models/migrations/v154.go
index 11407c30ee140..bb17fb4725a04 100644
--- a/models/migrations/v154.go
+++ b/models/migrations/v154.go
@@ -30,7 +30,7 @@ func addTimeStamps(x *xorm.Engine) error {
return err
}
- // Follow represents relations of user and his/her followers.
+ // Follow represents relations of user and their followers.
type Follow struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}
diff --git a/models/org.go b/models/org.go
index 849c9b985b38a..efcb7183e7669 100644
--- a/models/org.go
+++ b/models/org.go
@@ -149,7 +149,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
}
}
- // Delete member in his/her teams.
+ // Delete member in their teams.
teams, err := organization.GetUserOrgTeams(ctx, org.ID, userID)
if err != nil {
return err
diff --git a/models/task.go b/models/task.go
index cabb96c60831e..67f04d9562f42 100644
--- a/models/task.go
+++ b/models/task.go
@@ -156,14 +156,14 @@ type ErrTaskDoesNotExist struct {
Type structs.TaskType
}
-// IsErrTaskDoesNotExist checks if an error is a ErrTaskIsNotExist.
+// IsErrTaskDoesNotExist checks if an error is a ErrTaskDoesNotExist.
func IsErrTaskDoesNotExist(err error) bool {
_, ok := err.(ErrTaskDoesNotExist)
return ok
}
func (err ErrTaskDoesNotExist) Error() string {
- return fmt.Sprintf("task is not exist [id: %d, repo_id: %d, type: %d]",
+ return fmt.Sprintf("task does not exist [id: %d, repo_id: %d, type: %d]",
err.ID, err.RepoID, err.Type)
}
diff --git a/models/user/follow.go b/models/user/follow.go
index 6b02486c438c7..5f24f706d16b5 100644
--- a/models/user/follow.go
+++ b/models/user/follow.go
@@ -9,7 +9,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
)
-// Follow represents relations of user and his/her followers.
+// Follow represents relations of user and their followers.
type Follow struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"UNIQUE(follow)"`
diff --git a/models/user/user.go b/models/user/user.go
index f7d457b91b5a5..9460bd38fe428 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -86,7 +86,7 @@ type User struct {
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
// MustChangePassword is an attribute that determines if a user
- // is to change his/her password after registration.
+ // is to change their password after registration.
MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
LoginType auth.Type
@@ -537,7 +537,7 @@ func GetUserSalt() (string, error) {
return hex.EncodeToString(rBytes), nil
}
-// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
+// NewGhostUser creates and returns a fake user for someone has deleted their account.
func NewGhostUser() *User {
return &User{
ID: -1,
diff --git a/modules/doctor/mergebase.go b/modules/doctor/mergebase.go
index 46369290a13d7..2da91cdcc35f6 100644
--- a/modules/doctor/mergebase.go
+++ b/modules/doctor/mergebase.go
@@ -30,6 +30,9 @@ func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*rep
}
func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) error {
+ if err := git.InitOnceWithSync(ctx); err != nil {
+ return err
+ }
numRepos := 0
numPRs := 0
numPRsUpdated := 0
diff --git a/modules/doctor/misc.go b/modules/doctor/misc.go
index 9bee78303e1a5..24175fcaf4bec 100644
--- a/modules/doctor/misc.go
+++ b/modules/doctor/misc.go
@@ -189,6 +189,75 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err
return nil
}
+func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) error {
+ if err := git.InitOnceWithSync(ctx); err != nil {
+ return err
+ }
+
+ numRepos := 0
+ numNeedUpdate := 0
+ numWritten := 0
+ if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
+ numRepos++
+
+ commitGraphExists := func() (bool, error) {
+ // Check commit-graph exists
+ commitGraphFile := path.Join(repo.RepoPath(), `objects/info/commit-graph`)
+ isExist, err := util.IsExist(commitGraphFile)
+ if err != nil {
+ logger.Error("Unable to check if %s exists. Error: %v", commitGraphFile, err)
+ return false, err
+ }
+
+ if !isExist {
+ commitGraphsDir := path.Join(repo.RepoPath(), `objects/info/commit-graphs`)
+ isExist, err = util.IsExist(commitGraphsDir)
+ if err != nil {
+ logger.Error("Unable to check if %s exists. Error: %v", commitGraphsDir, err)
+ return false, err
+ }
+ }
+ return isExist, nil
+ }
+
+ isExist, err := commitGraphExists()
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ numNeedUpdate++
+ if autofix {
+ if err := git.WriteCommitGraph(ctx, repo.RepoPath()); err != nil {
+ logger.Error("Unable to write commit-graph in %s. Error: %v", repo.FullName(), err)
+ return err
+ }
+ isExist, err := commitGraphExists()
+ if err != nil {
+ return err
+ }
+ if isExist {
+ numWritten++
+ logger.Info("Commit-graph written: %s", repo.FullName())
+ } else {
+ logger.Warn("No commit-graph written: %s", repo.FullName())
+ }
+ }
+ }
+ return nil
+ }); err != nil {
+ logger.Critical("Unable to checkCommitGraph: %v", err)
+ return err
+ }
+
+ if autofix {
+ logger.Info("Wrote commit-graph files for %d of %d repositories.", numWritten, numRepos)
+ } else {
+ logger.Info("Checked %d repositories, %d without commit-graphs.", numRepos, numNeedUpdate)
+ }
+
+ return nil
+}
+
func init() {
Register(&Check{
Title: "Check if SCRIPT_TYPE is available",
@@ -225,4 +294,11 @@ func init() {
Run: checkDaemonExport,
Priority: 8,
})
+ Register(&Check{
+ Title: "Check commit-graphs",
+ Name: "check-commit-graphs",
+ IsDefault: false,
+ Run: checkCommitGraph,
+ Priority: 9,
+ })
}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 88f306b3fa7e3..7be0842b56a6d 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -207,6 +207,7 @@ var (
// UI settings
UI = struct {
ExplorePagingNum int
+ SitemapPagingNum int
IssuePagingNum int
RepoSearchPagingNum int
MembersPagingNum int
@@ -260,6 +261,7 @@ var (
} `ini:"ui.meta"`
}{
ExplorePagingNum: 20,
+ SitemapPagingNum: 20,
IssuePagingNum: 10,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
diff --git a/modules/sitemap/sitemap.go b/modules/sitemap/sitemap.go
new file mode 100644
index 0000000000000..14953765abbc2
--- /dev/null
+++ b/modules/sitemap/sitemap.go
@@ -0,0 +1,69 @@
+// Copyright 2022 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 sitemap
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "time"
+)
+
+// sitemapFileLimit contains the maximum size of a sitemap file
+const sitemapFileLimit = 50 * 1024 * 1024
+
+// Url represents a single sitemap entry
+type URL struct {
+ URL string `xml:"loc"`
+ LastMod *time.Time `xml:"lastmod,omitempty"`
+}
+
+// SitemapUrl represents a sitemap
+type Sitemap struct {
+ XMLName xml.Name
+ Namespace string `xml:"xmlns,attr"`
+
+ URLs []URL `xml:"url"`
+}
+
+// NewSitemap creates a sitemap
+func NewSitemap() *Sitemap {
+ return &Sitemap{
+ XMLName: xml.Name{Local: "urlset"},
+ Namespace: "http://www.sitemaps.org/schemas/sitemap/0.9",
+ }
+}
+
+// NewSitemap creates a sitemap index.
+func NewSitemapIndex() *Sitemap {
+ return &Sitemap{
+ XMLName: xml.Name{Local: "sitemapindex"},
+ Namespace: "http://www.sitemaps.org/schemas/sitemap/0.9",
+ }
+}
+
+// Add adds a URL to the sitemap
+func (s *Sitemap) Add(u URL) {
+ s.URLs = append(s.URLs, u)
+}
+
+// Write writes the sitemap to a response
+func (s *Sitemap) WriteTo(w io.Writer) (int64, error) {
+ if len(s.URLs) > 50000 {
+ return 0, fmt.Errorf("The sitemap contains too many URLs: %d", len(s.URLs))
+ }
+ buf := bytes.NewBufferString(xml.Header)
+ if err := xml.NewEncoder(buf).Encode(s); err != nil {
+ return 0, err
+ }
+ if err := buf.WriteByte('\n'); err != nil {
+ return 0, err
+ }
+ if buf.Len() > sitemapFileLimit {
+ return 0, fmt.Errorf("The sitemap is too big: %d", buf.Len())
+ }
+ return buf.WriteTo(w)
+}
diff --git a/modules/sitemap/sitemap_test.go b/modules/sitemap/sitemap_test.go
new file mode 100644
index 0000000000000..63007b84791f6
--- /dev/null
+++ b/modules/sitemap/sitemap_test.go
@@ -0,0 +1,77 @@
+// Copyright 2022 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 sitemap
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestOk(t *testing.T) {
+ testReal := func(s *Sitemap, name string, urls []URL, expected string) {
+ for _, url := range urls {
+ s.Add(url)
+ }
+ buf := &bytes.Buffer{}
+ _, err := s.WriteTo(buf)
+ assert.NoError(t, nil, err)
+ assert.Equal(t, xml.Header+"<"+name+" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"+expected+""+name+">\n", buf.String())
+ }
+ test := func(urls []URL, expected string) {
+ testReal(NewSitemap(), "urlset", urls, expected)
+ testReal(NewSitemapIndex(), "sitemapindex", urls, expected)
+ }
+
+ ts := time.Unix(1651322008, 0).UTC()
+
+ test(
+ []URL{},
+ "",
+ )
+ test(
+ []URL{
+ {URL: "https://gitea.io/test1", LastMod: &ts},
+ },
+ "
{{.i18n.Tr "repo.settings.protect_check_status_contexts_desc"}}