Compare commits

...

16 Commits

Author SHA1 Message Date
Yarden Shoham
f2f0fb43e0
Fix broken clone script on an empty archived repo (#24339)
I made it render the script even if the repo is archived

- Fixes #24324

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
2023-04-25 23:28:37 -04:00
wxiaoguang
59d060622d
Improve RSS (#24335)
Follow  #22719

### Major changes

1. `ServerError` doesn't do format, so remove the `%s`
2. Simplify `RenderBranchFeed` (slightly)
3. Remove unused `BranchFeedRSS`
4. Make `feed.RenderBranchFeed` respect `EnableFeed` config
5. Make `RepoBranchTagSelector.vue` respect `EnableFeed` setting,
otherwise there is always RSS icon
6. The `(branchURLPrefix + item.url).replace('src', 'rss')` doesn't seem
right for all cases, for example, the string `src` could appear in
`branchURLPrefix`, so we need a separate `rssURLPrefix`
7. The `<a>` in Vue menu needs `@click.stop`, otherwise the menu itself
would be triggered at the same time
8. Change `<a><button></button></a>` to `<a role=button>`
9. Use `{{PathEscapeSegments .TreePath}}` instead of `{{range $i, $v :=
.TreeNames}}/{{$v}}{{end}}`


Screenshot of changed parts:

<details>


![image](https://user-images.githubusercontent.com/2114189/234315538-66603694-9093-48a8-af33-83575fd7a018.png)


![image](https://user-images.githubusercontent.com/2114189/234315786-f1efa60b-012e-490b-8ce2-d448dc6fe5c9.png)


![image](https://user-images.githubusercontent.com/2114189/234334941-446941bc-1baa-4256-8850-ccc439476cda.png)

</details>


### Other thoughts

Should we remove the RSS icon from the branch dropdown list? It seems
too complex for a list UI, and users already have the chance to get the
RSS feed URL from "branches" page.

---------

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: silverwind <me@silverwind.io>
2023-04-25 22:53:44 -04:00
yp05327
5f21e0f8eb
Automatically select the org when click create repo from org dashboard (#24325)
![image](https://user-images.githubusercontent.com/18380374/234209941-d661b07f-e963-427b-a673-78c46043a792.png)

In org dashboard, the create repo link will be `repo/create?org={orgId}`
2023-04-25 22:25:29 -04:00
techknowlogick
2ec2d06531
on schedule 2023-04-25 21:38:49 -04:00
techknowlogick
abc5f8c235
on schedule 2023-04-25 21:38:33 -04:00
techknowlogick
61a73edbf3
switch to use Actions from drone for cron (#24314)
Move drone cron pipelines to (GitHub) Actions. As these are mostly
compatible with Gitea Actions, when we start to dogfood, these will
already be migrated.
2023-04-25 21:32:49 -04:00
GiteaBot
fa86a1b74a [skip ci] Updated translations via Crowdin 2023-04-26 00:07:58 +00:00
silverwind
ee6fa8d633
Restore bold on repolist (#24337)
Looking at it again, it does look a bit "odd" without bold, so revert
the repolist change done in
https://github.com/go-gitea/gitea/pull/24307.

<img width="141" alt="image"
src="https://user-images.githubusercontent.com/115237/234331813-c6e2402f-e099-43b3-aed6-46a0e24e3899.png">
2023-04-25 17:19:22 -04:00
wxiaoguang
0e8045d8ea
Fix template function DateTime (#24317)
Before, 500 error


![image](https://user-images.githubusercontent.com/2114189/234170176-403ffd1b-ec27-42be-bff9-86184dc8d74d.png)
2023-04-25 15:48:30 -04:00
wxiaoguang
9219534447
Fix incorrect CORS response in Http Git handler (#24303)
Use the general `cors.Handler` for CORS
2023-04-25 14:35:50 -04:00
TATHAGATA ROY
d5e93413bc
Updated upgrade script that is informing user that Gitea service has to be running in order to upgrade it (#24260)
Hello sir,
This pull request solves issue #23949
I have updated the upgrade script such that it will check if the Gitea
service is running, if it's not running then it will exit the process
and if it's running then the process will move further ahead.
Thank You.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
2023-04-25 13:19:58 -04:00
Zettat123
30c1cd9775
Add tags list for repos whose release setting is disabled (#23465)
Close #23427 

Co-Author: @wxiaoguang 

If a repo's release setting is enabled, the logic has't changed.
Clicking the "Tags" button will jump to `/{user}/{repo}/tags` and
`templates/repo/release/list.tmpl` template will be used.

<img
src="https://user-images.githubusercontent.com/15528715/224939362-bd8974fd-08b0-4f79-a114-3389d15847ca.png"
width="600px" />

If the release setting is disabled, clicking the "Tags" button will
still jump to `/{user}/{repo}/tags` but a new template
`templates/repo/tag/list.tmpl` will be used.

<img
src="https://user-images.githubusercontent.com/15528715/233834564-74741e49-f4e9-47c8-ac12-e306642798dc.png"
width="600px" />

Since both templates above need to render the tags list, I moved the
tags list to a shared template located in
`templates/repo/tag/table.tmpl`.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-25 18:29:00 +02:00
Lunny Xiao
5cf7da63ee
Refactor config provider (#24245)
This PR introduces more abstract about `ConfigProvider` and hides more `ini` references.

---------

Co-authored-by: delvh <dev.lh@web.de>
2023-04-25 23:06:39 +08:00
jladbrook
56d4893b2a
Add RSS Feeds for branches and files (#22719)
Fix #22228 adding RSS feeds for branches and files. 

RSS feeds are accessed through:

* [gitea]/src/branch/{branch}.rss
* [gitea]/src/branch/{branch}/{file_name}.rss

No changes have been made to the UI to expose the feed urls for branches
and files.
2023-04-25 22:08:29 +08:00
wxiaoguang
f16b668980
Make SVG in dropdown menu have the same margin-right as IMG (#24316)
Fix #24226



![image](https://user-images.githubusercontent.com/2114189/234167393-0fe36a0d-29c9-46b6-b71d-52436f2b27f3.png)

Co-authored-by: silverwind <me@silverwind.io>
2023-04-25 07:34:37 -04:00
Gary Moon
ab42c139a2
Respect the REGISTER_MANUAL_CONFIRM setting when registering via OIDC (#24035)
This change prevents Gitea from bypassing the manual approval process
for newly registered users when OIDC is used.

- Resolves https://github.com/go-gitea/gitea/issues/23392

Signed-off-by: Gary Moon <gary@garymoon.net>
2023-04-25 14:40:48 +08:00
82 changed files with 945 additions and 606 deletions

View File

@ -773,109 +773,6 @@ steps:
- name: deps
path: /go
---
kind: pipeline
name: update_translations
platform:
os: linux
arch: arm64
trigger:
branch:
- main
event:
- cron
cron:
- update_translations
steps:
- name: download
image: jonasfranz/crowdin
pull: always
settings:
download: true
export_dir: options/locale/
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
- name: update
image: alpine:3.17
pull: always
commands:
- ./build/update-locales.sh
- name: push
image: appleboy/drone-git-push
pull: always
settings:
author_email: "teabot@gitea.io"
author_name: GiteaBot
branch: main
commit: true
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "git@github.com:go-gitea/gitea.git"
environment:
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
DRONE_COMMIT_AUTHOR: GiteaBot
GIT_PUSH_SSH_KEY:
from_secret: git_push_ssh_key
- name: upload_translations
image: jonasfranz/crowdin
pull: always
settings:
files:
locale_en-US.ini: options/locale/locale_en-US.ini
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
---
kind: pipeline
type: docker
name: update_gitignore_and_licenses
platform:
os: linux
arch: arm64
trigger:
branch:
- main
event:
- cron
cron:
- update_gitignore_and_licenses
steps:
- name: download
image: gitea/test_env:linux-1.20-amd64
pull: always
commands:
- timeout -s ABRT 40m make generate-license generate-gitignore
- name: push
image: appleboy/drone-git-push
pull: always
settings:
author_email: "teabot@gitea.io"
author_name: "GiteaBot"
branch: main
commit: true
commit_message: "[skip ci] Updated licenses and gitignores"
remote: "git@github.com:go-gitea/gitea.git"
environment:
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
DRONE_COMMIT_AUTHOR: "GiteaBot"
GIT_PUSH_SSH_KEY:
from_secret: git_push_ssh_key
---
kind: pipeline
type: docker

28
.github/workflows/cron-licenses.yml vendored Normal file
View 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
View 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

View File

@ -24,7 +24,6 @@ import (
"github.com/felixge/fgprof"
"github.com/urfave/cli"
ini "gopkg.in/ini.v1"
)
// PIDFile could be set from build tag
@ -223,9 +222,10 @@ func setPort(port string) error {
defaultLocalURL += ":" + setting.HTTPPort + "/"
// Save LOCAL_ROOT_URL if port changed
setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) {
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
})
setting.CfgProvider.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
if err := setting.CfgProvider.Save(); err != nil {
return fmt.Errorf("Failed to save config file: %v", err)
}
}
return nil
}

View File

@ -10,6 +10,15 @@
# upgrade.sh 1.15.10
# giteahome=/opt/gitea giteaconf=$giteahome/app.ini upgrade.sh
# Check if gitea service is running
if ! pidof gitea &> /dev/null; then
echo "Error: gitea is not running."
exit 1
fi
# Continue with rest of the script if gitea is running
echo "Gitea is running. Continuing with rest of script..."
# apply variables from environment
: "${giteabin:="/usr/local/bin/gitea"}"
: "${giteahome:="/var/lib/gitea"}"

View File

@ -16,7 +16,6 @@ import (
_ "code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
"gopkg.in/ini.v1"
)
func TestMain(m *testing.M) {
@ -27,7 +26,7 @@ func TestMain(m *testing.M) {
func TestBleveSearchIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.CfgProvider = ini.Empty()
setting.CfgProvider = setting.NewEmptyConfigProvider()
tmpIndexerDir := t.TempDir()

View File

@ -18,7 +18,6 @@ import (
_ "code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
"gopkg.in/ini.v1"
)
func TestMain(m *testing.M) {
@ -29,7 +28,7 @@ func TestMain(m *testing.M) {
func TestRepoStatsIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.CfgProvider = ini.Empty()
setting.CfgProvider = setting.NewEmptyConfigProvider()
setting.LoadQueueSettings()

View File

@ -4,20 +4,157 @@
package setting
import (
"fmt"
"os"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
ini "gopkg.in/ini.v1"
)
type ConfigSection interface {
Name() string
MapTo(interface{}) error
HasKey(key string) bool
NewKey(name, value string) (*ini.Key, error)
Key(key string) *ini.Key
Keys() []*ini.Key
ChildSections() []*ini.Section
}
// ConfigProvider represents a config provider
type ConfigProvider interface {
Section(section string) *ini.Section
NewSection(name string) (*ini.Section, error)
GetSection(name string) (*ini.Section, error)
Section(section string) ConfigSection
NewSection(name string) (ConfigSection, error)
GetSection(name string) (ConfigSection, error)
DeleteSection(name string) error
Save() error
}
type iniFileConfigProvider struct {
*ini.File
filepath string // the ini file path
newFile bool // whether the file has not existed previously
allowEmpty bool // whether not finding configuration files is allowed (only true for the tests)
}
// NewEmptyConfigProvider create a new empty config provider
func NewEmptyConfigProvider() ConfigProvider {
cp, _ := newConfigProviderFromData("")
return cp
}
// newConfigProviderFromData this function is only for testing
func newConfigProviderFromData(configContent string) (ConfigProvider, error) {
var cfg *ini.File
var err error
if configContent == "" {
cfg = ini.Empty()
} else {
cfg, err = ini.Load(strings.NewReader(configContent))
if err != nil {
return nil, err
}
}
cfg.NameMapper = ini.SnackCase
return &iniFileConfigProvider{
File: cfg,
newFile: true,
}, nil
}
// newConfigProviderFromFile load configuration from file.
// NOTE: do not print any log except error.
func newConfigProviderFromFile(customConf string, allowEmpty bool, extraConfig string) (*iniFileConfigProvider, error) {
cfg := ini.Empty()
newFile := true
if customConf != "" {
isFile, err := util.IsFile(customConf)
if err != nil {
return nil, fmt.Errorf("unable to check if %s is a file. Error: %v", customConf, err)
}
if isFile {
if err := cfg.Append(customConf); err != nil {
return nil, fmt.Errorf("failed to load custom conf '%s': %v", customConf, err)
}
newFile = false
}
}
if newFile && !allowEmpty {
return nil, fmt.Errorf("unable to find configuration file: %q, please ensure you are running in the correct environment or set the correct configuration file with -c", CustomConf)
}
if extraConfig != "" {
if err := cfg.Append([]byte(extraConfig)); err != nil {
return nil, fmt.Errorf("unable to append more config: %v", err)
}
}
cfg.NameMapper = ini.SnackCase
return &iniFileConfigProvider{
File: cfg,
filepath: customConf,
newFile: newFile,
allowEmpty: allowEmpty,
}, nil
}
func (p *iniFileConfigProvider) Section(section string) ConfigSection {
return p.File.Section(section)
}
func (p *iniFileConfigProvider) NewSection(name string) (ConfigSection, error) {
return p.File.NewSection(name)
}
func (p *iniFileConfigProvider) GetSection(name string) (ConfigSection, error) {
return p.File.GetSection(name)
}
func (p *iniFileConfigProvider) DeleteSection(name string) error {
p.File.DeleteSection(name)
return nil
}
// Save save the content into file
func (p *iniFileConfigProvider) Save() error {
if p.filepath == "" {
if !p.allowEmpty {
return fmt.Errorf("custom config path must not be empty")
}
return nil
}
if p.newFile {
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
return fmt.Errorf("failed to create '%s': %v", CustomConf, err)
}
}
if err := p.SaveTo(p.filepath); err != nil {
return fmt.Errorf("failed to save '%s': %v", p.filepath, err)
}
// Change permissions to be more restrictive
fi, err := os.Stat(CustomConf)
if err != nil {
return fmt.Errorf("failed to determine current conf file permissions: %v", err)
}
if fi.Mode().Perm() > 0o600 {
if err = os.Chmod(CustomConf, 0o600); err != nil {
log.Warn("Failed changing conf file permissions to -rw-------. Consider changing them manually.")
}
}
return nil
}
// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, …
var _ ConfigProvider = &ini.File{}
var _ ConfigProvider = &iniFileConfigProvider{}
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) {
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {

View File

@ -7,7 +7,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
ini "gopkg.in/ini.v1"
)
func Test_getCronSettings(t *testing.T) {
@ -23,11 +22,11 @@ func Test_getCronSettings(t *testing.T) {
iniStr := `
[cron.test]
Base = true
Second = white rabbit
Extend = true
BASE = true
SECOND = white rabbit
EXTEND = true
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
extended := &Extended{

View File

@ -9,8 +9,6 @@ import (
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
// LFS represents the configuration for Git LFS
@ -38,8 +36,7 @@ func loadLFSFrom(rootCfg ConfigProvider) {
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
// if these are removed, the warning will not be shown
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
lfsSec.Key("PATH").MustString(
sec.Key("LFS_CONTENT_PATH").String())
lfsSec.Key("PATH").MustString(sec.Key("LFS_CONTENT_PATH").String())
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
@ -62,9 +59,11 @@ func loadLFSFrom(rootCfg ConfigProvider) {
}
// Save secret
CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) {
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
})
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
if err := rootCfg.Save(); err != nil {
log.Fatal("Error saving JWT Secret for custom config: %v", err)
return
}
}
}
}

View File

@ -15,8 +15,6 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
ini "gopkg.in/ini.v1"
)
var (
@ -131,12 +129,12 @@ type LogDescription struct {
SubLogDescriptions []SubLogDescription
}
func getLogLevel(section *ini.Section, key string, defaultValue log.Level) log.Level {
func getLogLevel(section ConfigSection, key string, defaultValue log.Level) log.Level {
value := section.Key(key).MustString(defaultValue.String())
return log.FromString(value)
}
func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) string {
func getStacktraceLogLevel(section ConfigSection, key, defaultValue string) string {
value := section.Key(key).MustString(defaultValue)
return log.FromString(value).String()
}
@ -165,7 +163,7 @@ func loadLogFrom(rootCfg ConfigProvider) {
Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
}
func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
func generateLogConfig(sec ConfigSection, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
level := getLogLevel(sec, "LEVEL", Log.Level)
levelName = level.String()
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)

View File

@ -7,11 +7,10 @@ import (
"testing"
"github.com/stretchr/testify/assert"
ini "gopkg.in/ini.v1"
)
func Test_loadMailerFrom(t *testing.T) {
iniFile := ini.Empty()
iniFile := NewEmptyConfigProvider()
kases := map[string]*Mailer{
"smtp.mydomain.com": {
SMTPAddr: "smtp.mydomain.com",

View File

@ -8,8 +8,6 @@ import (
"strings"
"code.gitea.io/gitea/modules/log"
"gopkg.in/ini.v1"
)
// ExternalMarkupRenderers represents the external markup renderers
@ -82,7 +80,7 @@ func loadMarkupFrom(rootCfg ConfigProvider) {
}
}
func newMarkupSanitizer(name string, sec *ini.Section) {
func newMarkupSanitizer(name string, sec ConfigSection) {
rule, ok := createMarkupSanitizerRule(name, sec)
if ok {
if strings.HasPrefix(name, "sanitizer.") {
@ -99,7 +97,7 @@ func newMarkupSanitizer(name string, sec *ini.Section) {
}
}
func createMarkupSanitizerRule(name string, sec *ini.Section) (MarkupSanitizerRule, bool) {
func createMarkupSanitizerRule(name string, sec ConfigSection) (MarkupSanitizerRule, bool) {
var rule MarkupSanitizerRule
ok := false
@ -141,7 +139,7 @@ func createMarkupSanitizerRule(name string, sec *ini.Section) (MarkupSanitizerRu
return rule, true
}
func newMarkupRenderer(name string, sec *ini.Section) {
func newMarkupRenderer(name string, sec ConfigSection) {
extensionReg := regexp.MustCompile(`\.\w`)
extensions := sec.Key("FILE_EXTENSIONS").Strings(",")

View File

@ -4,12 +4,12 @@
package setting
import (
"encoding/base64"
"math"
"path/filepath"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"gopkg.in/ini.v1"
)
// OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data
@ -80,7 +80,7 @@ func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
}
}
func parseScopes(sec *ini.Section, name string) []string {
func parseScopes(sec ConfigSection, name string) []string {
parts := sec.Key(name).Strings(" ")
scopes := make([]string, 0, len(parts))
for _, scope := range parts {
@ -119,4 +119,19 @@ func loadOAuth2From(rootCfg ConfigProvider) {
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
}
key := make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(key, []byte(OAuth2.JWTSecretBase64))
if err != nil || n != 32 {
key, err = generate.NewJwtSecret()
if err != nil {
log.Fatal("error generating JWT secret: %v", err)
}
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
if err := rootCfg.Save(); err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
}
}
}

View File

@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/modules/log"
"github.com/dustin/go-humanize"
ini "gopkg.in/ini.v1"
)
// Package registry settings
@ -86,7 +85,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
}
func mustBytes(section *ini.Section, key string) int64 {
func mustBytes(section ConfigSection, key string) int64 {
const noLimit = "-1"
value := section.Key(key).MustString(noLimit)

View File

@ -10,8 +10,6 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
// QueueSettings represent the settings for a queue from the ini
@ -195,7 +193,7 @@ func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection,
// toDirectlySetKeysSet returns a set of keys directly set by this section
// Note: we cannot use section.HasKey(...) as that will immediately set the Key if a parent section has the Key
// but this section does not.
func toDirectlySetKeysSet(section *ini.Section) container.Set[string] {
func toDirectlySetKeysSet(section ConfigSection) container.Set[string] {
sections := make(container.Set[string])
for _, key := range section.Keys() {
sections.Add(key.Name())

View File

@ -11,8 +11,6 @@ import (
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
var (
@ -43,7 +41,7 @@ var (
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string {
// don't allow setting both URI and verbatim string
uri := sec.Key(uriKey).String()
verbatim := sec.Key(verbatimKey).String()
@ -84,16 +82,17 @@ func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
}
// generateSaveInternalToken generates and saves the internal token to app.ini
func generateSaveInternalToken() {
func generateSaveInternalToken(rootCfg ConfigProvider) {
token, err := generate.NewInternalToken()
if err != nil {
log.Fatal("Error generate internal token: %v", err)
}
InternalToken = token
CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) {
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
})
rootCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
if err := rootCfg.Save(); err != nil {
log.Fatal("Error saving internal token: %v", err)
}
}
func loadSecurityFrom(rootCfg ConfigProvider) {
@ -141,7 +140,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
// some users do cluster deployment, they still depend on this auto-generating behavior.
generateSaveInternalToken()
generateSaveInternalToken(rootCfg)
}
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")

View File

@ -18,9 +18,6 @@ import (
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
"code.gitea.io/gitea/modules/util"
ini "gopkg.in/ini.v1"
)
// settings
@ -208,17 +205,29 @@ func PrepareAppDataPath() error {
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
func InitProviderFromExistingFile() {
CfgProvider = newFileProviderFromConf(CustomConf, false, "")
var err error
CfgProvider, err = newConfigProviderFromFile(CustomConf, false, "")
if err != nil {
log.Fatal("InitProviderFromExistingFile: %v", err)
}
}
// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
func InitProviderAllowEmpty() {
CfgProvider = newFileProviderFromConf(CustomConf, true, "")
var err error
CfgProvider, err = newConfigProviderFromFile(CustomConf, true, "")
if err != nil {
log.Fatal("InitProviderAllowEmpty: %v", err)
}
}
// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
CfgProvider = newFileProviderFromConf(CustomConf, true, strings.Join(extraConfigs, "\n"))
var err error
CfgProvider, err = newConfigProviderFromFile(CustomConf, true, strings.Join(extraConfigs, "\n"))
if err != nil {
log.Fatal("InitProviderAndLoadCommonSettingsForTest: %v", err)
}
loadCommonSettingsFrom(CfgProvider)
if err := PrepareAppDataPath(); err != nil {
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
@ -229,33 +238,6 @@ func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
}
// newFileProviderFromConf initializes configuration context.
// NOTE: do not print any log except error.
func newFileProviderFromConf(customConf string, allowEmpty bool, extraConfig string) *ini.File {
cfg := ini.Empty()
isFile, err := util.IsFile(customConf)
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", customConf, err)
}
if isFile {
if err := cfg.Append(customConf); err != nil {
log.Fatal("Failed to load custom conf '%s': %v", customConf, err)
}
} else if !allowEmpty {
log.Fatal("Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c.", CustomConf)
} // else: no config file, a config file might be created at CustomConf later (might not)
if extraConfig != "" {
if err = cfg.Append([]byte(extraConfig)); err != nil {
log.Fatal("Unable to append more config: %v", err)
}
}
cfg.NameMapper = ini.SnackCase
return cfg
}
// LoadCommonSettings loads common configurations from a configuration provider.
func LoadCommonSettings() {
loadCommonSettingsFrom(CfgProvider)
@ -319,51 +301,6 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
}
}
// CreateOrAppendToCustomConf creates or updates the custom config.
// Use the callback to set individual values.
func CreateOrAppendToCustomConf(purpose string, callback func(cfg *ini.File)) {
if CustomConf == "" {
log.Error("Custom config path must not be empty")
return
}
cfg := ini.Empty()
isFile, err := util.IsFile(CustomConf)
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", CustomConf, err)
}
if isFile {
if err := cfg.Append(CustomConf); err != nil {
log.Error("failed to load custom conf %s: %v", CustomConf, err)
return
}
}
callback(cfg)
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
log.Fatal("failed to create '%s': %v", CustomConf, err)
return
}
if err := cfg.SaveTo(CustomConf); err != nil {
log.Fatal("error saving to custom config: %v", err)
}
log.Info("Settings for %s saved to: %q", purpose, CustomConf)
// Change permissions to be more restrictive
fi, err := os.Stat(CustomConf)
if err != nil {
log.Error("Failed to determine current conf file permissions: %v", err)
return
}
if fi.Mode().Perm() > 0o600 {
if err = os.Chmod(CustomConf, 0o600); err != nil {
log.Warn("Failed changing conf file permissions to -rw-------. Consider changing them manually.")
}
}
}
// LoadSettings initializes the settings for normal start up
func LoadSettings() {
loadDBSetting(CfgProvider)

View File

@ -6,15 +6,13 @@ package setting
import (
"path/filepath"
"reflect"
ini "gopkg.in/ini.v1"
)
// Storage represents configuration of storages
type Storage struct {
Type string
Path string
Section *ini.Section
Section ConfigSection
ServeDirect bool
}
@ -30,7 +28,7 @@ func (s *Storage) MapTo(v interface{}) error {
return nil
}
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage {
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec ConfigSection) Storage {
const sectionName = "storage"
sec := rootCfg.Section(sectionName)
@ -52,7 +50,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section
storage.Section = targetSec
storage.Type = typ
overrides := make([]*ini.Section, 0, 3)
overrides := make([]ConfigSection, 0, 3)
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
if err == nil {
overrides = append(overrides, nameSec)

View File

@ -7,7 +7,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
ini "gopkg.in/ini.v1"
)
func Test_getStorageCustomType(t *testing.T) {
@ -20,7 +19,7 @@ MINIO_BUCKET = gitea-attachment
STORAGE_TYPE = minio
MINIO_ENDPOINT = my_minio:9000
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -43,7 +42,7 @@ MINIO_BUCKET = gitea-attachment
[storage.minio]
MINIO_BUCKET = gitea
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -65,7 +64,7 @@ MINIO_BUCKET = gitea-minio
[storage]
MINIO_BUCKET = gitea
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -88,7 +87,7 @@ MINIO_BUCKET = gitea
[storage]
STORAGE_TYPE = local
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -100,7 +99,7 @@ STORAGE_TYPE = local
}
func Test_getStorageGetDefaults(t *testing.T) {
cfg, err := ini.Load([]byte(""))
cfg, err := newConfigProviderFromData("")
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -121,7 +120,7 @@ MINIO_BUCKET = gitea-attachment
[storage]
MINIO_BUCKET = gitea-storage
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
{
@ -155,7 +154,7 @@ STORAGE_TYPE = lfs
[storage.lfs]
MINIO_BUCKET = gitea-storage
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
{
@ -179,7 +178,7 @@ func Test_getStorageInheritStorageType(t *testing.T) {
[storage]
STORAGE_TYPE = minio
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")
@ -194,7 +193,7 @@ func Test_getStorageInheritNameSectionType(t *testing.T) {
[storage.attachments]
STORAGE_TYPE = minio
`
cfg, err := ini.Load([]byte(iniStr))
cfg, err := newConfigProviderFromData(iniStr)
assert.NoError(t, err)
sec := cfg.Section("attachment")

View File

@ -7,19 +7,54 @@ import (
"fmt"
"html"
"html/template"
"time"
)
// DateTime renders an absolute time HTML given a time as a string
func DateTime(format, datetime, fallback string) template.HTML {
datetimeEscaped := html.EscapeString(datetime)
fallbackEscaped := html.EscapeString(fallback)
// DateTime renders an absolute time HTML element by datetime.
func DateTime(format string, datetime any) template.HTML {
if p, ok := datetime.(*time.Time); ok {
datetime = *p
}
if p, ok := datetime.(*TimeStamp); ok {
datetime = *p
}
switch v := datetime.(type) {
case TimeStamp:
datetime = v.AsTime()
case int:
datetime = TimeStamp(v).AsTime()
case int64:
datetime = TimeStamp(v).AsTime()
}
var datetimeEscaped, textEscaped string
switch v := datetime.(type) {
case nil:
return "N/A"
case string:
datetimeEscaped = html.EscapeString(v)
textEscaped = datetimeEscaped
case time.Time:
if v.IsZero() || v.Unix() == 0 {
return "N/A"
}
datetimeEscaped = html.EscapeString(v.Format(time.RFC3339))
if format == "full" {
textEscaped = html.EscapeString(v.Format("2006-01-02 15:04:05 -07:00"))
} else {
textEscaped = html.EscapeString(v.Format("2006-01-02"))
}
default:
panic(fmt.Sprintf("Unsupported time type %T", datetime))
}
switch format {
case "short":
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
case "long":
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
case "full":
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, datetimeEscaped, fallbackEscaped))
return template.HTML(fmt.Sprintf(`<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
}
return template.HTML("error in DateTime")
panic(fmt.Sprintf("Unsupported format %s", format))
}

View 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)
}

View File

@ -115,7 +115,7 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
}
func timeSinceUnix(then, now time.Time, lang translation.Locale) template.HTML {
friendlyText := then.Format("2006-01-02 15:04:05 +07:00")
friendlyText := then.Format("2006-01-02 15:04:05 -07:00")
// document: https://github.com/github/relative-time-element
attrs := `tense="past"`

View File

@ -462,6 +462,7 @@ team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προορι
[modal]
yes=Ναι
no=Όχι
confirm=Επιβεβαίωση
cancel=Ακύρωση
modify=Ενημέρωση
@ -1622,7 +1623,10 @@ pulls.tab_files=Αρχεία Με Αλλαγές
pulls.reopen_to_merge=Παρακαλώ ανοίξτε ξανά αυτό το pull request για να εκτελέσετε μια συγχώνευση.
pulls.cant_reopen_deleted_branch=Αυτό το pull request δεν μπορεί να ανοίξει ξανά επειδή ο κλάδος διαγράφηκε.
pulls.merged=Συγχωνευμένο
pulls.merged_success=Το pull request συγχωνεύτηκε και έκλεισε επιτυχώς
pulls.closed=Το pull request έκλεισε
pulls.manually_merged=Συγχωνεύτηκαν χειροκίνητα
pulls.merged_info_text=Ο κλάδος %s μπορεί τώρα να διαγραφεί.
pulls.is_closed=Το pull request έχει κλείσει.
pulls.title_wip_desc=`<a href="#">Ξεκινήστε τον τίτλο με <strong>%s</strong></a> για να αποτρέψετε την τυχαία συγχώνευση του pull request.`
pulls.cannot_merge_work_in_progress=Αυτό το pull request επισημαίνεται ως μια εργασία σε εξέλιξη.
@ -3414,4 +3418,7 @@ runs.no_matching_runner_helper=Δε ταιριάζει εκτελεστής: %s
need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork.
[projects]
type-1.display_name=Ατομικό Έργο
type-2.display_name=Έργο Αποθετηρίου
type-3.display_name=Έργο Οργανισμού

View File

@ -284,7 +284,7 @@ show_only_public=只显示公开的
issues.in_your_repos=在您的仓库中
[explore]
repos=仓库管理
repos=仓库
users=用户
organizations=组织管理
search=搜索

View File

@ -966,7 +966,7 @@ func SignInOAuthCallback(ctx *context.Context) {
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm),
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm),
}
source := authSource.Cfg.(*oauth2.Source)

View 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
View 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)
}

View 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)
}
}

View File

@ -32,43 +32,31 @@ import (
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/go-chi/cors"
)
func HTTPGitEnabledHandler(ctx *context.Context) {
if setting.Repository.DisableHTTPGit {
ctx.Resp.WriteHeader(http.StatusForbidden)
_, _ = ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
}
}
func CorsHandler() func(next http.Handler) http.Handler {
if setting.Repository.AccessControlAllowOrigin != "" {
return cors.Handler(cors.Options{
AllowedOrigins: []string{setting.Repository.AccessControlAllowOrigin},
AllowedHeaders: []string{"Content-Type", "Authorization", "User-Agent"},
})
}
return func(next http.Handler) http.Handler {
return next
}
}
// httpBase implementation git smart HTTP protocol
func httpBase(ctx *context.Context) (h *serviceHandler) {
if setting.Repository.DisableHTTPGit {
ctx.Resp.WriteHeader(http.StatusForbidden)
_, err := ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
if err != nil {
log.Error(err.Error())
}
return
}
if len(setting.Repository.AccessControlAllowOrigin) > 0 {
allowedOrigin := setting.Repository.AccessControlAllowOrigin
// Set CORS headers for browser-based git clients
ctx.Resp.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
ctx.Resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent")
// Handle preflight OPTIONS request
if ctx.Req.Method == "OPTIONS" {
if allowedOrigin == "*" {
ctx.Status(http.StatusOK)
} else if allowedOrigin == "null" {
ctx.Status(http.StatusForbidden)
} else {
origin := ctx.Req.Header.Get("Origin")
if len(origin) > 0 && origin == allowedOrigin {
ctx.Status(http.StatusOK)
} else {
ctx.Status(http.StatusForbidden)
}
}
return
}
}
username := ctx.Params(":username")
reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")

View File

@ -29,8 +29,9 @@ import (
)
const (
tplReleases base.TplName = "repo/release/list"
tplReleaseNew base.TplName = "repo/release/new"
tplReleasesList base.TplName = "repo/release/list"
tplReleaseNew base.TplName = "repo/release/new"
tplTagsList base.TplName = "repo/tag/list"
)
// calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
@ -58,16 +59,19 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model
// Releases render releases list page
func Releases(ctx *context.Context) {
ctx.Data["PageIsReleaseList"] = true
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
releasesOrTags(ctx, false)
}
// TagsList render tags list page
func TagsList(ctx *context.Context) {
ctx.Data["PageIsTagList"] = true
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
releasesOrTags(ctx, true)
}
func releasesOrTags(ctx *context.Context, isTagList bool) {
ctx.Data["PageIsReleaseList"] = true
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
ctx.Data["IsViewBranch"] = false
ctx.Data["IsViewTag"] = true
@ -75,14 +79,6 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
ctx.Data["CanCreateBranch"] = false
ctx.Data["HideBranchesInDropdown"] = true
if isTagList {
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
ctx.Data["PageIsTagList"] = true
} else {
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
ctx.Data["PageIsTagList"] = false
}
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: ctx.FormInt("limit"),
@ -196,7 +192,11 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplReleases)
if isTagList {
ctx.HTML(http.StatusOK, tplTagsList)
} else {
ctx.HTML(http.StatusOK, tplReleasesList)
}
}
// ReleasesFeedRSS get feeds for releases in RSS format
@ -282,7 +282,7 @@ func SingleRelease(ctx *context.Context) {
}
ctx.Data["Releases"] = []*repo_model.Release{release}
ctx.HTML(http.StatusOK, tplReleases)
ctx.HTML(http.StatusOK, tplReleasesList)
}
// LatestRelease redirects to the latest release

View File

@ -710,7 +710,14 @@ func Home(ctx *context.Context) {
if setting.Other.EnableFeed {
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
if isFeed {
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
switch {
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath == "":
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath != "":
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
}
return
}
}

View File

@ -1198,7 +1198,7 @@ func RegisterRoutes(m *web.Route) {
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
// Releases
// Tags
m.Group("/{username}/{reponame}", func() {
m.Group("/tags", func() {
m.Get("", repo.TagsList)
@ -1207,6 +1207,12 @@ func RegisterRoutes(m *web.Route) {
}, func(ctx *context.Context) {
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
}, repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
// Releases
m.Group("/{username}/{reponame}", func() {
m.Group("/releases", func() {
m.Get("/", repo.Releases)
m.Get("/tag/*", repo.SingleRelease)
@ -1224,8 +1230,6 @@ func RegisterRoutes(m *web.Route) {
m.Post("/attachments", repo.UploadReleaseAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
@ -1450,6 +1454,9 @@ func RegisterRoutes(m *web.Route) {
m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
m.Group("/src", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
@ -1508,7 +1515,7 @@ func RegisterRoutes(m *web.Route) {
m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
}, ignSignInAndCsrf, context_service.UserAssignmentWeb())
}, ignSignInAndCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
})
})
// ***** END: Repository *****

View File

@ -18,14 +18,12 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/golang-jwt/jwt/v4"
"github.com/minio/sha256-simd"
ini "gopkg.in/ini.v1"
)
// ErrInvalidAlgorithmType represents an invalid algorithm error.
@ -316,8 +314,7 @@ func InitSigningKey() error {
case "HS384":
fallthrough
case "HS512":
key, err = loadOrCreateSymmetricKey()
key, err = loadSymmetricKey()
case "RS256":
fallthrough
case "RS384":
@ -332,7 +329,6 @@ func InitSigningKey() error {
fallthrough
case "EdDSA":
key, err = loadOrCreateAsymmetricKey()
default:
return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm}
}
@ -351,22 +347,16 @@ func InitSigningKey() error {
return nil
}
// loadOrCreateSymmetricKey checks if the configured secret is valid.
// If it is not valid a new secret is created and saved in the configuration file.
func loadOrCreateSymmetricKey() (interface{}, error) {
// loadSymmetricKey checks if the configured secret is valid.
// If it is not valid, it will return an error.
func loadSymmetricKey() (interface{}, error) {
key := make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
if err != nil || n != 32 {
key, err = generate.NewJwtSecret()
if err != nil {
log.Fatal("error generating JWT secret: %v", err)
return nil, err
}
setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) {
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
})
if err != nil {
return nil, err
}
if n != 32 {
return nil, fmt.Errorf("JWT secret must be 32 bytes long")
}
return key, nil

View File

@ -26,8 +26,8 @@
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
<td>{{.TypeName}}</td>
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .UpdatedUnix}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr>
{{end}}

View File

@ -21,8 +21,8 @@
<td><button type="submit" class="ui green button" name="op" value="{{.Name}}" title="{{$.locale.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
<td>{{$.locale.Tr (printf "admin.dashboard.%s" .Name)}}</td>
<td>{{.Spec}}</td>
<td>{{DateTime "full" (DateFmtLong .Next) (DateFmtLong .Next)}}</td>
<td>{{if gt .Prev.Year 1}}{{DateTime "full" (DateFmtLong .Prev) (DateFmtLong .Prev)}}{{else}}N/A{{end}}</td>
<td>{{DateTime "full" (DateFmtLong .Next)}}</td>
<td>{{if gt .Prev.Year 1}}{{DateTime "full" .Prev}}{{else}}N/A{{end}}</td>
<td>{{.ExecTimes}}</td>
<td {{if ne .Status ""}}data-tooltip-content="{{.FormatLastMessage $.locale}}"{{end}} >{{if eq .Status ""}}{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td>
</tr>

View File

@ -26,7 +26,7 @@
<td>{{.ID}}</td>
<td>{{$.locale.Tr .TrStr}}</td>
<td class="view-detail"><span class="notice-description text truncate">{{.Description}}</span></td>
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
<td><a href="#">{{svg "octicon-note" 16 "view-detail"}}</a></td>
</tr>
{{end}}

View File

@ -41,7 +41,7 @@
<td>{{.NumTeams}}</td>
<td>{{.NumMembers}}</td>
<td>{{.NumRepos}}</td>
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
<td><a href="{{.OrganisationLink}}/settings">{{svg "octicon-pencil"}}</a></td>
</tr>
{{end}}

View File

@ -65,7 +65,7 @@
{{end}}
</td>
<td>{{FileSize .CalculateBlobSize}}</td>
<td>{{DateTime "short" .Version.CreatedUnix.FormatLong .Version.CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .Version.CreatedUnix}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
</tr>
{{end}}

View File

@ -155,8 +155,8 @@
{{range .Queue.Workers}}
<tr>
<td>{{.Workers}}{{if .IsFlusher}}<span title="{{$.locale.Tr "admin.monitor.queue.flush"}}">{{svg "octicon-sync"}}</span>{{end}}</td>
<td>{{DateTime "full" (DateFmtLong .Start) (DateFmtLong .Start)}}</td>
<td>{{if .HasTimeout}}{{DateTime "full" (DateFmtLong .Timeout) (DateFmtLong .Timeout)}}{{else}}-{{end}}</td>
<td>{{DateTime "full" .Start}}</td>
<td>{{if .HasTimeout}}{{DateTime "full" .Timeout}}{{else}}-{{end}}</td>
<td>
<a class="delete-button" href="" data-url="{{$.Link}}/cancel/{{.PID}}" data-id="{{.PID}}" data-name="{{.Workers}}" title="{{$.locale.Tr "remove"}}">{{svg "octicon-trash"}}</a>
</td>

View File

@ -80,7 +80,7 @@
<td>{{.NumForks}}</td>
<td>{{.NumIssues}}</td>
<td>{{FileSize .Size}}</td>
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
</tr>
{{end}}

View File

@ -91,9 +91,9 @@
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{.NumRepos}}</td>
<td>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
{{if .LastLoginUnix}}
<td>{{DateTime "short" .LastLoginUnix.FormatLong .LastLoginUnix.FormatShort}}</td>
<td>{{DateTime "short" .LastLoginUnix}}</td>
{{else}}
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
{{end}}

View File

@ -23,7 +23,7 @@
{{svg "octicon-link"}}
<a href="{{.Website}}" rel="nofollow">{{.Website}}</a>
{{end}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
</div>
</div>
</div>

View File

@ -18,7 +18,7 @@
{{svg "octicon-mail"}}
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
{{end}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
</div>
</div>
</div>

View File

@ -22,7 +22,7 @@
<td><a href="{{.FullWebLink}}">{{.Version.Version}}</a></td>
<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
<td>{{FileSize .CalculateBlobSize}}</td>
<td>{{DateTime "short" .Version.CreatedUnix.FormatLong .Version.CreatedUnix.FormatShort}}</td>
<td>{{DateTime "short" .Version.CreatedUnix}}</td>
</tr>
{{else}}
<tr>

View File

@ -86,7 +86,7 @@
{{range .LatestVersions}}
<div class="item gt-df">
<a class="gt-f1" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
<span class="text small">{{DateTime "short" (.CreatedUnix.FormatDate) (.CreatedUnix.FormatDate)}}</span>
<span class="text small">{{DateTime "short" .CreatedUnix}}</span>
</div>
{{end}}
</div>

View File

@ -2,7 +2,7 @@
<div role="main" aria-label="{{.Title}}" class="page-content repository commits">
{{template "repo/header" .}}
<div class="ui container">
<h2 class="ui header">{{DateTime "long" .DateFrom .DateFrom}} - {{DateTime "long" .DateUntil .DateUntil}}
<h2 class="ui header">{{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}}
<div class="ui right">
<!-- Period -->
<div class="ui floating dropdown jump filter">

View File

@ -26,6 +26,11 @@
{{svg "octicon-git-branch"}}
</button>
{{end}}
{{if .EnableFeed}}
<a role="button" class="ui basic button icon" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranch}}">
{{svg "octicon-rss"}}
</a>
{{end}}
{{if not $.DisableDownloadSourceArchives}}
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranch)}}">
{{svg "octicon-download"}}
@ -113,6 +118,11 @@
{{svg "octicon-git-branch"}}
</button>
{{end}}
{{if $.EnableFeed}}
<a role="button" class="ui basic button icon" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .Name}}">
{{svg "octicon-rss"}}
</a>
{{end}}
{{if and (not .IsDeleted) (not $.DisableDownloadSourceArchives)}}
<button class="ui basic jump dropdown icon button" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.Name)}}">
{{svg "octicon-download"}}

View File

@ -42,6 +42,8 @@
'branches': {{.root.Branches}},
'tags': {{.root.Tags}},
'defaultBranch': {{$defaultBranch}},
'enableFeed': {{.root.EnableFeed}},
'rssURLPrefix': '{{$.root.RepoLink}}/rss/branch/',
'branchURLPrefix': '{{if .branchURLPrefix}}{{.branchURLPrefix}}{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/branch/{{end}}',
'branchURLSuffix': '{{if .branchURLSuffix}}{{.branchURLSuffix}}{{else}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}{{end}}',
'tagURLPrefix': '{{if .tagURLPrefix}}{{.tagURLPrefix}}{{else if .release}}{{$.root.RepoLink}}/compare/{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/tag/{{end}}',

View File

@ -63,13 +63,13 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
</div>
</div>
{{template "repo/clone_script" .}}
{{end}}
{{else}}
<div class="ui segment center">
{{.locale.Tr "repo.empty_message"}}
</div>
{{end}}
{{template "repo/clone_script" .}}
</div>
</div>
</div>

View File

@ -63,12 +63,13 @@
{{$n := len .TreeNames}}
{{$l := Eval $n "-" 1}}
<!-- If home page, show new pr. If not, show breadcrumb -->
{{if and (eq $n 0) .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{CompareLink .BaseRepo .Repository .BranchName}}"
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}">
{{svg "octicon-git-pull-request"}}
</a>
{{end}}
{{if eq $n 0}}
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
<a href="{{CompareLink .BaseRepo .Repository .BranchName}}">
<button id="new-pull-request" class="ui compact basic button" data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}"><span class="text">{{svg "octicon-git-pull-request"}}</span></button>
</a>
{{end}}
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{.locale.Tr "repo.find_file.go_to_file"}}</a>
{{end}}

View File

@ -35,7 +35,7 @@
{{else}}
{{svg "octicon-calendar"}}
{{if .Milestone.DeadlineString}}
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .Milestone.DeadlineString .Milestone.DeadlineString}}</span>
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .Milestone.DeadlineString}}</span>
{{else}}
{{$.locale.Tr "repo.milestones.no_due_date"}}
{{end}}

View File

@ -77,7 +77,7 @@
{{else}}
{{svg "octicon-calendar"}}
{{if .DeadlineString}}
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
{{else}}
{{$.locale.Tr "repo.milestones.no_due_date"}}
{{end}}

View File

@ -295,7 +295,7 @@
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$.locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content .Content) $createdStr | Safe}}
{{$.locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr | Safe}}
</span>
</div>
{{else if eq .Type 17}}
@ -305,8 +305,8 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$parsedDeadline := .Content | ParseDeadline}}
{{$from := DateTime "long" (index $parsedDeadline 1) (index $parsedDeadline 1)}}
{{$to := DateTime "long" (index $parsedDeadline 0) (index $parsedDeadline 0)}}
{{$from := DateTime "long" (index $parsedDeadline 1)}}
{{$to := DateTime "long" (index $parsedDeadline 0)}}
{{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}}
</span>
</div>
@ -316,7 +316,7 @@
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$.locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content .Content) $createdStr | Safe}}
{{$.locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr | Safe}}
</span>
</div>
{{else if eq .Type 19}}

View File

@ -385,7 +385,7 @@
<div class="gt-df gt-sb gt-ac">
<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{.locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
{{svg "octicon-calendar" 16 "gt-mr-3"}}
{{DateTime "long" .Issue.DeadlineUnix.FormatDate .Issue.DeadlineUnix.FormatDate}}
{{DateTime "long" .Issue.DeadlineUnix}}
</div>
<div>
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}

View File

@ -1,73 +1,16 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository release">
<div role="main" aria-label="{{.Title}}" class="page-content repository releases">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<h2 class="ui compact small menu header">
{{if .Permission.CanRead $.UnitTypeReleases}}
<a class="{{if (and (not .PageIsSingleTag) (not .PageIsTagList))}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeCode}}
<a class="{{if (or .PageIsSingleTag .PageIsTagList)}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a>
{{end}}
</h2>
{{if .EnableFeed}}
<a href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"><i class="ui grey icon gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
{{end}}
{{if (and .CanCreateRelease (not .PageIsTagList))}}
{{template "repo/sub_menu_release_tag" .}}
{{if .CanCreateRelease}}
<a class="ui right small green button" href="{{$.RepoLink}}/releases/new">
{{.locale.Tr "repo.release.new_release"}}
</a>
{{end}}
{{if .PageIsTagList}}
<div class="ui divider"></div>
{{if gt .ReleasesNum 0}}
<h4 class="ui top attached header">
<div class="five wide column gt-df gt-ac">
{{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.release.tags"}}
</div>
</h4>
<div class="ui attached table segment">
<table class="ui very basic striped fixed table single line" id="tags-table">
<thead></thead>
<tbody class="tag-list">
{{range $idx, $release := .Releases}}
<tr>
<td class="tag">
<h3 class="release-tag-name gt-mb-3">
<a class="gt-df gt-ac" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
</h3>
<div class="download gt-df gt-ac">
{{if $.Permission.CanRead $.UnitTypeCode}}
{{if .CreatedUnix}}
<span class="gt-mr-3">{{svg "octicon-clock" 16 "gt-mr-2"}}{{TimeSinceUnix .CreatedUnix $.locale}}</span>
{{end}}
<a class="gt-mr-3 gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a>
{{if not $.DisableDownloadSourceArchives}}
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}ZIP</a>
<a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}TAR.GZ</a>
{{end}}
{{if (and $.CanCreateRelease $release.IsTag)}}
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.new_release"}}</a>
{{end}}
{{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}}
<a class="ui delete-button gt-mr-3 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
{{svg "octicon-trash" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.delete_tag"}}
</a>
{{end}}
{{if (not $release.IsTag)}}
<a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.detail"}}</a>
{{end}}
{{end}}
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{end}}
{{else}}
<ul id="release-list">
{{range $idx, $release := .Releases}}
<li class="ui grid">
@ -178,7 +121,7 @@
</li>
{{end}}
</ul>
{{end}}
{{template "base/paginate" .}}
</div>
</div>

View File

@ -60,7 +60,7 @@
{{.Fingerprint}}
</div>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i>
</div>
</div>
</div>

View File

@ -89,7 +89,7 @@
<tr>
<td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td>
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
<td>{{DateTime "full" .Mirror.UpdatedUnix.FormatLong .Mirror.UpdatedUnix.AsTime}}</td>
<td>{{DateTime "full" .Mirror.UpdatedUnix}}</td>
<td class="right aligned">
<form method="post" style="display: inline-block">
{{.CsrfTokenHtml}}
@ -167,7 +167,7 @@
{{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}}
<td class="gt-word-break">{{$address.Address}}</td>
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
<td>{{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix.FormatLong .LastUpdateUnix.AsTime}}{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td>
<td>{{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix}}{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td>
<td class="right aligned">
<form method="post" style="display: inline-block">
{{$.CsrfTokenHtml}}

View File

@ -10,7 +10,7 @@
<a href="{{.RepoLink}}/branches">{{svg "octicon-git-branch"}} <b>{{.BranchesCount}}</b> {{.locale.TrN .BranchesCount "repo.branch" "repo.branches"}}</a>
</div>
{{if $.Permission.CanRead $.UnitTypeCode}}
<div class="item">
<div class="item{{if .PageIsTagList}} active{{end}}">
<a href="{{.RepoLink}}/tags">{{svg "octicon-tag"}} <b>{{.NumTags}}</b> {{.locale.TrN .NumTags "repo.tag" "repo.tags"}}</a>
</div>
{{end}}

View 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}}

View 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" .}}

View File

@ -18,7 +18,7 @@
{{else if .Location}}
{{svg "octicon-location"}} {{.Location}}
{{else}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{DateTime "short" .CreatedUnix}}
{{end}}
</div>
</li>

View File

@ -42,6 +42,9 @@
</div>
<a download href="{{$.RawFileLink}}"><span class="btn-octicon" data-tooltip-content="{{.locale.Tr "repo.download_file"}}">{{svg "octicon-download"}}</span></a>
<a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{.locale.Tr "copy_content"}}{{else}}{{.locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy" 14}}</a>
{{if .EnableFeed}}
<a class="btn-octicon" href="{{$.FeedURL}}/rss/{{$.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{svg "octicon-rss" 14}}</a>
{{end}}
{{if .Repository.CanEnableEditor}}
{{if .CanEditFile}}
<a href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .TreePath}}"><span class="btn-octicon" data-tooltip-content="{{.EditFileTooltip}}">{{svg "octicon-pencil"}}</span></a>

View File

@ -106,7 +106,7 @@
<span class="due-date" data-tooltip-content="{{$.locale.Tr "repo.issues.due_date"}}">
<span{{if .IsOverdue}} class="overdue"{{end}}>
{{svg "octicon-calendar" 14 "gt-mr-2"}}
{{DateTime "short" .DeadlineUnix.FormatDate .DeadlineUnix.FormatShort}}
{{DateTime "short" .DeadlineUnix}}
</span>
</span>
{{end}}

View File

@ -6,7 +6,7 @@
<div class="ui container gt-mt-5">
{{if .ErrorMsg}}
<p>{{.locale.Tr "error.occurred"}}:</p>
<pre class="gt-whitespace-pre-wrap">{{.ErrorMsg}}</pre>
<pre class="gt-whitespace-pre-wrap gt-break-all">{{.ErrorMsg}}</pre>
{{end}}
<div class="center gt-mt-5">

View File

@ -97,7 +97,7 @@
{{else}}
{{svg "octicon-calendar"}}
{{if .DeadlineString}}
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString .DeadlineString}}</span>
<span {{if .IsOverdue}}class="overdue"{{end}}>{{DateTime "short" .DeadlineString}}</span>
{{else}}
{{$.locale.Tr "repo.milestones.no_due_date"}}
{{end}}

View File

@ -44,8 +44,10 @@ data.teamId = {{.Team.ID}};
{{if not .ContextUser.IsOrganization}}
data.organizations = [{{range .Orgs}}{'name': {{.Name}}, 'num_repos': {{.NumRepos}}},{{end}}];
data.isOrganization = false;
data.organizationsTotalCount = {{.UserOrgsCount}}
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}}
data.organizationsTotalCount = {{.UserOrgsCount}};
data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}};
{{else}}
data.organizationId = {{.ContextUser.ID}};
{{end}}
window.config.pageData.dashboardRepoList = data;

View File

@ -73,7 +73,7 @@
</li>
{{end}}
{{end}}
<li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} {{DateTime "short" .ContextUser.CreatedUnix.FormatLong .ContextUser.CreatedUnix.FormatShort}}</li>
<li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} {{DateTime "short" .ContextUser.CreatedUnix}}</li>
{{if and .Orgs .HasOrgsVisible}}
<li>
<ul class="user-orgs">

View File

@ -27,7 +27,7 @@
</ul>
</details>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
</div>

View File

@ -20,7 +20,7 @@
<div class="content">
<strong>{{$grant.Application.Name}}</strong>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" $grant.CreatedUnix.FormatLong $grant.CreatedUnix.FormatShort}}</span></i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" $grant.CreatedUnix}}</span></i>
</div>
</div>
</div>

View File

@ -68,9 +68,9 @@
<b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
</div>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .AddedUnix.FormatLong .AddedUnix.FormatShort}}</span></i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .AddedUnix}}</span></i>
-
<i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span>{{DateTime "short" .ExpiredUnix.FormatLong .ExpiredUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i>
<i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span>{{DateTime "short" .ExpiredUnix}}</span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i>
</div>
</div>
</div>

View File

@ -25,7 +25,7 @@
<div class="content">
<strong>{{.Name}}</strong>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
</div>

View File

@ -59,7 +59,7 @@
{{.Fingerprint}}
</div>
<div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix.FormatLong .CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
<i>{{$.locale.Tr "settings.add_on"}} <span>{{DateTime "short" .CreatedUnix}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix.FormatLong .UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
</div>

View File

@ -22,7 +22,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, titl
// Click the PR button to create a pull
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("#new-pull-request").Parent().Attr("href")
link, exists := htmlDoc.doc.Find("#new-pull-request").Attr("href")
assert.True(t, exists, "The template has changed")
if branch != "master" {
link = strings.Replace(link, ":master", ":"+branch, 1)

View File

@ -864,6 +864,9 @@ a.label,
margin-top: -0.25rem;
margin-bottom: -0.25rem;
}
.ui.dropdown .menu > .item > svg {
margin-right: .78rem; /* use the same margin as for <img> */
}
.ui.selection.dropdown .menu > .item {
border-color: var(--color-secondary);

View File

@ -23,15 +23,6 @@ Gitea's private styles use `g-` prefix.
.gt-h-100 { height: 100% !important; }
.gt-br-0 { border-radius: 0 !important; }
/* below class names match Tailwind CSS */
.gt-pointer-events-none { pointer-events: none !important; }
.gt-relative { position: relative !important; }
.gt-overflow-x-scroll { overflow-x: scroll !important; }
.gt-cursor-default { cursor: default !important; }
.gt-items-start { align-items: flex-start !important; }
.gt-whitespace-pre { white-space: pre !important; }
.gt-invisible { visibility: hidden !important; }
.gt-mono {
font-family: var(--fonts-monospace) !important;
font-size: .95em !important; /* compensate for monospace fonts being usually slightly larger */
@ -51,6 +42,19 @@ Gitea's private styles use `g-` prefix.
text-overflow: ellipsis !important;
}
/* below class names match Tailwind CSS */
.gt-break-all { word-break: break-all !important; }
.gt-content-center { align-content: center !important; }
.gt-cursor-default { cursor: default !important; }
.gt-invisible { visibility: hidden !important; }
.gt-items-start { align-items: flex-start !important; }
.gt-overflow-x-scroll { overflow-x: scroll !important; }
.gt-pointer-events-none { pointer-events: none !important; }
.gt-relative { position: relative !important; }
.gt-whitespace-nowrap { white-space: nowrap !important; }
.gt-whitespace-pre { white-space: pre !important; }
.gt-whitespace-pre-wrap { white-space: pre-wrap !important; }
.gt-w-screen { width: 100vw !important; }
.gt-h-screen { height: 100vh !important; }
@ -203,11 +207,7 @@ Gitea's private styles use `g-` prefix.
.gt-gap-y-4 { row-gap: 1rem !important; }
.gt-gap-y-5 { row-gap: 2rem !important; }
.gt-content-center { align-content: center !important; }
.gt-shrink-0 { flex-shrink: 0 !important; }
.gt-whitespace-nowrap { white-space: nowrap !important; }
.gt-whitespace-pre-wrap { white-space: pre-wrap !important; }
@media (max-width: 767px) {
.gt-db-small { display: block !important; }

View File

@ -30,6 +30,7 @@
@import "./install.css";
@import "./form.css";
@import "./repository.css";
@import "./repository-release-tag.css";
@import "./editor.css";
@import "./editor-markdown.css";
@import "./organization.css";

View 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;
}

View File

@ -1923,157 +1923,6 @@
flex: 1
}
.repository.release #release-list {
border-top: 1px solid var(--color-secondary);
margin-top: 20px;
padding-top: 15px;
padding-left: 0;
}
.repository.release #release-list .release-list-title {
font-size: 2rem;
font-weight: normal;
margin-top: -4px;
margin-bottom: 0;
}
.repository.release #release-list > li {
list-style: none;
}
.repository.release #release-list > li .meta,
.repository.release #release-list > li .detail {
padding-top: 30px;
padding-bottom: 40px;
}
.repository.release #release-list > li .meta {
text-align: right;
position: relative;
}
.repository.release #release-list > li .meta .label {
margin-right: 0;
}
.repository.release #release-list > li .meta .commit {
display: block;
margin-top: 10px;
}
.repository.release #release-list > li .meta .choose {
margin-top: 15px;
}
.repository.release #release-list > li .meta .choose .button {
margin-right: 0;
}
.repository.release #release-list > li .detail {
border-left: 2px solid var(--color-secondary);
}
.repository.release #release-list > li .detail .author img {
margin-bottom: 3px;
}
.repository.release #release-list > li .detail .download > a .svg {
margin-left: 5px;
margin-right: 5px;
}
.repository.release #release-list > li .detail .download .list {
padding-left: 0;
}
.repository.release #release-list > li .detail .download .list li {
list-style: none;
display: block;
padding: 8px;
border: 1px solid var(--color-secondary);
background: var(--color-light);
}
.repository.release #release-list > li .detail .download .list li a > .text.right {
margin-right: 5px;
}
.repository.release #release-list > li .detail .download .list li + li {
border-top: 0;
}
.repository.release #release-list > li .detail .download .list li:first-of-type {
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
.repository.release #release-list > li .detail .download .list li:last-of-type {
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
.repository.release #release-list > li .detail .dot {
width: 10px;
height: 10px;
background-color: var(--color-secondary-dark-3);
z-index: 9;
position: absolute;
display: block;
left: -6px;
top: 40px;
border-radius: 100%;
border: 2.5px solid var(--color-body);
}
.repository.release #tags-table .tag {
padding: 8px 12px;
}
.repository.release #tags-table .release-tag-name {
font-size: 18px;
font-weight: normal;
}
.repository.new.release .target {
min-width: 500px;
}
.repository.new.release .target #tag-name {
margin-top: -4px;
}
.repository.new.release .target .at {
margin-left: -5px;
margin-right: 5px;
}
.repository.new.release .target .selection.dropdown {
padding-top: 10px;
padding-bottom: 10px;
}
.repository.new.release .prerelease.field {
margin-bottom: 0;
}
@media (max-width: 438px) {
.repository.new.release .field button,
.repository.new.release .field input {
width: 100%;
}
}
@media (max-width: 767px) {
.repository.new.release .field button {
margin-bottom: 1em;
}
}
.repository.new.release .field .wrap_remove {
height: 38px;
}
.repository.new.release .field .attachment_edit {
width: 450px !important;
}
.repository.packages .empty {
padding-top: 70px;

View File

@ -10,7 +10,7 @@
{{ textMyRepos }}
<span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
</div>
<a :href="subUrl + '/repo/create'" :data-tooltip-content="textNewRepo">
<a :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo">
<svg-icon name="octicon-plus"/>
<span class="sr-only">{{ textNewRepo }}</span>
</a>
@ -74,7 +74,7 @@
<a class="repo-list-link gt-df gt-ac gt-sb" :href="repo.link">
<div class="item-name gt-df gt-ac gt-f1">
<svg-icon :name="repoIcon(repo)" :size="16" class-name="gt-mr-2"/>
<div class="text truncate gt-ml-1">{{ repo.full_name }}</div>
<div class="text gt-bold truncate gt-ml-1">{{ repo.full_name }}</div>
<span v-if="repo.archived">
<svg-icon name="octicon-archive" :size="16" class-name="gt-ml-2"/>
</span>
@ -199,6 +199,7 @@ const sfc = {
isOrganization: true,
canCreateOrganization: false,
organizationsTotalCount: 0,
organizationId: 0,
subUrl: appSubUrl,
...pageData.dashboardRepoList,

View File

@ -39,6 +39,9 @@
<div class="scrolling menu" ref="scrollContainer">
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index">
{{ item.name }}
<a v-if="enableFeed && mode === 'branches'" role="button" class="ui compact muted right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
<svg-icon name="octicon-rss" :size="14"/>
</a>
</div>
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
<a href="#" @click="createNewBranch()">
@ -291,3 +294,12 @@ export function initRepoBranchTagSelector(selector) {
export default sfc; // activate IDE's Vue plugin
</script>
<style scoped>
.menu .item a {
display: none; /* only show RSS icon on hover */
}
.menu .item:hover a {
display: inline-block;
}
</style>

View File

@ -43,6 +43,7 @@ import octiconChevronLeft from '../../public/img/svg/octicon-chevron-left.svg';
import octiconOrganization from '../../public/img/svg/octicon-organization.svg';
import octiconTag from '../../public/img/svg/octicon-tag.svg';
import octiconGitBranch from '../../public/img/svg/octicon-git-branch.svg';
import octiconRss from '../../public/img/svg/octicon-rss.svg';
const svgs = {
'octicon-blocked': octiconBlocked,
@ -89,6 +90,7 @@ const svgs = {
'octicon-organization': octiconOrganization,
'octicon-tag': octiconTag,
'octicon-git-branch': octiconGitBranch,
'octicon-rss': octiconRss,
};
// TODO: use a more general approach to access SVG icons.