mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add system setting table with cache and also add cache supports for user setting (#18058)
This commit is contained in:
		@@ -14,7 +14,6 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"os/user"
 | 
						"os/user"
 | 
				
			||||||
@@ -62,11 +61,7 @@ func runPR() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	setting.AppWorkPath = curDir
 | 
						setting.AppWorkPath = curDir
 | 
				
			||||||
	setting.StaticRootPath = curDir
 | 
						setting.StaticRootPath = curDir
 | 
				
			||||||
	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
 | 
						setting.GravatarSource = "https://secure.gravatar.com/avatar/"
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatalf("url.Parse: %v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setting.AppURL = "http://localhost:8080/"
 | 
						setting.AppURL = "http://localhost:8080/"
 | 
				
			||||||
	setting.HTTPPort = "8080"
 | 
						setting.HTTPPort = "8080"
 | 
				
			||||||
	setting.SSH.Domain = "localhost"
 | 
						setting.SSH.Domain = "localhost"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,117 +0,0 @@
 | 
				
			|||||||
// Copyright 2017 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 admin_test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/unittest"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestNotice_TrStr(t *testing.T) {
 | 
					 | 
				
			||||||
	notice := &admin.Notice{
 | 
					 | 
				
			||||||
		Type:        admin.NoticeRepository,
 | 
					 | 
				
			||||||
		Description: "test description",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	assert.Equal(t, "admin.notices.type_1", notice.TrStr())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCreateNotice(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	noticeBean := &admin.Notice{
 | 
					 | 
				
			||||||
		Type:        admin.NoticeRepository,
 | 
					 | 
				
			||||||
		Description: "test description",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, noticeBean)
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, noticeBean)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCreateRepositoryNotice(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	noticeBean := &admin.Notice{
 | 
					 | 
				
			||||||
		Type:        admin.NoticeRepository,
 | 
					 | 
				
			||||||
		Description: "test description",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, noticeBean)
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.CreateRepositoryNotice(noticeBean.Description))
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, noticeBean)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO TestRemoveAllWithNotice
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCountNotices(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
	assert.Equal(t, int64(3), admin.CountNotices())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestNotices(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	notices, err := admin.Notices(1, 2)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
					 | 
				
			||||||
	if assert.Len(t, notices, 2) {
 | 
					 | 
				
			||||||
		assert.Equal(t, int64(3), notices[0].ID)
 | 
					 | 
				
			||||||
		assert.Equal(t, int64(2), notices[1].ID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	notices, err = admin.Notices(2, 2)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
					 | 
				
			||||||
	if assert.Len(t, notices, 1) {
 | 
					 | 
				
			||||||
		assert.Equal(t, int64(1), notices[0].ID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDeleteNotice(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.DeleteNotice(3))
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDeleteNotices(t *testing.T) {
 | 
					 | 
				
			||||||
	// delete a non-empty range
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.DeleteNotices(1, 2))
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDeleteNotices2(t *testing.T) {
 | 
					 | 
				
			||||||
	// delete an empty range
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.DeleteNotices(3, 2))
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDeleteNoticesByIDs(t *testing.T) {
 | 
					 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
	assert.NoError(t, admin.DeleteNoticesByIDs([]int64{1, 3}))
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
 | 
					 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
 | 
					 | 
				
			||||||
	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/cache"
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -72,7 +73,7 @@ func GetEmailForHash(md5Sum string) (string, error) {
 | 
				
			|||||||
// LibravatarURL returns the URL for the given email. Slow due to the DNS lookup.
 | 
					// LibravatarURL returns the URL for the given email. Slow due to the DNS lookup.
 | 
				
			||||||
// This function should only be called if a federated avatar service is enabled.
 | 
					// This function should only be called if a federated avatar service is enabled.
 | 
				
			||||||
func LibravatarURL(email string) (*url.URL, error) {
 | 
					func LibravatarURL(email string) (*url.URL, error) {
 | 
				
			||||||
	urlStr, err := setting.LibravatarService.FromEmail(email)
 | 
						urlStr, err := system_model.LibravatarService.FromEmail(email)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err)
 | 
							log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -149,8 +150,10 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
 | 
				
			|||||||
		return DefaultAvatarLink()
 | 
							return DefaultAvatarLink()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enableFederatedAvatar, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
 | 
						if enableFederatedAvatar != nil && enableFederatedAvatar.GetValueBool() && system_model.LibravatarService != nil {
 | 
				
			||||||
		emailHash := saveEmailHash(email)
 | 
							emailHash := saveEmailHash(email)
 | 
				
			||||||
		if final {
 | 
							if final {
 | 
				
			||||||
			// for final link, we can spend more time on slow external query
 | 
								// for final link, we can spend more time on slow external query
 | 
				
			||||||
@@ -166,12 +169,16 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
 | 
				
			|||||||
			urlStr += "?size=" + strconv.Itoa(size)
 | 
								urlStr += "?size=" + strconv.Itoa(size)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return urlStr
 | 
							return urlStr
 | 
				
			||||||
	} else if !setting.DisableGravatar {
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						disableGravatar, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
 | 
				
			||||||
 | 
						if disableGravatar != nil && !disableGravatar.GetValueBool() {
 | 
				
			||||||
		// copy GravatarSourceURL, because we will modify its Path.
 | 
							// copy GravatarSourceURL, because we will modify its Path.
 | 
				
			||||||
		avatarURLCopy := *setting.GravatarSourceURL
 | 
							avatarURLCopy := *system_model.GravatarSourceURL
 | 
				
			||||||
		avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))
 | 
							avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))
 | 
				
			||||||
		return generateRecognizedAvatarURL(avatarURLCopy, size)
 | 
							return generateRecognizedAvatarURL(avatarURLCopy, size)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return DefaultAvatarLink()
 | 
						return DefaultAvatarLink()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,13 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package avatars
 | 
					package avatars_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avatars_model "code.gitea.io/gitea/models/avatars"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -15,40 +16,43 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const gravatarSource = "https://secure.gravatar.com/avatar/"
 | 
					const gravatarSource = "https://secure.gravatar.com/avatar/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func disableGravatar() {
 | 
					func disableGravatar(t *testing.T) {
 | 
				
			||||||
	setting.EnableFederatedAvatar = false
 | 
						err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, "false")
 | 
				
			||||||
	setting.LibravatarService = nil
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	setting.DisableGravatar = true
 | 
						err = system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "true")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						system_model.LibravatarService = nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func enableGravatar(t *testing.T) {
 | 
					func enableGravatar(t *testing.T) {
 | 
				
			||||||
	setting.DisableGravatar = false
 | 
						err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
 | 
				
			||||||
	var err error
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	setting.GravatarSourceURL, err = url.Parse(gravatarSource)
 | 
						setting.GravatarSource = gravatarSource
 | 
				
			||||||
 | 
						err = system_model.Init()
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHashEmail(t *testing.T) {
 | 
					func TestHashEmail(t *testing.T) {
 | 
				
			||||||
	assert.Equal(t,
 | 
						assert.Equal(t,
 | 
				
			||||||
		"d41d8cd98f00b204e9800998ecf8427e",
 | 
							"d41d8cd98f00b204e9800998ecf8427e",
 | 
				
			||||||
		HashEmail(""),
 | 
							avatars_model.HashEmail(""),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	assert.Equal(t,
 | 
						assert.Equal(t,
 | 
				
			||||||
		"353cbad9b58e69c96154ad99f92bedc7",
 | 
							"353cbad9b58e69c96154ad99f92bedc7",
 | 
				
			||||||
		HashEmail("gitea@example.com"),
 | 
							avatars_model.HashEmail("gitea@example.com"),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestSizedAvatarLink(t *testing.T) {
 | 
					func TestSizedAvatarLink(t *testing.T) {
 | 
				
			||||||
	setting.AppSubURL = "/testsuburl"
 | 
						setting.AppSubURL = "/testsuburl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	disableGravatar()
 | 
						disableGravatar(t)
 | 
				
			||||||
	assert.Equal(t, "/testsuburl/assets/img/avatar_default.png",
 | 
						assert.Equal(t, "/testsuburl/assets/img/avatar_default.png",
 | 
				
			||||||
		GenerateEmailAvatarFastLink("gitea@example.com", 100))
 | 
							avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enableGravatar(t)
 | 
						enableGravatar(t)
 | 
				
			||||||
	assert.Equal(t,
 | 
						assert.Equal(t,
 | 
				
			||||||
		"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
 | 
							"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
 | 
				
			||||||
		GenerateEmailAvatarFastLink("gitea@example.com", 100),
 | 
							avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package admin_test
 | 
					package avatars_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
							
								
								
									
										15
									
								
								models/fixtures/system_setting.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								models/fixtures/system_setting.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 1
 | 
				
			||||||
 | 
					  setting_key: 'disable_gravatar'
 | 
				
			||||||
 | 
					  setting_value: 'false'
 | 
				
			||||||
 | 
					  version: 1
 | 
				
			||||||
 | 
					  created: 1653533198
 | 
				
			||||||
 | 
					  updated: 1653533198
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 2
 | 
				
			||||||
 | 
					  setting_key: 'enable_federated_avatar'
 | 
				
			||||||
 | 
					  setting_value: 'false'
 | 
				
			||||||
 | 
					  version: 1
 | 
				
			||||||
 | 
					  created: 1653533198
 | 
				
			||||||
 | 
					  updated: 1653533198
 | 
				
			||||||
@@ -13,7 +13,6 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/models/foreignreference"
 | 
						"code.gitea.io/gitea/models/foreignreference"
 | 
				
			||||||
	"code.gitea.io/gitea/models/organization"
 | 
						"code.gitea.io/gitea/models/organization"
 | 
				
			||||||
@@ -21,6 +20,7 @@ import (
 | 
				
			|||||||
	access_model "code.gitea.io/gitea/models/perm/access"
 | 
						access_model "code.gitea.io/gitea/models/perm/access"
 | 
				
			||||||
	project_model "code.gitea.io/gitea/models/project"
 | 
						project_model "code.gitea.io/gitea/models/project"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
@@ -2470,7 +2470,7 @@ func DeleteOrphanedIssues() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Remove issue attachment files.
 | 
						// Remove issue attachment files.
 | 
				
			||||||
	for i := range attachmentPaths {
 | 
						for i := range attachmentPaths {
 | 
				
			||||||
		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
 | 
							system_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,8 @@ import (
 | 
				
			|||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ "code.gitea.io/gitea/models/system"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -415,6 +415,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
 | 
						NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
 | 
				
			||||||
	// v226 -> v227
 | 
						// v226 -> v227
 | 
				
			||||||
	NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
 | 
						NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
 | 
				
			||||||
 | 
						// v227 -> v228
 | 
				
			||||||
 | 
						NewMigration("Create key/value table for system settings", createSystemSettingsTable),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCurrentDBVersion returns the current db version
 | 
					// GetCurrentDBVersion returns the current db version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										64
									
								
								models/migrations/v227.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								models/migrations/v227.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					// 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 migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SystemSetting struct {
 | 
				
			||||||
 | 
						ID           int64              `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
						SettingKey   string             `xorm:"varchar(255) unique"` // ensure key is always lowercase
 | 
				
			||||||
 | 
						SettingValue string             `xorm:"text"`
 | 
				
			||||||
 | 
						Version      int                `xorm:"version"` // prevent to override
 | 
				
			||||||
 | 
						Created      timeutil.TimeStamp `xorm:"created"`
 | 
				
			||||||
 | 
						Updated      timeutil.TimeStamp `xorm:"updated"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func insertSettingsIfNotExist(x *xorm.Engine, sysSettings []*SystemSetting) error {
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, setting := range sysSettings {
 | 
				
			||||||
 | 
							exist, err := sess.Table("system_setting").Where("setting_key=?", setting.SettingKey).Exist()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !exist {
 | 
				
			||||||
 | 
								if _, err := sess.Insert(setting); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createSystemSettingsTable(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						if err := x.Sync2(new(SystemSetting)); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("sync2: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// migrate xx to database
 | 
				
			||||||
 | 
						sysSettings := []*SystemSetting{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SettingKey:   "picture.disable_gravatar",
 | 
				
			||||||
 | 
								SettingValue: strconv.FormatBool(setting.DisableGravatar),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SettingKey:   "picture.enable_federated_avatar",
 | 
				
			||||||
 | 
								SettingValue: strconv.FormatBool(setting.EnableFederatedAvatar),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return insertSettingsIfNotExist(x, sysSettings)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -22,6 +22,7 @@ import (
 | 
				
			|||||||
	access_model "code.gitea.io/gitea/models/perm/access"
 | 
						access_model "code.gitea.io/gitea/models/perm/access"
 | 
				
			||||||
	project_model "code.gitea.io/gitea/models/project"
 | 
						project_model "code.gitea.io/gitea/models/project"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/models/webhook"
 | 
						"code.gitea.io/gitea/models/webhook"
 | 
				
			||||||
@@ -32,9 +33,13 @@ import (
 | 
				
			|||||||
	"xorm.io/builder"
 | 
						"xorm.io/builder"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRepoContext creates a new repository context
 | 
					// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
 | 
				
			||||||
func NewRepoContext() {
 | 
					var ItemsPerPage = 40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Init initialize model
 | 
				
			||||||
 | 
					func Init() error {
 | 
				
			||||||
	unit.LoadUnitConfig()
 | 
						unit.LoadUnitConfig()
 | 
				
			||||||
 | 
						return system_model.Init()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteRepository deletes a repository for a user or organization.
 | 
					// DeleteRepository deletes a repository for a user or organization.
 | 
				
			||||||
@@ -267,36 +272,36 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Remove repository files.
 | 
						// Remove repository files.
 | 
				
			||||||
	repoPath := repo.RepoPath()
 | 
						repoPath := repo.RepoPath()
 | 
				
			||||||
	admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
 | 
						system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove wiki files
 | 
						// Remove wiki files
 | 
				
			||||||
	if repo.HasWiki() {
 | 
						if repo.HasWiki() {
 | 
				
			||||||
		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
 | 
							system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove archives
 | 
						// Remove archives
 | 
				
			||||||
	for _, archive := range archivePaths {
 | 
						for _, archive := range archivePaths {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
 | 
							system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove lfs objects
 | 
						// Remove lfs objects
 | 
				
			||||||
	for _, lfsObj := range lfsPaths {
 | 
						for _, lfsObj := range lfsPaths {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
 | 
							system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove issue attachment files.
 | 
						// Remove issue attachment files.
 | 
				
			||||||
	for _, attachment := range attachmentPaths {
 | 
						for _, attachment := range attachmentPaths {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
 | 
							system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove release attachment files.
 | 
						// Remove release attachment files.
 | 
				
			||||||
	for _, releaseAttachment := range releaseAttachments {
 | 
						for _, releaseAttachment := range releaseAttachments {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
 | 
							system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove attachment with no issue_id and release_id.
 | 
						// Remove attachment with no issue_id and release_id.
 | 
				
			||||||
	for _, newAttachment := range newAttachmentPaths {
 | 
						for _, newAttachment := range newAttachmentPaths {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
 | 
							system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(repo.Avatar) > 0 {
 | 
						if len(repo.Avatar) > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package appstate
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
							
								
								
									
										21
									
								
								models/system/main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								models/system/main_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					// Copyright 2020 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 system_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ "code.gitea.io/gitea/models"        // register models
 | 
				
			||||||
 | 
						_ "code.gitea.io/gitea/models/system" // register models of system
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
 | 
						unittest.MainTest(m, &unittest.TestOptions{
 | 
				
			||||||
 | 
							GiteaRootPath: filepath.Join("..", ".."),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package admin
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
							
								
								
									
										117
									
								
								models/system/notice_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								models/system/notice_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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 system_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/system"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNotice_TrStr(t *testing.T) {
 | 
				
			||||||
 | 
						notice := &system.Notice{
 | 
				
			||||||
 | 
							Type:        system.NoticeRepository,
 | 
				
			||||||
 | 
							Description: "test description",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.Equal(t, "admin.notices.type_1", notice.TrStr())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateNotice(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						noticeBean := &system.Notice{
 | 
				
			||||||
 | 
							Type:        system.NoticeRepository,
 | 
				
			||||||
 | 
							Description: "test description",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, noticeBean)
 | 
				
			||||||
 | 
						assert.NoError(t, system.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, noticeBean)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateRepositoryNotice(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						noticeBean := &system.Notice{
 | 
				
			||||||
 | 
							Type:        system.NoticeRepository,
 | 
				
			||||||
 | 
							Description: "test description",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, noticeBean)
 | 
				
			||||||
 | 
						assert.NoError(t, system.CreateRepositoryNotice(noticeBean.Description))
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, noticeBean)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO TestRemoveAllWithNotice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCountNotices(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
						assert.Equal(t, int64(3), system.CountNotices())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNotices(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notices, err := system.Notices(1, 2)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						if assert.Len(t, notices, 2) {
 | 
				
			||||||
 | 
							assert.Equal(t, int64(3), notices[0].ID)
 | 
				
			||||||
 | 
							assert.Equal(t, int64(2), notices[1].ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notices, err = system.Notices(2, 2)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						if assert.Len(t, notices, 1) {
 | 
				
			||||||
 | 
							assert.Equal(t, int64(1), notices[0].ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteNotice(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
						assert.NoError(t, system.DeleteNotice(3))
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteNotices(t *testing.T) {
 | 
				
			||||||
 | 
						// delete a non-empty range
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
						assert.NoError(t, system.DeleteNotices(1, 2))
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteNotices2(t *testing.T) {
 | 
				
			||||||
 | 
						// delete an empty range
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
						assert.NoError(t, system.DeleteNotices(3, 2))
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeleteNoticesByIDs(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
						assert.NoError(t, system.DeleteNoticesByIDs([]int64{1, 3}))
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, &system.Notice{ID: 1})
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
 | 
				
			||||||
 | 
						unittest.AssertNotExistsBean(t, &system.Notice{ID: 3})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										261
									
								
								models/system/setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								models/system/setting.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,261 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"strk.kbt.io/projects/go/libravatar"
 | 
				
			||||||
 | 
						"xorm.io/builder"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Setting is a key value store of user settings
 | 
				
			||||||
 | 
					type Setting struct {
 | 
				
			||||||
 | 
						ID           int64              `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
						SettingKey   string             `xorm:"varchar(255) unique"` // ensure key is always lowercase
 | 
				
			||||||
 | 
						SettingValue string             `xorm:"text"`
 | 
				
			||||||
 | 
						Version      int                `xorm:"version"` // prevent to override
 | 
				
			||||||
 | 
						Created      timeutil.TimeStamp `xorm:"created"`
 | 
				
			||||||
 | 
						Updated      timeutil.TimeStamp `xorm:"updated"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TableName sets the table name for the settings struct
 | 
				
			||||||
 | 
					func (s *Setting) TableName() string {
 | 
				
			||||||
 | 
						return "system_setting"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Setting) GetValueBool() bool {
 | 
				
			||||||
 | 
						b, _ := strconv.ParseBool(s.SettingValue)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						db.RegisterModel(new(Setting))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrSettingIsNotExist represents an error that a setting is not exist with special key
 | 
				
			||||||
 | 
					type ErrSettingIsNotExist struct {
 | 
				
			||||||
 | 
						Key string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Error implements error
 | 
				
			||||||
 | 
					func (err ErrSettingIsNotExist) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("System setting[%s] is not exist", err.Key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrSettingIsNotExist return true if err is ErrSettingIsNotExist
 | 
				
			||||||
 | 
					func IsErrSettingIsNotExist(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrSettingIsNotExist)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrDataExpired represents an error that update a record which has been updated by another thread
 | 
				
			||||||
 | 
					type ErrDataExpired struct {
 | 
				
			||||||
 | 
						Key string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Error implements error
 | 
				
			||||||
 | 
					func (err ErrDataExpired) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("System setting[%s] has been updated by another thread", err.Key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrDataExpired return true if err is ErrDataExpired
 | 
				
			||||||
 | 
					func IsErrDataExpired(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrDataExpired)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSetting returns specific setting
 | 
				
			||||||
 | 
					func GetSetting(key string) (*Setting, error) {
 | 
				
			||||||
 | 
						v, err := GetSettings([]string{key})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(v) == 0 {
 | 
				
			||||||
 | 
							return nil, ErrSettingIsNotExist{key}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v[key], nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSettings returns specific settings
 | 
				
			||||||
 | 
					func GetSettings(keys []string) (map[string]*Setting, error) {
 | 
				
			||||||
 | 
						for i := 0; i < len(keys); i++ {
 | 
				
			||||||
 | 
							keys[i] = strings.ToLower(keys[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						settings := make([]*Setting, 0, len(keys))
 | 
				
			||||||
 | 
						if err := db.GetEngine(db.DefaultContext).
 | 
				
			||||||
 | 
							Where(builder.In("setting_key", keys)).
 | 
				
			||||||
 | 
							Find(&settings); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						settingsMap := make(map[string]*Setting)
 | 
				
			||||||
 | 
						for _, s := range settings {
 | 
				
			||||||
 | 
							settingsMap[s.SettingKey] = s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return settingsMap, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AllSettings map[string]*Setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (settings AllSettings) Get(key string) Setting {
 | 
				
			||||||
 | 
						if v, ok := settings[key]; ok {
 | 
				
			||||||
 | 
							return *v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return Setting{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (settings AllSettings) GetBool(key string) bool {
 | 
				
			||||||
 | 
						b, _ := strconv.ParseBool(settings.Get(key).SettingValue)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (settings AllSettings) GetVersion(key string) int {
 | 
				
			||||||
 | 
						return settings.Get(key).Version
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAllSettings returns all settings from user
 | 
				
			||||||
 | 
					func GetAllSettings() (AllSettings, error) {
 | 
				
			||||||
 | 
						settings := make([]*Setting, 0, 5)
 | 
				
			||||||
 | 
						if err := db.GetEngine(db.DefaultContext).
 | 
				
			||||||
 | 
							Find(&settings); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						settingsMap := make(map[string]*Setting)
 | 
				
			||||||
 | 
						for _, s := range settings {
 | 
				
			||||||
 | 
							settingsMap[s.SettingKey] = s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return settingsMap, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteSetting deletes a specific setting for a user
 | 
				
			||||||
 | 
					func DeleteSetting(setting *Setting) error {
 | 
				
			||||||
 | 
						_, err := db.GetEngine(db.DefaultContext).Delete(setting)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SetSettingNoVersion(key, value string) error {
 | 
				
			||||||
 | 
						s, err := GetSetting(key)
 | 
				
			||||||
 | 
						if IsErrSettingIsNotExist(err) {
 | 
				
			||||||
 | 
							return SetSetting(&Setting{
 | 
				
			||||||
 | 
								SettingKey:   key,
 | 
				
			||||||
 | 
								SettingValue: value,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s.SettingValue = value
 | 
				
			||||||
 | 
						return SetSetting(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetSetting updates a users' setting for a specific key
 | 
				
			||||||
 | 
					func SetSetting(setting *Setting) error {
 | 
				
			||||||
 | 
						if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						setting.Version++
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func upsertSettingValue(key, value string, version int) error {
 | 
				
			||||||
 | 
						return db.WithTx(func(ctx context.Context) error {
 | 
				
			||||||
 | 
							e := db.GetEngine(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// here we use a general method to do a safe upsert for different databases (and most transaction levels)
 | 
				
			||||||
 | 
							// 1. try to UPDATE the record and acquire the transaction write lock
 | 
				
			||||||
 | 
							//    if UPDATE returns non-zero rows are changed, OK, the setting is saved correctly
 | 
				
			||||||
 | 
							//    if UPDATE returns "0 rows changed", two possibilities: (a) record doesn't exist  (b) value is not changed
 | 
				
			||||||
 | 
							// 2. do a SELECT to check if the row exists or not (we already have the transaction lock)
 | 
				
			||||||
 | 
							// 3. if the row doesn't exist, do an INSERT (we are still protected by the transaction lock, so it's safe)
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// to optimize the SELECT in step 2, we can use an extra column like `revision=revision+1`
 | 
				
			||||||
 | 
							//    to make sure the UPDATE always returns a non-zero value for existing (unchanged) records.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							res, err := e.Exec("UPDATE system_setting SET setting_value=?, version = version+1 WHERE setting_key=? AND version=?", value, key, version)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rows, _ := res.RowsAffected()
 | 
				
			||||||
 | 
							if rows > 0 {
 | 
				
			||||||
 | 
								// the existing row is updated, so we can return
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// in case the value isn't changed, update would return 0 rows changed, so we need this check
 | 
				
			||||||
 | 
							has, err := e.Exist(&Setting{SettingKey: key})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if has {
 | 
				
			||||||
 | 
								return ErrDataExpired{Key: key}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// if no existing row, insert a new row
 | 
				
			||||||
 | 
							_, err = e.Insert(&Setting{SettingKey: key, SettingValue: value})
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						GravatarSourceURL *url.URL
 | 
				
			||||||
 | 
						LibravatarService *libravatar.Libravatar
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Init() error {
 | 
				
			||||||
 | 
						var disableGravatar bool
 | 
				
			||||||
 | 
						disableGravatarSetting, err := GetSetting(KeyPictureDisableGravatar)
 | 
				
			||||||
 | 
						if IsErrSettingIsNotExist(err) {
 | 
				
			||||||
 | 
							disableGravatar = setting.GetDefaultDisableGravatar()
 | 
				
			||||||
 | 
							disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							disableGravatar = disableGravatarSetting.GetValueBool()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var enableFederatedAvatar bool
 | 
				
			||||||
 | 
						enableFederatedAvatarSetting, err := GetSetting(KeyPictureEnableFederatedAvatar)
 | 
				
			||||||
 | 
						if IsErrSettingIsNotExist(err) {
 | 
				
			||||||
 | 
							enableFederatedAvatar = setting.GetDefaultEnableFederatedAvatar(disableGravatar)
 | 
				
			||||||
 | 
							enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							enableFederatedAvatar = disableGravatarSetting.GetValueBool()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if setting.OfflineMode {
 | 
				
			||||||
 | 
							disableGravatar = true
 | 
				
			||||||
 | 
							enableFederatedAvatar = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if disableGravatar || !enableFederatedAvatar {
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							GravatarSourceURL, err = url.Parse(setting.GravatarSource)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("Failed to parse Gravatar URL(%s): %v", setting.GravatarSource, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if enableFederatedAvatarSetting.GetValueBool() {
 | 
				
			||||||
 | 
							LibravatarService = libravatar.New()
 | 
				
			||||||
 | 
							if GravatarSourceURL.Scheme == "https" {
 | 
				
			||||||
 | 
								LibravatarService.SetUseHTTPS(true)
 | 
				
			||||||
 | 
								LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LibravatarService.SetUseHTTPS(false)
 | 
				
			||||||
 | 
								LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								models/system/setting_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								models/system/setting_key.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// 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 system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// enumerate all system setting keys
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						KeyPictureDisableGravatar       = "picture.disable_gravatar"
 | 
				
			||||||
 | 
						KeyPictureEnableFederatedAvatar = "picture.enable_federated_avatar"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										53
									
								
								models/system/setting_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								models/system/setting_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 system_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/system"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSettings(t *testing.T) {
 | 
				
			||||||
 | 
						keyName := "server.LFS_LOCKS_PAGING_NUM"
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newSetting := &system.Setting{SettingKey: keyName, SettingValue: "50"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create setting
 | 
				
			||||||
 | 
						err := system.SetSetting(newSetting)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						// test about saving unchanged values
 | 
				
			||||||
 | 
						err = system.SetSetting(newSetting)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get specific setting
 | 
				
			||||||
 | 
						settings, err := system.GetSettings([]string{keyName})
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Len(t, settings, 1)
 | 
				
			||||||
 | 
						assert.EqualValues(t, newSetting.SettingValue, settings[strings.ToLower(keyName)].SettingValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// updated setting
 | 
				
			||||||
 | 
						updatedSetting := &system.Setting{SettingKey: keyName, SettingValue: "100", Version: newSetting.Version}
 | 
				
			||||||
 | 
						err = system.SetSetting(updatedSetting)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get all settings
 | 
				
			||||||
 | 
						settings, err = system.GetAllSettings()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Len(t, settings, 3)
 | 
				
			||||||
 | 
						assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// delete setting
 | 
				
			||||||
 | 
						err = system.DeleteSetting(&system.Setting{SettingKey: strings.ToLower(keyName)})
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						settings, err = system.GetAllSettings()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Len(t, settings, 2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,12 +7,12 @@ package unittest
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -91,10 +91,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
 | 
				
			|||||||
	setting.AppDataPath = appDataPath
 | 
						setting.AppDataPath = appDataPath
 | 
				
			||||||
	setting.AppWorkPath = testOpts.GiteaRootPath
 | 
						setting.AppWorkPath = testOpts.GiteaRootPath
 | 
				
			||||||
	setting.StaticRootPath = testOpts.GiteaRootPath
 | 
						setting.StaticRootPath = testOpts.GiteaRootPath
 | 
				
			||||||
	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
 | 
						setting.GravatarSource = "https://secure.gravatar.com/avatar/"
 | 
				
			||||||
	if err != nil {
 | 
					
 | 
				
			||||||
		fatalTestError("url.Parse: %v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments")
 | 
						setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs")
 | 
						setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs")
 | 
				
			||||||
@@ -112,6 +110,9 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
 | 
				
			|||||||
	if err = storage.Init(); err != nil {
 | 
						if err = storage.Init(); err != nil {
 | 
				
			||||||
		fatalTestError("storage.Init: %v\n", err)
 | 
							fatalTestError("storage.Init: %v\n", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if err = system_model.Init(); err != nil {
 | 
				
			||||||
 | 
							fatalTestError("models.Init: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = util.RemoveAll(repoRootPath); err != nil {
 | 
						if err = util.RemoveAll(repoRootPath); err != nil {
 | 
				
			||||||
		fatalTestError("util.RemoveAll: %v\n", err)
 | 
							fatalTestError("util.RemoveAll: %v\n", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/avatars"
 | 
						"code.gitea.io/gitea/models/avatars"
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/avatar"
 | 
						"code.gitea.io/gitea/modules/avatar"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -67,10 +68,16 @@ func (u *User) AvatarLinkWithSize(size int) string {
 | 
				
			|||||||
	useLocalAvatar := false
 | 
						useLocalAvatar := false
 | 
				
			||||||
	autoGenerateAvatar := false
 | 
						autoGenerateAvatar := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var disableGravatar bool
 | 
				
			||||||
 | 
						disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
 | 
				
			||||||
 | 
						if disableGravatarSetting != nil {
 | 
				
			||||||
 | 
							disableGravatar = disableGravatarSetting.GetValueBool()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case u.UseCustomAvatar:
 | 
						case u.UseCustomAvatar:
 | 
				
			||||||
		useLocalAvatar = true
 | 
							useLocalAvatar = true
 | 
				
			||||||
	case setting.DisableGravatar, setting.OfflineMode:
 | 
						case disableGravatar, setting.OfflineMode:
 | 
				
			||||||
		useLocalAvatar = true
 | 
							useLocalAvatar = true
 | 
				
			||||||
		autoGenerateAvatar = true
 | 
							autoGenerateAvatar = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,34 @@ func init() {
 | 
				
			|||||||
	db.RegisterModel(new(Setting))
 | 
						db.RegisterModel(new(Setting))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrUserSettingIsNotExist represents an error that a setting is not exist with special key
 | 
				
			||||||
 | 
					type ErrUserSettingIsNotExist struct {
 | 
				
			||||||
 | 
						Key string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Error implements error
 | 
				
			||||||
 | 
					func (err ErrUserSettingIsNotExist) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("Setting[%s] is not exist", err.Key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist
 | 
				
			||||||
 | 
					func IsErrUserSettingIsNotExist(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrUserSettingIsNotExist)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSetting returns specific setting
 | 
				
			||||||
 | 
					func GetSetting(uid int64, key string) (*Setting, error) {
 | 
				
			||||||
 | 
						v, err := GetUserSettings(uid, []string{key})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(v) == 0 {
 | 
				
			||||||
 | 
							return nil, ErrUserSettingIsNotExist{key}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v[key], nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetUserSettings returns specific settings from user
 | 
					// GetUserSettings returns specific settings from user
 | 
				
			||||||
func GetUserSettings(uid int64, keys []string) (map[string]*Setting, error) {
 | 
					func GetUserSettings(uid int64, keys []string) (map[string]*Setting, error) {
 | 
				
			||||||
	settings := make([]*Setting, 0, len(keys))
 | 
						settings := make([]*Setting, 0, len(keys))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,10 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unittest"
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -100,6 +102,14 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
 | 
				
			|||||||
	assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
 | 
						assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func enableGravatar(t *testing.T) {
 | 
				
			||||||
 | 
						err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						setting.GravatarSource = "https://secure.gravatar.com/avatar"
 | 
				
			||||||
 | 
						err = system_model.Init()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPushCommits_AvatarLink(t *testing.T) {
 | 
					func TestPushCommits_AvatarLink(t *testing.T) {
 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,6 +133,8 @@ func TestPushCommits_AvatarLink(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enableGravatar(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t,
 | 
						assert.Equal(t,
 | 
				
			||||||
		"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84",
 | 
							"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84",
 | 
				
			||||||
		pushCommits.AvatarLink("user2@example.com"))
 | 
							pushCommits.AvatarLink("user2@example.com"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,14 +4,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package setting
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"strk.kbt.io/projects/go/libravatar"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// settings
 | 
					// settings
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	// Picture settings
 | 
						// Picture settings
 | 
				
			||||||
@@ -30,10 +22,8 @@ var (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GravatarSource        string
 | 
						GravatarSource        string
 | 
				
			||||||
	GravatarSourceURL     *url.URL
 | 
						DisableGravatar       bool // Depreciated: migrated to database
 | 
				
			||||||
	DisableGravatar       bool
 | 
						EnableFederatedAvatar bool // Depreciated: migrated to database
 | 
				
			||||||
	EnableFederatedAvatar bool
 | 
					 | 
				
			||||||
	LibravatarService     *libravatar.Libravatar
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RepoAvatar = struct {
 | 
						RepoAvatar = struct {
 | 
				
			||||||
		Storage
 | 
							Storage
 | 
				
			||||||
@@ -69,38 +59,30 @@ func newPictureService() {
 | 
				
			|||||||
	default:
 | 
						default:
 | 
				
			||||||
		GravatarSource = source
 | 
							GravatarSource = source
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
 | 
					 | 
				
			||||||
	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock)
 | 
					 | 
				
			||||||
	if OfflineMode {
 | 
					 | 
				
			||||||
		DisableGravatar = true
 | 
					 | 
				
			||||||
		EnableFederatedAvatar = false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if DisableGravatar {
 | 
					 | 
				
			||||||
		EnableFederatedAvatar = false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if EnableFederatedAvatar || !DisableGravatar {
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		GravatarSourceURL, err = url.Parse(GravatarSource)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatal("Failed to parse Gravatar URL(%s): %v",
 | 
					 | 
				
			||||||
				GravatarSource, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if EnableFederatedAvatar {
 | 
						DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
 | 
				
			||||||
		LibravatarService = libravatar.New()
 | 
						deprecatedSettingDB("", "DISABLE_GRAVATAR")
 | 
				
			||||||
		if GravatarSourceURL.Scheme == "https" {
 | 
						EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
 | 
				
			||||||
			LibravatarService.SetUseHTTPS(true)
 | 
						deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR")
 | 
				
			||||||
			LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			LibravatarService.SetUseHTTPS(false)
 | 
					 | 
				
			||||||
			LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newRepoAvatarService()
 | 
						newRepoAvatarService()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetDefaultDisableGravatar() bool {
 | 
				
			||||||
 | 
						return !OfflineMode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
 | 
				
			||||||
 | 
						v := !InstallLock
 | 
				
			||||||
 | 
						if OfflineMode {
 | 
				
			||||||
 | 
							v = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if disableGravatar {
 | 
				
			||||||
 | 
							v = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newRepoAvatarService() {
 | 
					func newRepoAvatarService() {
 | 
				
			||||||
	sec := Cfg.Section("picture")
 | 
						sec := Cfg.Section("picture")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -606,6 +606,13 @@ func deprecatedSetting(oldSection, oldKey, newSection, newKey string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
 | 
				
			||||||
 | 
					func deprecatedSettingDB(oldSection, oldKey string) {
 | 
				
			||||||
 | 
						if Cfg.Section(oldSection).HasKey(oldKey) {
 | 
				
			||||||
 | 
							log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// loadFromConf initializes configuration context.
 | 
					// loadFromConf initializes configuration context.
 | 
				
			||||||
// NOTE: do not print any log except error.
 | 
					// NOTE: do not print any log except error.
 | 
				
			||||||
func loadFromConf(allowEmpty bool, extraConfig string) {
 | 
					func loadFromConf(allowEmpty bool, extraConfig string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package appstate
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StateStore is the interface to get/set app state items
 | 
					// StateStore is the interface to get/set app state items
 | 
				
			||||||
type StateStore interface {
 | 
					type StateStore interface {
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package appstate
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
@@ -2,10 +2,10 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package appstate
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"code.gitea.io/gitea/models/appstate"
 | 
						"code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/yuin/goldmark/util"
 | 
						"github.com/yuin/goldmark/util"
 | 
				
			||||||
@@ -16,7 +16,7 @@ type DBStore struct{}
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Get reads the state item
 | 
					// Get reads the state item
 | 
				
			||||||
func (f *DBStore) Get(item StateItem) error {
 | 
					func (f *DBStore) Get(item StateItem) error {
 | 
				
			||||||
	content, err := appstate.GetAppStateContent(item.Name())
 | 
						content, err := system.GetAppStateContent(item.Name())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -32,5 +32,5 @@ func (f *DBStore) Set(item StateItem) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return appstate.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b))
 | 
						return system.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package appstate
 | 
					package system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RuntimeState contains app state for runtime, and we can save remote version for update checker here in future
 | 
					// RuntimeState contains app state for runtime, and we can save remote version for update checker here in future
 | 
				
			||||||
type RuntimeState struct {
 | 
					type RuntimeState struct {
 | 
				
			||||||
							
								
								
									
										46
									
								
								modules/system/setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								modules/system/setting.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/system"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func genKey(key string) string {
 | 
				
			||||||
 | 
						return "system.setting." + key
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSetting returns the setting value via the key
 | 
				
			||||||
 | 
					func GetSetting(key string) (string, error) {
 | 
				
			||||||
 | 
						return cache.GetString(genKey(key), func() (string, error) {
 | 
				
			||||||
 | 
							res, err := system.GetSetting(key)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res.SettingValue, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSettingBool return bool value of setting,
 | 
				
			||||||
 | 
					// none existing keys and errors are ignored and result in false
 | 
				
			||||||
 | 
					func GetSettingBool(key string) bool {
 | 
				
			||||||
 | 
						s, _ := GetSetting(key)
 | 
				
			||||||
 | 
						b, _ := strconv.ParseBool(s)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetSetting sets the setting value
 | 
				
			||||||
 | 
					func SetSetting(key, value string, version int) error {
 | 
				
			||||||
 | 
						cache.Remove(genKey(key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return system.SetSetting(&system.Setting{
 | 
				
			||||||
 | 
							SettingKey:   key,
 | 
				
			||||||
 | 
							SettingValue: value,
 | 
				
			||||||
 | 
							Version:      version,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								modules/system/user_setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								modules/system/user_setting.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func genUserKey(userID int64, key string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("user_%d.setting.%s", userID, key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUserSetting returns the user setting value via the key
 | 
				
			||||||
 | 
					func GetUserSetting(userID int64, key string) (string, error) {
 | 
				
			||||||
 | 
						return cache.GetString(genUserKey(userID, key), func() (string, error) {
 | 
				
			||||||
 | 
							res, err := user.GetSetting(userID, key)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res.SettingValue, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetUserSetting sets the user setting value
 | 
				
			||||||
 | 
					func SetUserSetting(userID int64, key, value string) error {
 | 
				
			||||||
 | 
						cache.Remove(genUserKey(userID, key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return user.SetUserSetting(userID, key, value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -29,6 +29,7 @@ import (
 | 
				
			|||||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						issues_model "code.gitea.io/gitea/models/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/models/organization"
 | 
						"code.gitea.io/gitea/models/organization"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/emoji"
 | 
						"code.gitea.io/gitea/modules/emoji"
 | 
				
			||||||
@@ -41,6 +42,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/repository"
 | 
						"code.gitea.io/gitea/modules/repository"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/svg"
 | 
						"code.gitea.io/gitea/modules/svg"
 | 
				
			||||||
 | 
						system_module "code.gitea.io/gitea/modules/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/services/gitdiff"
 | 
						"code.gitea.io/gitea/services/gitdiff"
 | 
				
			||||||
@@ -85,7 +87,7 @@ func NewFuncMap() []template.FuncMap {
 | 
				
			|||||||
			return setting.AssetVersion
 | 
								return setting.AssetVersion
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"DisableGravatar": func() bool {
 | 
							"DisableGravatar": func() bool {
 | 
				
			||||||
			return setting.DisableGravatar
 | 
								return system_module.GetSettingBool(system_model.KeyPictureDisableGravatar)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"DefaultShowFullName": func() bool {
 | 
							"DefaultShowFullName": func() bool {
 | 
				
			||||||
			return setting.UI.DefaultShowFullName
 | 
								return setting.UI.DefaultShowFullName
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,10 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/appstate"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/proxy"
 | 
						"code.gitea.io/gitea/modules/proxy"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/system"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/hashicorp/go-version"
 | 
						"github.com/hashicorp/go-version"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -64,13 +64,13 @@ func GiteaUpdateChecker(httpEndpoint string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// UpdateRemoteVersion updates the latest available version of Gitea
 | 
					// UpdateRemoteVersion updates the latest available version of Gitea
 | 
				
			||||||
func UpdateRemoteVersion(version string) (err error) {
 | 
					func UpdateRemoteVersion(version string) (err error) {
 | 
				
			||||||
	return appstate.AppState.Set(&CheckerState{LatestVersion: version})
 | 
						return system.AppState.Set(&CheckerState{LatestVersion: version})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
 | 
					// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
 | 
				
			||||||
func GetRemoteVersion() string {
 | 
					func GetRemoteVersion() string {
 | 
				
			||||||
	item := new(CheckerState)
 | 
						item := new(CheckerState)
 | 
				
			||||||
	if err := appstate.AppState.Get(item); err != nil {
 | 
						if err := system.AppState.Get(item); err != nil {
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return item.LatestVersion
 | 
						return item.LatestVersion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2879,6 +2879,9 @@ config.access_log_template = Template
 | 
				
			|||||||
config.xorm_log_mode = XORM Log Mode
 | 
					config.xorm_log_mode = XORM Log Mode
 | 
				
			||||||
config.xorm_log_sql = Log SQL
 | 
					config.xorm_log_sql = Log SQL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config.get_setting_failed = Get setting %s failed
 | 
				
			||||||
 | 
					config.set_setting_failed = Set setting %s failed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
monitor.cron = Cron Tasks
 | 
					monitor.cron = Cron Tasks
 | 
				
			||||||
monitor.name = Name
 | 
					monitor.name = Name
 | 
				
			||||||
monitor.schedule = Schedule
 | 
					monitor.schedule = Schedule
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
						asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/appstate"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/cache"
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/eventsource"
 | 
						"code.gitea.io/gitea/modules/eventsource"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
@@ -27,6 +26,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/ssh"
 | 
						"code.gitea.io/gitea/modules/ssh"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/storage"
 | 
						"code.gitea.io/gitea/modules/storage"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/svg"
 | 
						"code.gitea.io/gitea/modules/svg"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -76,8 +76,8 @@ func InitGitServices() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func syncAppPathForGit(ctx context.Context) error {
 | 
					func syncAppPathForGit(ctx context.Context) error {
 | 
				
			||||||
	runtimeState := new(appstate.RuntimeState)
 | 
						runtimeState := new(system.RuntimeState)
 | 
				
			||||||
	if err := appstate.AppState.Get(runtimeState); err != nil {
 | 
						if err := system.AppState.Get(runtimeState); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if runtimeState.LastAppPath != setting.AppPath {
 | 
						if runtimeState.LastAppPath != setting.AppPath {
 | 
				
			||||||
@@ -90,7 +90,7 @@ func syncAppPathForGit(ctx context.Context) error {
 | 
				
			|||||||
		mustInit(asymkey_model.RewriteAllPublicKeys)
 | 
							mustInit(asymkey_model.RewriteAllPublicKeys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		runtimeState.LastAppPath = setting.AppPath
 | 
							runtimeState.LastAppPath = setting.AppPath
 | 
				
			||||||
		return appstate.AppState.Set(runtimeState)
 | 
							return system.AppState.Set(runtimeState)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -133,10 +133,10 @@ func GlobalInitInstalled(ctx context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	mustInitCtx(ctx, common.InitDBEngine)
 | 
						mustInitCtx(ctx, common.InitDBEngine)
 | 
				
			||||||
	log.Info("ORM engine initialization successful!")
 | 
						log.Info("ORM engine initialization successful!")
 | 
				
			||||||
	mustInit(appstate.Init)
 | 
						mustInit(system.Init)
 | 
				
			||||||
	mustInit(oauth2.Init)
 | 
						mustInit(oauth2.Init)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	models.NewRepoContext()
 | 
						mustInit(models.Init)
 | 
				
			||||||
	mustInit(repo_service.Init)
 | 
						mustInit(repo_service.Init)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Booting long running goroutines.
 | 
						// Booting long running goroutines.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,12 +12,14 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	db_install "code.gitea.io/gitea/models/db/install"
 | 
						db_install "code.gitea.io/gitea/models/db/install"
 | 
				
			||||||
	"code.gitea.io/gitea/models/migrations"
 | 
						"code.gitea.io/gitea/models/migrations"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
@@ -147,8 +149,19 @@ func Install(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Server and other services settings
 | 
						// Server and other services settings
 | 
				
			||||||
	form.OfflineMode = setting.OfflineMode
 | 
						form.OfflineMode = setting.OfflineMode
 | 
				
			||||||
	form.DisableGravatar = setting.DisableGravatar
 | 
						disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
 | 
				
			||||||
	form.EnableFederatedAvatar = setting.EnableFederatedAvatar
 | 
						if disableGravatarSetting != nil {
 | 
				
			||||||
 | 
							form.DisableGravatar = disableGravatarSetting.GetValueBool()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							form.DisableGravatar = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enableFederatedAvatarSetting, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar)
 | 
				
			||||||
 | 
						if enableFederatedAvatarSetting != nil {
 | 
				
			||||||
 | 
							form.EnableFederatedAvatar = enableFederatedAvatarSetting.GetValueBool()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							form.EnableFederatedAvatar = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
 | 
						form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
 | 
				
			||||||
	form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
 | 
						form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
 | 
				
			||||||
	form.DisableRegistration = setting.Service.DisableRegistration
 | 
						form.DisableRegistration = setting.Service.DisableRegistration
 | 
				
			||||||
@@ -439,7 +452,11 @@ func SubmitInstall(ctx *context.Context) {
 | 
				
			|||||||
	cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify))
 | 
						cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode))
 | 
						cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode))
 | 
				
			||||||
	cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(fmt.Sprint(form.DisableGravatar))
 | 
						// if you are reinstalling, this maybe not right because of missing version
 | 
				
			||||||
 | 
						if err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, strconv.FormatBool(form.DisableGravatar)); err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(fmt.Sprint(form.EnableFederatedAvatar))
 | 
						cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(fmt.Sprint(form.EnableFederatedAvatar))
 | 
				
			||||||
	cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn))
 | 
						cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn))
 | 
				
			||||||
	cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp))
 | 
						cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,18 +8,13 @@ package admin
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	activities_model "code.gitea.io/gitea/models/activities"
 | 
						activities_model "code.gitea.io/gitea/models/activities"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/process"
 | 
						"code.gitea.io/gitea/modules/process"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/queue"
 | 
						"code.gitea.io/gitea/modules/queue"
 | 
				
			||||||
@@ -27,18 +22,13 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/updatechecker"
 | 
						"code.gitea.io/gitea/modules/updatechecker"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/services/cron"
 | 
						"code.gitea.io/gitea/services/cron"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	"code.gitea.io/gitea/services/mailer"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"gitea.com/go-chi/session"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tplDashboard  base.TplName = "admin/dashboard"
 | 
						tplDashboard  base.TplName = "admin/dashboard"
 | 
				
			||||||
	tplConfig     base.TplName = "admin/config"
 | 
					 | 
				
			||||||
	tplMonitor    base.TplName = "admin/monitor"
 | 
						tplMonitor    base.TplName = "admin/monitor"
 | 
				
			||||||
	tplStacktrace base.TplName = "admin/stacktrace"
 | 
						tplStacktrace base.TplName = "admin/stacktrace"
 | 
				
			||||||
	tplQueue      base.TplName = "admin/queue"
 | 
						tplQueue      base.TplName = "admin/queue"
 | 
				
			||||||
@@ -165,165 +155,6 @@ func DashboardPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SendTestMail send test mail to confirm mail service is OK
 | 
					 | 
				
			||||||
func SendTestMail(ctx *context.Context) {
 | 
					 | 
				
			||||||
	email := ctx.FormString("email")
 | 
					 | 
				
			||||||
	// Send a test email to the user's email address and redirect back to Config
 | 
					 | 
				
			||||||
	if err := mailer.SendTestMail(email); err != nil {
 | 
					 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/admin/config")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func shadowPasswordKV(cfgItem, splitter string) string {
 | 
					 | 
				
			||||||
	fields := strings.Split(cfgItem, splitter)
 | 
					 | 
				
			||||||
	for i := 0; i < len(fields); i++ {
 | 
					 | 
				
			||||||
		if strings.HasPrefix(fields[i], "password=") {
 | 
					 | 
				
			||||||
			fields[i] = "password=******"
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return strings.Join(fields, splitter)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func shadowURL(provider, cfgItem string) string {
 | 
					 | 
				
			||||||
	u, err := url.Parse(cfgItem)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Error("Shadowing Password for %v failed: %v", provider, err)
 | 
					 | 
				
			||||||
		return cfgItem
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if u.User != nil {
 | 
					 | 
				
			||||||
		atIdx := strings.Index(cfgItem, "@")
 | 
					 | 
				
			||||||
		if atIdx > 0 {
 | 
					 | 
				
			||||||
			colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
 | 
					 | 
				
			||||||
			if colonIdx > 0 {
 | 
					 | 
				
			||||||
				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cfgItem
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func shadowPassword(provider, cfgItem string) string {
 | 
					 | 
				
			||||||
	switch provider {
 | 
					 | 
				
			||||||
	case "redis":
 | 
					 | 
				
			||||||
		return shadowPasswordKV(cfgItem, ",")
 | 
					 | 
				
			||||||
	case "mysql":
 | 
					 | 
				
			||||||
		// root:@tcp(localhost:3306)/macaron?charset=utf8
 | 
					 | 
				
			||||||
		atIdx := strings.Index(cfgItem, "@")
 | 
					 | 
				
			||||||
		if atIdx > 0 {
 | 
					 | 
				
			||||||
			colonIdx := strings.Index(cfgItem[:atIdx], ":")
 | 
					 | 
				
			||||||
			if colonIdx > 0 {
 | 
					 | 
				
			||||||
				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return cfgItem
 | 
					 | 
				
			||||||
	case "postgres":
 | 
					 | 
				
			||||||
		// user=jiahuachen dbname=macaron port=5432 sslmode=disable
 | 
					 | 
				
			||||||
		if !strings.HasPrefix(cfgItem, "postgres://") {
 | 
					 | 
				
			||||||
			return shadowPasswordKV(cfgItem, " ")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fallthrough
 | 
					 | 
				
			||||||
	case "couchbase":
 | 
					 | 
				
			||||||
		return shadowURL(provider, cfgItem)
 | 
					 | 
				
			||||||
		// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
 | 
					 | 
				
			||||||
		// Notice: use shadowURL
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cfgItem
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Config show admin config page
 | 
					 | 
				
			||||||
func Config(ctx *context.Context) {
 | 
					 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("admin.config")
 | 
					 | 
				
			||||||
	ctx.Data["PageIsAdmin"] = true
 | 
					 | 
				
			||||||
	ctx.Data["PageIsAdminConfig"] = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["CustomConf"] = setting.CustomConf
 | 
					 | 
				
			||||||
	ctx.Data["AppUrl"] = setting.AppURL
 | 
					 | 
				
			||||||
	ctx.Data["Domain"] = setting.Domain
 | 
					 | 
				
			||||||
	ctx.Data["OfflineMode"] = setting.OfflineMode
 | 
					 | 
				
			||||||
	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 | 
					 | 
				
			||||||
	ctx.Data["RunUser"] = setting.RunUser
 | 
					 | 
				
			||||||
	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
 | 
					 | 
				
			||||||
	ctx.Data["GitVersion"] = git.VersionInfo()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["RepoRootPath"] = setting.RepoRootPath
 | 
					 | 
				
			||||||
	ctx.Data["CustomRootPath"] = setting.CustomPath
 | 
					 | 
				
			||||||
	ctx.Data["StaticRootPath"] = setting.StaticRootPath
 | 
					 | 
				
			||||||
	ctx.Data["LogRootPath"] = setting.LogRootPath
 | 
					 | 
				
			||||||
	ctx.Data["ScriptType"] = setting.ScriptType
 | 
					 | 
				
			||||||
	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
 | 
					 | 
				
			||||||
	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
 | 
					 | 
				
			||||||
	ctx.Data["ReverseProxyAuthFullName"] = setting.ReverseProxyAuthFullName
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["SSH"] = setting.SSH
 | 
					 | 
				
			||||||
	ctx.Data["LFS"] = setting.LFS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["Service"] = setting.Service
 | 
					 | 
				
			||||||
	ctx.Data["DbCfg"] = setting.Database
 | 
					 | 
				
			||||||
	ctx.Data["Webhook"] = setting.Webhook
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["MailerEnabled"] = false
 | 
					 | 
				
			||||||
	if setting.MailService != nil {
 | 
					 | 
				
			||||||
		ctx.Data["MailerEnabled"] = true
 | 
					 | 
				
			||||||
		ctx.Data["Mailer"] = setting.MailService
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
 | 
					 | 
				
			||||||
	ctx.Data["CacheInterval"] = setting.CacheService.Interval
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
 | 
					 | 
				
			||||||
	ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sessionCfg := setting.SessionConfig
 | 
					 | 
				
			||||||
	if sessionCfg.Provider == "VirtualSession" {
 | 
					 | 
				
			||||||
		var realSession session.Options
 | 
					 | 
				
			||||||
		if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
 | 
					 | 
				
			||||||
			log.Error("Unable to unmarshall session config for virtualed provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		sessionCfg.Provider = realSession.Provider
 | 
					 | 
				
			||||||
		sessionCfg.ProviderConfig = realSession.ProviderConfig
 | 
					 | 
				
			||||||
		sessionCfg.CookieName = realSession.CookieName
 | 
					 | 
				
			||||||
		sessionCfg.CookiePath = realSession.CookiePath
 | 
					 | 
				
			||||||
		sessionCfg.Gclifetime = realSession.Gclifetime
 | 
					 | 
				
			||||||
		sessionCfg.Maxlifetime = realSession.Maxlifetime
 | 
					 | 
				
			||||||
		sessionCfg.Secure = realSession.Secure
 | 
					 | 
				
			||||||
		sessionCfg.Domain = realSession.Domain
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
 | 
					 | 
				
			||||||
	ctx.Data["SessionConfig"] = sessionCfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["DisableGravatar"] = setting.DisableGravatar
 | 
					 | 
				
			||||||
	ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["Git"] = setting.Git
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	type envVar struct {
 | 
					 | 
				
			||||||
		Name, Value string
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	envVars := map[string]*envVar{}
 | 
					 | 
				
			||||||
	if len(os.Getenv("GITEA_WORK_DIR")) > 0 {
 | 
					 | 
				
			||||||
		envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(os.Getenv("GITEA_CUSTOM")) > 0 {
 | 
					 | 
				
			||||||
		envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["EnvVars"] = envVars
 | 
					 | 
				
			||||||
	ctx.Data["Loggers"] = setting.GetLogDescriptions()
 | 
					 | 
				
			||||||
	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
 | 
					 | 
				
			||||||
	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
 | 
					 | 
				
			||||||
	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 | 
					 | 
				
			||||||
	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog
 | 
					 | 
				
			||||||
	ctx.Data["LogSQL"] = setting.Database.LogSQL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.HTML(http.StatusOK, tplConfig)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Monitor show admin monitor page
 | 
					// Monitor show admin monitor page
 | 
				
			||||||
func Monitor(ctx *context.Context) {
 | 
					func Monitor(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("admin.monitor")
 | 
						ctx.Data["Title"] = ctx.Tr("admin.monitor")
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										217
									
								
								routers/web/admin/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								routers/web/admin/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					// 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 admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						system_module "code.gitea.io/gitea/modules/system"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/services/mailer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.com/go-chi/session"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tplConfig base.TplName = "admin/config"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SendTestMail send test mail to confirm mail service is OK
 | 
				
			||||||
 | 
					func SendTestMail(ctx *context.Context) {
 | 
				
			||||||
 | 
						email := ctx.FormString("email")
 | 
				
			||||||
 | 
						// Send a test email to the user's email address and redirect back to Config
 | 
				
			||||||
 | 
						if err := mailer.SendTestMail(email); err != nil {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/config")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func shadowPasswordKV(cfgItem, splitter string) string {
 | 
				
			||||||
 | 
						fields := strings.Split(cfgItem, splitter)
 | 
				
			||||||
 | 
						for i := 0; i < len(fields); i++ {
 | 
				
			||||||
 | 
							if strings.HasPrefix(fields[i], "password=") {
 | 
				
			||||||
 | 
								fields[i] = "password=******"
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Join(fields, splitter)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func shadowURL(provider, cfgItem string) string {
 | 
				
			||||||
 | 
						u, err := url.Parse(cfgItem)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("Shadowing Password for %v failed: %v", provider, err)
 | 
				
			||||||
 | 
							return cfgItem
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if u.User != nil {
 | 
				
			||||||
 | 
							atIdx := strings.Index(cfgItem, "@")
 | 
				
			||||||
 | 
							if atIdx > 0 {
 | 
				
			||||||
 | 
								colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
 | 
				
			||||||
 | 
								if colonIdx > 0 {
 | 
				
			||||||
 | 
									return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cfgItem
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func shadowPassword(provider, cfgItem string) string {
 | 
				
			||||||
 | 
						switch provider {
 | 
				
			||||||
 | 
						case "redis":
 | 
				
			||||||
 | 
							return shadowPasswordKV(cfgItem, ",")
 | 
				
			||||||
 | 
						case "mysql":
 | 
				
			||||||
 | 
							// root:@tcp(localhost:3306)/macaron?charset=utf8
 | 
				
			||||||
 | 
							atIdx := strings.Index(cfgItem, "@")
 | 
				
			||||||
 | 
							if atIdx > 0 {
 | 
				
			||||||
 | 
								colonIdx := strings.Index(cfgItem[:atIdx], ":")
 | 
				
			||||||
 | 
								if colonIdx > 0 {
 | 
				
			||||||
 | 
									return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return cfgItem
 | 
				
			||||||
 | 
						case "postgres":
 | 
				
			||||||
 | 
							// user=jiahuachen dbname=macaron port=5432 sslmode=disable
 | 
				
			||||||
 | 
							if !strings.HasPrefix(cfgItem, "postgres://") {
 | 
				
			||||||
 | 
								return shadowPasswordKV(cfgItem, " ")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case "couchbase":
 | 
				
			||||||
 | 
							return shadowURL(provider, cfgItem)
 | 
				
			||||||
 | 
							// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
 | 
				
			||||||
 | 
							// Notice: use shadowURL
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cfgItem
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config show admin config page
 | 
				
			||||||
 | 
					func Config(ctx *context.Context) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("admin.config")
 | 
				
			||||||
 | 
						ctx.Data["PageIsAdmin"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsAdminConfig"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						systemSettings, err := system_model.GetAllSettings()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.ServerError("system_model.GetAllSettings", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// All editable settings from UI
 | 
				
			||||||
 | 
						ctx.Data["SystemSettings"] = systemSettings
 | 
				
			||||||
 | 
						ctx.PageData["adminConfigPage"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["CustomConf"] = setting.CustomConf
 | 
				
			||||||
 | 
						ctx.Data["AppUrl"] = setting.AppURL
 | 
				
			||||||
 | 
						ctx.Data["Domain"] = setting.Domain
 | 
				
			||||||
 | 
						ctx.Data["OfflineMode"] = setting.OfflineMode
 | 
				
			||||||
 | 
						ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 | 
				
			||||||
 | 
						ctx.Data["RunUser"] = setting.RunUser
 | 
				
			||||||
 | 
						ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
 | 
				
			||||||
 | 
						ctx.Data["GitVersion"] = git.VersionInfo()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["RepoRootPath"] = setting.RepoRootPath
 | 
				
			||||||
 | 
						ctx.Data["CustomRootPath"] = setting.CustomPath
 | 
				
			||||||
 | 
						ctx.Data["StaticRootPath"] = setting.StaticRootPath
 | 
				
			||||||
 | 
						ctx.Data["LogRootPath"] = setting.LogRootPath
 | 
				
			||||||
 | 
						ctx.Data["ScriptType"] = setting.ScriptType
 | 
				
			||||||
 | 
						ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
 | 
				
			||||||
 | 
						ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["SSH"] = setting.SSH
 | 
				
			||||||
 | 
						ctx.Data["LFS"] = setting.LFS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["Service"] = setting.Service
 | 
				
			||||||
 | 
						ctx.Data["DbCfg"] = setting.Database
 | 
				
			||||||
 | 
						ctx.Data["Webhook"] = setting.Webhook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["MailerEnabled"] = false
 | 
				
			||||||
 | 
						if setting.MailService != nil {
 | 
				
			||||||
 | 
							ctx.Data["MailerEnabled"] = true
 | 
				
			||||||
 | 
							ctx.Data["Mailer"] = setting.MailService
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
 | 
				
			||||||
 | 
						ctx.Data["CacheInterval"] = setting.CacheService.Interval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
 | 
				
			||||||
 | 
						ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sessionCfg := setting.SessionConfig
 | 
				
			||||||
 | 
						if sessionCfg.Provider == "VirtualSession" {
 | 
				
			||||||
 | 
							var realSession session.Options
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
 | 
				
			||||||
 | 
								log.Error("Unable to unmarshall session config for virtual provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sessionCfg.Provider = realSession.Provider
 | 
				
			||||||
 | 
							sessionCfg.ProviderConfig = realSession.ProviderConfig
 | 
				
			||||||
 | 
							sessionCfg.CookieName = realSession.CookieName
 | 
				
			||||||
 | 
							sessionCfg.CookiePath = realSession.CookiePath
 | 
				
			||||||
 | 
							sessionCfg.Gclifetime = realSession.Gclifetime
 | 
				
			||||||
 | 
							sessionCfg.Maxlifetime = realSession.Maxlifetime
 | 
				
			||||||
 | 
							sessionCfg.Secure = realSession.Secure
 | 
				
			||||||
 | 
							sessionCfg.Domain = realSession.Domain
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
 | 
				
			||||||
 | 
						ctx.Data["SessionConfig"] = sessionCfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["Git"] = setting.Git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type envVar struct {
 | 
				
			||||||
 | 
							Name, Value string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						envVars := map[string]*envVar{}
 | 
				
			||||||
 | 
						if len(os.Getenv("GITEA_WORK_DIR")) > 0 {
 | 
				
			||||||
 | 
							envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(os.Getenv("GITEA_CUSTOM")) > 0 {
 | 
				
			||||||
 | 
							envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["EnvVars"] = envVars
 | 
				
			||||||
 | 
						ctx.Data["Loggers"] = setting.GetLogDescriptions()
 | 
				
			||||||
 | 
						ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
 | 
				
			||||||
 | 
						ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
 | 
				
			||||||
 | 
						ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 | 
				
			||||||
 | 
						ctx.Data["EnableXORMLog"] = setting.EnableXORMLog
 | 
				
			||||||
 | 
						ctx.Data["LogSQL"] = setting.Database.LogSQL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.HTML(http.StatusOK, tplConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ChangeConfig(ctx *context.Context) {
 | 
				
			||||||
 | 
						key := strings.TrimSpace(ctx.FormString("key"))
 | 
				
			||||||
 | 
						if key == "" {
 | 
				
			||||||
 | 
							ctx.JSON(http.StatusOK, map[string]string{
 | 
				
			||||||
 | 
								"redirect": ctx.Req.URL.String(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						value := ctx.FormString("value")
 | 
				
			||||||
 | 
						version := ctx.FormInt("version")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := system_module.SetSetting(key, value, version); err != nil {
 | 
				
			||||||
 | 
							log.Error("set setting failed: %v", err)
 | 
				
			||||||
 | 
							ctx.JSON(http.StatusOK, map[string]string{
 | 
				
			||||||
 | 
								"err": ctx.Tr("admin.config.set_setting_failed", key),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
 | 
							"version": version + 1,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -26,13 +26,13 @@ func Notices(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["PageIsAdmin"] = true
 | 
						ctx.Data["PageIsAdmin"] = true
 | 
				
			||||||
	ctx.Data["PageIsAdminNotices"] = true
 | 
						ctx.Data["PageIsAdminNotices"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	total := admin_model.CountNotices()
 | 
						total := system_model.CountNotices()
 | 
				
			||||||
	page := ctx.FormInt("page")
 | 
						page := ctx.FormInt("page")
 | 
				
			||||||
	if page <= 1 {
 | 
						if page <= 1 {
 | 
				
			||||||
		page = 1
 | 
							page = 1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	notices, err := admin_model.Notices(page, setting.UI.Admin.NoticePagingNum)
 | 
						notices, err := system_model.Notices(page, setting.UI.Admin.NoticePagingNum)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("Notices", err)
 | 
							ctx.ServerError("Notices", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -57,7 +57,7 @@ func DeleteNotices(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := admin_model.DeleteNoticesByIDs(ids); err != nil {
 | 
						if err := system_model.DeleteNoticesByIDs(ids); err != nil {
 | 
				
			||||||
		ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error())
 | 
							ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error())
 | 
				
			||||||
		ctx.Status(http.StatusInternalServerError)
 | 
							ctx.Status(http.StatusInternalServerError)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -68,7 +68,7 @@ func DeleteNotices(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// EmptyNotices delete all the notices
 | 
					// EmptyNotices delete all the notices
 | 
				
			||||||
func EmptyNotices(ctx *context.Context) {
 | 
					func EmptyNotices(ctx *context.Context) {
 | 
				
			||||||
	if err := admin_model.DeleteNotices(0, 0); err != nil {
 | 
						if err := system_model.DeleteNotices(0, 0); err != nil {
 | 
				
			||||||
		ctx.ServerError("DeleteNotices", err)
 | 
							ctx.ServerError("DeleteNotices", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ package repo
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
@@ -24,7 +24,7 @@ func SetEditorconfigIfExists(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if err != nil && !git.IsErrNotExist(err) {
 | 
						if err != nil && !git.IsErrNotExist(err) {
 | 
				
			||||||
		description := fmt.Sprintf("Error while getting .editorconfig file: %v", err)
 | 
							description := fmt.Sprintf("Error while getting .editorconfig file: %v", err)
 | 
				
			||||||
		if err := admin_model.CreateRepositoryNotice(description); err != nil {
 | 
							if err := system_model.CreateRepositoryNotice(description); err != nil {
 | 
				
			||||||
			ctx.ServerError("ErrCreatingReporitoryNotice", err)
 | 
								ctx.ServerError("ErrCreatingReporitoryNotice", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -473,8 +473,13 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
	m.Group("/admin", func() {
 | 
						m.Group("/admin", func() {
 | 
				
			||||||
		m.Get("", adminReq, admin.Dashboard)
 | 
							m.Get("", adminReq, admin.Dashboard)
 | 
				
			||||||
		m.Post("", adminReq, bindIgnErr(forms.AdminDashboardForm{}), admin.DashboardPost)
 | 
							m.Post("", adminReq, bindIgnErr(forms.AdminDashboardForm{}), admin.DashboardPost)
 | 
				
			||||||
		m.Get("/config", admin.Config)
 | 
					
 | 
				
			||||||
		m.Post("/config/test_mail", admin.SendTestMail)
 | 
							m.Group("/config", func() {
 | 
				
			||||||
 | 
								m.Get("", admin.Config)
 | 
				
			||||||
 | 
								m.Post("", admin.ChangeConfig)
 | 
				
			||||||
 | 
								m.Post("/test_mail", admin.SendTestMail)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/monitor", func() {
 | 
							m.Group("/monitor", func() {
 | 
				
			||||||
			m.Get("", admin.Monitor)
 | 
								m.Get("", admin.Monitor)
 | 
				
			||||||
			m.Get("/stacktrace", admin.GoroutineStacktrace)
 | 
								m.Get("/stacktrace", admin.GoroutineStacktrace)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,8 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -114,7 +114,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
 | 
				
			|||||||
			t.LastDoer = doerName
 | 
								t.LastDoer = doerName
 | 
				
			||||||
			t.lock.Unlock()
 | 
								t.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil {
 | 
								if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil {
 | 
				
			||||||
				log.Error("CreateNotice: %v", err)
 | 
									log.Error("CreateNotice: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -127,7 +127,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
 | 
				
			|||||||
		t.lock.Unlock()
 | 
							t.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if config.DoNoticeOnSuccess() {
 | 
							if config.DoNoticeOnSuccess() {
 | 
				
			||||||
			if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil {
 | 
								if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil {
 | 
				
			||||||
				log.Error("CreateNotice: %v", err)
 | 
									log.Error("CreateNotice: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,9 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	activities_model "code.gitea.io/gitea/models/activities"
 | 
						activities_model "code.gitea.io/gitea/models/activities"
 | 
				
			||||||
	"code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
						asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/updatechecker"
 | 
						"code.gitea.io/gitea/modules/updatechecker"
 | 
				
			||||||
@@ -166,7 +166,7 @@ func registerDeleteOldSystemNotices() {
 | 
				
			|||||||
		OlderThan: 365 * 24 * time.Hour,
 | 
							OlderThan: 365 * 24 * time.Hour,
 | 
				
			||||||
	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 | 
						}, func(ctx context.Context, _ *user_model.User, config Config) error {
 | 
				
			||||||
		olderThanConfig := config.(*OlderThanConfig)
 | 
							olderThanConfig := config.(*OlderThanConfig)
 | 
				
			||||||
		return admin.DeleteOldSystemNotices(olderThanConfig.OlderThan)
 | 
							return system.DeleteOldSystemNotices(olderThanConfig.OlderThan)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,12 +8,12 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	activities_model "code.gitea.io/gitea/models/activities"
 | 
						activities_model "code.gitea.io/gitea/models/activities"
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						issues_model "code.gitea.io/gitea/models/issues"
 | 
				
			||||||
	access_model "code.gitea.io/gitea/models/perm/access"
 | 
						access_model "code.gitea.io/gitea/models/perm/access"
 | 
				
			||||||
	project_model "code.gitea.io/gitea/models/project"
 | 
						project_model "code.gitea.io/gitea/models/project"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/notification"
 | 
						"code.gitea.io/gitea/modules/notification"
 | 
				
			||||||
@@ -234,7 +234,7 @@ func deleteIssue(issue *issues_model.Issue) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range issue.Attachments {
 | 
						for i := range issue.Attachments {
 | 
				
			||||||
		admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
 | 
							system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// delete all database data still assigned to this issue
 | 
						// delete all database data still assigned to this issue
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	base "code.gitea.io/gitea/modules/migration"
 | 
						base "code.gitea.io/gitea/modules/migration"
 | 
				
			||||||
@@ -17,7 +17,7 @@ import (
 | 
				
			|||||||
// WarnAndNotice will log the provided message and send a repository notice
 | 
					// WarnAndNotice will log the provided message and send a repository notice
 | 
				
			||||||
func WarnAndNotice(fmtStr string, args ...interface{}) {
 | 
					func WarnAndNotice(fmtStr string, args ...interface{}) {
 | 
				
			||||||
	log.Warn(fmtStr, args...)
 | 
						log.Warn(fmtStr, args...)
 | 
				
			||||||
	if err := admin_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil {
 | 
						if err := system_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil {
 | 
				
			||||||
		log.Error("create repository notice failed: ", err)
 | 
							log.Error("create repository notice failed: ", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,8 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/hostmatcher"
 | 
						"code.gitea.io/gitea/modules/hostmatcher"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -132,7 +132,7 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str
 | 
				
			|||||||
		if err1 := uploader.Rollback(); err1 != nil {
 | 
							if err1 := uploader.Rollback(); err1 != nil {
 | 
				
			||||||
			log.Error("rollback failed: %v", err1)
 | 
								log.Error("rollback failed: %v", err1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err2 := admin_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
 | 
							if err2 := system_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
 | 
				
			||||||
			log.Error("create respotiry notice failed: ", err2)
 | 
								log.Error("create respotiry notice failed: ", err2)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,9 +10,9 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/cache"
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/lfs"
 | 
						"code.gitea.io/gitea/modules/lfs"
 | 
				
			||||||
@@ -188,7 +188,7 @@ func pruneBrokenReferences(ctx context.Context,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr)
 | 
							log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr)
 | 
				
			||||||
		desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage)
 | 
							desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage)
 | 
				
			||||||
		if err := admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
							if err := system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
			log.Error("CreateRepositoryNotice: %v", err)
 | 
								log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// this if will only be reached on a successful prune so try to get the mirror again
 | 
							// this if will only be reached on a successful prune so try to get the mirror again
 | 
				
			||||||
@@ -267,7 +267,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("SyncMirrors [repo: %-v]: failed to update mirror repository:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
 | 
								log.Error("SyncMirrors [repo: %-v]: failed to update mirror repository:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
 | 
				
			||||||
			desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage)
 | 
								desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage)
 | 
				
			||||||
			if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
								if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
				log.Error("CreateRepositoryNotice: %v", err)
 | 
									log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return nil, false
 | 
								return nil, false
 | 
				
			||||||
@@ -356,7 +356,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Error("SyncMirrors [repo: %-v Wiki]: failed to update mirror repository wiki:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
 | 
									log.Error("SyncMirrors [repo: %-v Wiki]: failed to update mirror repository wiki:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
 | 
				
			||||||
				desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage)
 | 
									desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage)
 | 
				
			||||||
				if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
									if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
					log.Error("CreateRepositoryNotice: %v", err)
 | 
										log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return nil, false
 | 
									return nil, false
 | 
				
			||||||
@@ -568,7 +568,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository
 | 
				
			|||||||
			if !git.IsErrUnsupportedVersion(err) {
 | 
								if !git.IsErrUnsupportedVersion(err) {
 | 
				
			||||||
				log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
 | 
									log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
 | 
				
			||||||
				desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err)
 | 
									desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err)
 | 
				
			||||||
				if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
									if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
					log.Error("CreateRepositoryNotice: %v", err)
 | 
										log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return false
 | 
									return false
 | 
				
			||||||
@@ -579,7 +579,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository
 | 
				
			|||||||
		if err := repo_model.UpdateRepositoryCols(db.DefaultContext, m.Repo, "default_branch", "is_empty"); err != nil {
 | 
							if err := repo_model.UpdateRepositoryCols(db.DefaultContext, m.Repo, "default_branch", "is_empty"); err != nil {
 | 
				
			||||||
			log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err)
 | 
								log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err)
 | 
				
			||||||
			desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err)
 | 
								desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err)
 | 
				
			||||||
			if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
								if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
				log.Error("CreateRepositoryNotice: %v", err)
 | 
									log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return false
 | 
								return false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,9 +11,9 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -42,7 +42,7 @@ func GitFsck(ctx context.Context, timeout time.Duration, args []string) error {
 | 
				
			|||||||
			repoPath := repo.RepoPath()
 | 
								repoPath := repo.RepoPath()
 | 
				
			||||||
			if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil {
 | 
								if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil {
 | 
				
			||||||
				log.Warn("Failed to health check repository (%v): %v", repo, err)
 | 
									log.Warn("Failed to health check repository (%v): %v", repo, err)
 | 
				
			||||||
				if err = admin_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil {
 | 
									if err = system_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil {
 | 
				
			||||||
					log.Error("CreateRepositoryNotice: %v", err)
 | 
										log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -83,7 +83,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
 | 
									log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
 | 
				
			||||||
				desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
 | 
									desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
 | 
				
			||||||
				if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
									if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
					log.Error("CreateRepositoryNotice: %v", err)
 | 
										log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
 | 
									return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
 | 
				
			||||||
@@ -93,7 +93,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
 | 
				
			|||||||
			if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
 | 
								if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
 | 
				
			||||||
				log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
 | 
									log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
 | 
				
			||||||
				desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
 | 
									desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
 | 
				
			||||||
				if err = admin_model.CreateRepositoryNotice(desc); err != nil {
 | 
									if err = system_model.CreateRepositoryNotice(desc); err != nil {
 | 
				
			||||||
					log.Error("CreateRepositoryNotice: %v", err)
 | 
										log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
 | 
									return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
 | 
				
			||||||
@@ -135,7 +135,7 @@ func gatherMissingRepoRecords(ctx context.Context) ([]*repo_model.Repository, er
 | 
				
			|||||||
		if strings.HasPrefix(err.Error(), "Aborted gathering missing repo") {
 | 
							if strings.HasPrefix(err.Error(), "Aborted gathering missing repo") {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err2 := admin_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil {
 | 
							if err2 := system_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil {
 | 
				
			||||||
			log.Error("CreateRepositoryNotice: %v", err2)
 | 
								log.Error("CreateRepositoryNotice: %v", err2)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -163,7 +163,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error
 | 
				
			|||||||
		log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
 | 
							log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
 | 
				
			||||||
		if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
 | 
							if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
 | 
				
			||||||
			log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err)
 | 
								log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err)
 | 
				
			||||||
			if err2 := admin_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
 | 
								if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
 | 
				
			||||||
				log.Error("CreateRepositoryNotice: %v", err)
 | 
									log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -191,7 +191,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
 | 
				
			|||||||
		log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
 | 
							log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
 | 
				
			||||||
		if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil {
 | 
							if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil {
 | 
				
			||||||
			log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err)
 | 
								log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err)
 | 
				
			||||||
			if err2 := admin_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
 | 
								if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
 | 
				
			||||||
				log.Error("CreateRepositoryNotice: %v", err2)
 | 
									log.Error("CreateRepositoryNotice: %v", err2)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,12 +9,12 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						issues_model "code.gitea.io/gitea/models/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/models/organization"
 | 
						"code.gitea.io/gitea/models/organization"
 | 
				
			||||||
	packages_model "code.gitea.io/gitea/models/packages"
 | 
						packages_model "code.gitea.io/gitea/models/packages"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -83,8 +83,8 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo
 | 
				
			|||||||
// Init start repository service
 | 
					// Init start repository service
 | 
				
			||||||
func Init() error {
 | 
					func Init() error {
 | 
				
			||||||
	repo_module.LoadRepoConfig()
 | 
						repo_module.LoadRepoConfig()
 | 
				
			||||||
	admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
 | 
						system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
 | 
				
			||||||
	admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
 | 
						system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
 | 
				
			||||||
	return initPushQueue()
 | 
						return initPushQueue()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,12 +13,12 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
						asymkey_model "code.gitea.io/gitea/models/asymkey"
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/models/organization"
 | 
						"code.gitea.io/gitea/models/organization"
 | 
				
			||||||
	packages_model "code.gitea.io/gitea/models/packages"
 | 
						packages_model "code.gitea.io/gitea/models/packages"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/avatar"
 | 
						"code.gitea.io/gitea/modules/avatar"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/eventsource"
 | 
						"code.gitea.io/gitea/modules/eventsource"
 | 
				
			||||||
@@ -186,7 +186,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 | 
				
			|||||||
	path := user_model.UserPath(u.Name)
 | 
						path := user_model.UserPath(u.Name)
 | 
				
			||||||
	if err := util.RemoveAll(path); err != nil {
 | 
						if err := util.RemoveAll(path); err != nil {
 | 
				
			||||||
		err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
 | 
							err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
 | 
				
			||||||
		_ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
							_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -194,7 +194,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 | 
				
			|||||||
		avatarPath := u.CustomAvatarRelativePath()
 | 
							avatarPath := u.CustomAvatarRelativePath()
 | 
				
			||||||
		if err := storage.Avatars.Delete(avatarPath); err != nil {
 | 
							if err := storage.Avatars.Delete(avatarPath); err != nil {
 | 
				
			||||||
			err = fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
 | 
								err = fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
 | 
				
			||||||
			_ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
								_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,8 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
					 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						system_model "code.gitea.io/gitea/models/system"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
@@ -384,6 +384,6 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
 | 
						system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -301,10 +301,18 @@
 | 
				
			|||||||
		<div class="ui attached table segment">
 | 
							<div class="ui attached table segment">
 | 
				
			||||||
			<dl class="dl-horizontal admin-dl-horizontal">
 | 
								<dl class="dl-horizontal admin-dl-horizontal">
 | 
				
			||||||
				<dt>{{.locale.Tr "admin.config.disable_gravatar"}}</dt>
 | 
									<dt>{{.locale.Tr "admin.config.disable_gravatar"}}</dt>
 | 
				
			||||||
				<dd>{{if .DisableGravatar}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
									<dd>
 | 
				
			||||||
 | 
										<div class="ui toggle checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" name="picture.disable_gravatar" version="{{.SystemSettings.GetVersion "picture.disable_gravatar"}}"{{if .SystemSettings.GetBool "picture.disable_gravatar"}} checked{{end}}>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
				<div class="ui divider"></div>
 | 
									<div class="ui divider"></div>
 | 
				
			||||||
				<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt>
 | 
									<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt>
 | 
				
			||||||
				<dd>{{if .EnableFederatedAvatar}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
									<dd>
 | 
				
			||||||
 | 
										<div class="ui toggle checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" name="picture.enable_federated_avatar" version="{{.SystemSettings.GetVersion "picture.enable_federated_avatar"}}"{{if .SystemSettings.GetBool "picture.enable_federated_avatar"}} checked{{end}}>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
			</dl>
 | 
								</dl>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								web_src/js/features/admin/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web_src/js/features/admin/config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import $ from 'jquery';
 | 
				
			||||||
 | 
					import {showTemporaryTooltip} from '../../modules/tippy.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {appSubUrl, csrfToken, pageData} = window.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function initAdminConfigs() {
 | 
				
			||||||
 | 
					  const isAdminConfigPage = pageData?.adminConfigPage;
 | 
				
			||||||
 | 
					  if (!isAdminConfigPage) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $("input[type='checkbox']").on('change', (e) => {
 | 
				
			||||||
 | 
					    const $this = $(e.currentTarget);
 | 
				
			||||||
 | 
					    $.ajax({
 | 
				
			||||||
 | 
					      url: `${appSubUrl}/admin/config`,
 | 
				
			||||||
 | 
					      type: 'POST',
 | 
				
			||||||
 | 
					      data: {
 | 
				
			||||||
 | 
					        _csrf: csrfToken,
 | 
				
			||||||
 | 
					        key: $this.attr('name'),
 | 
				
			||||||
 | 
					        value: $this.is(':checked'),
 | 
				
			||||||
 | 
					        version: $this.attr('version'),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }).done((resp) => {
 | 
				
			||||||
 | 
					      if (resp) {
 | 
				
			||||||
 | 
					        if (resp.redirect) {
 | 
				
			||||||
 | 
					          window.location.href = resp.redirect;
 | 
				
			||||||
 | 
					        } else if (resp.version) {
 | 
				
			||||||
 | 
					          $this.attr('version', resp.version);
 | 
				
			||||||
 | 
					        } else if (resp.err) {
 | 
				
			||||||
 | 
					          showTemporaryTooltip(e.currentTarget, resp.err);
 | 
				
			||||||
 | 
					          $this.prop('checked', !$this.is(':checked'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,7 +16,8 @@ import initRepoMigration from './features/repo-migration.js';
 | 
				
			|||||||
import initRepoProject from './features/repo-projects.js';
 | 
					import initRepoProject from './features/repo-projects.js';
 | 
				
			||||||
import initServiceWorker from './features/serviceworker.js';
 | 
					import initServiceWorker from './features/serviceworker.js';
 | 
				
			||||||
import initTableSort from './features/tablesort.js';
 | 
					import initTableSort from './features/tablesort.js';
 | 
				
			||||||
import {initAdminUserListSearchForm} from './features/admin-users.js';
 | 
					import {initAdminUserListSearchForm} from './features/admin/users.js';
 | 
				
			||||||
 | 
					import {initAdminConfigs} from './features/admin/config.js';
 | 
				
			||||||
import {initMarkupAnchors} from './markup/anchors.js';
 | 
					import {initMarkupAnchors} from './markup/anchors.js';
 | 
				
			||||||
import {initNotificationCount, initNotificationsTable} from './features/notification.js';
 | 
					import {initNotificationCount, initNotificationsTable} from './features/notification.js';
 | 
				
			||||||
import {initRepoIssueContentHistory} from './features/repo-issue-content.js';
 | 
					import {initRepoIssueContentHistory} from './features/repo-issue-content.js';
 | 
				
			||||||
@@ -60,8 +61,8 @@ import {
 | 
				
			|||||||
  initGlobalTooltips,
 | 
					  initGlobalTooltips,
 | 
				
			||||||
} from './features/common-global.js';
 | 
					} from './features/common-global.js';
 | 
				
			||||||
import {initRepoTopicBar} from './features/repo-home.js';
 | 
					import {initRepoTopicBar} from './features/repo-home.js';
 | 
				
			||||||
import {initAdminEmails} from './features/admin-emails.js';
 | 
					import {initAdminEmails} from './features/admin/emails.js';
 | 
				
			||||||
import {initAdminCommon} from './features/admin-common.js';
 | 
					import {initAdminCommon} from './features/admin/common.js';
 | 
				
			||||||
import {initRepoTemplateSearch} from './features/repo-template.js';
 | 
					import {initRepoTemplateSearch} from './features/repo-template.js';
 | 
				
			||||||
import {initRepoCodeView} from './features/repo-code.js';
 | 
					import {initRepoCodeView} from './features/repo-code.js';
 | 
				
			||||||
import {initSshKeyFormParser} from './features/sshkey-helper.js';
 | 
					import {initSshKeyFormParser} from './features/sshkey-helper.js';
 | 
				
			||||||
@@ -139,6 +140,7 @@ $(document).ready(() => {
 | 
				
			|||||||
  initAdminCommon();
 | 
					  initAdminCommon();
 | 
				
			||||||
  initAdminEmails();
 | 
					  initAdminEmails();
 | 
				
			||||||
  initAdminUserListSearchForm();
 | 
					  initAdminUserListSearchForm();
 | 
				
			||||||
 | 
					  initAdminConfigs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  initDashboardRepoList();
 | 
					  initDashboardRepoList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,6 +131,7 @@
 | 
				
			|||||||
  --color-timeline: #ececec;
 | 
					  --color-timeline: #ececec;
 | 
				
			||||||
  --color-input-text: #212121;
 | 
					  --color-input-text: #212121;
 | 
				
			||||||
  --color-input-background: #ffffff;
 | 
					  --color-input-background: #ffffff;
 | 
				
			||||||
 | 
					  --color-input-toggle-background: #dedede;
 | 
				
			||||||
  --color-input-border: #dedede;
 | 
					  --color-input-border: #dedede;
 | 
				
			||||||
  --color-input-border-hover: #cecece;
 | 
					  --color-input-border-hover: #cecece;
 | 
				
			||||||
  --color-navbar: #f8f8f8;
 | 
					  --color-navbar: #f8f8f8;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,7 +113,7 @@ textarea:focus,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.ui.toggle.checkbox label::before {
 | 
					.ui.toggle.checkbox label::before {
 | 
				
			||||||
  background: var(--color-input-background);
 | 
					  background: var(--color-input-toggle-background);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.ui.toggle.checkbox label,
 | 
					.ui.toggle.checkbox label,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,6 +106,7 @@
 | 
				
			|||||||
  --color-timeline: #4c525e;
 | 
					  --color-timeline: #4c525e;
 | 
				
			||||||
  --color-input-text: #d5dbe6;
 | 
					  --color-input-text: #d5dbe6;
 | 
				
			||||||
  --color-input-background: #232933;
 | 
					  --color-input-background: #232933;
 | 
				
			||||||
 | 
					  --color-input-toggle-background: #454a57;
 | 
				
			||||||
  --color-input-border: #454a57;
 | 
					  --color-input-border: #454a57;
 | 
				
			||||||
  --color-input-border-hover: #505667;
 | 
					  --color-input-border-hover: #505667;
 | 
				
			||||||
  --color-navbar: #2a2e3a;
 | 
					  --color-navbar: #2a2e3a;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user