mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-13 00:03:31 -04:00
Compare commits
16 Commits
74aa44625b
...
f2f0fb43e0
Author | SHA1 | Date | |
---|---|---|---|
|
f2f0fb43e0 | ||
|
59d060622d | ||
|
5f21e0f8eb | ||
|
2ec2d06531 | ||
|
abc5f8c235 | ||
|
61a73edbf3 | ||
|
fa86a1b74a | ||
|
ee6fa8d633 | ||
|
0e8045d8ea | ||
|
9219534447 | ||
|
d5e93413bc | ||
|
30c1cd9775 | ||
|
5cf7da63ee | ||
|
56d4893b2a | ||
|
f16b668980 | ||
|
ab42c139a2 |
103
.drone.yml
103
.drone.yml
@ -773,109 +773,6 @@ steps:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: update_translations
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- cron
|
||||
cron:
|
||||
- update_translations
|
||||
|
||||
steps:
|
||||
- name: download
|
||||
image: jonasfranz/crowdin
|
||||
pull: always
|
||||
settings:
|
||||
download: true
|
||||
export_dir: options/locale/
|
||||
ignore_branch: true
|
||||
project_identifier: gitea
|
||||
environment:
|
||||
CROWDIN_KEY:
|
||||
from_secret: crowdin_key
|
||||
|
||||
- name: update
|
||||
image: alpine:3.17
|
||||
pull: always
|
||||
commands:
|
||||
- ./build/update-locales.sh
|
||||
|
||||
- name: push
|
||||
image: appleboy/drone-git-push
|
||||
pull: always
|
||||
settings:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated translations via Crowdin"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
environment:
|
||||
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
|
||||
DRONE_COMMIT_AUTHOR: GiteaBot
|
||||
GIT_PUSH_SSH_KEY:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
- name: upload_translations
|
||||
image: jonasfranz/crowdin
|
||||
pull: always
|
||||
settings:
|
||||
files:
|
||||
locale_en-US.ini: options/locale/locale_en-US.ini
|
||||
ignore_branch: true
|
||||
project_identifier: gitea
|
||||
environment:
|
||||
CROWDIN_KEY:
|
||||
from_secret: crowdin_key
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: update_gitignore_and_licenses
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- cron
|
||||
cron:
|
||||
- update_gitignore_and_licenses
|
||||
|
||||
steps:
|
||||
- name: download
|
||||
image: gitea/test_env:linux-1.20-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- timeout -s ABRT 40m make generate-license generate-gitignore
|
||||
|
||||
- name: push
|
||||
image: appleboy/drone-git-push
|
||||
pull: always
|
||||
settings:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: "GiteaBot"
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated licenses and gitignores"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
environment:
|
||||
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
|
||||
DRONE_COMMIT_AUTHOR: "GiteaBot"
|
||||
GIT_PUSH_SSH_KEY:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
|
28
.github/workflows/cron-licenses.yml
vendored
Normal file
28
.github/workflows/cron-licenses.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
on:
|
||||
schedule:
|
||||
# weekly on Monday at 0:07 UTC
|
||||
- cron: "7 0 * * 1"
|
||||
|
||||
name: Update licenses and gitignores
|
||||
|
||||
jobs:
|
||||
cron:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '>=1.20.1'
|
||||
- name: update licenses and gitignores
|
||||
run: timeout -s ABRT 40m make generate-license generate-gitignore
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.2
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated licenses and gitignores"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
47
.github/workflows/cron-translations.yml
vendored
Normal file
47
.github/workflows/cron-translations.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
on:
|
||||
schedule:
|
||||
- cron: "7 0 * * *" # every day at 0:07 UTC
|
||||
|
||||
name: Pull translations from Crowdin
|
||||
|
||||
jobs:
|
||||
crowdin_pull:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: download from crowdin
|
||||
uses: docker://jonasfranz/crowdin
|
||||
env:
|
||||
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
||||
PLUGIN_DOWNLOAD: true
|
||||
PLUGIN_EXPORT_DIR: options/locale/
|
||||
PLUGIN_IGNORE_BRANCH: true
|
||||
PLUGIN_PROJECT_IDENTIFIER: gitea
|
||||
- name: update locales
|
||||
run: ./build/update-locales.sh
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.2
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated translations via Crowdin"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
||||
crowdin_push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: push translations to crowdin
|
||||
uses: docker://jonasfranz/crowdin
|
||||
env:
|
||||
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
||||
PLUGIN_UPLOAD: true
|
||||
PLUGIN_IGNORE_BRANCH: true
|
||||
PLUGIN_PROJECT_IDENTIFIER: gitea
|
||||
PLUGIN_FILES: |
|
||||
locale_en-US.ini: options/locale/locale_en-US.ini
|
||||
PLUGIN_BRANCH: main
|
@ -24,7 +24,6 @@ import (
|
||||
|
||||
"github.com/felixge/fgprof"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// PIDFile could be set from build tag
|
||||
@ -223,9 +222,10 @@ func setPort(port string) error {
|
||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) {
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
})
|
||||
setting.CfgProvider.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
if err := setting.CfgProvider.Save(); err != nil {
|
||||
return fmt.Errorf("Failed to save config file: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -10,6 +10,15 @@
|
||||
# upgrade.sh 1.15.10
|
||||
# giteahome=/opt/gitea giteaconf=$giteahome/app.ini upgrade.sh
|
||||
|
||||
# Check if gitea service is running
|
||||
if ! pidof gitea &> /dev/null; then
|
||||
echo "Error: gitea is not running."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Continue with rest of the script if gitea is running
|
||||
echo "Gitea is running. Continuing with rest of script..."
|
||||
|
||||
# apply variables from environment
|
||||
: "${giteabin:="/usr/local/bin/gitea"}"
|
||||
: "${giteahome:="/var/lib/gitea"}"
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
_ "code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@ -27,7 +26,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestBleveSearchIssues(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.CfgProvider = ini.Empty()
|
||||
setting.CfgProvider = setting.NewEmptyConfigProvider()
|
||||
|
||||
tmpIndexerDir := t.TempDir()
|
||||
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
_ "code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@ -29,7 +28,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestRepoStatsIndex(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.CfgProvider = ini.Empty()
|
||||
setting.CfgProvider = setting.NewEmptyConfigProvider()
|
||||
|
||||
setting.LoadQueueSettings()
|
||||
|
||||
|
@ -4,20 +4,157 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
type ConfigSection interface {
|
||||
Name() string
|
||||
MapTo(interface{}) error
|
||||
HasKey(key string) bool
|
||||
NewKey(name, value string) (*ini.Key, error)
|
||||
Key(key string) *ini.Key
|
||||
Keys() []*ini.Key
|
||||
ChildSections() []*ini.Section
|
||||
}
|
||||
|
||||
// ConfigProvider represents a config provider
|
||||
type ConfigProvider interface {
|
||||
Section(section string) *ini.Section
|
||||
NewSection(name string) (*ini.Section, error)
|
||||
GetSection(name string) (*ini.Section, error)
|
||||
Section(section string) ConfigSection
|
||||
NewSection(name string) (ConfigSection, error)
|
||||
GetSection(name string) (ConfigSection, error)
|
||||
DeleteSection(name string) error
|
||||
Save() error
|
||||
}
|
||||
|
||||
type iniFileConfigProvider struct {
|
||||
*ini.File
|
||||
filepath string // the ini file path
|
||||
newFile bool // whether the file has not existed previously
|
||||
allowEmpty bool // whether not finding configuration files is allowed (only true for the tests)
|
||||
}
|
||||
|
||||
// NewEmptyConfigProvider create a new empty config provider
|
||||
func NewEmptyConfigProvider() ConfigProvider {
|
||||
cp, _ := newConfigProviderFromData("")
|
||||
return cp
|
||||
}
|
||||
|
||||
// newConfigProviderFromData this function is only for testing
|
||||
func newConfigProviderFromData(configContent string) (ConfigProvider, error) {
|
||||
var cfg *ini.File
|
||||
var err error
|
||||
if configContent == "" {
|
||||
cfg = ini.Empty()
|
||||
} else {
|
||||
cfg, err = ini.Load(strings.NewReader(configContent))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
return &iniFileConfigProvider{
|
||||
File: cfg,
|
||||
newFile: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newConfigProviderFromFile load configuration from file.
|
||||
// NOTE: do not print any log except error.
|
||||
func newConfigProviderFromFile(customConf string, allowEmpty bool, extraConfig string) (*iniFileConfigProvider, error) {
|
||||
cfg := ini.Empty()
|
||||
newFile := true
|
||||
|
||||
if customConf != "" {
|
||||
isFile, err := util.IsFile(customConf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to check if %s is a file. Error: %v", customConf, err)
|
||||
}
|
||||
if isFile {
|
||||
if err := cfg.Append(customConf); err != nil {
|
||||
return nil, fmt.Errorf("failed to load custom conf '%s': %v", customConf, err)
|
||||
}
|
||||
newFile = false
|
||||
}
|
||||
}
|
||||
|
||||
if newFile && !allowEmpty {
|
||||
return nil, fmt.Errorf("unable to find configuration file: %q, please ensure you are running in the correct environment or set the correct configuration file with -c", CustomConf)
|
||||
}
|
||||
|
||||
if extraConfig != "" {
|
||||
if err := cfg.Append([]byte(extraConfig)); err != nil {
|
||||
return nil, fmt.Errorf("unable to append more config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
return &iniFileConfigProvider{
|
||||
File: cfg,
|
||||
filepath: customConf,
|
||||
newFile: newFile,
|
||||
allowEmpty: allowEmpty,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *iniFileConfigProvider) Section(section string) ConfigSection {
|
||||
return p.File.Section(section)
|
||||
}
|
||||
|
||||
func (p *iniFileConfigProvider) NewSection(name string) (ConfigSection, error) {
|
||||
return p.File.NewSection(name)
|
||||
}
|
||||
|
||||
func (p *iniFileConfigProvider) GetSection(name string) (ConfigSection, error) {
|
||||
return p.File.GetSection(name)
|
||||
}
|
||||
|
||||
func (p *iniFileConfigProvider) DeleteSection(name string) error {
|
||||
p.File.DeleteSection(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save save the content into file
|
||||
func (p *iniFileConfigProvider) Save() error {
|
||||
if p.filepath == "" {
|
||||
if !p.allowEmpty {
|
||||
return fmt.Errorf("custom config path must not be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.newFile {
|
||||
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to create '%s': %v", CustomConf, err)
|
||||
}
|
||||
}
|
||||
if err := p.SaveTo(p.filepath); err != nil {
|
||||
return fmt.Errorf("failed to save '%s': %v", p.filepath, err)
|
||||
}
|
||||
|
||||
// Change permissions to be more restrictive
|
||||
fi, err := os.Stat(CustomConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine current conf file permissions: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode().Perm() > 0o600 {
|
||||
if err = os.Chmod(CustomConf, 0o600); err != nil {
|
||||
log.Warn("Failed changing conf file permissions to -rw-------. Consider changing them manually.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, …
|
||||
var _ ConfigProvider = &ini.File{}
|
||||
var _ ConfigProvider = &iniFileConfigProvider{}
|
||||
|
||||
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) {
|
||||
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func Test_getCronSettings(t *testing.T) {
|
||||
@ -23,11 +22,11 @@ func Test_getCronSettings(t *testing.T) {
|
||||
|
||||
iniStr := `
|
||||
[cron.test]
|
||||
Base = true
|
||||
Second = white rabbit
|
||||
Extend = true
|
||||
BASE = true
|
||||
SECOND = white rabbit
|
||||
EXTEND = true
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
extended := &Extended{
|
||||
|
@ -9,8 +9,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// LFS represents the configuration for Git LFS
|
||||
@ -38,8 +36,7 @@ func loadLFSFrom(rootCfg ConfigProvider) {
|
||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||
// if these are removed, the warning will not be shown
|
||||
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
||||
lfsSec.Key("PATH").MustString(
|
||||
sec.Key("LFS_CONTENT_PATH").String())
|
||||
lfsSec.Key("PATH").MustString(sec.Key("LFS_CONTENT_PATH").String())
|
||||
|
||||
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
|
||||
|
||||
@ -62,9 +59,11 @@ func loadLFSFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
|
||||
// Save secret
|
||||
CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) {
|
||||
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
|
||||
})
|
||||
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
|
||||
if err := rootCfg.Save(); err != nil {
|
||||
log.Fatal("Error saving JWT Secret for custom config: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -131,12 +129,12 @@ type LogDescription struct {
|
||||
SubLogDescriptions []SubLogDescription
|
||||
}
|
||||
|
||||
func getLogLevel(section *ini.Section, key string, defaultValue log.Level) log.Level {
|
||||
func getLogLevel(section ConfigSection, key string, defaultValue log.Level) log.Level {
|
||||
value := section.Key(key).MustString(defaultValue.String())
|
||||
return log.FromString(value)
|
||||
}
|
||||
|
||||
func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) string {
|
||||
func getStacktraceLogLevel(section ConfigSection, key, defaultValue string) string {
|
||||
value := section.Key(key).MustString(defaultValue)
|
||||
return log.FromString(value).String()
|
||||
}
|
||||
@ -165,7 +163,7 @@ func loadLogFrom(rootCfg ConfigProvider) {
|
||||
Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
|
||||
}
|
||||
|
||||
func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
|
||||
func generateLogConfig(sec ConfigSection, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
|
||||
level := getLogLevel(sec, "LEVEL", Log.Level)
|
||||
levelName = level.String()
|
||||
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)
|
||||
|
@ -7,11 +7,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func Test_loadMailerFrom(t *testing.T) {
|
||||
iniFile := ini.Empty()
|
||||
iniFile := NewEmptyConfigProvider()
|
||||
kases := map[string]*Mailer{
|
||||
"smtp.mydomain.com": {
|
||||
SMTPAddr: "smtp.mydomain.com",
|
||||
|
@ -8,8 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// ExternalMarkupRenderers represents the external markup renderers
|
||||
@ -82,7 +80,7 @@ func loadMarkupFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
func newMarkupSanitizer(name string, sec *ini.Section) {
|
||||
func newMarkupSanitizer(name string, sec ConfigSection) {
|
||||
rule, ok := createMarkupSanitizerRule(name, sec)
|
||||
if ok {
|
||||
if strings.HasPrefix(name, "sanitizer.") {
|
||||
@ -99,7 +97,7 @@ func newMarkupSanitizer(name string, sec *ini.Section) {
|
||||
}
|
||||
}
|
||||
|
||||
func createMarkupSanitizerRule(name string, sec *ini.Section) (MarkupSanitizerRule, bool) {
|
||||
func createMarkupSanitizerRule(name string, sec ConfigSection) (MarkupSanitizerRule, bool) {
|
||||
var rule MarkupSanitizerRule
|
||||
|
||||
ok := false
|
||||
@ -141,7 +139,7 @@ func createMarkupSanitizerRule(name string, sec *ini.Section) (MarkupSanitizerRu
|
||||
return rule, true
|
||||
}
|
||||
|
||||
func newMarkupRenderer(name string, sec *ini.Section) {
|
||||
func newMarkupRenderer(name string, sec ConfigSection) {
|
||||
extensionReg := regexp.MustCompile(`\.\w`)
|
||||
|
||||
extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
|
||||
|
@ -4,12 +4,12 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"math"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data
|
||||
@ -80,7 +80,7 @@ func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseScopes(sec *ini.Section, name string) []string {
|
||||
func parseScopes(sec ConfigSection, name string) []string {
|
||||
parts := sec.Key(name).Strings(" ")
|
||||
scopes := make([]string, 0, len(parts))
|
||||
for _, scope := range parts {
|
||||
@ -119,4 +119,19 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
||||
}
|
||||
|
||||
key := make([]byte, 32)
|
||||
n, err := base64.RawURLEncoding.Decode(key, []byte(OAuth2.JWTSecretBase64))
|
||||
if err != nil || n != 32 {
|
||||
key, err = generate.NewJwtSecret()
|
||||
if err != nil {
|
||||
log.Fatal("error generating JWT secret: %v", err)
|
||||
}
|
||||
|
||||
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
|
||||
rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
|
||||
if err := rootCfg.Save(); err != nil {
|
||||
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// Package registry settings
|
||||
@ -86,7 +85,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
|
||||
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
|
||||
}
|
||||
|
||||
func mustBytes(section *ini.Section, key string) int64 {
|
||||
func mustBytes(section ConfigSection, key string) int64 {
|
||||
const noLimit = "-1"
|
||||
|
||||
value := section.Key(key).MustString(noLimit)
|
||||
|
@ -10,8 +10,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// QueueSettings represent the settings for a queue from the ini
|
||||
@ -195,7 +193,7 @@ func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection,
|
||||
// toDirectlySetKeysSet returns a set of keys directly set by this section
|
||||
// Note: we cannot use section.HasKey(...) as that will immediately set the Key if a parent section has the Key
|
||||
// but this section does not.
|
||||
func toDirectlySetKeysSet(section *ini.Section) container.Set[string] {
|
||||
func toDirectlySetKeysSet(section ConfigSection) container.Set[string] {
|
||||
sections := make(container.Set[string])
|
||||
for _, key := range section.Keys() {
|
||||
sections.Add(key.Name())
|
||||
|
@ -11,8 +11,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -43,7 +41,7 @@ var (
|
||||
|
||||
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
|
||||
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
|
||||
func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
|
||||
func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string {
|
||||
// don't allow setting both URI and verbatim string
|
||||
uri := sec.Key(uriKey).String()
|
||||
verbatim := sec.Key(verbatimKey).String()
|
||||
@ -84,16 +82,17 @@ func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
|
||||
}
|
||||
|
||||
// generateSaveInternalToken generates and saves the internal token to app.ini
|
||||
func generateSaveInternalToken() {
|
||||
func generateSaveInternalToken(rootCfg ConfigProvider) {
|
||||
token, err := generate.NewInternalToken()
|
||||
if err != nil {
|
||||
log.Fatal("Error generate internal token: %v", err)
|
||||
}
|
||||
|
||||
InternalToken = token
|
||||
CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) {
|
||||
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
|
||||
})
|
||||
rootCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
|
||||
if err := rootCfg.Save(); err != nil {
|
||||
log.Fatal("Error saving internal token: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
@ -141,7 +140,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
if InstallLock && InternalToken == "" {
|
||||
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
|
||||
// some users do cluster deployment, they still depend on this auto-generating behavior.
|
||||
generateSaveInternalToken()
|
||||
generateSaveInternalToken(rootCfg)
|
||||
}
|
||||
|
||||
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
|
||||
|
@ -18,9 +18,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// settings
|
||||
@ -208,17 +205,29 @@ func PrepareAppDataPath() error {
|
||||
|
||||
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
|
||||
func InitProviderFromExistingFile() {
|
||||
CfgProvider = newFileProviderFromConf(CustomConf, false, "")
|
||||
var err error
|
||||
CfgProvider, err = newConfigProviderFromFile(CustomConf, false, "")
|
||||
if err != nil {
|
||||
log.Fatal("InitProviderFromExistingFile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
|
||||
func InitProviderAllowEmpty() {
|
||||
CfgProvider = newFileProviderFromConf(CustomConf, true, "")
|
||||
var err error
|
||||
CfgProvider, err = newConfigProviderFromFile(CustomConf, true, "")
|
||||
if err != nil {
|
||||
log.Fatal("InitProviderAllowEmpty: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
|
||||
func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
|
||||
CfgProvider = newFileProviderFromConf(CustomConf, true, strings.Join(extraConfigs, "\n"))
|
||||
var err error
|
||||
CfgProvider, err = newConfigProviderFromFile(CustomConf, true, strings.Join(extraConfigs, "\n"))
|
||||
if err != nil {
|
||||
log.Fatal("InitProviderAndLoadCommonSettingsForTest: %v", err)
|
||||
}
|
||||
loadCommonSettingsFrom(CfgProvider)
|
||||
if err := PrepareAppDataPath(); err != nil {
|
||||
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
|
||||
@ -229,33 +238,6 @@ func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
|
||||
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
|
||||
}
|
||||
|
||||
// newFileProviderFromConf initializes configuration context.
|
||||
// NOTE: do not print any log except error.
|
||||
func newFileProviderFromConf(customConf string, allowEmpty bool, extraConfig string) *ini.File {
|
||||
cfg := ini.Empty()
|
||||
|
||||
isFile, err := util.IsFile(customConf)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s is a file. Error: %v", customConf, err)
|
||||
}
|
||||
if isFile {
|
||||
if err := cfg.Append(customConf); err != nil {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", customConf, err)
|
||||
}
|
||||
} else if !allowEmpty {
|
||||
log.Fatal("Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c.", CustomConf)
|
||||
} // else: no config file, a config file might be created at CustomConf later (might not)
|
||||
|
||||
if extraConfig != "" {
|
||||
if err = cfg.Append([]byte(extraConfig)); err != nil {
|
||||
log.Fatal("Unable to append more config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
return cfg
|
||||
}
|
||||
|
||||
// LoadCommonSettings loads common configurations from a configuration provider.
|
||||
func LoadCommonSettings() {
|
||||
loadCommonSettingsFrom(CfgProvider)
|
||||
@ -319,51 +301,6 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOrAppendToCustomConf creates or updates the custom config.
|
||||
// Use the callback to set individual values.
|
||||
func CreateOrAppendToCustomConf(purpose string, callback func(cfg *ini.File)) {
|
||||
if CustomConf == "" {
|
||||
log.Error("Custom config path must not be empty")
|
||||
return
|
||||
}
|
||||
|
||||
cfg := ini.Empty()
|
||||
isFile, err := util.IsFile(CustomConf)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s is a file. Error: %v", CustomConf, err)
|
||||
}
|
||||
if isFile {
|
||||
if err := cfg.Append(CustomConf); err != nil {
|
||||
log.Error("failed to load custom conf %s: %v", CustomConf, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
callback(cfg)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
|
||||
log.Fatal("failed to create '%s': %v", CustomConf, err)
|
||||
return
|
||||
}
|
||||
if err := cfg.SaveTo(CustomConf); err != nil {
|
||||
log.Fatal("error saving to custom config: %v", err)
|
||||
}
|
||||
log.Info("Settings for %s saved to: %q", purpose, CustomConf)
|
||||
|
||||
// Change permissions to be more restrictive
|
||||
fi, err := os.Stat(CustomConf)
|
||||
if err != nil {
|
||||
log.Error("Failed to determine current conf file permissions: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.Mode().Perm() > 0o600 {
|
||||
if err = os.Chmod(CustomConf, 0o600); err != nil {
|
||||
log.Warn("Failed changing conf file permissions to -rw-------. Consider changing them manually.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadSettings initializes the settings for normal start up
|
||||
func LoadSettings() {
|
||||
loadDBSetting(CfgProvider)
|
||||
|
@ -6,15 +6,13 @@ package setting
|
||||
import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// Storage represents configuration of storages
|
||||
type Storage struct {
|
||||
Type string
|
||||
Path string
|
||||
Section *ini.Section
|
||||
Section ConfigSection
|
||||
ServeDirect bool
|
||||
}
|
||||
|
||||
@ -30,7 +28,7 @@ func (s *Storage) MapTo(v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage {
|
||||
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec ConfigSection) Storage {
|
||||
const sectionName = "storage"
|
||||
sec := rootCfg.Section(sectionName)
|
||||
|
||||
@ -52,7 +50,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section
|
||||
storage.Section = targetSec
|
||||
storage.Type = typ
|
||||
|
||||
overrides := make([]*ini.Section, 0, 3)
|
||||
overrides := make([]ConfigSection, 0, 3)
|
||||
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
|
||||
if err == nil {
|
||||
overrides = append(overrides, nameSec)
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func Test_getStorageCustomType(t *testing.T) {
|
||||
@ -20,7 +19,7 @@ MINIO_BUCKET = gitea-attachment
|
||||
STORAGE_TYPE = minio
|
||||
MINIO_ENDPOINT = my_minio:9000
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -43,7 +42,7 @@ MINIO_BUCKET = gitea-attachment
|
||||
[storage.minio]
|
||||
MINIO_BUCKET = gitea
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -65,7 +64,7 @@ MINIO_BUCKET = gitea-minio
|
||||
[storage]
|
||||
MINIO_BUCKET = gitea
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -88,7 +87,7 @@ MINIO_BUCKET = gitea
|
||||
[storage]
|
||||
STORAGE_TYPE = local
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -100,7 +99,7 @@ STORAGE_TYPE = local
|
||||
}
|
||||
|
||||
func Test_getStorageGetDefaults(t *testing.T) {
|
||||
cfg, err := ini.Load([]byte(""))
|
||||
cfg, err := newConfigProviderFromData("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -121,7 +120,7 @@ MINIO_BUCKET = gitea-attachment
|
||||
[storage]
|
||||
MINIO_BUCKET = gitea-storage
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
{
|
||||
@ -155,7 +154,7 @@ STORAGE_TYPE = lfs
|
||||
[storage.lfs]
|
||||
MINIO_BUCKET = gitea-storage
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
{
|
||||
@ -179,7 +178,7 @@ func Test_getStorageInheritStorageType(t *testing.T) {
|
||||
[storage]
|
||||
STORAGE_TYPE = minio
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
@ -194,7 +193,7 @@ func Test_getStorageInheritNameSectionType(t *testing.T) {
|
||||
[storage.attachments]
|
||||
STORAGE_TYPE = minio
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
cfg, err := newConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
|
@ -7,19 +7,54 @@ import (
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DateTime renders an absolute time HTML given a time as a string
|
||||
func DateTime(format, datetime, fallback string) template.HTML {
|
||||
datetimeEscaped := html.EscapeString(datetime)
|
||||
fallbackEscaped := html.EscapeString(fallback)
|
||||
// DateTime renders an absolute time HTML element by datetime.
|
||||
func DateTime(format string, datetime any) template.HTML {
|
||||
if p, ok := datetime.(*time.Time); ok {
|
||||
datetime = *p
|
||||
}
|
||||
if p, ok := datetime.(*TimeStamp); ok {
|
||||
datetime = *p
|
||||
}
|
||||
switch v := datetime.(type) {
|
||||
case TimeStamp:
|
||||
datetime = v.AsTime()
|
||||
case int:
|
||||
datetime = TimeStamp(v).AsTime()
|
||||
case int64:
|
||||
datetime = TimeStamp(v).AsTime()
|
||||
}
|
||||
|
||||
var datetimeEscaped, textEscaped string
|
||||
switch v := datetime.(type) {
|
||||
case nil:
|
||||
return "N/A"
|
||||
case string:
|
||||
datetimeEscaped = html.EscapeString(v)
|
||||
textEscaped = datetimeEscaped
|
||||
case time.Time:
|
||||
if v.IsZero() || v.Unix() == 0 {
|
||||
return "N/A"
|
||||
}
|
||||
datetimeEscaped = html.EscapeString(v.Format(time.RFC3339))
|
||||
if format == "full" {
|
||||
textEscaped = html.EscapeString(v.Format("2006-01-02 15:04:05 -07:00"))
|
||||
} else {
|
||||
textEscaped = html.EscapeString(v.Format("2006-01-02"))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unsupported time type %T", datetime))
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "short":
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
|
||||
case "long":
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
|
||||
case "full":
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
|
||||
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
|
||||
}
|
||||
return template.HTML("error in DateTime")
|
||||
panic(fmt.Sprintf("Unsupported format %s", format))
|
||||
}
|
||||
|
45
modules/timeutil/datetime_test.go
Normal file
45
modules/timeutil/datetime_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package timeutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDateTime(t *testing.T) {
|
||||
oldTz := setting.DefaultUILocation
|
||||
setting.DefaultUILocation, _ = time.LoadLocation("America/New_York")
|
||||
defer func() {
|
||||
setting.DefaultUILocation = oldTz
|
||||
}()
|
||||
|
||||
refTimeStr := "2018-01-01T00:00:00Z"
|
||||
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
|
||||
refTimeStamp := TimeStamp(refTime.Unix())
|
||||
|
||||
assert.EqualValues(t, "N/A", DateTime("short", nil))
|
||||
assert.EqualValues(t, "N/A", DateTime("short", 0))
|
||||
assert.EqualValues(t, "N/A", DateTime("short", time.Time{}))
|
||||
assert.EqualValues(t, "N/A", DateTime("short", TimeStamp(0)))
|
||||
|
||||
actual := DateTime("short", "invalid")
|
||||
assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="invalid">invalid</relative-time>`, actual)
|
||||
|
||||
actual = DateTime("short", refTimeStr)
|
||||
assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</relative-time>`, actual)
|
||||
|
||||
actual = DateTime("short", refTime)
|
||||
assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01</relative-time>`, actual)
|
||||
|
||||
actual = DateTime("short", refTimeStamp)
|
||||
assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2017-12-31T19:00:00-05:00">2017-12-31</relative-time>`, actual)
|
||||
|
||||
actual = DateTime("full", refTimeStamp)
|
||||
assert.EqualValues(t, `<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
|
||||
}
|
@ -115,7 +115,7 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
|
||||
}
|
||||
|
||||
func timeSinceUnix(then, now time.Time, lang translation.Locale) template.HTML {
|
||||
friendlyText := then.Format("2006-01-02 15:04:05 +07:00")
|
||||
friendlyText := then.Format("2006-01-02 15:04:05 -07:00")
|
||||
|
||||
// document: https://github.com/github/relative-time-element
|
||||
attrs := `tense="past"`
|
||||
|
@ -462,6 +462,7 @@ team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προορι
|
||||
[modal]
|
||||
yes=Ναι
|
||||
no=Όχι
|
||||
confirm=Επιβεβαίωση
|
||||
cancel=Ακύρωση
|
||||
modify=Ενημέρωση
|
||||
|
||||
@ -1622,7 +1623,10 @@ pulls.tab_files=Αρχεία Με Αλλαγές
|
||||
pulls.reopen_to_merge=Παρακαλώ ανοίξτε ξανά αυτό το pull request για να εκτελέσετε μια συγχώνευση.
|
||||
pulls.cant_reopen_deleted_branch=Αυτό το pull request δεν μπορεί να ανοίξει ξανά επειδή ο κλάδος διαγράφηκε.
|
||||
pulls.merged=Συγχωνευμένο
|
||||
pulls.merged_success=Το pull request συγχωνεύτηκε και έκλεισε επιτυχώς
|
||||
pulls.closed=Το pull request έκλεισε
|
||||
pulls.manually_merged=Συγχωνεύτηκαν χειροκίνητα
|
||||
pulls.merged_info_text=Ο κλάδος %s μπορεί τώρα να διαγραφεί.
|
||||
pulls.is_closed=Το pull request έχει κλείσει.
|
||||
pulls.title_wip_desc=`<a href="#">Ξεκινήστε τον τίτλο με <strong>%s</strong></a> για να αποτρέψετε την τυχαία συγχώνευση του pull request.`
|
||||
pulls.cannot_merge_work_in_progress=Αυτό το pull request επισημαίνεται ως μια εργασία σε εξέλιξη.
|
||||
@ -3414,4 +3418,7 @@ runs.no_matching_runner_helper=Δε ταιριάζει εκτελεστής: %s
|
||||
need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork.
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Ατομικό Έργο
|
||||
type-2.display_name=Έργο Αποθετηρίου
|
||||
type-3.display_name=Έργο Οργανισμού
|
||||
|
||||
|
@ -284,7 +284,7 @@ show_only_public=只显示公开的
|
||||
issues.in_your_repos=在您的仓库中
|
||||
|
||||
[explore]
|
||||
repos=仓库管理
|
||||
repos=仓库
|
||||
users=用户
|
||||
organizations=组织管理
|
||||
search=搜索
|
||||
|
@ -966,7 +966,7 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
}
|
||||
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm),
|
||||
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm),
|
||||
}
|
||||
|
||||
source := authSource.Cfg.(*oauth2.Source)
|
||||
|
50
routers/web/feed/branch.go
Normal file
50
routers/web/feed/branch.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package feed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
|
||||
"github.com/gorilla/feeds"
|
||||
)
|
||||
|
||||
// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed
|
||||
func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
|
||||
commits, err := ctx.Repo.Commit.CommitsByRange(0, 10)
|
||||
if err != nil {
|
||||
ctx.ServerError("ShowBranchFeed", err)
|
||||
return
|
||||
}
|
||||
|
||||
title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName)
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL()}
|
||||
|
||||
feed := &feeds.Feed{
|
||||
Title: title,
|
||||
Link: link,
|
||||
Description: repo.Description,
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
for _, commit := range commits {
|
||||
feed.Items = append(feed.Items, &feeds.Item{
|
||||
Id: commit.ID.String(),
|
||||
Title: strings.TrimSpace(strings.Split(commit.Message(), "\n")[0]),
|
||||
Link: &feeds.Link{Href: repo.HTMLURL() + "/commit/" + commit.ID.String()},
|
||||
Author: &feeds.Author{
|
||||
Name: commit.Author.Name,
|
||||
Email: commit.Author.Email,
|
||||
},
|
||||
Description: commit.Message(),
|
||||
Content: commit.Message(),
|
||||
})
|
||||
}
|
||||
|
||||
writeFeed(ctx, feed, formatType)
|
||||
}
|
56
routers/web/feed/file.go
Normal file
56
routers/web/feed/file.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package feed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/gorilla/feeds"
|
||||
)
|
||||
|
||||
// ShowFileFeed shows tags and/or releases on the repo as RSS / Atom feed
|
||||
func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
|
||||
fileName := ctx.Repo.TreePath
|
||||
if len(fileName) == 0 {
|
||||
return
|
||||
}
|
||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(ctx.Repo.RefName, fileName, 1)
|
||||
if err != nil {
|
||||
ctx.ServerError("ShowBranchFeed", err)
|
||||
return
|
||||
}
|
||||
|
||||
title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath)
|
||||
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
|
||||
|
||||
feed := &feeds.Feed{
|
||||
Title: title,
|
||||
Link: link,
|
||||
Description: repo.Description,
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
for _, commit := range commits {
|
||||
feed.Items = append(feed.Items, &feeds.Item{
|
||||
Id: commit.ID.String(),
|
||||
Title: strings.TrimSpace(strings.Split(commit.Message(), "\n")[0]),
|
||||
Link: &feeds.Link{Href: repo.HTMLURL() + "/commit/" + commit.ID.String()},
|
||||
Author: &feeds.Author{
|
||||
Name: commit.Author.Name,
|
||||
Email: commit.Author.Email,
|
||||
},
|
||||
Description: commit.Message(),
|
||||
Content: commit.Message(),
|
||||
})
|
||||
}
|
||||
|
||||
writeFeed(ctx, feed, formatType)
|
||||
}
|
18
routers/web/feed/render.go
Normal file
18
routers/web/feed/render.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package feed
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
)
|
||||
|
||||
// RenderBranchFeed render format for branch or file
|
||||
func RenderBranchFeed(ctx *context.Context) {
|
||||
_, _, showFeedType := GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
} else {
|
||||
ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
}
|
||||
}
|
@ -32,43 +32,31 @@ import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
func HTTPGitEnabledHandler(ctx *context.Context) {
|
||||
if setting.Repository.DisableHTTPGit {
|
||||
ctx.Resp.WriteHeader(http.StatusForbidden)
|
||||
_, _ = ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
|
||||
}
|
||||
}
|
||||
|
||||
func CorsHandler() func(next http.Handler) http.Handler {
|
||||
if setting.Repository.AccessControlAllowOrigin != "" {
|
||||
return cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{setting.Repository.AccessControlAllowOrigin},
|
||||
AllowedHeaders: []string{"Content-Type", "Authorization", "User-Agent"},
|
||||
})
|
||||
}
|
||||
return func(next http.Handler) http.Handler {
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
// httpBase implementation git smart HTTP protocol
|
||||
func httpBase(ctx *context.Context) (h *serviceHandler) {
|
||||
if setting.Repository.DisableHTTPGit {
|
||||
ctx.Resp.WriteHeader(http.StatusForbidden)
|
||||
_, err := ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(setting.Repository.AccessControlAllowOrigin) > 0 {
|
||||
allowedOrigin := setting.Repository.AccessControlAllowOrigin
|
||||
// Set CORS headers for browser-based git clients
|
||||
ctx.Resp.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
|
||||
ctx.Resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent")
|
||||
|
||||
// Handle preflight OPTIONS request
|
||||
if ctx.Req.Method == "OPTIONS" {
|
||||
if allowedOrigin == "*" {
|
||||
ctx.Status(http.StatusOK)
|
||||
} else if allowedOrigin == "null" {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
} else {
|
||||
origin := ctx.Req.Header.Get("Origin")
|
||||
if len(origin) > 0 && origin == allowedOrigin {
|
||||
ctx.Status(http.StatusOK)
|
||||
} else {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
username := ctx.Params(":username")
|
||||
reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
|
||||
|
||||
|
@ -29,8 +29,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplReleases base.TplName = "repo/release/list"
|
||||
tplReleaseNew base.TplName = "repo/release/new"
|
||||
tplReleasesList base.TplName = "repo/release/list"
|
||||
tplReleaseNew base.TplName = "repo/release/new"
|
||||
tplTagsList base.TplName = "repo/tag/list"
|
||||
)
|
||||
|
||||
// calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
|
||||
@ -58,16 +59,19 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model
|
||||
|
||||
// Releases render releases list page
|
||||
func Releases(ctx *context.Context) {
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||
releasesOrTags(ctx, false)
|
||||
}
|
||||
|
||||
// TagsList render tags list page
|
||||
func TagsList(ctx *context.Context) {
|
||||
ctx.Data["PageIsTagList"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
|
||||
releasesOrTags(ctx, true)
|
||||
}
|
||||
|
||||
func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Data["IsViewBranch"] = false
|
||||
ctx.Data["IsViewTag"] = true
|
||||
@ -75,14 +79,6 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||
ctx.Data["CanCreateBranch"] = false
|
||||
ctx.Data["HideBranchesInDropdown"] = true
|
||||
|
||||
if isTagList {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
|
||||
ctx.Data["PageIsTagList"] = true
|
||||
} else {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||
ctx.Data["PageIsTagList"] = false
|
||||
}
|
||||
|
||||
listOptions := db.ListOptions{
|
||||
Page: ctx.FormInt("page"),
|
||||
PageSize: ctx.FormInt("limit"),
|
||||
@ -196,7 +192,11 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||
pager.SetDefaultParams(ctx)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
ctx.HTML(http.StatusOK, tplReleases)
|
||||
if isTagList {
|
||||
ctx.HTML(http.StatusOK, tplTagsList)
|
||||
} else {
|
||||
ctx.HTML(http.StatusOK, tplReleasesList)
|
||||
}
|
||||
}
|
||||
|
||||
// ReleasesFeedRSS get feeds for releases in RSS format
|
||||
@ -282,7 +282,7 @@ func SingleRelease(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["Releases"] = []*repo_model.Release{release}
|
||||
ctx.HTML(http.StatusOK, tplReleases)
|
||||
ctx.HTML(http.StatusOK, tplReleasesList)
|
||||
}
|
||||
|
||||
// LatestRelease redirects to the latest release
|
||||
|
@ -710,7 +710,14 @@ func Home(ctx *context.Context) {
|
||||
if setting.Other.EnableFeed {
|
||||
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||
if isFeed {
|
||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
switch {
|
||||
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
|
||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
case ctx.Repo.TreePath == "":
|
||||
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
case ctx.Repo.TreePath != "":
|
||||
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1198,7 +1198,7 @@ func RegisterRoutes(m *web.Route) {
|
||||
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
|
||||
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
|
||||
|
||||
// Releases
|
||||
// Tags
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/tags", func() {
|
||||
m.Get("", repo.TagsList)
|
||||
@ -1207,6 +1207,12 @@ func RegisterRoutes(m *web.Route) {
|
||||
}, func(ctx *context.Context) {
|
||||
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
|
||||
}, repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
|
||||
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
|
||||
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
|
||||
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
|
||||
|
||||
// Releases
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/", repo.Releases)
|
||||
m.Get("/tag/*", repo.SingleRelease)
|
||||
@ -1224,8 +1230,6 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Post("/attachments", repo.UploadReleaseAttachment)
|
||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
|
||||
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
|
||||
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
|
||||
@ -1450,6 +1454,9 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
|
||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
|
||||
|
||||
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
|
||||
m.Group("/src", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
|
||||
@ -1508,7 +1515,7 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
||||
}, ignSignInAndCsrf, context_service.UserAssignmentWeb())
|
||||
}, ignSignInAndCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
|
||||
})
|
||||
})
|
||||
// ***** END: Repository *****
|
||||
|
@ -18,14 +18,12 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/minio/sha256-simd"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// ErrInvalidAlgorithmType represents an invalid algorithm error.
|
||||
@ -316,8 +314,7 @@ func InitSigningKey() error {
|
||||
case "HS384":
|
||||
fallthrough
|
||||
case "HS512":
|
||||
key, err = loadOrCreateSymmetricKey()
|
||||
|
||||
key, err = loadSymmetricKey()
|
||||
case "RS256":
|
||||
fallthrough
|
||||
case "RS384":
|
||||
@ -332,7 +329,6 @@ func InitSigningKey() error {
|
||||
fallthrough
|
||||
case "EdDSA":
|
||||
key, err = loadOrCreateAsymmetricKey()
|
||||
|
||||
default:
|
||||
return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm}
|
||||
}
|
||||
@ -351,22 +347,16 @@ func InitSigningKey() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadOrCreateSymmetricKey checks if the configured secret is valid.
|
||||
// If it is not valid a new secret is created and saved in the configuration file.
|
||||
func loadOrCreateSymmetricKey() (interface{}, error) {
|
||||
// loadSymmetricKey checks if the configured secret is valid.
|
||||
// If it is not valid, it will return an error.
|
||||
func loadSymmetricKey() (interface{}, error) {
|
||||
key := make([]byte, 32)
|
||||
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
|
||||
if err != nil || n != 32 {
|
||||
key, err = generate.NewJwtSecret()
|
||||
if err != nil {
|
||||
log.Fatal("error generating JWT secret: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) {
|
||||
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
|
||||
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 32 {
|
||||
return nil, fmt.Errorf("JWT secret must be 32 bytes long")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
|
@ -26,8 +26,8 @@
|
||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
|
||||
<td>{{.TypeName}}</td>
|
||||
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||
<td>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .UpdatedUnix}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -21,8 +21,8 @@
|
||||
<td><button type="submit" class="ui green button" name="op" value="{{.Name}}" title="{{$.locale.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
|
||||
<td>{{$.locale.Tr (printf "admin.dashboard.%s" .Name)}}</td>
|
||||
<td>{{.Spec}}</td>
|
||||
<td>{{DateTime "full" (DateFmtLong .Next) (DateFmtLong .Next)}}</td>
|
||||
<td>{{if gt .Prev.Year 1}}{{DateTime "full" (DateFmtLong .Prev) (DateFmtLong .Prev)}}{{else}}N/A{{end}}</td>
|
||||
<td>{{DateTime "full" (DateFmtLong .Next)}}</td>
|
||||
<td>{{if gt .Prev.Year 1}}{{DateTime "full" .Prev}}{{else}}N/A{{end}}</td>
|
||||
<td>{{.ExecTimes}}</td>
|
||||
<td {{if ne .Status ""}}data-tooltip-content="{{.FormatLastMessage $.locale}}"{{end}} >{{if eq .Status ""}}—{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td>
|
||||
</tr>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<td>{{.ID}}</td>
|
||||
<td>{{$.locale.Tr .TrStr}}</td>
|
||||
<td class="view-detail"><span class="notice-description text truncate">{{.Description}}</span></td>
|
||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||
<td><a href="#">{{svg "octicon-note" 16 "view-detail"}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -41,7 +41,7 @@
|
||||
<td>{{.NumTeams}}</td>
|
||||
<td>{{.NumMembers}}</td>
|
||||
<td>{{.NumRepos}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||
<td><a href="{{.OrganisationLink}}/settings">{{svg "octicon-pencil"}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -65,7 +65,7 @@
|
||||
{{end}}
|
||||
</td>
|
||||
<td>{{FileSize .CalculateBlobSize}}</td>
|
||||
<td>{{DateTime "short" .Version.CreatedUnix.FormatLong .Version.CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .Version.CreatedUnix}}</td>
|
||||
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -155,8 +155,8 @@
|
||||
{{range .Queue.Workers}}
|
||||
<tr>
|
||||
<td>{{.Workers}}{{if .IsFlusher}}<span title="{{$.locale.Tr "admin.monitor.queue.flush"}}">{{svg "octicon-sync"}}</span>{{end}}</td>
|
||||
<td>{{DateTime "full" (DateFmtLong .Start) (DateFmtLong .Start)}}</td>
|
||||
<td>{{if .HasTimeout}}{{DateTime "full" (DateFmtLong .Timeout) (DateFmtLong .Timeout)}}{{else}}-{{end}}</td>
|
||||
<td>{{DateTime "full" .Start}}</td>
|
||||
<td>{{if .HasTimeout}}{{DateTime "full" .Timeout}}{{else}}-{{end}}</td>
|
||||
<td>
|
||||
<a class="delete-button" href="" data-url="{{$.Link}}/cancel/{{.PID}}" data-id="{{.PID}}" data-name="{{.Workers}}" title="{{$.locale.Tr "remove"}}">{{svg "octicon-trash"}}</a>
|
||||
</td>
|
||||
|
@ -80,7 +80,7 @@
|
||||
<td>{{.NumForks}}</td>
|
||||
<td>{{.NumIssues}}</td>
|
||||
<td>{{FileSize .Size}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -91,9 +91,9 @@
|
||||
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||
<td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||
<td>{{.NumRepos}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||
{{if .LastLoginUnix}}
|
||||
<td>{{DateTime "short" .LastLoginUnix.FormatLong .LastLoginUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .LastLoginUnix}}</td>
|
||||
{{else}}
|
||||
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
|
||||
{{end}}
|
||||
|
@ -23,7 +23,7 @@
|
||||
{{svg "octicon-link"}}
|
||||
<a href="{{.Website}}" rel="nofollow">{{.Website}}</a>
|
||||
{{end}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
{{svg "octicon-mail"}}
|
||||
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
|
||||
{{end}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<td><a href="{{.FullWebLink}}">{{.Version.Version}}</a></td>
|
||||
<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
|
||||
<td>{{FileSize .CalculateBlobSize}}</td>
|
||||
<td>{{DateTime "short" .Version.CreatedUnix.FormatLong .Version.CreatedUnix.FormatShort}}</td>
|
||||
<td>{{DateTime "short" .Version.CreatedUnix}}</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr>
|
||||
|
@ -86,7 +86,7 @@
|
||||
{{range .LatestVersions}}
|
||||
<div class="item gt-df">
|
||||
<a class="gt-f1" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
|
||||
<span class="text small">{{DateTime "short" (.CreatedUnix.FormatDate) (.CreatedUnix.FormatDate)}}</span>
|
||||
<span class="text small">{{DateTime "short" .CreatedUnix}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content repository commits">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
<h2 class="ui header">{{DateTime "long" .DateFrom .DateFrom}} - {{DateTime "long" .DateUntil .DateUntil}}
|
||||
<h2 class="ui header">{{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}}
|
||||
<div class="ui right">
|
||||
<!-- Period -->
|
||||
<div class="ui floating dropdown jump filter">
|
||||
|
@ -26,6 +26,11 @@
|
||||
{{svg "octicon-git-branch"}}
|
||||
</button>
|
||||
{{end}}
|
||||
{{if .EnableFeed}}
|
||||
<a role="button" class="ui basic button icon" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranch}}">
|
||||
{{svg "octicon-rss"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranch)}}">
|
||||
{{svg "octicon-download"}}
|
||||
@ -113,6 +118,11 @@
|
||||
{{svg "octicon-git-branch"}}
|
||||
</button>
|
||||
{{end}}
|
||||
{{if $.EnableFeed}}
|
||||
<a role="button" class="ui basic button icon" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .Name}}">
|
||||
{{svg "octicon-rss"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if and (not .IsDeleted) (not $.DisableDownloadSourceArchives)}}
|
||||
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.Name)}}">
|
||||
{{svg "octicon-download"}}
|
||||
|
@ -42,6 +42,8 @@
|
||||
'branches': {{.root.Branches}},
|
||||
'tags': {{.root.Tags}},
|
||||
'defaultBranch': {{$defaultBranch}},
|
||||
'enableFeed': {{.root.EnableFeed}},
|
||||
'rssURLPrefix': '{{$.root.RepoLink}}/rss/branch/',
|
||||
'branchURLPrefix': '{{if .branchURLPrefix}}{{.branchURLPrefix}}{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/branch/{{end}}',
|
||||
'branchURLSuffix': '{{if .branchURLSuffix}}{{.branchURLSuffix}}{{else}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}{{end}}',
|
||||
'tagURLPrefix': '{{if .tagURLPrefix}}{{.tagURLPrefix}}{{else if .release}}{{$.root.RepoLink}}/compare/{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/tag/{{end}}',
|
||||
|
@ -63,13 +63,13 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
{{template "repo/clone_script" .}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="ui segment center">
|
||||
{{.locale.Tr "repo.empty_message"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{template "repo/clone_script" .}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -63,12 +63,13 @@
|
||||
{{$n := len .TreeNames}}
|
||||
{{$l := Eval $n "-" 1}}
|
||||
<!-- If home page, show new pr. If not, show breadcrumb -->
|
||||
{{if and (eq $n 0) .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
|
||||
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{CompareLink .BaseRepo .Repository .BranchName}}"
|
||||
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}">
|
||||
{{svg "octicon-git-pull-request"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if eq $n 0}}
|
||||
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
|
||||
<a href="{{CompareLink .BaseRepo .Repository .BranchName}}">
|
||||
<button id="new-pull-request" class="ui compact basic button" data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}"><span class="text">{{svg "octicon-git-pull-request"}}</span></button>
|
||||
</a>
|
||||
{{end}}
|
||||
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{.locale.Tr "repo.find_file.go_to_file"}}</a>
|
||||
{{end}}
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
{{else}}
|
||||
{{svg "octicon-calendar"}}
|
||||
{{if .Milestone.DeadlineString}}
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .Milestone.DeadlineString .Milestone.DeadlineString}}</span>
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .Milestone.DeadlineString}}</span>
|
||||
{{else}}
|
||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||
{{end}}
|
||||
|
@ -77,7 +77,7 @@
|
||||
{{else}}
|
||||
{{svg "octicon-calendar"}}
|
||||
{{if .DeadlineString}}
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
|
||||
{{else}}
|
||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||
{{end}}
|
||||
|
@ -295,7 +295,7 @@
|
||||
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
||||
<span class="text grey muted-links">
|
||||
{{template "shared/user/authorlink" .Poster}}
|
||||
{{$.locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content .Content) $createdStr | Safe}}
|
||||
{{$.locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr | Safe}}
|
||||
</span>
|
||||
</div>
|
||||
{{else if eq .Type 17}}
|
||||
@ -305,8 +305,8 @@
|
||||
<span class="text grey muted-links">
|
||||
{{template "shared/user/authorlink" .Poster}}
|
||||
{{$parsedDeadline := .Content | ParseDeadline}}
|
||||
{{$from := DateTime "long" (index $parsedDeadline 1) (index $parsedDeadline 1)}}
|
||||
{{$to := DateTime "long" (index $parsedDeadline 0) (index $parsedDeadline 0)}}
|
||||
{{$from := DateTime "long" (index $parsedDeadline 1)}}
|
||||
{{$to := DateTime "long" (index $parsedDeadline 0)}}
|
||||
{{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}}
|
||||
</span>
|
||||
</div>
|
||||
@ -316,7 +316,7 @@
|
||||
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
||||
<span class="text grey muted-links">
|
||||
{{template "shared/user/authorlink" .Poster}}
|
||||
{{$.locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content .Content) $createdStr | Safe}}
|
||||
{{$.locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr | Safe}}
|
||||
</span>
|
||||
</div>
|
||||
{{else if eq .Type 19}}
|
||||
|
@ -385,7 +385,7 @@
|
||||
<div class="gt-df gt-sb gt-ac">
|
||||
<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{.locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
|
||||
{{svg "octicon-calendar" 16 "gt-mr-3"}}
|
||||
{{DateTime "long" .Issue.DeadlineUnix.FormatDate .Issue.DeadlineUnix.FormatDate}}
|
||||
{{DateTime "long" .Issue.DeadlineUnix}}
|
||||
</div>
|
||||
<div>
|
||||
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
|
||||
|
@ -1,73 +1,16 @@
|
||||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content repository release">
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content repository releases">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<h2 class="ui compact small menu header">
|
||||
{{if .Permission.CanRead $.UnitTypeReleases}}
|
||||
<a class="{{if (and (not .PageIsSingleTag) (not .PageIsTagList))}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a>
|
||||
{{end}}
|
||||
{{if .Permission.CanRead $.UnitTypeCode}}
|
||||
<a class="{{if (or .PageIsSingleTag .PageIsTagList)}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a>
|
||||
{{end}}
|
||||
</h2>
|
||||
{{if .EnableFeed}}
|
||||
<a href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"><i class="ui grey icon gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
|
||||
{{end}}
|
||||
{{if (and .CanCreateRelease (not .PageIsTagList))}}
|
||||
{{template "repo/sub_menu_release_tag" .}}
|
||||
|
||||
{{if .CanCreateRelease}}
|
||||
<a class="ui right small green button" href="{{$.RepoLink}}/releases/new">
|
||||
{{.locale.Tr "repo.release.new_release"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .PageIsTagList}}
|
||||
<div class="ui divider"></div>
|
||||
{{if gt .ReleasesNum 0}}
|
||||
<h4 class="ui top attached header">
|
||||
<div class="five wide column gt-df gt-ac">
|
||||
{{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.release.tags"}}
|
||||
</div>
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<table class="ui very basic striped fixed table single line" id="tags-table">
|
||||
<thead></thead>
|
||||
<tbody class="tag-list">
|
||||
{{range $idx, $release := .Releases}}
|
||||
<tr>
|
||||
<td class="tag">
|
||||
<h3 class="release-tag-name gt-mb-3">
|
||||
<a class="gt-df gt-ac" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
|
||||
</h3>
|
||||
<div class="download gt-df gt-ac">
|
||||
{{if $.Permission.CanRead $.UnitTypeCode}}
|
||||
{{if .CreatedUnix}}
|
||||
<span class="gt-mr-3">{{svg "octicon-clock" 16 "gt-mr-2"}}{{TimeSinceUnix .CreatedUnix $.locale}}</span>
|
||||
{{end}}
|
||||
<a class="gt-mr-3 gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a>
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}ZIP</a>
|
||||
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}TAR.GZ</a>
|
||||
{{end}}
|
||||
{{if (and $.CanCreateRelease $release.IsTag)}}
|
||||
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.new_release"}}</a>
|
||||
{{end}}
|
||||
{{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}}
|
||||
<a class="ui delete-button gt-mr-3 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
|
||||
{{svg "octicon-trash" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.delete_tag"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if (not $release.IsTag)}}
|
||||
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.detail"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
|
||||
<ul id="release-list">
|
||||
{{range $idx, $release := .Releases}}
|
||||
<li class="ui grid">
|
||||
@ -178,7 +121,7 @@
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{template "base/paginate" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -60,7 +60,7 @@
|
||||
{{.Fingerprint}}
|
||||
</div>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -89,7 +89,7 @@
|
||||
<tr>
|
||||
<td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td>
|
||||
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
|
||||
<td>{{DateTime "full" .Mirror.UpdatedUnix.FormatLong .Mirror.UpdatedUnix.AsTime}}</td>
|
||||
<td>{{DateTime "full" .Mirror.UpdatedUnix}}</td>
|
||||
<td class="right aligned">
|
||||
<form method="post" style="display: inline-block">
|
||||
{{.CsrfTokenHtml}}
|
||||
@ -167,7 +167,7 @@
|
||||
{{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}}
|
||||
<td class="gt-word-break">{{$address.Address}}</td>
|
||||
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
|
||||
<td>{{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix.FormatLong .LastUpdateUnix.AsTime}}{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td>
|
||||
<td>{{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix}}{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td>
|
||||
<td class="right aligned">
|
||||
<form method="post" style="display: inline-block">
|
||||
{{$.CsrfTokenHtml}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<a href="{{.RepoLink}}/branches">{{svg "octicon-git-branch"}} <b>{{.BranchesCount}}</b> {{.locale.TrN .BranchesCount "repo.branch" "repo.branches"}}</a>
|
||||
</div>
|
||||
{{if $.Permission.CanRead $.UnitTypeCode}}
|
||||
<div class="item">
|
||||
<div class="item{{if .PageIsTagList}} active{{end}}">
|
||||
<a href="{{.RepoLink}}/tags">{{svg "octicon-tag"}} <b>{{.NumTags}}</b> {{.locale.TrN .NumTags "repo.tag" "repo.tags"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
17
templates/repo/sub_menu_release_tag.tmpl
Normal file
17
templates/repo/sub_menu_release_tag.tmpl
Normal file
@ -0,0 +1,17 @@
|
||||
{{$canReadReleases := $.Permission.CanRead $.UnitTypeReleases}}
|
||||
{{$canReadCode := $.Permission.CanRead $.UnitTypeCode}}
|
||||
|
||||
{{if $canReadReleases}}
|
||||
<h2 class="ui compact small menu header">
|
||||
<a class="{{if .PageIsReleaseList}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a>
|
||||
{{if $canReadCode}}
|
||||
<a class="{{if .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a>
|
||||
{{end}}
|
||||
</h2>
|
||||
|
||||
{{if .EnableFeed}}
|
||||
<a href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"><i class="ui grey icon gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
|
||||
{{end}}
|
||||
{{else if $canReadCode}}
|
||||
{{template "repo/sub_menu" .}}
|
||||
{{end}}
|
85
templates/repo/tag/list.tmpl
Normal file
85
templates/repo/tag/list.tmpl
Normal file
@ -0,0 +1,85 @@
|
||||
{{template "base/head" .}}
|
||||
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content repository tags">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
{{template "repo/sub_menu_release_tag" .}}
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<h4 class="ui top attached header">
|
||||
<div class="five wide column gt-df gt-ac">
|
||||
{{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.release.tags"}}
|
||||
</div>
|
||||
</h4>
|
||||
|
||||
{{$canReadReleases := $.Permission.CanRead $.UnitTypeReleases}}
|
||||
|
||||
<div class="ui attached table segment">
|
||||
<table class="ui very basic striped fixed table single line" id="tags-table">
|
||||
<tbody class="tag-list">
|
||||
{{range $idx, $release := .Releases}}
|
||||
<tr>
|
||||
<td class="tag">
|
||||
<h3 class="release-tag-name gt-mb-3">
|
||||
{{if $canReadReleases}}
|
||||
<a class="gt-df gt-ac" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
|
||||
{{else}}
|
||||
<a class="gt-df gt-ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
|
||||
{{end}}
|
||||
</h3>
|
||||
<div class="download gt-df gt-ac">
|
||||
{{if $.Permission.CanRead $.UnitTypeCode}}
|
||||
{{if .CreatedUnix}}
|
||||
<span class="gt-mr-3">{{svg "octicon-clock" 16 "gt-mr-2"}}{{TimeSinceUnix .CreatedUnix $.locale}}</span>
|
||||
{{end}}
|
||||
|
||||
<a class="gt-mr-3 gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a>
|
||||
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}ZIP</a>
|
||||
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}TAR.GZ</a>
|
||||
{{end}}
|
||||
|
||||
{{if (and $canReadReleases $.CanCreateRelease $release.IsTag)}}
|
||||
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.new_release"}}</a>
|
||||
{{end}}
|
||||
|
||||
{{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}}
|
||||
<a class="ui delete-button gt-mr-3 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
|
||||
{{svg "octicon-trash" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.delete_tag"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if and $canReadReleases (not $release.IsTag)}}
|
||||
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.detail"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{template "base/paginate" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if $.Permission.CanWrite $.UnitTypeCode}}
|
||||
<div class="ui g-modal-confirm delete modal">
|
||||
<div class="header">
|
||||
{{svg "octicon-trash"}}
|
||||
{{.locale.Tr "repo.release.delete_tag"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{{.locale.Tr "repo.release.deletion_tag_desc"}}</p>
|
||||
</div>
|
||||
{{template "base/modal_actions_confirm" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
{{template "base/footer" .}}
|
@ -18,7 +18,7 @@
|
||||
{{else if .Location}}
|
||||
{{svg "octicon-location"}} {{.Location}}
|
||||
{{else}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
|
||||
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
|
||||
{{end}}
|
||||
</div>
|
||||
</li>
|
||||
|
@ -42,6 +42,9 @@
|
||||
</div>
|
||||
<a download href="{{$.RawFileLink}}"><span class="btn-octicon" data-tooltip-content="{{.locale.Tr "repo.download_file"}}">{{svg "octicon-download"}}</span></a>
|
||||
<a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{.locale.Tr "copy_content"}}{{else}}{{.locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy" 14}}</a>
|
||||
{{if .EnableFeed}}
|
||||
<a class="btn-octicon" href="{{$.FeedURL}}/rss/{{$.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{svg "octicon-rss" 14}}</a>
|
||||
{{end}}
|
||||
{{if .Repository.CanEnableEditor}}
|
||||
{{if .CanEditFile}}
|
||||
<a href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .TreePath}}"><span class="btn-octicon" data-tooltip-content="{{.EditFileTooltip}}">{{svg "octicon-pencil"}}</span></a>
|
||||
|
@ -106,7 +106,7 @@
|
||||
<span class="due-date" data-tooltip-content="{{$.locale.Tr "repo.issues.due_date"}}">
|
||||
<span{{if .IsOverdue}} class="overdue"{{end}}>
|
||||
{{svg "octicon-calendar" 14 "gt-mr-2"}}
|
||||
{{DateTime "short" .DeadlineUnix.FormatDate .DeadlineUnix.FormatShort}}
|
||||
{{DateTime "short" .DeadlineUnix}}
|
||||
</span>
|
||||
</span>
|
||||
{{end}}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div class="ui container gt-mt-5">
|
||||
{{if .ErrorMsg}}
|
||||
<p>{{.locale.Tr "error.occurred"}}:</p>
|
||||
<pre class="gt-whitespace-pre-wrap">{{.ErrorMsg}}</pre>
|
||||
<pre class="gt-whitespace-pre-wrap gt-break-all">{{.ErrorMsg}}</pre>
|
||||
{{end}}
|
||||
|
||||
<div class="center gt-mt-5">
|
||||
|
@ -97,7 +97,7 @@
|
||||
{{else}}
|
||||
{{svg "octicon-calendar"}}
|
||||
{{if .DeadlineString}}
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
|
||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
|
||||
{{else}}
|
||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||
{{end}}
|
||||
|
@ -44,8 +44,10 @@ data.teamId = {{.Team.ID}};
|
||||
{{if not .ContextUser.IsOrganization}}
|
||||
data.organizations = [{{range .Orgs}}{'name': {{.Name}}, 'num_repos': {{.NumRepos}}},{{end}}];
|
||||
data.isOrganization = false;
|
||||
data.organizationsTotalCount = {{.UserOrgsCount}}
|
||||
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}}
|
||||
data.organizationsTotalCount = {{.UserOrgsCount}};
|
||||
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}};
|
||||
{{else}}
|
||||
data.organizationId = {{.ContextUser.ID}};
|
||||
{{end}}
|
||||
|
||||
window.config.pageData.dashboardRepoList = data;
|
||||
|
@ -73,7 +73,7 @@
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} {{DateTime "short" .ContextUser.CreatedUnix.FormatLong .ContextUser.CreatedUnix.FormatShort}}</li>
|
||||
<li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} {{DateTime "short" .ContextUser.CreatedUnix}}</li>
|
||||
{{if and .Orgs .HasOrgsVisible}}
|
||||
<li>
|
||||
<ul class="user-orgs">
|
||||
|
@ -27,7 +27,7 @@
|
||||
</ul>
|
||||
</details>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<div class="content">
|
||||
<strong>{{$grant.Application.Name}}</strong>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" $grant.CreatedUnix.FormatLong $grant.CreatedUnix.FormatShort}}</span></i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" $grant.CreatedUnix}}</span></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -68,9 +68,9 @@
|
||||
<b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
|
||||
</div>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .AddedUnix.FormatLong .AddedUnix.FormatShort}}</span></i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .AddedUnix}}</span></i>
|
||||
-
|
||||
<i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span>{{DateTime "short" .ExpiredUnix.FormatLong .ExpiredUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i>
|
||||
<i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span>{{DateTime "short" .ExpiredUnix}}</span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,7 +25,7 @@
|
||||
<div class="content">
|
||||
<strong>{{.Name}}</strong>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,7 +59,7 @@
|
||||
{{.Fingerprint}}
|
||||
</div>
|
||||
<div class="activity meta">
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, titl
|
||||
|
||||
// Click the PR button to create a pull
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
link, exists := htmlDoc.doc.Find("#new-pull-request").Parent().Attr("href")
|
||||
link, exists := htmlDoc.doc.Find("#new-pull-request").Attr("href")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
if branch != "master" {
|
||||
link = strings.Replace(link, ":master", ":"+branch, 1)
|
||||
|
@ -864,6 +864,9 @@ a.label,
|
||||
margin-top: -0.25rem;
|
||||
margin-bottom: -0.25rem;
|
||||
}
|
||||
.ui.dropdown .menu > .item > svg {
|
||||
margin-right: .78rem; /* use the same margin as for <img> */
|
||||
}
|
||||
|
||||
.ui.selection.dropdown .menu > .item {
|
||||
border-color: var(--color-secondary);
|
||||
|
@ -23,15 +23,6 @@ Gitea's private styles use `g-` prefix.
|
||||
.gt-h-100 { height: 100% !important; }
|
||||
.gt-br-0 { border-radius: 0 !important; }
|
||||
|
||||
/* below class names match Tailwind CSS */
|
||||
.gt-pointer-events-none { pointer-events: none !important; }
|
||||
.gt-relative { position: relative !important; }
|
||||
.gt-overflow-x-scroll { overflow-x: scroll !important; }
|
||||
.gt-cursor-default { cursor: default !important; }
|
||||
.gt-items-start { align-items: flex-start !important; }
|
||||
.gt-whitespace-pre { white-space: pre !important; }
|
||||
.gt-invisible { visibility: hidden !important; }
|
||||
|
||||
.gt-mono {
|
||||
font-family: var(--fonts-monospace) !important;
|
||||
font-size: .95em !important; /* compensate for monospace fonts being usually slightly larger */
|
||||
@ -51,6 +42,19 @@ Gitea's private styles use `g-` prefix.
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
|
||||
/* below class names match Tailwind CSS */
|
||||
.gt-break-all { word-break: break-all !important; }
|
||||
.gt-content-center { align-content: center !important; }
|
||||
.gt-cursor-default { cursor: default !important; }
|
||||
.gt-invisible { visibility: hidden !important; }
|
||||
.gt-items-start { align-items: flex-start !important; }
|
||||
.gt-overflow-x-scroll { overflow-x: scroll !important; }
|
||||
.gt-pointer-events-none { pointer-events: none !important; }
|
||||
.gt-relative { position: relative !important; }
|
||||
.gt-whitespace-nowrap { white-space: nowrap !important; }
|
||||
.gt-whitespace-pre { white-space: pre !important; }
|
||||
.gt-whitespace-pre-wrap { white-space: pre-wrap !important; }
|
||||
|
||||
.gt-w-screen { width: 100vw !important; }
|
||||
.gt-h-screen { height: 100vh !important; }
|
||||
|
||||
@ -203,11 +207,7 @@ Gitea's private styles use `g-` prefix.
|
||||
.gt-gap-y-4 { row-gap: 1rem !important; }
|
||||
.gt-gap-y-5 { row-gap: 2rem !important; }
|
||||
|
||||
.gt-content-center { align-content: center !important; }
|
||||
|
||||
.gt-shrink-0 { flex-shrink: 0 !important; }
|
||||
.gt-whitespace-nowrap { white-space: nowrap !important; }
|
||||
.gt-whitespace-pre-wrap { white-space: pre-wrap !important; }
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.gt-db-small { display: block !important; }
|
||||
|
@ -30,6 +30,7 @@
|
||||
@import "./install.css";
|
||||
@import "./form.css";
|
||||
@import "./repository.css";
|
||||
@import "./repository-release-tag.css";
|
||||
@import "./editor.css";
|
||||
@import "./editor-markdown.css";
|
||||
@import "./organization.css";
|
||||
|
151
web_src/css/repository-release-tag.css
Normal file
151
web_src/css/repository-release-tag.css
Normal file
@ -0,0 +1,151 @@
|
||||
.repository.releases #release-list {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list .release-list-title {
|
||||
font-size: 2rem;
|
||||
font-weight: normal;
|
||||
margin-top: -4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta,
|
||||
.repository.releases #release-list > li .detail {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta {
|
||||
text-align: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta .label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta .commit {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta .choose {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .meta .choose .button {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail {
|
||||
border-left: 2px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .author img {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download > a .svg {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list li {
|
||||
list-style: none;
|
||||
display: block;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--color-secondary);
|
||||
background: var(--color-light);
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list li a > .text.right {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list li + li {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list li:first-of-type {
|
||||
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .download .list li:last-of-type {
|
||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||
}
|
||||
|
||||
.repository.releases #release-list > li .detail .dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: var(--color-secondary-dark-3);
|
||||
z-index: 9;
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: -6px;
|
||||
top: 40px;
|
||||
border-radius: 100%;
|
||||
border: 2.5px solid var(--color-body);
|
||||
}
|
||||
|
||||
.repository.tags #tags-table .tag {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.repository.tags #tags-table .release-tag-name {
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.repository.new.release .target {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.repository.new.release .target #tag-name {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.repository.new.release .target .at {
|
||||
margin-left: -5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.new.release .target .selection.dropdown {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.repository.new.release .prerelease.field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 438px) {
|
||||
.repository.new.release .field button,
|
||||
.repository.new.release .field input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.repository.new.release .field button {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.repository.new.release .field .wrap_remove {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.repository.new.release .field .attachment_edit {
|
||||
width: 450px !important;
|
||||
}
|
@ -1923,157 +1923,6 @@
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.repository.release #release-list {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list .release-list-title {
|
||||
font-size: 2rem;
|
||||
font-weight: normal;
|
||||
margin-top: -4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta,
|
||||
.repository.release #release-list > li .detail {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta {
|
||||
text-align: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta .label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta .commit {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta .choose {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .meta .choose .button {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail {
|
||||
border-left: 2px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .author img {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download > a .svg {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list li {
|
||||
list-style: none;
|
||||
display: block;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--color-secondary);
|
||||
background: var(--color-light);
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list li a > .text.right {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list li + li {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list li:first-of-type {
|
||||
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .download .list li:last-of-type {
|
||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||
}
|
||||
|
||||
.repository.release #release-list > li .detail .dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: var(--color-secondary-dark-3);
|
||||
z-index: 9;
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: -6px;
|
||||
top: 40px;
|
||||
border-radius: 100%;
|
||||
border: 2.5px solid var(--color-body);
|
||||
}
|
||||
|
||||
.repository.release #tags-table .tag {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.repository.release #tags-table .release-tag-name {
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.repository.new.release .target {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.repository.new.release .target #tag-name {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.repository.new.release .target .at {
|
||||
margin-left: -5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.repository.new.release .target .selection.dropdown {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.repository.new.release .prerelease.field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 438px) {
|
||||
.repository.new.release .field button,
|
||||
.repository.new.release .field input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.repository.new.release .field button {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.repository.new.release .field .wrap_remove {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.repository.new.release .field .attachment_edit {
|
||||
width: 450px !important;
|
||||
}
|
||||
|
||||
.repository.packages .empty {
|
||||
padding-top: 70px;
|
||||
|
@ -10,7 +10,7 @@
|
||||
{{ textMyRepos }}
|
||||
<span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
|
||||
</div>
|
||||
<a :href="subUrl + '/repo/create'" :data-tooltip-content="textNewRepo">
|
||||
<a :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo">
|
||||
<svg-icon name="octicon-plus"/>
|
||||
<span class="sr-only">{{ textNewRepo }}</span>
|
||||
</a>
|
||||
@ -74,7 +74,7 @@
|
||||
<a class="repo-list-link gt-df gt-ac gt-sb" :href="repo.link">
|
||||
<div class="item-name gt-df gt-ac gt-f1">
|
||||
<svg-icon :name="repoIcon(repo)" :size="16" class-name="gt-mr-2"/>
|
||||
<div class="text truncate gt-ml-1">{{ repo.full_name }}</div>
|
||||
<div class="text gt-bold truncate gt-ml-1">{{ repo.full_name }}</div>
|
||||
<span v-if="repo.archived">
|
||||
<svg-icon name="octicon-archive" :size="16" class-name="gt-ml-2"/>
|
||||
</span>
|
||||
@ -199,6 +199,7 @@ const sfc = {
|
||||
isOrganization: true,
|
||||
canCreateOrganization: false,
|
||||
organizationsTotalCount: 0,
|
||||
organizationId: 0,
|
||||
|
||||
subUrl: appSubUrl,
|
||||
...pageData.dashboardRepoList,
|
||||
|
@ -39,6 +39,9 @@
|
||||
<div class="scrolling menu" ref="scrollContainer">
|
||||
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index">
|
||||
{{ item.name }}
|
||||
<a v-if="enableFeed && mode === 'branches'" role="button" class="ui compact muted right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
|
||||
<svg-icon name="octicon-rss" :size="14"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
|
||||
<a href="#" @click="createNewBranch()">
|
||||
@ -291,3 +294,12 @@ export function initRepoBranchTagSelector(selector) {
|
||||
|
||||
export default sfc; // activate IDE's Vue plugin
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu .item a {
|
||||
display: none; /* only show RSS icon on hover */
|
||||
}
|
||||
.menu .item:hover a {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
@ -43,6 +43,7 @@ import octiconChevronLeft from '../../public/img/svg/octicon-chevron-left.svg';
|
||||
import octiconOrganization from '../../public/img/svg/octicon-organization.svg';
|
||||
import octiconTag from '../../public/img/svg/octicon-tag.svg';
|
||||
import octiconGitBranch from '../../public/img/svg/octicon-git-branch.svg';
|
||||
import octiconRss from '../../public/img/svg/octicon-rss.svg';
|
||||
|
||||
const svgs = {
|
||||
'octicon-blocked': octiconBlocked,
|
||||
@ -89,6 +90,7 @@ const svgs = {
|
||||
'octicon-organization': octiconOrganization,
|
||||
'octicon-tag': octiconTag,
|
||||
'octicon-git-branch': octiconGitBranch,
|
||||
'octicon-rss': octiconRss,
|
||||
};
|
||||
|
||||
// TODO: use a more general approach to access SVG icons.
|
||||
|
Loading…
x
Reference in New Issue
Block a user