mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-14 00:02:41 -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
|
- name: deps
|
||||||
path: /go
|
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
|
kind: pipeline
|
||||||
type: docker
|
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/felixge/fgprof"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PIDFile could be set from build tag
|
// PIDFile could be set from build tag
|
||||||
@ -223,9 +222,10 @@ func setPort(port string) error {
|
|||||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||||
|
|
||||||
// Save LOCAL_ROOT_URL if port changed
|
// Save LOCAL_ROOT_URL if port changed
|
||||||
setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) {
|
setting.CfgProvider.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||||
cfg.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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,15 @@
|
|||||||
# upgrade.sh 1.15.10
|
# upgrade.sh 1.15.10
|
||||||
# giteahome=/opt/gitea giteaconf=$giteahome/app.ini upgrade.sh
|
# 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
|
# apply variables from environment
|
||||||
: "${giteabin:="/usr/local/bin/gitea"}"
|
: "${giteabin:="/usr/local/bin/gitea"}"
|
||||||
: "${giteahome:="/var/lib/gitea"}"
|
: "${giteahome:="/var/lib/gitea"}"
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
_ "code.gitea.io/gitea/models"
|
_ "code.gitea.io/gitea/models"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@ -27,7 +26,7 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
func TestBleveSearchIssues(t *testing.T) {
|
func TestBleveSearchIssues(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
setting.CfgProvider = ini.Empty()
|
setting.CfgProvider = setting.NewEmptyConfigProvider()
|
||||||
|
|
||||||
tmpIndexerDir := t.TempDir()
|
tmpIndexerDir := t.TempDir()
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
_ "code.gitea.io/gitea/models"
|
_ "code.gitea.io/gitea/models"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@ -29,7 +28,7 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
func TestRepoStatsIndex(t *testing.T) {
|
func TestRepoStatsIndex(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
setting.CfgProvider = ini.Empty()
|
setting.CfgProvider = setting.NewEmptyConfigProvider()
|
||||||
|
|
||||||
setting.LoadQueueSettings()
|
setting.LoadQueueSettings()
|
||||||
|
|
||||||
|
@ -4,20 +4,157 @@
|
|||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
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
|
// ConfigProvider represents a config provider
|
||||||
type ConfigProvider interface {
|
type ConfigProvider interface {
|
||||||
Section(section string) *ini.Section
|
Section(section string) ConfigSection
|
||||||
NewSection(name string) (*ini.Section, error)
|
NewSection(name string) (ConfigSection, error)
|
||||||
GetSection(name string) (*ini.Section, 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, …
|
// 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{}) {
|
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) {
|
||||||
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
|
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getCronSettings(t *testing.T) {
|
func Test_getCronSettings(t *testing.T) {
|
||||||
@ -23,11 +22,11 @@ func Test_getCronSettings(t *testing.T) {
|
|||||||
|
|
||||||
iniStr := `
|
iniStr := `
|
||||||
[cron.test]
|
[cron.test]
|
||||||
Base = true
|
BASE = true
|
||||||
Second = white rabbit
|
SECOND = white rabbit
|
||||||
Extend = true
|
EXTEND = true
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
extended := &Extended{
|
extended := &Extended{
|
||||||
|
@ -9,8 +9,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LFS represents the configuration for Git LFS
|
// 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
|
// 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
|
// if these are removed, the warning will not be shown
|
||||||
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
||||||
lfsSec.Key("PATH").MustString(
|
lfsSec.Key("PATH").MustString(sec.Key("LFS_CONTENT_PATH").String())
|
||||||
sec.Key("LFS_CONTENT_PATH").String())
|
|
||||||
|
|
||||||
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
|
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
|
||||||
|
|
||||||
@ -62,9 +59,11 @@ func loadLFSFrom(rootCfg ConfigProvider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save secret
|
// Save secret
|
||||||
CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) {
|
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
|
||||||
cfg.Section("server").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/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -131,12 +129,12 @@ type LogDescription struct {
|
|||||||
SubLogDescriptions []SubLogDescription
|
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())
|
value := section.Key(key).MustString(defaultValue.String())
|
||||||
return log.FromString(value)
|
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)
|
value := section.Key(key).MustString(defaultValue)
|
||||||
return log.FromString(value).String()
|
return log.FromString(value).String()
|
||||||
}
|
}
|
||||||
@ -165,7 +163,7 @@ func loadLogFrom(rootCfg ConfigProvider) {
|
|||||||
Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
|
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)
|
level := getLogLevel(sec, "LEVEL", Log.Level)
|
||||||
levelName = level.String()
|
levelName = level.String()
|
||||||
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)
|
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)
|
||||||
|
@ -7,11 +7,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_loadMailerFrom(t *testing.T) {
|
func Test_loadMailerFrom(t *testing.T) {
|
||||||
iniFile := ini.Empty()
|
iniFile := NewEmptyConfigProvider()
|
||||||
kases := map[string]*Mailer{
|
kases := map[string]*Mailer{
|
||||||
"smtp.mydomain.com": {
|
"smtp.mydomain.com": {
|
||||||
SMTPAddr: "smtp.mydomain.com",
|
SMTPAddr: "smtp.mydomain.com",
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalMarkupRenderers represents the external markup renderers
|
// 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)
|
rule, ok := createMarkupSanitizerRule(name, sec)
|
||||||
if ok {
|
if ok {
|
||||||
if strings.HasPrefix(name, "sanitizer.") {
|
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
|
var rule MarkupSanitizerRule
|
||||||
|
|
||||||
ok := false
|
ok := false
|
||||||
@ -141,7 +139,7 @@ func createMarkupSanitizerRule(name string, sec *ini.Section) (MarkupSanitizerRu
|
|||||||
return rule, true
|
return rule, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMarkupRenderer(name string, sec *ini.Section) {
|
func newMarkupRenderer(name string, sec ConfigSection) {
|
||||||
extensionReg := regexp.MustCompile(`\.\w`)
|
extensionReg := regexp.MustCompile(`\.\w`)
|
||||||
|
|
||||||
extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
|
extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"math"
|
"math"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/generate"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data
|
// 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(" ")
|
parts := sec.Key(name).Strings(" ")
|
||||||
scopes := make([]string, 0, len(parts))
|
scopes := make([]string, 0, len(parts))
|
||||||
for _, scope := range parts {
|
for _, scope := range parts {
|
||||||
@ -119,4 +119,19 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
|||||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, 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"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package registry settings
|
// Package registry settings
|
||||||
@ -86,7 +85,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
|
|||||||
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
|
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"
|
const noLimit = "-1"
|
||||||
|
|
||||||
value := section.Key(key).MustString(noLimit)
|
value := section.Key(key).MustString(noLimit)
|
||||||
|
@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueueSettings represent the settings for a queue from the ini
|
// 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
|
// 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
|
// 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.
|
// 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])
|
sections := make(container.Set[string])
|
||||||
for _, key := range section.Keys() {
|
for _, key := range section.Keys() {
|
||||||
sections.Add(key.Name())
|
sections.Add(key.Name())
|
||||||
|
@ -11,8 +11,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -43,7 +41,7 @@ var (
|
|||||||
|
|
||||||
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
|
// 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.
|
// 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
|
// don't allow setting both URI and verbatim string
|
||||||
uri := sec.Key(uriKey).String()
|
uri := sec.Key(uriKey).String()
|
||||||
verbatim := sec.Key(verbatimKey).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
|
// generateSaveInternalToken generates and saves the internal token to app.ini
|
||||||
func generateSaveInternalToken() {
|
func generateSaveInternalToken(rootCfg ConfigProvider) {
|
||||||
token, err := generate.NewInternalToken()
|
token, err := generate.NewInternalToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error generate internal token: %v", err)
|
log.Fatal("Error generate internal token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalToken = token
|
InternalToken = token
|
||||||
CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) {
|
rootCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
|
||||||
cfg.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) {
|
func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||||
@ -141,7 +140,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
|||||||
if InstallLock && InternalToken == "" {
|
if InstallLock && InternalToken == "" {
|
||||||
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
|
// 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.
|
// some users do cluster deployment, they still depend on this auto-generating behavior.
|
||||||
generateSaveInternalToken()
|
generateSaveInternalToken(rootCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
|
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
|
||||||
|
@ -18,9 +18,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/user"
|
"code.gitea.io/gitea/modules/user"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
@ -208,17 +205,29 @@ func PrepareAppDataPath() error {
|
|||||||
|
|
||||||
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
|
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
|
||||||
func InitProviderFromExistingFile() {
|
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
|
// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
|
||||||
func InitProviderAllowEmpty() {
|
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
|
// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
|
||||||
func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
|
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)
|
loadCommonSettingsFrom(CfgProvider)
|
||||||
if err := PrepareAppDataPath(); err != nil {
|
if err := PrepareAppDataPath(); err != nil {
|
||||||
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
|
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
|
||||||
@ -229,33 +238,6 @@ func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
|
|||||||
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
|
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.
|
// LoadCommonSettings loads common configurations from a configuration provider.
|
||||||
func LoadCommonSettings() {
|
func LoadCommonSettings() {
|
||||||
loadCommonSettingsFrom(CfgProvider)
|
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
|
// LoadSettings initializes the settings for normal start up
|
||||||
func LoadSettings() {
|
func LoadSettings() {
|
||||||
loadDBSetting(CfgProvider)
|
loadDBSetting(CfgProvider)
|
||||||
|
@ -6,15 +6,13 @@ package setting
|
|||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage represents configuration of storages
|
// Storage represents configuration of storages
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
Type string
|
Type string
|
||||||
Path string
|
Path string
|
||||||
Section *ini.Section
|
Section ConfigSection
|
||||||
ServeDirect bool
|
ServeDirect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +28,7 @@ func (s *Storage) MapTo(v interface{}) error {
|
|||||||
return nil
|
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"
|
const sectionName = "storage"
|
||||||
sec := rootCfg.Section(sectionName)
|
sec := rootCfg.Section(sectionName)
|
||||||
|
|
||||||
@ -52,7 +50,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section
|
|||||||
storage.Section = targetSec
|
storage.Section = targetSec
|
||||||
storage.Type = typ
|
storage.Type = typ
|
||||||
|
|
||||||
overrides := make([]*ini.Section, 0, 3)
|
overrides := make([]ConfigSection, 0, 3)
|
||||||
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
|
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
overrides = append(overrides, nameSec)
|
overrides = append(overrides, nameSec)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getStorageCustomType(t *testing.T) {
|
func Test_getStorageCustomType(t *testing.T) {
|
||||||
@ -20,7 +19,7 @@ MINIO_BUCKET = gitea-attachment
|
|||||||
STORAGE_TYPE = minio
|
STORAGE_TYPE = minio
|
||||||
MINIO_ENDPOINT = my_minio:9000
|
MINIO_ENDPOINT = my_minio:9000
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -43,7 +42,7 @@ MINIO_BUCKET = gitea-attachment
|
|||||||
[storage.minio]
|
[storage.minio]
|
||||||
MINIO_BUCKET = gitea
|
MINIO_BUCKET = gitea
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -65,7 +64,7 @@ MINIO_BUCKET = gitea-minio
|
|||||||
[storage]
|
[storage]
|
||||||
MINIO_BUCKET = gitea
|
MINIO_BUCKET = gitea
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -88,7 +87,7 @@ MINIO_BUCKET = gitea
|
|||||||
[storage]
|
[storage]
|
||||||
STORAGE_TYPE = local
|
STORAGE_TYPE = local
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -100,7 +99,7 @@ STORAGE_TYPE = local
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_getStorageGetDefaults(t *testing.T) {
|
func Test_getStorageGetDefaults(t *testing.T) {
|
||||||
cfg, err := ini.Load([]byte(""))
|
cfg, err := newConfigProviderFromData("")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -121,7 +120,7 @@ MINIO_BUCKET = gitea-attachment
|
|||||||
[storage]
|
[storage]
|
||||||
MINIO_BUCKET = gitea-storage
|
MINIO_BUCKET = gitea-storage
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -155,7 +154,7 @@ STORAGE_TYPE = lfs
|
|||||||
[storage.lfs]
|
[storage.lfs]
|
||||||
MINIO_BUCKET = gitea-storage
|
MINIO_BUCKET = gitea-storage
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -179,7 +178,7 @@ func Test_getStorageInheritStorageType(t *testing.T) {
|
|||||||
[storage]
|
[storage]
|
||||||
STORAGE_TYPE = minio
|
STORAGE_TYPE = minio
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
@ -194,7 +193,7 @@ func Test_getStorageInheritNameSectionType(t *testing.T) {
|
|||||||
[storage.attachments]
|
[storage.attachments]
|
||||||
STORAGE_TYPE = minio
|
STORAGE_TYPE = minio
|
||||||
`
|
`
|
||||||
cfg, err := ini.Load([]byte(iniStr))
|
cfg, err := newConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
sec := cfg.Section("attachment")
|
||||||
|
@ -7,19 +7,54 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DateTime renders an absolute time HTML given a time as a string
|
// DateTime renders an absolute time HTML element by datetime.
|
||||||
func DateTime(format, datetime, fallback string) template.HTML {
|
func DateTime(format string, datetime any) template.HTML {
|
||||||
datetimeEscaped := html.EscapeString(datetime)
|
if p, ok := datetime.(*time.Time); ok {
|
||||||
fallbackEscaped := html.EscapeString(fallback)
|
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 {
|
switch format {
|
||||||
case "short":
|
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":
|
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":
|
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 {
|
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
|
// document: https://github.com/github/relative-time-element
|
||||||
attrs := `tense="past"`
|
attrs := `tense="past"`
|
||||||
|
@ -462,6 +462,7 @@ team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προορι
|
|||||||
[modal]
|
[modal]
|
||||||
yes=Ναι
|
yes=Ναι
|
||||||
no=Όχι
|
no=Όχι
|
||||||
|
confirm=Επιβεβαίωση
|
||||||
cancel=Ακύρωση
|
cancel=Ακύρωση
|
||||||
modify=Ενημέρωση
|
modify=Ενημέρωση
|
||||||
|
|
||||||
@ -1622,7 +1623,10 @@ pulls.tab_files=Αρχεία Με Αλλαγές
|
|||||||
pulls.reopen_to_merge=Παρακαλώ ανοίξτε ξανά αυτό το pull request για να εκτελέσετε μια συγχώνευση.
|
pulls.reopen_to_merge=Παρακαλώ ανοίξτε ξανά αυτό το pull request για να εκτελέσετε μια συγχώνευση.
|
||||||
pulls.cant_reopen_deleted_branch=Αυτό το pull request δεν μπορεί να ανοίξει ξανά επειδή ο κλάδος διαγράφηκε.
|
pulls.cant_reopen_deleted_branch=Αυτό το pull request δεν μπορεί να ανοίξει ξανά επειδή ο κλάδος διαγράφηκε.
|
||||||
pulls.merged=Συγχωνευμένο
|
pulls.merged=Συγχωνευμένο
|
||||||
|
pulls.merged_success=Το pull request συγχωνεύτηκε και έκλεισε επιτυχώς
|
||||||
|
pulls.closed=Το pull request έκλεισε
|
||||||
pulls.manually_merged=Συγχωνεύτηκαν χειροκίνητα
|
pulls.manually_merged=Συγχωνεύτηκαν χειροκίνητα
|
||||||
|
pulls.merged_info_text=Ο κλάδος %s μπορεί τώρα να διαγραφεί.
|
||||||
pulls.is_closed=Το pull request έχει κλείσει.
|
pulls.is_closed=Το pull request έχει κλείσει.
|
||||||
pulls.title_wip_desc=`<a href="#">Ξεκινήστε τον τίτλο με <strong>%s</strong></a> για να αποτρέψετε την τυχαία συγχώνευση του pull request.`
|
pulls.title_wip_desc=`<a href="#">Ξεκινήστε τον τίτλο με <strong>%s</strong></a> για να αποτρέψετε την τυχαία συγχώνευση του pull request.`
|
||||||
pulls.cannot_merge_work_in_progress=Αυτό το 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.
|
need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork.
|
||||||
|
|
||||||
[projects]
|
[projects]
|
||||||
|
type-1.display_name=Ατομικό Έργο
|
||||||
|
type-2.display_name=Έργο Αποθετηρίου
|
||||||
|
type-3.display_name=Έργο Οργανισμού
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ show_only_public=只显示公开的
|
|||||||
issues.in_your_repos=在您的仓库中
|
issues.in_your_repos=在您的仓库中
|
||||||
|
|
||||||
[explore]
|
[explore]
|
||||||
repos=仓库管理
|
repos=仓库
|
||||||
users=用户
|
users=用户
|
||||||
organizations=组织管理
|
organizations=组织管理
|
||||||
search=搜索
|
search=搜索
|
||||||
|
@ -966,7 +966,7 @@ func SignInOAuthCallback(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm),
|
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm),
|
||||||
}
|
}
|
||||||
|
|
||||||
source := authSource.Cfg.(*oauth2.Source)
|
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/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
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
|
// httpBase implementation git smart HTTP protocol
|
||||||
func httpBase(ctx *context.Context) (h *serviceHandler) {
|
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")
|
username := ctx.Params(":username")
|
||||||
reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
|
reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tplReleases base.TplName = "repo/release/list"
|
tplReleasesList base.TplName = "repo/release/list"
|
||||||
tplReleaseNew base.TplName = "repo/release/new"
|
tplReleaseNew base.TplName = "repo/release/new"
|
||||||
|
tplTagsList base.TplName = "repo/tag/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
// calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
|
// 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
|
// Releases render releases list page
|
||||||
func Releases(ctx *context.Context) {
|
func Releases(ctx *context.Context) {
|
||||||
|
ctx.Data["PageIsReleaseList"] = true
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||||
releasesOrTags(ctx, false)
|
releasesOrTags(ctx, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagsList render tags list page
|
// TagsList render tags list page
|
||||||
func TagsList(ctx *context.Context) {
|
func TagsList(ctx *context.Context) {
|
||||||
|
ctx.Data["PageIsTagList"] = true
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
|
||||||
releasesOrTags(ctx, true)
|
releasesOrTags(ctx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func releasesOrTags(ctx *context.Context, isTagList bool) {
|
func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||||
ctx.Data["PageIsReleaseList"] = true
|
|
||||||
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
|
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
|
||||||
ctx.Data["IsViewBranch"] = false
|
ctx.Data["IsViewBranch"] = false
|
||||||
ctx.Data["IsViewTag"] = true
|
ctx.Data["IsViewTag"] = true
|
||||||
@ -75,14 +79,6 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
|||||||
ctx.Data["CanCreateBranch"] = false
|
ctx.Data["CanCreateBranch"] = false
|
||||||
ctx.Data["HideBranchesInDropdown"] = true
|
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{
|
listOptions := db.ListOptions{
|
||||||
Page: ctx.FormInt("page"),
|
Page: ctx.FormInt("page"),
|
||||||
PageSize: ctx.FormInt("limit"),
|
PageSize: ctx.FormInt("limit"),
|
||||||
@ -196,7 +192,11 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
|||||||
pager.SetDefaultParams(ctx)
|
pager.SetDefaultParams(ctx)
|
||||||
ctx.Data["Page"] = pager
|
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
|
// 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.Data["Releases"] = []*repo_model.Release{release}
|
||||||
ctx.HTML(http.StatusOK, tplReleases)
|
ctx.HTML(http.StatusOK, tplReleasesList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestRelease redirects to the latest release
|
// LatestRelease redirects to the latest release
|
||||||
|
@ -710,7 +710,14 @@ func Home(ctx *context.Context) {
|
|||||||
if setting.Other.EnableFeed {
|
if setting.Other.EnableFeed {
|
||||||
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||||
if isFeed {
|
if isFeed {
|
||||||
|
switch {
|
||||||
|
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
|
||||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, 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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1198,7 +1198,7 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
|
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
|
||||||
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
|
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
|
||||||
|
|
||||||
// Releases
|
// Tags
|
||||||
m.Group("/{username}/{reponame}", func() {
|
m.Group("/{username}/{reponame}", func() {
|
||||||
m.Group("/tags", func() {
|
m.Group("/tags", func() {
|
||||||
m.Get("", repo.TagsList)
|
m.Get("", repo.TagsList)
|
||||||
@ -1207,6 +1207,12 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
}, func(ctx *context.Context) {
|
}, func(ctx *context.Context) {
|
||||||
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
|
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
|
||||||
}, repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
|
}, 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.Group("/releases", func() {
|
||||||
m.Get("/", repo.Releases)
|
m.Get("/", repo.Releases)
|
||||||
m.Get("/tag/*", repo.SingleRelease)
|
m.Get("/tag/*", repo.SingleRelease)
|
||||||
@ -1224,8 +1230,6 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Post("/attachments", repo.UploadReleaseAttachment)
|
m.Post("/attachments", repo.UploadReleaseAttachment)
|
||||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||||
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
|
}, 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.Group("/releases", func() {
|
||||||
m.Get("/edit/*", repo.EditRelease)
|
m.Get("/edit/*", repo.EditRelease)
|
||||||
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
|
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)
|
m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
|
||||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
|
}, 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.Group("/src", func() {
|
||||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
|
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
|
||||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), 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/{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}}.pack", repo.GetPackFile)
|
||||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
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 *****
|
// ***** END: Repository *****
|
||||||
|
@ -18,14 +18,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/minio/sha256-simd"
|
"github.com/minio/sha256-simd"
|
||||||
ini "gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrInvalidAlgorithmType represents an invalid algorithm error.
|
// ErrInvalidAlgorithmType represents an invalid algorithm error.
|
||||||
@ -316,8 +314,7 @@ func InitSigningKey() error {
|
|||||||
case "HS384":
|
case "HS384":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "HS512":
|
case "HS512":
|
||||||
key, err = loadOrCreateSymmetricKey()
|
key, err = loadSymmetricKey()
|
||||||
|
|
||||||
case "RS256":
|
case "RS256":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "RS384":
|
case "RS384":
|
||||||
@ -332,7 +329,6 @@ func InitSigningKey() error {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case "EdDSA":
|
case "EdDSA":
|
||||||
key, err = loadOrCreateAsymmetricKey()
|
key, err = loadOrCreateAsymmetricKey()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm}
|
return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm}
|
||||||
}
|
}
|
||||||
@ -351,22 +347,16 @@ func InitSigningKey() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadOrCreateSymmetricKey checks if the configured secret is valid.
|
// loadSymmetricKey checks if the configured secret is valid.
|
||||||
// If it is not valid a new secret is created and saved in the configuration file.
|
// If it is not valid, it will return an error.
|
||||||
func loadOrCreateSymmetricKey() (interface{}, error) {
|
func loadSymmetricKey() (interface{}, error) {
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
|
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
|
||||||
if err != nil || n != 32 {
|
|
||||||
key, err = generate.NewJwtSecret()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error generating JWT secret: %v", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if n != 32 {
|
||||||
setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) {
|
return nil, fmt.Errorf("JWT secret must be 32 bytes long")
|
||||||
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
|
|
||||||
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, nil
|
return key, nil
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
|
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
|
||||||
<td>{{.TypeName}}</td>
|
<td>{{.TypeName}}</td>
|
||||||
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||||
<td>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</td>
|
<td>{{DateTime "short" .UpdatedUnix}}</td>
|
||||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{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><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>{{$.locale.Tr (printf "admin.dashboard.%s" .Name)}}</td>
|
||||||
<td>{{.Spec}}</td>
|
<td>{{.Spec}}</td>
|
||||||
<td>{{DateTime "full" (DateFmtLong .Next) (DateFmtLong .Next)}}</td>
|
<td>{{DateTime "full" (DateFmtLong .Next)}}</td>
|
||||||
<td>{{if gt .Prev.Year 1}}{{DateTime "full" (DateFmtLong .Prev) (DateFmtLong .Prev)}}{{else}}N/A{{end}}</td>
|
<td>{{if gt .Prev.Year 1}}{{DateTime "full" .Prev}}{{else}}N/A{{end}}</td>
|
||||||
<td>{{.ExecTimes}}</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>
|
<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>
|
</tr>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<td>{{.ID}}</td>
|
<td>{{.ID}}</td>
|
||||||
<td>{{$.locale.Tr .TrStr}}</td>
|
<td>{{$.locale.Tr .TrStr}}</td>
|
||||||
<td class="view-detail"><span class="notice-description text truncate">{{.Description}}</span></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>
|
<td><a href="#">{{svg "octicon-note" 16 "view-detail"}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
<td>{{.NumTeams}}</td>
|
<td>{{.NumTeams}}</td>
|
||||||
<td>{{.NumMembers}}</td>
|
<td>{{.NumMembers}}</td>
|
||||||
<td>{{.NumRepos}}</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>
|
<td><a href="{{.OrganisationLink}}/settings">{{svg "octicon-pencil"}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{FileSize .CalculateBlobSize}}</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>
|
<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>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -155,8 +155,8 @@
|
|||||||
{{range .Queue.Workers}}
|
{{range .Queue.Workers}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{.Workers}}{{if .IsFlusher}}<span title="{{$.locale.Tr "admin.monitor.queue.flush"}}">{{svg "octicon-sync"}}</span>{{end}}</td>
|
<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>{{DateTime "full" .Start}}</td>
|
||||||
<td>{{if .HasTimeout}}{{DateTime "full" (DateFmtLong .Timeout) (DateFmtLong .Timeout)}}{{else}}-{{end}}</td>
|
<td>{{if .HasTimeout}}{{DateTime "full" .Timeout}}{{else}}-{{end}}</td>
|
||||||
<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>
|
<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>
|
</td>
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
<td>{{.NumForks}}</td>
|
<td>{{.NumForks}}</td>
|
||||||
<td>{{.NumIssues}}</td>
|
<td>{{.NumIssues}}</td>
|
||||||
<td>{{FileSize .Size}}</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>
|
<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>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -91,9 +91,9 @@
|
|||||||
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
<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>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||||
<td>{{.NumRepos}}</td>
|
<td>{{.NumRepos}}</td>
|
||||||
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
|
<td>{{DateTime "short" .CreatedUnix}}</td>
|
||||||
{{if .LastLoginUnix}}
|
{{if .LastLoginUnix}}
|
||||||
<td>{{DateTime "short" .LastLoginUnix.FormatLong .LastLoginUnix.FormatShort}}</td>
|
<td>{{DateTime "short" .LastLoginUnix}}</td>
|
||||||
{{else}}
|
{{else}}
|
||||||
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
|
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
{{svg "octicon-link"}}
|
{{svg "octicon-link"}}
|
||||||
<a href="{{.Website}}" rel="nofollow">{{.Website}}</a>
|
<a href="{{.Website}}" rel="nofollow">{{.Website}}</a>
|
||||||
{{end}}
|
{{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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
{{svg "octicon-mail"}}
|
{{svg "octicon-mail"}}
|
||||||
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
|
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
|
||||||
{{end}}
|
{{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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<td><a href="{{.FullWebLink}}">{{.Version.Version}}</a></td>
|
<td><a href="{{.FullWebLink}}">{{.Version.Version}}</a></td>
|
||||||
<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
|
<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
|
||||||
<td>{{FileSize .CalculateBlobSize}}</td>
|
<td>{{FileSize .CalculateBlobSize}}</td>
|
||||||
<td>{{DateTime "short" .Version.CreatedUnix.FormatLong .Version.CreatedUnix.FormatShort}}</td>
|
<td>{{DateTime "short" .Version.CreatedUnix}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
{{range .LatestVersions}}
|
{{range .LatestVersions}}
|
||||||
<div class="item gt-df">
|
<div class="item gt-df">
|
||||||
<a class="gt-f1" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
|
<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>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div role="main" aria-label="{{.Title}}" class="page-content repository commits">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository commits">
|
||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container">
|
<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">
|
<div class="ui right">
|
||||||
<!-- Period -->
|
<!-- Period -->
|
||||||
<div class="ui floating dropdown jump filter">
|
<div class="ui floating dropdown jump filter">
|
||||||
|
@ -26,6 +26,11 @@
|
|||||||
{{svg "octicon-git-branch"}}
|
{{svg "octicon-git-branch"}}
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{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}}
|
{{if not $.DisableDownloadSourceArchives}}
|
||||||
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranch)}}">
|
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranch)}}">
|
||||||
{{svg "octicon-download"}}
|
{{svg "octicon-download"}}
|
||||||
@ -113,6 +118,11 @@
|
|||||||
{{svg "octicon-git-branch"}}
|
{{svg "octicon-git-branch"}}
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{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)}}
|
{{if and (not .IsDeleted) (not $.DisableDownloadSourceArchives)}}
|
||||||
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.Name)}}">
|
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.Name)}}">
|
||||||
{{svg "octicon-download"}}
|
{{svg "octicon-download"}}
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
'branches': {{.root.Branches}},
|
'branches': {{.root.Branches}},
|
||||||
'tags': {{.root.Tags}},
|
'tags': {{.root.Tags}},
|
||||||
'defaultBranch': {{$defaultBranch}},
|
'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}}',
|
'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}}',
|
'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}}',
|
'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>
|
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{template "repo/clone_script" .}}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="ui segment center">
|
<div class="ui segment center">
|
||||||
{{.locale.Tr "repo.empty_message"}}
|
{{.locale.Tr "repo.empty_message"}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{template "repo/clone_script" .}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,12 +63,13 @@
|
|||||||
{{$n := len .TreeNames}}
|
{{$n := len .TreeNames}}
|
||||||
{{$l := Eval $n "-" 1}}
|
{{$l := Eval $n "-" 1}}
|
||||||
<!-- If home page, show new pr. If not, show breadcrumb -->
|
<!-- If home page, show new pr. If not, show breadcrumb -->
|
||||||
{{if eq $n 0}}
|
{{if and (eq $n 0) .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
|
||||||
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
|
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{CompareLink .BaseRepo .Repository .BranchName}}"
|
||||||
<a href="{{CompareLink .BaseRepo .Repository .BranchName}}">
|
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}">
|
||||||
<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>
|
{{svg "octicon-git-pull-request"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if eq $n 0}}
|
||||||
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{.locale.Tr "repo.find_file.go_to_file"}}</a>
|
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{.locale.Tr "repo.find_file.go_to_file"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar"}}
|
{{svg "octicon-calendar"}}
|
||||||
{{if .Milestone.DeadlineString}}
|
{{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}}
|
{{else}}
|
||||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar"}}
|
{{svg "octicon-calendar"}}
|
||||||
{{if .DeadlineString}}
|
{{if .DeadlineString}}
|
||||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
|
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -295,7 +295,7 @@
|
|||||||
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 17}}
|
{{else if eq .Type 17}}
|
||||||
@ -305,8 +305,8 @@
|
|||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{template "shared/user/authorlink" .Poster}}
|
||||||
{{$parsedDeadline := .Content | ParseDeadline}}
|
{{$parsedDeadline := .Content | ParseDeadline}}
|
||||||
{{$from := DateTime "long" (index $parsedDeadline 1) (index $parsedDeadline 1)}}
|
{{$from := DateTime "long" (index $parsedDeadline 1)}}
|
||||||
{{$to := DateTime "long" (index $parsedDeadline 0) (index $parsedDeadline 0)}}
|
{{$to := DateTime "long" (index $parsedDeadline 0)}}
|
||||||
{{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}}
|
{{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -316,7 +316,7 @@
|
|||||||
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 19}}
|
{{else if eq .Type 19}}
|
||||||
|
@ -385,7 +385,7 @@
|
|||||||
<div class="gt-df gt-sb gt-ac">
|
<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}}>
|
<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"}}
|
{{svg "octicon-calendar" 16 "gt-mr-3"}}
|
||||||
{{DateTime "long" .Issue.DeadlineUnix.FormatDate .Issue.DeadlineUnix.FormatDate}}
|
{{DateTime "long" .Issue.DeadlineUnix}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
|
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
|
||||||
|
@ -1,73 +1,16 @@
|
|||||||
{{template "base/head" .}}
|
{{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" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<h2 class="ui compact small menu header">
|
{{template "repo/sub_menu_release_tag" .}}
|
||||||
{{if .Permission.CanRead $.UnitTypeReleases}}
|
|
||||||
<a class="{{if (and (not .PageIsSingleTag) (not .PageIsTagList))}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a>
|
{{if .CanCreateRelease}}
|
||||||
{{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))}}
|
|
||||||
<a class="ui right small green button" href="{{$.RepoLink}}/releases/new">
|
<a class="ui right small green button" href="{{$.RepoLink}}/releases/new">
|
||||||
{{.locale.Tr "repo.release.new_release"}}
|
{{.locale.Tr "repo.release.new_release"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{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">
|
<ul id="release-list">
|
||||||
{{range $idx, $release := .Releases}}
|
{{range $idx, $release := .Releases}}
|
||||||
<li class="ui grid">
|
<li class="ui grid">
|
||||||
@ -178,7 +121,7 @@
|
|||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
{{end}}
|
|
||||||
{{template "base/paginate" .}}
|
{{template "base/paginate" .}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
{{.Fingerprint}}
|
{{.Fingerprint}}
|
||||||
</div>
|
</div>
|
||||||
<div class="activity meta">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td>
|
<td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td>
|
||||||
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.pull"}}</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">
|
<td class="right aligned">
|
||||||
<form method="post" style="display: inline-block">
|
<form method="post" style="display: inline-block">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
@ -167,7 +167,7 @@
|
|||||||
{{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}}
|
{{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}}
|
||||||
<td class="gt-word-break">{{$address.Address}}</td>
|
<td class="gt-word-break">{{$address.Address}}</td>
|
||||||
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.push"}}</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">
|
<td class="right aligned">
|
||||||
<form method="post" style="display: inline-block">
|
<form method="post" style="display: inline-block">
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<a href="{{.RepoLink}}/branches">{{svg "octicon-git-branch"}} <b>{{.BranchesCount}}</b> {{.locale.TrN .BranchesCount "repo.branch" "repo.branches"}}</a>
|
<a href="{{.RepoLink}}/branches">{{svg "octicon-git-branch"}} <b>{{.BranchesCount}}</b> {{.locale.TrN .BranchesCount "repo.branch" "repo.branches"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{if $.Permission.CanRead $.UnitTypeCode}}
|
{{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>
|
<a href="{{.RepoLink}}/tags">{{svg "octicon-tag"}} <b>{{.NumTags}}</b> {{.locale.TrN .NumTags "repo.tag" "repo.tags"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{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}}
|
{{else if .Location}}
|
||||||
{{svg "octicon-location"}} {{.Location}}
|
{{svg "octicon-location"}} {{.Location}}
|
||||||
{{else}}
|
{{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}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -42,6 +42,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<a download href="{{$.RawFileLink}}"><span class="btn-octicon" data-tooltip-content="{{.locale.Tr "repo.download_file"}}">{{svg "octicon-download"}}</span></a>
|
<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>
|
<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 .Repository.CanEnableEditor}}
|
||||||
{{if .CanEditFile}}
|
{{if .CanEditFile}}
|
||||||
<a href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .TreePath}}"><span class="btn-octicon" data-tooltip-content="{{.EditFileTooltip}}">{{svg "octicon-pencil"}}</span></a>
|
<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 class="due-date" data-tooltip-content="{{$.locale.Tr "repo.issues.due_date"}}">
|
||||||
<span{{if .IsOverdue}} class="overdue"{{end}}>
|
<span{{if .IsOverdue}} class="overdue"{{end}}>
|
||||||
{{svg "octicon-calendar" 14 "gt-mr-2"}}
|
{{svg "octicon-calendar" 14 "gt-mr-2"}}
|
||||||
{{DateTime "short" .DeadlineUnix.FormatDate .DeadlineUnix.FormatShort}}
|
{{DateTime "short" .DeadlineUnix}}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<div class="ui container gt-mt-5">
|
<div class="ui container gt-mt-5">
|
||||||
{{if .ErrorMsg}}
|
{{if .ErrorMsg}}
|
||||||
<p>{{.locale.Tr "error.occurred"}}:</p>
|
<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}}
|
{{end}}
|
||||||
|
|
||||||
<div class="center gt-mt-5">
|
<div class="center gt-mt-5">
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar"}}
|
{{svg "octicon-calendar"}}
|
||||||
{{if .DeadlineString}}
|
{{if .DeadlineString}}
|
||||||
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
|
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
{{$.locale.Tr "repo.milestones.no_due_date"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -44,8 +44,10 @@ data.teamId = {{.Team.ID}};
|
|||||||
{{if not .ContextUser.IsOrganization}}
|
{{if not .ContextUser.IsOrganization}}
|
||||||
data.organizations = [{{range .Orgs}}{'name': {{.Name}}, 'num_repos': {{.NumRepos}}},{{end}}];
|
data.organizations = [{{range .Orgs}}{'name': {{.Name}}, 'num_repos': {{.NumRepos}}},{{end}}];
|
||||||
data.isOrganization = false;
|
data.isOrganization = false;
|
||||||
data.organizationsTotalCount = {{.UserOrgsCount}}
|
data.organizationsTotalCount = {{.UserOrgsCount}};
|
||||||
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}}
|
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}};
|
||||||
|
{{else}}
|
||||||
|
data.organizationId = {{.ContextUser.ID}};
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
window.config.pageData.dashboardRepoList = data;
|
window.config.pageData.dashboardRepoList = data;
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{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}}
|
{{if and .Orgs .HasOrgsVisible}}
|
||||||
<li>
|
<li>
|
||||||
<ul class="user-orgs">
|
<ul class="user-orgs">
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<div class="activity meta">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<strong>{{$grant.Application.Name}}</strong>
|
<strong>{{$grant.Application.Name}}</strong>
|
||||||
<div class="activity meta">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,9 +68,9 @@
|
|||||||
<b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
|
<b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="activity meta">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<strong>{{.Name}}</strong>
|
<strong>{{.Name}}</strong>
|
||||||
<div class="activity meta">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{{.Fingerprint}}
|
{{.Fingerprint}}
|
||||||
</div>
|
</div>
|
||||||
<div class="activity meta">
|
<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>
|
</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
|
// Click the PR button to create a pull
|
||||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
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")
|
assert.True(t, exists, "The template has changed")
|
||||||
if branch != "master" {
|
if branch != "master" {
|
||||||
link = strings.Replace(link, ":master", ":"+branch, 1)
|
link = strings.Replace(link, ":master", ":"+branch, 1)
|
||||||
|
@ -864,6 +864,9 @@ a.label,
|
|||||||
margin-top: -0.25rem;
|
margin-top: -0.25rem;
|
||||||
margin-bottom: -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 {
|
.ui.selection.dropdown .menu > .item {
|
||||||
border-color: var(--color-secondary);
|
border-color: var(--color-secondary);
|
||||||
|
@ -23,15 +23,6 @@ Gitea's private styles use `g-` prefix.
|
|||||||
.gt-h-100 { height: 100% !important; }
|
.gt-h-100 { height: 100% !important; }
|
||||||
.gt-br-0 { border-radius: 0 !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 {
|
.gt-mono {
|
||||||
font-family: var(--fonts-monospace) !important;
|
font-family: var(--fonts-monospace) !important;
|
||||||
font-size: .95em !important; /* compensate for monospace fonts being usually slightly larger */
|
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;
|
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-w-screen { width: 100vw !important; }
|
||||||
.gt-h-screen { height: 100vh !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-4 { row-gap: 1rem !important; }
|
||||||
.gt-gap-y-5 { row-gap: 2rem !important; }
|
.gt-gap-y-5 { row-gap: 2rem !important; }
|
||||||
|
|
||||||
.gt-content-center { align-content: center !important; }
|
|
||||||
|
|
||||||
.gt-shrink-0 { flex-shrink: 0 !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) {
|
@media (max-width: 767px) {
|
||||||
.gt-db-small { display: block !important; }
|
.gt-db-small { display: block !important; }
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
@import "./install.css";
|
@import "./install.css";
|
||||||
@import "./form.css";
|
@import "./form.css";
|
||||||
@import "./repository.css";
|
@import "./repository.css";
|
||||||
|
@import "./repository-release-tag.css";
|
||||||
@import "./editor.css";
|
@import "./editor.css";
|
||||||
@import "./editor-markdown.css";
|
@import "./editor-markdown.css";
|
||||||
@import "./organization.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
|
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 {
|
.repository.packages .empty {
|
||||||
padding-top: 70px;
|
padding-top: 70px;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{{ textMyRepos }}
|
{{ textMyRepos }}
|
||||||
<span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
|
<span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
|
||||||
</div>
|
</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"/>
|
<svg-icon name="octicon-plus"/>
|
||||||
<span class="sr-only">{{ textNewRepo }}</span>
|
<span class="sr-only">{{ textNewRepo }}</span>
|
||||||
</a>
|
</a>
|
||||||
@ -74,7 +74,7 @@
|
|||||||
<a class="repo-list-link gt-df gt-ac gt-sb" :href="repo.link">
|
<a class="repo-list-link gt-df gt-ac gt-sb" :href="repo.link">
|
||||||
<div class="item-name gt-df gt-ac gt-f1">
|
<div class="item-name gt-df gt-ac gt-f1">
|
||||||
<svg-icon :name="repoIcon(repo)" :size="16" class-name="gt-mr-2"/>
|
<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">
|
<span v-if="repo.archived">
|
||||||
<svg-icon name="octicon-archive" :size="16" class-name="gt-ml-2"/>
|
<svg-icon name="octicon-archive" :size="16" class-name="gt-ml-2"/>
|
||||||
</span>
|
</span>
|
||||||
@ -199,6 +199,7 @@ const sfc = {
|
|||||||
isOrganization: true,
|
isOrganization: true,
|
||||||
canCreateOrganization: false,
|
canCreateOrganization: false,
|
||||||
organizationsTotalCount: 0,
|
organizationsTotalCount: 0,
|
||||||
|
organizationId: 0,
|
||||||
|
|
||||||
subUrl: appSubUrl,
|
subUrl: appSubUrl,
|
||||||
...pageData.dashboardRepoList,
|
...pageData.dashboardRepoList,
|
||||||
|
@ -39,6 +39,9 @@
|
|||||||
<div class="scrolling menu" ref="scrollContainer">
|
<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">
|
<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 }}
|
{{ 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>
|
||||||
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
|
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
|
||||||
<a href="#" @click="createNewBranch()">
|
<a href="#" @click="createNewBranch()">
|
||||||
@ -291,3 +294,12 @@ export function initRepoBranchTagSelector(selector) {
|
|||||||
|
|
||||||
export default sfc; // activate IDE's Vue plugin
|
export default sfc; // activate IDE's Vue plugin
|
||||||
</script>
|
</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 octiconOrganization from '../../public/img/svg/octicon-organization.svg';
|
||||||
import octiconTag from '../../public/img/svg/octicon-tag.svg';
|
import octiconTag from '../../public/img/svg/octicon-tag.svg';
|
||||||
import octiconGitBranch from '../../public/img/svg/octicon-git-branch.svg';
|
import octiconGitBranch from '../../public/img/svg/octicon-git-branch.svg';
|
||||||
|
import octiconRss from '../../public/img/svg/octicon-rss.svg';
|
||||||
|
|
||||||
const svgs = {
|
const svgs = {
|
||||||
'octicon-blocked': octiconBlocked,
|
'octicon-blocked': octiconBlocked,
|
||||||
@ -89,6 +90,7 @@ const svgs = {
|
|||||||
'octicon-organization': octiconOrganization,
|
'octicon-organization': octiconOrganization,
|
||||||
'octicon-tag': octiconTag,
|
'octicon-tag': octiconTag,
|
||||||
'octicon-git-branch': octiconGitBranch,
|
'octicon-git-branch': octiconGitBranch,
|
||||||
|
'octicon-rss': octiconRss,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: use a more general approach to access SVG icons.
|
// TODO: use a more general approach to access SVG icons.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user