mirror of
https://github.com/go-gitea/gitea
synced 2024-06-01 19:06:52 +02:00
Merge branch 'main' into feature/remove-signin-tabs
This commit is contained in:
commit
bc282f73cd
37
cmd/hook.go
37
cmd/hook.go
|
@ -448,23 +448,26 @@ Gitea or set your environment appropriately.`, "")
|
|||
|
||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||
for _, res := range results {
|
||||
if !res.Message {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if res.Create {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
os.Stderr.Sync()
|
||||
hookPrintResult(res.Message, res.Create, res.Branch, res.URL)
|
||||
}
|
||||
}
|
||||
|
||||
func hookPrintResult(output, isCreate bool, branch, url string) {
|
||||
if !output {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if isCreate {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
os.Stderr.Sync()
|
||||
}
|
||||
|
||||
func pushOptions() map[string]string {
|
||||
opts := make(map[string]string)
|
||||
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
|
||||
|
@ -691,6 +694,12 @@ Gitea or set your environment appropriately.`, "")
|
|||
}
|
||||
err = writeFlushPktLine(ctx, os.Stdout)
|
||||
|
||||
if err == nil {
|
||||
for _, res := range resp.Results {
|
||||
hookPrintResult(res.ShouldShowMessage, res.IsCreatePR, res.HeadBranch, res.URL)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ func showWebStartupMessage(msg string) {
|
|||
log.Info("* WorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("* CustomPath: %s", setting.CustomPath)
|
||||
log.Info("* ConfigFile: %s", setting.CustomConf)
|
||||
log.Info("%s", msg)
|
||||
log.Info("%s", msg) // show startup message
|
||||
}
|
||||
|
||||
func serveInstall(ctx *cli.Context) error {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -227,7 +228,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||
if runner.RepoID != 0 {
|
||||
jobCond = builder.Eq{"repo_id": runner.RepoID}
|
||||
} else if runner.OwnerID != 0 {
|
||||
jobCond = builder.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": runner.OwnerID}))
|
||||
jobCond = builder.In("repo_id", builder.Select("id").From("repository").
|
||||
Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id").
|
||||
Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions}))
|
||||
}
|
||||
if jobCond.IsValid() {
|
||||
jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond))
|
||||
|
|
|
@ -76,23 +76,14 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
|
|||
// CalcFingerprint calculate public key's fingerprint
|
||||
func CalcFingerprint(publicKeyContent string) (string, error) {
|
||||
// Call the method based on configuration
|
||||
var (
|
||||
fnName, fp string
|
||||
err error
|
||||
)
|
||||
if len(setting.SSH.KeygenPath) == 0 {
|
||||
fnName = "calcFingerprintNative"
|
||||
fp, err = calcFingerprintNative(publicKeyContent)
|
||||
} else {
|
||||
fnName = "calcFingerprintSSHKeygen"
|
||||
fp, err = calcFingerprintSSHKeygen(publicKeyContent)
|
||||
}
|
||||
useNative := setting.SSH.KeygenPath == ""
|
||||
calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
|
||||
fp, err := calcFn(publicKeyContent)
|
||||
if err != nil {
|
||||
if IsErrKeyUnableVerify(err) {
|
||||
log.Info("%s", publicKeyContent)
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf("%s: %w", fnName, err)
|
||||
return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
|
||||
}
|
||||
return fp, nil
|
||||
}
|
||||
|
|
|
@ -10,21 +10,21 @@ import (
|
|||
)
|
||||
|
||||
// CountOrphanedObjects count subjects with have no existing refobject anymore
|
||||
func CountOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) (int64, error) {
|
||||
func CountOrphanedObjects(ctx context.Context, subject, refObject, joinCond string) (int64, error) {
|
||||
return GetEngine(ctx).
|
||||
Table("`"+subject+"`").
|
||||
Join("LEFT", "`"+refobject+"`", joinCond).
|
||||
Where(builder.IsNull{"`" + refobject + "`.id"}).
|
||||
Join("LEFT", "`"+refObject+"`", joinCond).
|
||||
Where(builder.IsNull{"`" + refObject + "`.id"}).
|
||||
Select("COUNT(`" + subject + "`.`id`)").
|
||||
Count()
|
||||
}
|
||||
|
||||
// DeleteOrphanedObjects delete subjects with have no existing refobject anymore
|
||||
func DeleteOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) error {
|
||||
func DeleteOrphanedObjects(ctx context.Context, subject, refObject, joinCond string) error {
|
||||
subQuery := builder.Select("`"+subject+"`.id").
|
||||
From("`"+subject+"`").
|
||||
Join("LEFT", "`"+refobject+"`", joinCond).
|
||||
Where(builder.IsNull{"`" + refobject + "`.id"})
|
||||
Join("LEFT", "`"+refObject+"`", joinCond).
|
||||
Where(builder.IsNull{"`" + refObject + "`.id"})
|
||||
b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`")
|
||||
_, err := GetEngine(ctx).Exec(b)
|
||||
return err
|
||||
|
|
|
@ -53,7 +53,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool {
|
|||
var u *RepoUnit
|
||||
var err error
|
||||
if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
|
||||
log.Trace("%s", err)
|
||||
log.Trace("IsDependenciesEnabled: %v", err)
|
||||
return setting.Service.DefaultEnableDependencies
|
||||
}
|
||||
return u.IssuesConfig().EnableDependencies
|
||||
|
|
|
@ -65,7 +65,7 @@ func createDefaultPolicy() *bluemonday.Policy {
|
|||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-num$`)).OnElements("td")
|
||||
policy.AllowAttrs("data-line-number").OnElements("span")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-code chroma$`)).OnElements("td")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("code")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("div")
|
||||
|
||||
// For code preview (unicode escape)
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^file-view( unicode-escaped)?$`)).OnElements("table")
|
||||
|
|
|
@ -58,6 +58,7 @@ type Package struct {
|
|||
type Metadata struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
ReleaseNotes string `json:"release_notes,omitempty"`
|
||||
Readme string `json:"readme,omitempty"`
|
||||
Authors string `json:"authors,omitempty"`
|
||||
ProjectURL string `json:"project_url,omitempty"`
|
||||
RepositoryURL string `json:"repository_url,omitempty"`
|
||||
|
@ -71,6 +72,7 @@ type Dependency struct {
|
|||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/nuget/reference/nuspec
|
||||
type nuspecPackage struct {
|
||||
Metadata struct {
|
||||
ID string `xml:"id"`
|
||||
|
@ -80,6 +82,7 @@ type nuspecPackage struct {
|
|||
ProjectURL string `xml:"projectUrl"`
|
||||
Description string `xml:"description"`
|
||||
ReleaseNotes string `xml:"releaseNotes"`
|
||||
Readme string `xml:"readme"`
|
||||
PackageTypes struct {
|
||||
PackageType []struct {
|
||||
Name string `xml:"name,attr"`
|
||||
|
@ -89,6 +92,11 @@ type nuspecPackage struct {
|
|||
URL string `xml:"url,attr"`
|
||||
} `xml:"repository"`
|
||||
Dependencies struct {
|
||||
Dependency []struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Version string `xml:"version,attr"`
|
||||
Exclude string `xml:"exclude,attr"`
|
||||
} `xml:"dependency"`
|
||||
Group []struct {
|
||||
TargetFramework string `xml:"targetFramework,attr"`
|
||||
Dependency []struct {
|
||||
|
@ -122,14 +130,14 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
return ParseNuspecMetaData(f)
|
||||
return ParseNuspecMetaData(archive, f)
|
||||
}
|
||||
}
|
||||
return nil, ErrMissingNuspecFile
|
||||
}
|
||||
|
||||
// ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
|
||||
func ParseNuspecMetaData(r io.Reader) (*Package, error) {
|
||||
func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
|
||||
var p nuspecPackage
|
||||
if err := xml.NewDecoder(r).Decode(&p); err != nil {
|
||||
return nil, err
|
||||
|
@ -166,6 +174,28 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
|
|||
Dependencies: make(map[string][]Dependency),
|
||||
}
|
||||
|
||||
if p.Metadata.Readme != "" {
|
||||
f, err := archive.Open(p.Metadata.Readme)
|
||||
if err == nil {
|
||||
buf, _ := io.ReadAll(f)
|
||||
m.Readme = string(buf)
|
||||
_ = f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.Metadata.Dependencies.Dependency) > 0 {
|
||||
deps := make([]Dependency, 0, len(p.Metadata.Dependencies.Dependency))
|
||||
for _, dep := range p.Metadata.Dependencies.Dependency {
|
||||
if dep.ID == "" || dep.Version == "" {
|
||||
continue
|
||||
}
|
||||
deps = append(deps, Dependency{
|
||||
ID: dep.ID,
|
||||
Version: dep.Version,
|
||||
})
|
||||
}
|
||||
m.Dependencies[""] = deps
|
||||
}
|
||||
for _, group := range p.Metadata.Dependencies.Group {
|
||||
deps := make([]Dependency, 0, len(group.Dependency))
|
||||
for _, dep := range group.Dependency {
|
||||
|
|
|
@ -6,7 +6,6 @@ package nuget
|
|||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -19,6 +18,7 @@ const (
|
|||
projectURL = "https://gitea.io"
|
||||
description = "Package Description"
|
||||
releaseNotes = "Package Release Notes"
|
||||
readme = "Readme"
|
||||
repositoryURL = "https://gitea.io/gitea/gitea"
|
||||
targetFramework = ".NETStandard2.1"
|
||||
dependencyID = "System.Text.Json"
|
||||
|
@ -36,6 +36,7 @@ const nuspecContent = `<?xml version="1.0" encoding="utf-8"?>
|
|||
<description>` + description + `</description>
|
||||
<releaseNotes>` + releaseNotes + `</releaseNotes>
|
||||
<repository url="` + repositoryURL + `" />
|
||||
<readme>README.md</readme>
|
||||
<dependencies>
|
||||
<group targetFramework="` + targetFramework + `">
|
||||
<dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
|
||||
|
@ -60,17 +61,19 @@ const symbolsNuspecContent = `<?xml version="1.0" encoding="utf-8"?>
|
|||
</package>`
|
||||
|
||||
func TestParsePackageMetaData(t *testing.T) {
|
||||
createArchive := func(name, content string) []byte {
|
||||
createArchive := func(files map[string]string) []byte {
|
||||
var buf bytes.Buffer
|
||||
archive := zip.NewWriter(&buf)
|
||||
w, _ := archive.Create(name)
|
||||
w.Write([]byte(content))
|
||||
for name, content := range files {
|
||||
w, _ := archive.Create(name)
|
||||
w.Write([]byte(content))
|
||||
}
|
||||
archive.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
t.Run("MissingNuspecFile", func(t *testing.T) {
|
||||
data := createArchive("dummy.txt", "")
|
||||
data := createArchive(map[string]string{"dummy.txt": ""})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.Nil(t, np)
|
||||
|
@ -78,7 +81,7 @@ func TestParsePackageMetaData(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("MissingNuspecFileInRoot", func(t *testing.T) {
|
||||
data := createArchive("sub/package.nuspec", "")
|
||||
data := createArchive(map[string]string{"sub/package.nuspec": ""})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.Nil(t, np)
|
||||
|
@ -86,7 +89,7 @@ func TestParsePackageMetaData(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("InvalidNuspecFile", func(t *testing.T) {
|
||||
data := createArchive("package.nuspec", "")
|
||||
data := createArchive(map[string]string{"package.nuspec": ""})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.Nil(t, np)
|
||||
|
@ -94,10 +97,10 @@ func TestParsePackageMetaData(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("InvalidPackageId", func(t *testing.T) {
|
||||
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
|
||||
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata></metadata>
|
||||
</package>`)
|
||||
</package>`})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.Nil(t, np)
|
||||
|
@ -105,30 +108,34 @@ func TestParsePackageMetaData(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("InvalidPackageVersion", func(t *testing.T) {
|
||||
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
|
||||
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>`+id+`</id>
|
||||
<id>` + id + `</id>
|
||||
</metadata>
|
||||
</package>`)
|
||||
</package>`})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.Nil(t, np)
|
||||
assert.ErrorIs(t, err, ErrNuspecInvalidVersion)
|
||||
})
|
||||
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
data := createArchive("package.nuspec", nuspecContent)
|
||||
t.Run("MissingReadme", func(t *testing.T) {
|
||||
data := createArchive(map[string]string{"package.nuspec": nuspecContent})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, np)
|
||||
assert.Empty(t, np.Metadata.Readme)
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseNuspecMetaData(t *testing.T) {
|
||||
t.Run("Dependency Package", func(t *testing.T) {
|
||||
np, err := ParseNuspecMetaData(strings.NewReader(nuspecContent))
|
||||
data := createArchive(map[string]string{
|
||||
"package.nuspec": nuspecContent,
|
||||
"README.md": readme,
|
||||
})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, np)
|
||||
assert.Equal(t, DependencyPackage, np.PackageType)
|
||||
|
@ -139,6 +146,7 @@ func TestParseNuspecMetaData(t *testing.T) {
|
|||
assert.Equal(t, projectURL, np.Metadata.ProjectURL)
|
||||
assert.Equal(t, description, np.Metadata.Description)
|
||||
assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
|
||||
assert.Equal(t, readme, np.Metadata.Readme)
|
||||
assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
|
||||
assert.Len(t, np.Metadata.Dependencies, 1)
|
||||
assert.Contains(t, np.Metadata.Dependencies, targetFramework)
|
||||
|
@ -148,13 +156,15 @@ func TestParseNuspecMetaData(t *testing.T) {
|
|||
assert.Equal(t, dependencyVersion, deps[0].Version)
|
||||
|
||||
t.Run("NormalizedVersion", func(t *testing.T) {
|
||||
np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>test</id>
|
||||
<version>1.04.5.2.5-rc.1+metadata</version>
|
||||
</metadata>
|
||||
</package>`))
|
||||
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>test</id>
|
||||
<version>1.04.5.2.5-rc.1+metadata</version>
|
||||
</metadata>
|
||||
</package>`})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, np)
|
||||
assert.Equal(t, "1.4.5.2-rc.1", np.Version)
|
||||
|
@ -162,7 +172,9 @@ func TestParseNuspecMetaData(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("Symbols Package", func(t *testing.T) {
|
||||
np, err := ParseNuspecMetaData(strings.NewReader(symbolsNuspecContent))
|
||||
data := createArchive(map[string]string{"package.nuspec": symbolsNuspecContent})
|
||||
|
||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, np)
|
||||
assert.Equal(t, SymbolsPackage, np.PackageType)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -32,13 +33,13 @@ const (
|
|||
)
|
||||
|
||||
// Bool checks for a key in the map and parses as a boolean
|
||||
func (g GitPushOptions) Bool(key string, def bool) bool {
|
||||
func (g GitPushOptions) Bool(key string) optional.Option[bool] {
|
||||
if val, ok := g[key]; ok {
|
||||
if b, err := strconv.ParseBool(val); err == nil {
|
||||
return b
|
||||
return optional.Some(b)
|
||||
}
|
||||
}
|
||||
return def
|
||||
return optional.None[bool]()
|
||||
}
|
||||
|
||||
// HookOptions represents the options for the Hook calls
|
||||
|
@ -87,13 +88,17 @@ type HookProcReceiveResult struct {
|
|||
|
||||
// HookProcReceiveRefResult represents an individual result from ProcReceive
|
||||
type HookProcReceiveRefResult struct {
|
||||
OldOID string
|
||||
NewOID string
|
||||
Ref string
|
||||
OriginalRef git.RefName
|
||||
IsForcePush bool
|
||||
IsNotMatched bool
|
||||
Err string
|
||||
OldOID string
|
||||
NewOID string
|
||||
Ref string
|
||||
OriginalRef git.RefName
|
||||
IsForcePush bool
|
||||
IsNotMatched bool
|
||||
Err string
|
||||
IsCreatePR bool
|
||||
URL string
|
||||
ShouldShowMessage bool
|
||||
HeadBranch string
|
||||
}
|
||||
|
||||
// HookPreReceive check whether the provided commits are allowed
|
||||
|
|
|
@ -315,21 +315,25 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
|
|||
}
|
||||
}
|
||||
|
||||
// DeprecatedWarnings contains the warning message for various deprecations, including: setting option, file/folder, etc
|
||||
var DeprecatedWarnings []string
|
||||
// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
|
||||
var StartupProblems []string
|
||||
|
||||
func logStartupProblem(skip int, level log.Level, format string, args ...any) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
log.Log(skip+1, level, "%s", msg)
|
||||
StartupProblems = append(StartupProblems, msg)
|
||||
}
|
||||
|
||||
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
msg := fmt.Sprintf("Deprecated config option `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||
log.Error("%v", msg)
|
||||
DeprecatedWarnings = append(DeprecatedWarnings, msg)
|
||||
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||
}
|
||||
}
|
||||
|
||||
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
||||
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
|
||||
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
|
|||
if !filepath.IsAbs(Indexer.IssuePath) {
|
||||
Indexer.IssuePath = filepath.ToSlash(filepath.Join(AppWorkPath, Indexer.IssuePath))
|
||||
}
|
||||
checkOverlappedPath("indexer.ISSUE_INDEXER_PATH", Indexer.IssuePath)
|
||||
checkOverlappedPath("[indexer].ISSUE_INDEXER_PATH", Indexer.IssuePath)
|
||||
} else {
|
||||
Indexer.IssueConnStr = sec.Key("ISSUE_INDEXER_CONN_STR").MustString(Indexer.IssueConnStr)
|
||||
if Indexer.IssueType == "meilisearch" {
|
||||
|
|
|
@ -118,6 +118,10 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
|||
return
|
||||
}
|
||||
|
||||
if sec.HasKey("DEFAULT_APPLICATIONS") && sec.Key("DEFAULT_APPLICATIONS").String() == "" {
|
||||
OAuth2.DefaultApplications = nil
|
||||
}
|
||||
|
||||
// Handle the rename of ENABLE to ENABLED
|
||||
deprecatedSetting(rootCfg, "oauth2", "ENABLE", "oauth2", "ENABLED", "v1.23.0")
|
||||
if sec.HasKey("ENABLE") && !sec.HasKey("ENABLED") {
|
||||
|
@ -168,7 +172,7 @@ func GetGeneralTokenSigningSecret() []byte {
|
|||
}
|
||||
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
||||
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
||||
log.Warn("OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
||||
logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
||||
return jwtSecret
|
||||
}
|
||||
return *generalSigningSecret.Load()
|
||||
|
|
|
@ -32,3 +32,21 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
|||
assert.Len(t, actual, 32)
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestOauth2DefaultApplications(t *testing.T) {
|
||||
cfg, _ := NewConfigProviderFromData(``)
|
||||
loadOAuth2From(cfg)
|
||||
assert.Equal(t, []string{"git-credential-oauth", "git-credential-manager", "tea"}, OAuth2.DefaultApplications)
|
||||
|
||||
cfg, _ = NewConfigProviderFromData(`[oauth2]
|
||||
DEFAULT_APPLICATIONS = tea
|
||||
`)
|
||||
loadOAuth2From(cfg)
|
||||
assert.Equal(t, []string{"tea"}, OAuth2.DefaultApplications)
|
||||
|
||||
cfg, _ = NewConfigProviderFromData(`[oauth2]
|
||||
DEFAULT_APPLICATIONS =
|
||||
`)
|
||||
loadOAuth2From(cfg)
|
||||
assert.Nil(t, nil, OAuth2.DefaultApplications)
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
|||
RepoRootPath = filepath.Clean(RepoRootPath)
|
||||
}
|
||||
|
||||
checkOverlappedPath("repository.ROOT", RepoRootPath)
|
||||
checkOverlappedPath("[repository].ROOT", RepoRootPath)
|
||||
|
||||
defaultDetectedCharsetsOrder := make([]string, 0, len(Repository.DetectedCharsetsOrder))
|
||||
for _, charset := range Repository.DetectedCharsetsOrder {
|
||||
|
|
|
@ -331,7 +331,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
|||
if !filepath.IsAbs(PprofDataPath) {
|
||||
PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
|
||||
}
|
||||
checkOverlappedPath("server.PPROF_DATA_PATH", PprofDataPath)
|
||||
checkOverlappedPath("[server].PPROF_DATA_PATH", PprofDataPath)
|
||||
|
||||
landingPage := sec.Key("LANDING_PAGE").MustString("home")
|
||||
switch landingPage {
|
||||
|
|
|
@ -46,7 +46,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
|
|||
SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(filepath.Join(AppDataPath, "sessions")), "\" ")
|
||||
if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
|
||||
SessionConfig.ProviderConfig = filepath.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
||||
checkOverlappedPath("session.PROVIDER_CONFIG", SessionConfig.ProviderConfig)
|
||||
checkOverlappedPath("[session].PROVIDER_CONFIG", SessionConfig.ProviderConfig)
|
||||
}
|
||||
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
|
||||
SessionConfig.CookiePath = AppSubURL
|
||||
|
|
|
@ -235,9 +235,7 @@ var configuredPaths = make(map[string]string)
|
|||
func checkOverlappedPath(name, path string) {
|
||||
// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
|
||||
if targetName, ok := configuredPaths[path]; ok && targetName != name {
|
||||
msg := fmt.Sprintf("Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
|
||||
log.Error("%s", msg)
|
||||
DeprecatedWarnings = append(DeprecatedWarnings, msg)
|
||||
logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
|
||||
}
|
||||
configuredPaths[path] = name
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType,
|
|||
}
|
||||
}
|
||||
|
||||
checkOverlappedPath("storage."+name+".PATH", storage.Path)
|
||||
checkOverlappedPath("[storage."+name+"].PATH", storage.Path)
|
||||
|
||||
return &storage, nil
|
||||
}
|
||||
|
|
|
@ -53,13 +53,13 @@ func NewFuncMap() template.FuncMap {
|
|||
"JsonUtils": NewJsonUtils,
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// svg / avatar / icon
|
||||
// svg / avatar / icon / color
|
||||
"svg": svg.RenderHTML,
|
||||
"EntryIcon": base.EntryIcon,
|
||||
"MigrationIcon": MigrationIcon,
|
||||
"ActionIcon": ActionIcon,
|
||||
|
||||
"SortArrow": SortArrow,
|
||||
"SortArrow": SortArrow,
|
||||
"ContrastColor": util.ContrastColor,
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// time / number / format
|
||||
|
|
|
@ -123,16 +123,10 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string)
|
|||
func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML {
|
||||
var (
|
||||
archivedCSSClass string
|
||||
textColor = "#111"
|
||||
textColor = util.ContrastColor(label.Color)
|
||||
labelScope = label.ExclusiveScope()
|
||||
)
|
||||
|
||||
r, g, b := util.HexToRBGColor(label.Color)
|
||||
// Determine if label text should be light or dark to be readable on background color
|
||||
if util.UseLightTextOnBackground(r, g, b) {
|
||||
textColor = "#eee"
|
||||
}
|
||||
|
||||
description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
|
||||
|
||||
if label.IsArchived() {
|
||||
|
@ -153,7 +147,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m
|
|||
|
||||
// Make scope and item background colors slightly darker and lighter respectively.
|
||||
// More contrast needed with higher luminance, empirically tweaked.
|
||||
luminance := util.GetLuminance(r, g, b)
|
||||
luminance := util.GetRelativeLuminance(label.Color)
|
||||
contrast := 0.01 + luminance*0.03
|
||||
// Ensure we add the same amount of contrast also near 0 and 1.
|
||||
darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
|
||||
|
@ -162,6 +156,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m
|
|||
darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
|
||||
lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
|
||||
|
||||
r, g, b := util.HexToRBGColor(label.Color)
|
||||
scopeBytes := []byte{
|
||||
uint8(math.Min(math.Round(r*darkenFactor), 255)),
|
||||
uint8(math.Min(math.Round(g*darkenFactor), 255)),
|
||||
|
|
|
@ -4,22 +4,10 @@ package util
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Check similar implementation in web_src/js/utils/color.js and keep synchronization
|
||||
|
||||
// Return R, G, B values defined in reletive luminance
|
||||
func getLuminanceRGB(channel float64) float64 {
|
||||
sRGB := channel / 255
|
||||
if sRGB <= 0.03928 {
|
||||
return sRGB / 12.92
|
||||
}
|
||||
return math.Pow((sRGB+0.055)/1.055, 2.4)
|
||||
}
|
||||
|
||||
// Get color as RGB values in 0..255 range from the hex color string (with or without #)
|
||||
func HexToRBGColor(colorString string) (float64, float64, float64) {
|
||||
hexString := colorString
|
||||
|
@ -47,19 +35,23 @@ func HexToRBGColor(colorString string) (float64, float64, float64) {
|
|||
return r, g, b
|
||||
}
|
||||
|
||||
// return luminance given RGB channels
|
||||
// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
|
||||
func GetLuminance(r, g, b float64) float64 {
|
||||
R := getLuminanceRGB(r)
|
||||
G := getLuminanceRGB(g)
|
||||
B := getLuminanceRGB(b)
|
||||
luminance := 0.2126*R + 0.7152*G + 0.0722*B
|
||||
return luminance
|
||||
// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
|
||||
// Keep this in sync with web_src/js/utils/color.js
|
||||
func GetRelativeLuminance(color string) float64 {
|
||||
r, g, b := HexToRBGColor(color)
|
||||
return (0.2126729*r + 0.7151522*g + 0.0721750*b) / 255
|
||||
}
|
||||
|
||||
// Reference from: https://firsching.ch/github_labels.html
|
||||
// In the future WCAG 3 APCA may be a better solution.
|
||||
// Check if text should use light color based on RGB of background
|
||||
func UseLightTextOnBackground(r, g, b float64) bool {
|
||||
return GetLuminance(r, g, b) < 0.453
|
||||
func UseLightText(backgroundColor string) bool {
|
||||
return GetRelativeLuminance(backgroundColor) < 0.453
|
||||
}
|
||||
|
||||
// Given a background color, returns a black or white foreground color that the highest
|
||||
// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
|
||||
// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
|
||||
func ContrastColor(backgroundColor string) string {
|
||||
if UseLightText(backgroundColor) {
|
||||
return "#fff"
|
||||
}
|
||||
return "#000"
|
||||
}
|
||||
|
|
|
@ -33,33 +33,31 @@ func Test_HexToRBGColor(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_UseLightTextOnBackground(t *testing.T) {
|
||||
func Test_UseLightText(t *testing.T) {
|
||||
cases := []struct {
|
||||
r float64
|
||||
g float64
|
||||
b float64
|
||||
expected bool
|
||||
color string
|
||||
expected string
|
||||
}{
|
||||
{215, 58, 74, true},
|
||||
{0, 117, 202, true},
|
||||
{207, 211, 215, false},
|
||||
{162, 238, 239, false},
|
||||
{112, 87, 255, true},
|
||||
{0, 134, 114, true},
|
||||
{228, 230, 105, false},
|
||||
{216, 118, 227, true},
|
||||
{255, 255, 255, false},
|
||||
{43, 134, 133, true},
|
||||
{43, 135, 134, true},
|
||||
{44, 135, 134, true},
|
||||
{59, 182, 179, true},
|
||||
{124, 114, 104, true},
|
||||
{126, 113, 108, true},
|
||||
{129, 112, 109, true},
|
||||
{128, 112, 112, true},
|
||||
{"#d73a4a", "#fff"},
|
||||
{"#0075ca", "#fff"},
|
||||
{"#cfd3d7", "#000"},
|
||||
{"#a2eeef", "#000"},
|
||||
{"#7057ff", "#fff"},
|
||||
{"#008672", "#fff"},
|
||||
{"#e4e669", "#000"},
|
||||
{"#d876e3", "#000"},
|
||||
{"#ffffff", "#000"},
|
||||
{"#2b8684", "#fff"},
|
||||
{"#2b8786", "#fff"},
|
||||
{"#2c8786", "#000"},
|
||||
{"#3bb6b3", "#000"},
|
||||
{"#7c7268", "#fff"},
|
||||
{"#7e716c", "#fff"},
|
||||
{"#81706d", "#fff"},
|
||||
{"#807070", "#fff"},
|
||||
{"#84b6eb", "#000"},
|
||||
}
|
||||
for n, c := range cases {
|
||||
result := UseLightTextOnBackground(c.r, c.g, c.b)
|
||||
assert.Equal(t, c.expected, result, "case %d: error should match", n)
|
||||
assert.Equal(t, c.expected, ContrastColor(c.color), "case %d: error should match", n)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ func ToPointer[T any](val T) *T {
|
|||
}
|
||||
|
||||
// Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal"
|
||||
func Iif[T comparable](condition bool, trueVal, falseVal T) T {
|
||||
func Iif[T any](condition bool, trueVal, falseVal T) T {
|
||||
if condition {
|
||||
return trueVal
|
||||
}
|
||||
|
|
|
@ -210,21 +210,21 @@ PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE
|
|||
|
||||
The Initial Contributor is:
|
||||
____________________________________________________
|
||||
|
||||
|
||||
[Enter full name of Initial Contributor]
|
||||
|
||||
Address of Initial Contributor:
|
||||
________________________________________________
|
||||
|
||||
|
||||
________________________________________________
|
||||
|
||||
|
||||
________________________________________________
|
||||
|
||||
|
||||
[Enter address above]
|
||||
|
||||
The Designated Web Site is:
|
||||
__________________________________________________
|
||||
|
||||
|
||||
[Enter URL for Designated Web Site of Initial Contributor]
|
||||
|
||||
NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6.
|
||||
|
@ -237,27 +237,27 @@ The date on which the Initial Work was first available under this License: _____
|
|||
|
||||
PART 3: GOVERNING JURISDICTION
|
||||
|
||||
For the purposes of this License, the Governing Jurisdiction is _________________________________________________.
[Initial Contributor to Enter Governing Jurisdiction here]
|
||||
For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here]
|
||||
|
||||
PART 4: THIRD PARTIES
|
||||
|
||||
For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected.
|
||||
SELECTION
|
||||
|
||||
|
||||
BOX PARAGRAPH
|
||||
[ ] A. "THIRD PARTY" means any third party.
|
||||
|
||||
|
||||
[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b).
|
||||
|
||||
|
||||
[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest.
|
||||
|
||||
|
||||
[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
|
||||
|
||||
|
||||
[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
|
||||
[ ] A. "THIRD PARTY" means any third party.
|
||||
|
||||
|
||||
[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b).
|
||||
|
||||
|
||||
[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest.
|
||||
|
||||
|
||||
[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
|
||||
|
||||
|
||||
[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
|
||||
The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor.
|
||||
|
||||
PART 5: NOTICE
|
||||
|
@ -271,8 +271,8 @@ PART 6: PATENT LICENSING TERMS
|
|||
For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below.
|
||||
|
||||
Is this a Patents-Included License pursuant to Section 2.2 of the License?
|
||||
YES [ ]
|
||||
NO [ ]
|
||||
YES [ ]
|
||||
NO [ ]
|
||||
|
||||
By default, if YES is not selected by the Initial Contributor, the answer is NO.
|
||||
|
||||
|
|
|
@ -4,5 +4,5 @@ Any user of this software should understand that IBM cannot provide technical su
|
|||
|
||||
Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software.
|
||||
|
||||
COPYRIGHT I B M CORPORATION 2002
|
||||
LICENSED MATERIAL - PROGRAM PROPERTY OF I B M
|
||||
COPYRIGHT I B M CORPORATION 2002
|
||||
LICENSED MATERIAL - PROGRAM PROPERTY OF I B M
|
||||
|
|
|
@ -12,15 +12,15 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice
|
|||
This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
|
||||
|
||||
You are free to:
|
||||
copy, publish, distribute and transmit the Information;
|
||||
copy, publish, distribute and transmit the Information;
|
||||
adapt the Information;
|
||||
exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application.
|
||||
|
||||
You are not permitted to:
|
||||
exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation.
|
||||
exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation.
|
||||
|
||||
You must, where you do any of the above:
|
||||
acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
|
||||
acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
|
||||
|
||||
If the Information Provider does not provide a specific attribution statement, you must use the following:
|
||||
Contains information licensed under the Non-Commercial Government Licence v2.0.
|
||||
|
|
|
@ -2,7 +2,7 @@ Netscape Public LIcense version 1.1
|
|||
|
||||
AMENDMENTS
|
||||
|
||||
The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1.
|
||||
The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1.
|
||||
|
||||
Additional Terms applicable to the Netscape Public License.
|
||||
|
||||
|
@ -28,7 +28,7 @@ Additional Terms applicable to the Netscape Public License.
|
|||
Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License.
|
||||
|
||||
EXHIBIT A-Netscape Public License.
|
||||
|
||||
|
||||
"The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
|
||||
|
@ -37,8 +37,8 @@ The Original Code is Mozilla Communicator client code, released March 31, 1998.
|
|||
|
||||
The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved.
|
||||
Contributor(s): ______________________________________.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License."
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License."
|
||||
|
||||
|
||||
Mozilla Public License Version 1.1
|
||||
|
|
|
@ -6,7 +6,7 @@ OPEN CASCADE releases and makes publicly available the source code of the softwa
|
|||
It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
|
||||
Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software.
|
||||
|
||||
|
||||
1. Definitions
|
||||
|
||||
Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning.
|
||||
|
@ -26,13 +26,13 @@ Unless there is something in the subject matter or in the context inconsistent t
|
|||
"Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof.
|
||||
|
||||
"You" or "Your": means an individual or a legal entity exercising rights under this License
|
||||
|
||||
|
||||
2. Acceptance of license
|
||||
By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You.
|
||||
|
||||
|
||||
3. Scope and purpose
|
||||
This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License.
|
||||
|
||||
|
||||
4. Contributor license
|
||||
Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that:
|
||||
|
||||
|
|
|
@ -10,20 +10,20 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice
|
|||
This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
|
||||
|
||||
You are free to:
|
||||
copy, publish, distribute and transmit the Information;
|
||||
copy, publish, distribute and transmit the Information;
|
||||
adapt the Information;
|
||||
exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application.
|
||||
|
||||
You must, where you do any of the above:
|
||||
acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
|
||||
If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following:
Contains public sector information licensed under the Open Government Licence v1.0.
|
||||
acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
|
||||
If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0.
|
||||
ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information;
|
||||
ensure that you do not mislead others or misrepresent the Information or its source;
|
||||
ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003.
|
||||
|
||||
These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
|
||||
|
||||
Exemptions
|
||||
Exemptions
|
||||
|
||||
This licence does not cover the use of:
|
||||
- personal data in the Information;
|
||||
|
@ -48,22 +48,22 @@ Definitions
|
|||
|
||||
In this licence, the terms below have the following meanings:
|
||||
|
||||
‘Information’
means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
|
||||
‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
|
||||
|
||||
‘Information Provider’
means the person or organisation providing the Information under this licence.
|
||||
‘Information Provider’ means the person or organisation providing the Information under this licence.
|
||||
|
||||
‘Licensor’
means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
|
||||
‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
|
||||
|
||||
‘Use’
as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
|
||||
‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
|
||||
|
||||
‘You’
means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
|
||||
‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
|
||||
|
||||
About the Open Government Licence
|
||||
The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence.
|
||||
|
||||
The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
|
||||
The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
|
||||
|
||||
This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so.
|
||||
These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights.
|
||||
|
||||
Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
|
||||
Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
|
||||
|
|
|
@ -100,7 +100,8 @@ If it is impossible for You to comply with any of the terms of this License with
|
|||
5.1 Failure to Comply
|
||||
The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice.
|
||||
|
||||
5.2 Patent Infringement Claims
If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
|
||||
5.2 Patent Infringement Claims
|
||||
If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
|
||||
|
||||
5.3 Additional Compliance Terms
|
||||
Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years.
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
# Solderpad Hardware Licence Version 2.0
|
||||
|
||||
This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation.
|
||||
This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation.
|
||||
|
||||
1. The definitions set out in the Apache License are modified as follows:
|
||||
|
||||
Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below).
|
||||
Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below).
|
||||
|
||||
Contribution also includes any design, as well as any work of authorship.
|
||||
Contribution also includes any design, as well as any work of authorship.
|
||||
|
||||
Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof.
|
||||
Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof.
|
||||
|
||||
Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
|
||||
Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
|
||||
|
||||
Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
|
||||
Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
|
||||
|
||||
Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
|
||||
Work also includes a design or work of authorship, whether in Source form or other Object form.
|
||||
Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
|
||||
Work also includes a design or work of authorship, whether in Source form or other Object form.
|
||||
|
||||
2. Grant of Licence
|
||||
|
||||
2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
|
||||
2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
|
||||
|
|
|
@ -19,7 +19,7 @@ The following definitions shall replace the corresponding definitions in the Apa
|
|||
"License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
|
||||
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
|
||||
|
|
|
@ -36,13 +36,13 @@ Sun Industry Standards Source License - Version 1.1
|
|||
|
||||
2.0 SOURCE CODE LICENSE
|
||||
|
||||
2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
|
||||
2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
|
||||
|
||||
(b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
|
||||
(c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
|
||||
(d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.
|
||||
(d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.
|
||||
|
||||
3.0 DISTRIBUTION OBLIGATIONS
|
||||
|
||||
|
@ -92,14 +92,14 @@ This License represents the complete agreement concerning subject matter hereof.
|
|||
|
||||
EXHIBIT A - Sun Standards License
|
||||
|
||||
"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________.
|
||||
"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________.
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
|
||||
Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
|
||||
express or implied. See the License for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is ______________________________________.
|
||||
|
||||
The Initial Developer of the Original Code is:
|
||||
The Initial Developer of the Original Code is:
|
||||
Sun Microsystems, Inc..
|
||||
|
||||
Portions created by: _______________________________________
|
||||
|
|
|
@ -4,7 +4,7 @@ Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of T
|
|||
|
||||
This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
|
||||
Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
|
||||
|
||||
1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
|
||||
|
||||
|
|
|
@ -1331,12 +1331,53 @@ runners.task_list.repository=Repositori
|
|||
runners.task_list.commit=Memperbuat
|
||||
|
||||
runs.commit=Memperbuat
|
||||
runs.no_matching_online_runner_helper=Tidak ada runner online yang cocok dengan label: %s
|
||||
runs.actor=Aktor
|
||||
runs.status=Status
|
||||
runs.actors_no_select=Semua aktor
|
||||
runs.status_no_select=Semua status
|
||||
runs.no_results=Tidak ada hasil yang cocok.
|
||||
runs.no_workflows=Belum ada alur kerja.
|
||||
runs.no_workflows.quick_start=Tidak tahu cara memulai dengan Gitea Actions? Lihat <a target="_blank" rel="noopener noreferrer" href="%s">panduan cepat</a>.
|
||||
runs.no_workflows.documentation=Untuk informasi lebih lanjut tentang Gitea Actions, lihat <a target="_blank" rel="noopener noreferrer" href="%s">dokumentasi</a>.
|
||||
runs.no_runs=Alur kerja belum berjalan.
|
||||
runs.empty_commit_message=(pesan commit kosong)
|
||||
|
||||
workflow.disable=Nonaktifkan Alur Kerja
|
||||
workflow.disable_success=Alur kerja '%s' berhasil dinonaktifkan.
|
||||
workflow.enable=Aktifkan Alur Kerja
|
||||
workflow.enable_success=Alur kerja '%s' berhasil diaktifkan.
|
||||
workflow.disabled=Alur kerja dinonaktifkan.
|
||||
|
||||
need_approval_desc=Butuh persetujuan untuk menjalankan alur kerja untuk pull request fork.
|
||||
|
||||
variables=Variabel
|
||||
variables.management=Managemen Variabel
|
||||
variables.creation=Tambah Variabel
|
||||
variables.none=Belum ada variabel.
|
||||
variables.deletion=Hapus variabel
|
||||
variables.deletion.description=Menghapus variabel bersifat permanen dan tidak dapat dibatalkan. Lanjutkan?
|
||||
variables.description=Variabel akan diteruskan ke beberapa tindakan dan tidak dapat dibaca sebaliknya.
|
||||
variables.id_not_exist=Variabel dengan ID %d tidak ada.
|
||||
variables.edit=Edit Variabel
|
||||
variables.deletion.failed=Gagal menghapus variabel.
|
||||
variables.deletion.success=Variabel telah dihapus.
|
||||
variables.creation.failed=Gagal menambahkan variabel.
|
||||
variables.creation.success=Variabel "%s" telah ditambahkan.
|
||||
variables.update.failed=Gagal mengedit variabel.
|
||||
variables.update.success=Variabel telah diedit.
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Proyek Individu
|
||||
type-2.display_name=Proyek Repositori
|
||||
type-3.display_name=Proyek Organisasi
|
||||
|
||||
[git.filemode]
|
||||
changed_filemode=%[1]s → %[2]s
|
||||
; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
|
||||
directory=Directory
|
||||
normal_file=Normal file
|
||||
executable_file=Executable file
|
||||
symbolic_link=Symbolic link
|
||||
submodule=Submodule
|
||||
|
||||
|
|
|
@ -652,7 +652,7 @@ block.unblock.failure=ユーザーのブロック解除に失敗しました: %s
|
|||
block.blocked=あなたはこのユーザーをブロックしています。
|
||||
block.title=ユーザーをブロックする
|
||||
block.info=ユーザーをブロックすると、そのユーザーは、プルリクエストやイシューの作成、コメントの投稿など、リポジトリに対する操作ができなくなります。 ユーザーのブロックについてはよく確認してください。
|
||||
block.info_1=ユーザーをブロックすることで、あなたのアカウントとリポジトリに対する以下の行為を防ぎます:
|
||||
block.info_1=ユーザーをブロックすることで、あなたのアカウントとあなたのリポジトリに対する以下の行為を阻止します:
|
||||
block.info_2=あなたのアカウントのフォロー
|
||||
block.info_3=あなたのユーザー名で@メンションして通知を送ること
|
||||
block.info_4=そのユーザーのリポジトリに、あなたを共同作業者として招待すること
|
||||
|
@ -709,8 +709,8 @@ language=言語
|
|||
ui=テーマ
|
||||
hidden_comment_types=非表示にするコメントの種類
|
||||
hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「<ユーザー> が <ラベル> を追加/削除」といったコメントはすべて除去されます。
|
||||
hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照されたというコメント
|
||||
hidden_comment_types.issue_ref_tooltip=このイシューに関連付けるブランチやタグをユーザーが変更したというコメント
|
||||
hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照された、というコメント
|
||||
hidden_comment_types.issue_ref_tooltip=このイシューのブランチやタグへの関連付けをユーザーが変更した、というコメント
|
||||
comment_type_group_reference=参照
|
||||
comment_type_group_label=ラベル
|
||||
comment_type_group_milestone=マイルストーン
|
||||
|
@ -780,7 +780,7 @@ add_email_success=新しいメールアドレスを追加しました。
|
|||
email_preference_set_success=メール設定を保存しました。
|
||||
add_openid_success=新しいOpenIDアドレスを追加しました。
|
||||
keep_email_private=メールアドレスを隠す
|
||||
keep_email_private_popup=これによりプロフィールでメールアドレスが隠され、Webインターフェースでのプルリクエスト作成やファイル編集でもメールアドレスが隠されます。 プッシュ済みのコミットは変更されません。
|
||||
keep_email_private_popup=あなたのプロフィールからメールアドレスが隠され、Webインターフェースを使ったプルリクエスト作成やファイル編集でも、メールアドレスが隠されます。 プッシュ済みのコミットは変更されません。 コミットであなたのアカウントに関連付ける場合は %s を使用してください。
|
||||
openid_desc=OpenIDを使うと外部プロバイダーに認証を委任することができます。
|
||||
|
||||
manage_ssh_keys=SSHキーの管理
|
||||
|
@ -2961,12 +2961,12 @@ packages.size=サイズ
|
|||
packages.published=配布
|
||||
|
||||
defaulthooks=デフォルトWebhook
|
||||
defaulthooks.desc=Webhookは、特定のGiteaイベントのトリガーが発生した際に、自動的にHTTP POSTリクエストをサーバーへ送信するものです。 ここで定義されたWebhookはデフォルトとなり、全ての新規リポジトリにコピーされます。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
|
||||
defaulthooks.desc=Webhookは、特定のGiteaイベントが発生したときに、サーバーにHTTP POSTリクエストを自動的に送信するものです。 ここで定義したWebhookはデフォルトとなり、全ての新規リポジトリにコピーされます。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
|
||||
defaulthooks.add_webhook=デフォルトWebhookの追加
|
||||
defaulthooks.update_webhook=デフォルトWebhookの更新
|
||||
|
||||
systemhooks=システムWebhook
|
||||
systemhooks.desc=Webhookは、特定のGiteaイベントのトリガーが発生した際に、自動的にHTTP POSTリクエストをサーバーへ送信するものです。 ここで定義したWebhookはシステム内のすべてのリポジトリで呼び出されます。 そのため、パフォーマンスに及ぼす影響を考慮したうえで設定してください。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
|
||||
systemhooks.desc=Webhookは、特定のGiteaイベントが発生したときに、サーバーにHTTP POSTリクエストを自動的に送信するものです。 ここで定義したWebhookは、システム内のすべてのリポジトリで呼び出されます。 そのため、パフォーマンスに及ぼす影響を考慮したうえで設定してください。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
|
||||
systemhooks.add_webhook=システムWebhookを追加
|
||||
systemhooks.update_webhook=システムWebhookを更新
|
||||
|
||||
|
@ -3342,9 +3342,9 @@ raw_seconds=秒
|
|||
raw_minutes=分
|
||||
|
||||
[dropzone]
|
||||
default_message=ここにファイルをドロップまたはクリックしてアップロードします。
|
||||
default_message=ファイルをここにドロップ、またはここをクリックしてアップロード
|
||||
invalid_input_type=この種類のファイルはアップロードできません。
|
||||
file_too_big=アップロードされたファイルのサイズ ({{filesize}} MB) が最大サイズ ({{maxFilesize}} MB) を超えています。
|
||||
file_too_big=アップロードされたファイルのサイズ ({{filesize}} MB) は、最大サイズ ({{maxFilesize}} MB) を超えています。
|
||||
remove_file=ファイル削除
|
||||
|
||||
[notification]
|
||||
|
@ -3369,7 +3369,7 @@ error.no_committer_account=コミッターのメールアドレスに対応す
|
|||
error.no_gpg_keys_found=この署名に対応する既知のキーがデータベースに存在しません
|
||||
error.not_signed_commit=署名されたコミットではありません
|
||||
error.failed_retrieval_gpg_keys=コミッターのアカウントに登録されたキーを取得できませんでした
|
||||
error.probable_bad_signature=警告! このIDの鍵はデータベースに登録されていますが、その鍵でコミットの検証が通りません! これは疑わしいコミットです。
|
||||
error.probable_bad_signature=警告! このIDに該当する鍵がデータベースにありますが、コミットの検証が通りません! これは疑わしいコミットです。
|
||||
error.probable_bad_default_signature=警告! これはデフォルト鍵のIDですが、デフォルト鍵ではコミットの検証が通りません! これは疑わしいコミットです。
|
||||
|
||||
[units]
|
||||
|
@ -3382,7 +3382,7 @@ title=パッケージ
|
|||
desc=リポジトリ パッケージを管理します。
|
||||
empty=パッケージはまだありません。
|
||||
empty.documentation=パッケージレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
|
||||
empty.repo=パッケージはアップロードしたけども、ここに表示されない? <a href="%[1]s">パッケージ設定</a>を開いて、パッケージをこのリポジトリにリンクしてください。
|
||||
empty.repo=パッケージはアップロード済みで、ここに表示されていないですか? <a href="%[1]s">パッケージ設定</a>を開いて、パッケージをこのリポジトリにリンクしてください。
|
||||
registry.documentation=%sレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
|
||||
filter.type=タイプ
|
||||
filter.type.all=すべて
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
|
@ -201,7 +200,6 @@ func ReadRepoNotifications(ctx *context.APIContext) {
|
|||
if !ctx.FormBool("all") {
|
||||
statuses := ctx.FormStrings("status-types")
|
||||
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
|
||||
log.Error("%v", opts.Status)
|
||||
}
|
||||
nl, err := db.Find[activities_model.Notification](ctx, opts)
|
||||
if err != nil {
|
||||
|
|
|
@ -311,7 +311,7 @@ func SearchIssues(ctx *context.APIContext) {
|
|||
|
||||
ctx.SetLinkHeader(int(total), limit)
|
||||
ctx.SetTotalCountHeader(total)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
// ListIssues list the issues of a repository
|
||||
|
@ -548,7 +548,7 @@ func ListIssues(ctx *context.APIContext) {
|
|||
|
||||
ctx.SetLinkHeader(int(total), listOptions.PageSize)
|
||||
ctx.SetTotalCountHeader(total)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
|
||||
|
@ -614,7 +614,7 @@ func GetIssue(ctx *context.APIContext) {
|
|||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue))
|
||||
}
|
||||
|
||||
// CreateIssue create an issue of a repository
|
||||
|
@ -737,7 +737,7 @@ func CreateIssue(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
|
||||
}
|
||||
|
||||
// EditIssue modify an issue of a repository
|
||||
|
@ -911,7 +911,7 @@ func EditIssue(ctx *context.APIContext) {
|
|||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
|
||||
}
|
||||
|
||||
func DeleteIssue(ctx *context.APIContext) {
|
||||
|
|
|
@ -107,7 +107,7 @@ func ListIssueAttachments(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue).Attachments)
|
||||
}
|
||||
|
||||
// CreateIssueAttachment creates an attachment and saves the given file
|
||||
|
|
|
@ -153,7 +153,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
|
|||
blockerIssues = append(blockerIssues, &blocker.Issue)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues))
|
||||
}
|
||||
|
||||
// CreateIssueDependency create a new issue dependencies
|
||||
|
@ -214,7 +214,7 @@ func CreateIssueDependency(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
|
||||
}
|
||||
|
||||
// RemoveIssueDependency remove an issue dependency
|
||||
|
@ -275,7 +275,7 @@ func RemoveIssueDependency(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
|
||||
}
|
||||
|
||||
// GetIssueBlocks list issues that are blocked by this issue
|
||||
|
@ -381,7 +381,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
|
|||
issues = append(issues, &depMeta.Issue)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
// CreateIssueBlocking block the issue given in the body by the issue in path
|
||||
|
@ -438,7 +438,7 @@ func CreateIssueBlocking(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
|
||||
}
|
||||
|
||||
// RemoveIssueBlocking unblock the issue given in the body by the issue in path
|
||||
|
@ -495,7 +495,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
|
||||
}
|
||||
|
||||
func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
|
||||
|
|
|
@ -207,7 +207,7 @@ func ListPinnedIssues(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
// ListPinnedPullRequests returns a list of all pinned PRs
|
||||
|
|
|
@ -138,7 +138,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
|
||||
}
|
||||
|
||||
// AddTime add time manual to the given issue
|
||||
|
@ -225,7 +225,7 @@ func AddTime(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, trackedTime))
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime))
|
||||
}
|
||||
|
||||
// ResetIssueTime reset time manual to the given issue
|
||||
|
@ -455,7 +455,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
|
||||
}
|
||||
|
||||
// ListTrackedTimesByRepository lists all tracked times of the repository
|
||||
|
@ -567,7 +567,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
|
||||
}
|
||||
|
||||
// ListMyTrackedTimes lists all tracked times of the current user
|
||||
|
@ -629,5 +629,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
|
|||
defer rd.Close()
|
||||
|
||||
if err := json.NewDecoder(rd).Decode(&genRequest); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("JSON Decode failed: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: err.Error(),
|
||||
})
|
||||
|
@ -35,7 +35,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
|
|||
|
||||
owner, repo, err := parseScope(ctx, genRequest.Scope)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("parseScope failed: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: err.Error(),
|
||||
})
|
||||
|
@ -45,18 +45,18 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
|
|||
if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) {
|
||||
token, err = actions_model.NewRunnerToken(ctx, owner, repo)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("error while creating runner token: %v", err)
|
||||
log.Error("%v", err)
|
||||
errMsg := fmt.Sprintf("error while creating runner token: %v", err)
|
||||
log.Error("NewRunnerToken failed: %v", errMsg)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: err,
|
||||
Err: errMsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
} else if err != nil {
|
||||
err := fmt.Sprintf("could not get unactivated runner token: %v", err)
|
||||
log.Error("%v", err)
|
||||
errMsg := fmt.Sprintf("could not get unactivated runner token: %v", err)
|
||||
log.Error("GetLatestRunnerToken failed: %v", errMsg)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: err,
|
||||
Err: errMsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ package private
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -159,8 +160,10 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
}
|
||||
}
|
||||
|
||||
isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate)
|
||||
isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate)
|
||||
// Handle Push Options
|
||||
if len(opts.GitPushOptions) > 0 {
|
||||
if isPrivate.Has() || isTemplate.Has() {
|
||||
// load the repository
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
|
@ -171,13 +174,49 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
wasEmpty = repo.IsEmpty
|
||||
}
|
||||
|
||||
repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate)
|
||||
repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate)
|
||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, "is_private", "is_template"); err != nil {
|
||||
pusher, err := user_model.GetUserByID(ctx, opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, pusher)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if !perm.IsOwner() && !perm.IsAdmin() {
|
||||
ctx.JSON(http.StatusNotFound, private.HookPostReceiveResult{
|
||||
Err: "Permissions denied",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
cols := make([]string, 0, len(opts.GitPushOptions))
|
||||
|
||||
if isPrivate.Has() {
|
||||
repo.IsPrivate = isPrivate.Value()
|
||||
cols = append(cols, "is_private")
|
||||
}
|
||||
|
||||
if isTemplate.Has() {
|
||||
repo.IsTemplate = isTemplate.Value()
|
||||
cols = append(cols, "is_template")
|
||||
}
|
||||
|
||||
if len(cols) > 0 {
|
||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, cols...); err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,42 +231,6 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
refFullName := opts.RefFullNames[i]
|
||||
newCommitID := opts.NewCommitIDs[i]
|
||||
|
||||
// post update for agit pull request
|
||||
// FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR
|
||||
if git.DefaultFeatures.SupportProcReceive && refFullName.IsPull() {
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pullIndex, _ := strconv.ParseInt(refFullName.PullName(), 10, 64)
|
||||
if pullIndex <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetPullRequestByIndex(ctx, repo.ID, pullIndex)
|
||||
if err != nil && !issues_model.IsErrPullRequestNotExist(err) {
|
||||
log.Error("Failed to get PR by index %v Error: %v", pullIndex, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Failed to get PR by index %v Error: %v", pullIndex, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if pr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, private.HookPostReceiveBranchResult{
|
||||
Message: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
|
||||
Create: false,
|
||||
Branch: "",
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// If we've pushed a branch (and not deleted it)
|
||||
if !git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() {
|
||||
// First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo
|
||||
|
|
|
@ -47,7 +47,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
|
|||
_ = stdoutWriter.Close()
|
||||
err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("readAndVerifyCommitsFromShaReader failed: %v", err)
|
||||
cancel()
|
||||
}
|
||||
_ = stdoutReader.Close()
|
||||
|
@ -66,7 +66,6 @@ func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository
|
|||
line := scanner.Text()
|
||||
err := readAndVerifyCommit(line, repo, env)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ func SendEmail(ctx *context.PrivateContext) {
|
|||
defer rd.Close()
|
||||
|
||||
if err := json.NewDecoder(rd).Decode(&mail); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("JSON Decode failed: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: err.Error(),
|
||||
})
|
||||
|
|
|
@ -117,11 +117,11 @@ func updateSystemStatus() {
|
|||
sysStatus.NumGC = m.NumGC
|
||||
}
|
||||
|
||||
func prepareDeprecatedWarningsAlert(ctx *context.Context) {
|
||||
if len(setting.DeprecatedWarnings) > 0 {
|
||||
content := setting.DeprecatedWarnings[0]
|
||||
if len(setting.DeprecatedWarnings) > 1 {
|
||||
content += fmt.Sprintf(" (and %d more)", len(setting.DeprecatedWarnings)-1)
|
||||
func prepareStartupProblemsAlert(ctx *context.Context) {
|
||||
if len(setting.StartupProblems) > 0 {
|
||||
content := setting.StartupProblems[0]
|
||||
if len(setting.StartupProblems) > 1 {
|
||||
content += fmt.Sprintf(" (and %d more)", len(setting.StartupProblems)-1)
|
||||
}
|
||||
ctx.Flash.Error(content, true)
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ func Dashboard(ctx *context.Context) {
|
|||
updateSystemStatus()
|
||||
ctx.Data["SysStatus"] = sysStatus
|
||||
ctx.Data["SSH"] = setting.SSH
|
||||
prepareDeprecatedWarningsAlert(ctx)
|
||||
prepareStartupProblemsAlert(ctx)
|
||||
ctx.HTML(http.StatusOK, tplDashboard)
|
||||
}
|
||||
|
||||
|
@ -191,10 +191,10 @@ func DashboardPost(ctx *context.Context) {
|
|||
func SelfCheck(ctx *context.Context) {
|
||||
ctx.Data["PageIsAdminSelfCheck"] = true
|
||||
|
||||
ctx.Data["DeprecatedWarnings"] = setting.DeprecatedWarnings
|
||||
if len(setting.DeprecatedWarnings) == 0 && !setting.IsProd {
|
||||
ctx.Data["StartupProblems"] = setting.StartupProblems
|
||||
if len(setting.StartupProblems) == 0 && !setting.IsProd {
|
||||
if time.Now().Unix()%2 == 0 {
|
||||
ctx.Data["DeprecatedWarnings"] = []string{"This is a test warning message in dev mode"}
|
||||
ctx.Data["StartupProblems"] = []string{"This is a test warning message in dev mode"}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ func Config(ctx *context.Context) {
|
|||
|
||||
ctx.Data["Loggers"] = log.GetManager().DumpLoggers()
|
||||
config.GetDynGetter().InvalidateCache()
|
||||
prepareDeprecatedWarningsAlert(ctx)
|
||||
prepareStartupProblemsAlert(ctx)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplConfig)
|
||||
}
|
||||
|
|
|
@ -403,7 +403,6 @@ func EditUserPost(ctx *context.Context) {
|
|||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form)
|
||||
case password.IsErrIsPwnedRequest(err):
|
||||
log.Error("%s", err.Error())
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form)
|
||||
default:
|
||||
|
|
|
@ -214,7 +214,6 @@ func ResetPasswdPost(ctx *context.Context) {
|
|||
case errors.Is(err, password.ErrIsPwned):
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplResetPassword, nil)
|
||||
case password.IsErrIsPwnedRequest(err):
|
||||
log.Error("%s", err.Error())
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplResetPassword, nil)
|
||||
default:
|
||||
ctx.ServerError("UpdateAuth", err)
|
||||
|
@ -298,7 +297,6 @@ func MustChangePasswordPost(ctx *context.Context) {
|
|||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplMustChangePassword, &form)
|
||||
case password.IsErrIsPwnedRequest(err):
|
||||
log.Error("%s", err.Error())
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplMustChangePassword, &form)
|
||||
default:
|
||||
|
|
|
@ -2179,7 +2179,7 @@ func GetIssueInfo(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue))
|
||||
}
|
||||
|
||||
// UpdateIssueTitle change issue's title
|
||||
|
@ -2709,7 +2709,7 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.SetTotalCountHeader(total)
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
|
||||
|
@ -2879,7 +2879,7 @@ func ListIssues(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.SetTotalCountHeader(total)
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
|
||||
}
|
||||
|
||||
func BatchDeleteIssues(ctx *context.Context) {
|
||||
|
|
|
@ -226,6 +226,12 @@ func TestWikiRaw(t *testing.T) {
|
|||
func TestDefaultWikiBranch(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
|
||||
// repo with no wiki
|
||||
repoWithNoWiki := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.False(t, repoWithNoWiki.HasWiki())
|
||||
assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repoWithNoWiki, "main"))
|
||||
|
||||
// repo with wiki
|
||||
assert.NoError(t, repo_model.UpdateRepositoryCols(db.DefaultContext, &repo_model.Repository{ID: 1, DefaultWikiBranch: "wrong-branch"}))
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki")
|
||||
|
|
|
@ -74,7 +74,6 @@ func AccountPost(ctx *context.Context) {
|
|||
case errors.Is(err, password.ErrIsPwned):
|
||||
ctx.Flash.Error(ctx.Tr("auth.password_pwned"))
|
||||
case password.IsErrIsPwnedRequest(err):
|
||||
log.Error("%s", err.Error())
|
||||
ctx.Flash.Error(ctx.Tr("auth.password_pwned_err"))
|
||||
default:
|
||||
ctx.ServerError("UpdateAuth", err)
|
||||
|
|
|
@ -49,7 +49,7 @@ func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
|
|||
newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{
|
||||
Action: api.HookIssueOpened,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, issue.Poster, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, issue.Poster, nil),
|
||||
}).Notify(withMethod(ctx, "NewIssue"))
|
||||
|
@ -89,7 +89,7 @@ func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
|
|||
WithPayload(&api.IssuePayload{
|
||||
Action: api.HookIssueEdited,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
|
@ -127,7 +127,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
|
|||
}
|
||||
apiIssue := &api.IssuePayload{
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues
|
|||
WithPayload(&api.IssuePayload{
|
||||
Action: action,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
|
@ -293,7 +293,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen
|
|||
|
||||
payload := &api.IssueCommentPayload{
|
||||
Action: action,
|
||||
Issue: convert.ToAPIIssue(ctx, comment.Issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, comment.Issue),
|
||||
Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
|
||||
Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
)
|
||||
|
@ -145,10 +146,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
|
|||
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
|
||||
|
||||
results = append(results, private.HookProcReceiveRefResult{
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
OldOID: objectFormat.EmptyObjectID().String(),
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
OldOID: objectFormat.EmptyObjectID().String(),
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
IsCreatePR: true,
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
|
||||
HeadBranch: headBranch,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
@ -208,11 +213,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
|
|||
isForcePush := comment != nil && comment.IsForcePush
|
||||
|
||||
results = append(results, private.HookProcReceiveRefResult{
|
||||
OldOID: oldCommitID,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
IsForcePush: isForcePush,
|
||||
OldOID: oldCommitID,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
IsForcePush: isForcePush,
|
||||
IsCreatePR: false,
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -79,11 +79,11 @@ func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
|
|||
case setting.CfTurnstile:
|
||||
valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
|
||||
default:
|
||||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
|
||||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Debug("%v", err)
|
||||
log.Debug("Captcha Verify failed: %v", err)
|
||||
}
|
||||
|
||||
if !valid {
|
||||
|
|
|
@ -18,19 +18,19 @@ import (
|
|||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, issue, WebAssetDownloadURL)
|
||||
func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, doer, issue, WebAssetDownloadURL)
|
||||
}
|
||||
|
||||
// ToAPIIssue converts an Issue to API format
|
||||
// it assumes some fields assigned with values:
|
||||
// Required - Poster, Labels,
|
||||
// Optional - Milestone, Assignee, PullRequest
|
||||
func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, issue, APIAssetDownloadURL)
|
||||
func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, doer, issue, APIAssetDownloadURL)
|
||||
}
|
||||
|
||||
func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
|
||||
func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
|
||||
if err := issue.LoadLabels(ctx); err != nil {
|
||||
return &api.Issue{}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
|
|||
apiIssue := &api.Issue{
|
||||
ID: issue.ID,
|
||||
Index: issue.Index,
|
||||
Poster: ToUser(ctx, issue.Poster, nil),
|
||||
Poster: ToUser(ctx, issue.Poster, doer),
|
||||
Title: issue.Title,
|
||||
Body: issue.Content,
|
||||
Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL),
|
||||
|
@ -114,25 +114,25 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
|
|||
}
|
||||
|
||||
// ToIssueList converts an IssueList to API format
|
||||
func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
|
||||
func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
|
||||
result := make([]*api.Issue, len(il))
|
||||
for i := range il {
|
||||
result[i] = ToIssue(ctx, il[i])
|
||||
result[i] = ToIssue(ctx, doer, il[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToAPIIssueList converts an IssueList to API format
|
||||
func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
|
||||
func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
|
||||
result := make([]*api.Issue, len(il))
|
||||
for i := range il {
|
||||
result[i] = ToAPIIssue(ctx, il[i])
|
||||
result[i] = ToAPIIssue(ctx, doer, il[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToTrackedTime converts TrackedTime to API format
|
||||
func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
|
||||
func ToTrackedTime(ctx context.Context, doer *user_model.User, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
|
||||
apiT = &api.TrackedTime{
|
||||
ID: t.ID,
|
||||
IssueID: t.IssueID,
|
||||
|
@ -141,7 +141,7 @@ func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.
|
|||
Created: t.Created,
|
||||
}
|
||||
if t.Issue != nil {
|
||||
apiT.Issue = ToAPIIssue(ctx, t.Issue)
|
||||
apiT.Issue = ToAPIIssue(ctx, doer, t.Issue)
|
||||
}
|
||||
if t.User != nil {
|
||||
apiT.UserName = t.User.Name
|
||||
|
@ -192,10 +192,10 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop
|
|||
}
|
||||
|
||||
// ToTrackedTimeList converts TrackedTimeList to API format
|
||||
func ToTrackedTimeList(ctx context.Context, tl issues_model.TrackedTimeList) api.TrackedTimeList {
|
||||
func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList {
|
||||
result := make([]*api.TrackedTime, 0, len(tl))
|
||||
for _, t := range tl {
|
||||
result = append(result, ToTrackedTime(ctx, t))
|
||||
result = append(result, ToTrackedTime(ctx, doer, t))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
|
|||
return nil
|
||||
}
|
||||
|
||||
comment.TrackedTime = ToTrackedTime(ctx, c.Time)
|
||||
comment.TrackedTime = ToTrackedTime(ctx, doer, c.Time)
|
||||
}
|
||||
|
||||
if c.RefIssueID != 0 {
|
||||
|
@ -129,7 +129,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
|
|||
log.Error("GetIssueByID(%d): %v", c.RefIssueID, err)
|
||||
return nil
|
||||
}
|
||||
comment.RefIssue = ToAPIIssue(ctx, issue)
|
||||
comment.RefIssue = ToAPIIssue(ctx, doer, issue)
|
||||
}
|
||||
|
||||
if c.RefCommentID != 0 {
|
||||
|
@ -180,7 +180,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
|
|||
}
|
||||
|
||||
if c.DependentIssue != nil {
|
||||
comment.DependentIssue = ToAPIIssue(ctx, c.DependentIssue)
|
||||
comment.DependentIssue = ToAPIIssue(ctx, doer, c.DependentIssue)
|
||||
}
|
||||
|
||||
return comment
|
||||
|
|
|
@ -33,7 +33,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
|||
return nil
|
||||
}
|
||||
|
||||
apiIssue := ToAPIIssue(ctx, pr.Issue)
|
||||
apiIssue := ToAPIIssue(ctx, doer, pr.Issue)
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
|
|
|
@ -61,26 +61,20 @@ func asFixer(fn func(ctx context.Context) error) func(ctx context.Context) (int6
|
|||
}
|
||||
}
|
||||
|
||||
func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCheck {
|
||||
func genericOrphanCheck(name, subject, refObject, joinCond string) consistencyCheck {
|
||||
return consistencyCheck{
|
||||
Name: name,
|
||||
Counter: func(ctx context.Context) (int64, error) {
|
||||
return db.CountOrphanedObjects(ctx, subject, refobject, joincond)
|
||||
return db.CountOrphanedObjects(ctx, subject, refObject, joinCond)
|
||||
},
|
||||
Fixer: func(ctx context.Context) (int64, error) {
|
||||
err := db.DeleteOrphanedObjects(ctx, subject, refobject, joincond)
|
||||
err := db.DeleteOrphanedObjects(ctx, subject, refObject, joinCond)
|
||||
return -1, err
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
// make sure DB version is uptodate
|
||||
if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
|
||||
logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
|
||||
return err
|
||||
}
|
||||
|
||||
func prepareDBConsistencyChecks() []consistencyCheck {
|
||||
consistencyChecks := []consistencyCheck{
|
||||
{
|
||||
// find labels without existing repo or org
|
||||
|
@ -210,7 +204,7 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
|||
"oauth2_grant", "user", "oauth2_grant.user_id=`user`.id"),
|
||||
// find OAuth2Application without existing user
|
||||
genericOrphanCheck("Orphaned OAuth2Application without existing User",
|
||||
"oauth2_application", "user", "oauth2_application.uid=`user`.id"),
|
||||
"oauth2_application", "user", "oauth2_application.uid=0 OR oauth2_application.uid=`user`.id"),
|
||||
// find OAuth2AuthorizationCode without existing OAuth2Grant
|
||||
genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
|
||||
"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
|
||||
|
@ -224,7 +218,16 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
|||
genericOrphanCheck("Orphaned Redirects without existing redirect user",
|
||||
"user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"),
|
||||
)
|
||||
return consistencyChecks
|
||||
}
|
||||
|
||||
func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
// make sure DB version is uptodate
|
||||
if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
|
||||
logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
|
||||
return err
|
||||
}
|
||||
consistencyChecks := prepareDBConsistencyChecks()
|
||||
for _, c := range consistencyChecks {
|
||||
if err := c.Run(ctx, logger, autofix); err != nil {
|
||||
return err
|
||||
|
|
51
services/doctor/dbconsistency_test.go
Normal file
51
services/doctor/dbconsistency_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConsistencyCheck(t *testing.T) {
|
||||
checks := prepareDBConsistencyChecks()
|
||||
idx := slices.IndexFunc(checks, func(check consistencyCheck) bool {
|
||||
return check.Name == "Orphaned OAuth2Application without existing User"
|
||||
})
|
||||
if !assert.NotEqual(t, -1, idx) {
|
||||
return
|
||||
}
|
||||
|
||||
_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &user.User{})
|
||||
_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &auth.OAuth2Application{})
|
||||
|
||||
err := db.Insert(db.DefaultContext, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-1", ClientID: "client-id-1"})
|
||||
assert.NoError(t, err)
|
||||
err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-2", ClientID: "client-id-2", UID: 1})
|
||||
assert.NoError(t, err)
|
||||
err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-3", ClientID: "client-id-3", UID: 99999999})
|
||||
assert.NoError(t, err)
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-1"})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-2"})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-3"})
|
||||
|
||||
oauth2AppCheck := checks[idx]
|
||||
err = oauth2AppCheck.Run(db.DefaultContext, log.GetManager().GetLogger(log.DEFAULT), true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-1"})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-2"})
|
||||
unittest.AssertNotExistsBean(t, &auth.OAuth2Application{ClientID: "client-id-3"})
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
)
|
||||
|
||||
// Check represents a Doctor check
|
||||
|
@ -25,6 +26,7 @@ type Check struct {
|
|||
AbortIfFailed bool
|
||||
SkipDatabaseInitialization bool
|
||||
Priority int
|
||||
InitStorage bool
|
||||
}
|
||||
|
||||
func initDBSkipLogger(ctx context.Context) error {
|
||||
|
@ -84,6 +86,7 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
|
|||
logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
|
||||
loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
|
||||
dbIsInit := false
|
||||
storageIsInit := false
|
||||
for i, check := range checks {
|
||||
if !dbIsInit && !check.SkipDatabaseInitialization {
|
||||
// Only open database after the most basic configuration check
|
||||
|
@ -94,6 +97,14 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
|
|||
}
|
||||
dbIsInit = true
|
||||
}
|
||||
if !storageIsInit && check.InitStorage {
|
||||
if err := storage.Init(); err != nil {
|
||||
logger.Error("Error whilst initializing the storage: %v", err)
|
||||
logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
storageIsInit = true
|
||||
}
|
||||
logger.Info("\n[%d] %s", i+1, check.Title)
|
||||
if err := check.Run(ctx, loggerStep, autofix); err != nil {
|
||||
if check.AbortIfFailed {
|
||||
|
|
14
services/doctor/main_test.go
Normal file
14
services/doctor/main_test.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
|
@ -36,10 +36,10 @@ func TestProcessorHelperCodePreview(t *testing.T) {
|
|||
<table class="file-view">
|
||||
<tbody><tr>
|
||||
<td class="lines-num"><span data-line-number="1"></span></td>
|
||||
<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
|
||||
<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1</div></td>
|
||||
</tr><tr>
|
||||
<td class="lines-num"><span data-line-number="2"></span></td>
|
||||
<td class="lines-code chroma"><code class="code-inner"></span><span class="gh"></span></code></td>
|
||||
<td class="lines-code chroma"><div class="code-inner"></span><span class="gh"></span></div></td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -63,7 +63,7 @@ func TestProcessorHelperCodePreview(t *testing.T) {
|
|||
<table class="file-view">
|
||||
<tbody><tr>
|
||||
<td class="lines-num"><span data-line-number="1"></span></td>
|
||||
<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
|
||||
<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1</div></td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -91,7 +91,7 @@ func AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues
|
|||
// NewPullRequest notifies new pull request to notifiers
|
||||
func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) {
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("LoadIssue failed: %v", err)
|
||||
return
|
||||
}
|
||||
if err := pr.Issue.LoadPoster(ctx); err != nil {
|
||||
|
@ -112,7 +112,7 @@ func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *iss
|
|||
// PullRequestReview notifies new pull request review
|
||||
func PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
|
||||
if err := review.LoadReviewer(ctx); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("LoadReviewer failed: %v", err)
|
||||
return
|
||||
}
|
||||
for _, notifier := range notifiers {
|
||||
|
|
|
@ -28,7 +28,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
|
|||
|
||||
t, err := NewTemporaryUploadRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("NewTemporaryUploadRepository failed: %v", err)
|
||||
}
|
||||
defer t.Close()
|
||||
if err := t.Clone(opts.OldBranch, false); err != nil {
|
||||
|
|
|
@ -111,7 +111,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
|
|||
|
||||
t, err := NewTemporaryUploadRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("NewTemporaryUploadRepository failed: %v", err)
|
||||
}
|
||||
defer t.Close()
|
||||
if err := t.Clone(opts.OldBranch, true); err != nil {
|
||||
|
|
|
@ -143,7 +143,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
|||
|
||||
t, err := NewTemporaryUploadRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("NewTemporaryUploadRepository failed: %v", err)
|
||||
}
|
||||
defer t.Close()
|
||||
hasOldBranch := true
|
||||
|
|
|
@ -67,7 +67,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model
|
|||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
|
@ -168,7 +168,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo
|
|||
permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||
apiIssue := &api.IssuePayload{
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model
|
|||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
|
@ -250,7 +250,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
|
|||
} else {
|
||||
apiIssue := &api.IssuePayload{
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
CommitID: commitID,
|
||||
|
@ -281,7 +281,7 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
|
|||
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
|
||||
Action: api.HookIssueOpened,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, issue.Poster, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, issue.Poster, nil),
|
||||
}); err != nil {
|
||||
|
@ -349,7 +349,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
|
|||
From: oldContent,
|
||||
},
|
||||
},
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
|
@ -384,7 +384,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us
|
|||
permission, _ := access_model.GetUserRepoPermission(ctx, c.Issue.Repo, doer)
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentEdited,
|
||||
Issue: convert.ToAPIIssue(ctx, c.Issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, c.Issue),
|
||||
Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c),
|
||||
Changes: &api.ChangesPayload{
|
||||
Body: &api.ChangesFromPayload{
|
||||
|
@ -412,7 +412,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod
|
|||
permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer)
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentCreated,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Comment: convert.ToAPIComment(ctx, repo, comment),
|
||||
Repository: convert.ToRepo(ctx, repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
|
@ -449,7 +449,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
|
|||
permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer)
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentDeleted,
|
||||
Issue: convert.ToAPIIssue(ctx, comment.Issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, comment.Issue),
|
||||
Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
|
||||
Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
|
@ -533,7 +533,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
|
|||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
|
@ -575,7 +575,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
|
|||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{
|
||||
Action: hookAction,
|
||||
Index: issue.Index,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Issue: convert.ToAPIIssue(ctx, doer, issue),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
|
|
|
@ -161,7 +161,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||
if isOldWikiExist {
|
||||
err := gitRepo.RemoveFilesFromIndex(oldWikiPath)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("RemoveFilesFromIndex failed: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -171,18 +171,18 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||
|
||||
objectHash, err := gitRepo.HashObject(strings.NewReader(content))
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("HashObject failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("AddObjectToIndex failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
tree, err := gitRepo.WriteTree()
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("WriteTree failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||
|
||||
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("CommitTree failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -222,11 +222,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||
0,
|
||||
),
|
||||
}); err != nil {
|
||||
log.Error("%v", err)
|
||||
log.Error("Push failed: %v", err)
|
||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("Push: %w", err)
|
||||
return fmt.Errorf("failed to push: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -370,6 +370,10 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
|
|||
return fmt.Errorf("unable to update database: %w", err)
|
||||
}
|
||||
|
||||
if !repo.HasWiki() {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldDefBranch, err := gitrepo.GetWikiDefaultBranch(ctx, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get default branch: %w", err)
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
{{ctx.Locale.Tr "admin.self_check"}}
|
||||
</h4>
|
||||
|
||||
{{if .DeprecatedWarnings}}
|
||||
{{if .StartupProblems}}
|
||||
<div class="ui attached segment">
|
||||
<div class="ui warning message">
|
||||
<div>{{ctx.Locale.Tr "admin.self_check.startup_warnings"}}</div>
|
||||
<ul class="tw-w-full">{{range .DeprecatedWarnings}}<li>{{.}}</li>{{end}}</ul>
|
||||
<ul class="tw-w-full">{{range .StartupProblems}}<li>{{.}}</li>{{end}}</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if and (not .DeprecatedWarnings) (not .DatabaseCheckHasProblems)}}
|
||||
{{if and (not .StartupProblems) (not .DatabaseCheckHasProblems)}}
|
||||
<div class="ui attached segment">
|
||||
{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{- $lineEscapeStatus := index $.LineEscapeStatus $idx -}}
|
||||
<td class="lines-escape">{{if $lineEscapeStatus.Escaped}}<a href="#" class="toggle-escape-button btn interact-bg" title="{{if $lineEscapeStatus.HasInvisible}}{{ctx.Locale.Tr "repo.invisible_runes_line"}} {{end}}{{if $lineEscapeStatus.HasAmbiguous}}{{ctx.Locale.Tr "repo.ambiguous_runes_line"}}{{end}}"></a>{{end}}</td>
|
||||
{{- end}}
|
||||
<td class="lines-code chroma"><code class="code-inner">{{$line.FormattedContent}}</code></td>
|
||||
<td class="lines-code chroma"><div class="code-inner">{{$line.FormattedContent}}</div></td>{{/* only div works, span generates incorrect HTML structure */}}
|
||||
</tr>
|
||||
{{- end -}}
|
||||
</tbody>
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes}}
|
||||
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}
|
||||
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}}
|
||||
{{if .PackageDescriptor.Metadata.ReleaseNotes}}{{.PackageDescriptor.Metadata.ReleaseNotes}}{{end}}
|
||||
</div>
|
||||
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}</div>{{end}}
|
||||
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment markup markdown">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
||||
{{if .PackageDescriptor.Metadata.ReleaseNotes}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.ReleaseNotes}}</div>{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if .PackageDescriptor.Metadata.Dependencies}}
|
||||
|
|
|
@ -66,13 +66,13 @@
|
|||
<div id="project-board">
|
||||
<div class="board {{if .CanWriteProjects}}sortable{{end}}">
|
||||
{{range .Columns}}
|
||||
<div class="ui segment project-column" style="background: {{.Color}} !important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
|
||||
<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
|
||||
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
|
||||
<div class="ui large label project-column-title tw-py-1">
|
||||
<div class="ui small circular grey label project-column-issue-count">
|
||||
{{.NumIssues ctx}}
|
||||
</div>
|
||||
{{.Title}}
|
||||
<span class="project-column-title-label">{{.Title}}</span>
|
||||
</div>
|
||||
{{if $canWriteProject}}
|
||||
<div class="ui dropdown jump item">
|
||||
|
@ -153,9 +153,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="divider"{{if .Color}} style="color: {{ContrastColor .Color}} !important"{{end}}></div>
|
||||
<div class="ui cards" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
|
||||
{{range (index $.IssuesMap .ID)}}
|
||||
<div class="issue-card gt-word-break {{if $canWriteProject}}tw-cursor-grab{{end}}" data-issue="{{.ID}}">
|
||||
|
|
|
@ -164,9 +164,9 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="ui horizontal list tw-flex tw-items-center">
|
||||
<div class="tw-flex tw-items-center">
|
||||
{{if .Parents}}
|
||||
<div class="item">
|
||||
<div>
|
||||
<span>{{ctx.Locale.Tr "repo.diff.parent"}}</span>
|
||||
{{range .Parents}}
|
||||
{{if $.PageIsWiki}}
|
||||
|
|
|
@ -1,92 +1,91 @@
|
|||
<div class="ui attached table segment commit-table">
|
||||
<table class="ui very basic striped table unstackable" id="commits-table">
|
||||
<thead>
|
||||
<table class="ui very basic striped table unstackable" id="commits-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">{{ctx.Locale.Tr "repo.commits.author"}}</th>
|
||||
<th class="two wide sha">{{StringUtils.ToUpper $.Repository.ObjectFormatName}}</th>
|
||||
<th class="eight wide message">{{ctx.Locale.Tr "repo.commits.message"}}</th>
|
||||
<th class="two wide right aligned">{{ctx.Locale.Tr "repo.commits.date"}}</th>
|
||||
<th class="one wide"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="commit-list">
|
||||
{{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
|
||||
{{range .Commits}}
|
||||
<tr>
|
||||
<th class="three wide">{{ctx.Locale.Tr "repo.commits.author"}}</th>
|
||||
<th class="two wide sha">{{StringUtils.ToUpper $.Repository.ObjectFormatName}}</th>
|
||||
<th class="eight wide message">{{ctx.Locale.Tr "repo.commits.message"}}</th>
|
||||
<th class="two wide right aligned">{{ctx.Locale.Tr "repo.commits.date"}}</th>
|
||||
<th class="one wide"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="commit-list">
|
||||
{{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
|
||||
{{range .Commits}}
|
||||
<tr>
|
||||
<td class="author tw-flex">
|
||||
{{$userName := .Author.Name}}
|
||||
{{if .User}}
|
||||
{{if and .User.FullName DefaultShowFullName}}
|
||||
{{$userName = .User.FullName}}
|
||||
{{end}}
|
||||
{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
|
||||
{{else}}
|
||||
{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
|
||||
<span class="author-wrapper">{{$userName}}</span>
|
||||
<td class="author tw-flex">
|
||||
{{$userName := .Author.Name}}
|
||||
{{if .User}}
|
||||
{{if and .User.FullName DefaultShowFullName}}
|
||||
{{$userName = .User.FullName}}
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="sha">
|
||||
{{$class := "ui sha label"}}
|
||||
{{if .Signature}}
|
||||
{{$class = (print $class " isSigned")}}
|
||||
{{if .Verification.Verified}}
|
||||
{{if eq .Verification.TrustStatus "trusted"}}
|
||||
{{$class = (print $class " isVerified")}}
|
||||
{{else if eq .Verification.TrustStatus "untrusted"}}
|
||||
{{$class = (print $class " isVerifiedUntrusted")}}
|
||||
{{else}}
|
||||
{{$class = (print $class " isVerifiedUnmatched")}}
|
||||
{{end}}
|
||||
{{else if .Verification.Warning}}
|
||||
{{$class = (print $class " isWarning")}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{$commitShaLink := ""}}
|
||||
{{if $.PageIsWiki}}
|
||||
{{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
|
||||
{{else if $.PageIsPullCommits}}
|
||||
{{$commitShaLink = (printf "%s/pulls/%d/commits/%s" $commitRepoLink $.Issue.Index (PathEscape .ID.String))}}
|
||||
{{else if $.Reponame}}
|
||||
{{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
|
||||
{{end}}
|
||||
<a {{if $commitShaLink}}href="{{$commitShaLink}}"{{end}} class="{{$class}}">
|
||||
<span class="shortsha">{{ShortSha .ID.String}}</span>
|
||||
{{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
|
||||
</a>
|
||||
</td>
|
||||
<td class="message">
|
||||
<span class="message-wrapper">
|
||||
{{if $.PageIsWiki}}
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
|
||||
{{else}}
|
||||
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body tw-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
</td>
|
||||
{{if .Committer}}
|
||||
<td class="text right aligned">{{TimeSince .Committer.When ctx.Locale}}</td>
|
||||
{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
|
||||
{{else}}
|
||||
<td class="text right aligned">{{TimeSince .Author.When ctx.Locale}}</td>
|
||||
{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
|
||||
<span class="author-wrapper">{{$userName}}</span>
|
||||
{{end}}
|
||||
<td class="text right aligned tw-py-0">
|
||||
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
|
||||
<a
|
||||
class="btn interact-bg tw-p-2"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}"
|
||||
href="{{if $.FileName}}{{printf "%s/src/commit/%s/%s" $commitRepoLink (PathEscape .ID.String) (PathEscapeSegments $.FileName)}}{{else}}{{printf "%s/src/commit/%s" $commitRepoLink (PathEscape .ID.String)}}{{end}}">
|
||||
{{svg "octicon-file-code"}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td class="sha">
|
||||
{{$class := "ui sha label"}}
|
||||
{{if .Signature}}
|
||||
{{$class = (print $class " isSigned")}}
|
||||
{{if .Verification.Verified}}
|
||||
{{if eq .Verification.TrustStatus "trusted"}}
|
||||
{{$class = (print $class " isVerified")}}
|
||||
{{else if eq .Verification.TrustStatus "untrusted"}}
|
||||
{{$class = (print $class " isVerifiedUntrusted")}}
|
||||
{{else}}
|
||||
{{$class = (print $class " isVerifiedUnmatched")}}
|
||||
{{end}}
|
||||
{{else if .Verification.Warning}}
|
||||
{{$class = (print $class " isWarning")}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{$commitShaLink := ""}}
|
||||
{{if $.PageIsWiki}}
|
||||
{{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
|
||||
{{else if $.PageIsPullCommits}}
|
||||
{{$commitShaLink = (printf "%s/pulls/%d/commits/%s" $commitRepoLink $.Issue.Index (PathEscape .ID.String))}}
|
||||
{{else if $.Reponame}}
|
||||
{{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
|
||||
{{end}}
|
||||
<a {{if $commitShaLink}}href="{{$commitShaLink}}"{{end}} class="{{$class}}">
|
||||
<span class="shortsha">{{ShortSha .ID.String}}</span>
|
||||
{{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
|
||||
</a>
|
||||
</td>
|
||||
<td class="message">
|
||||
<span class="message-wrapper">
|
||||
{{if $.PageIsWiki}}
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
|
||||
{{else}}
|
||||
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body tw-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
</td>
|
||||
{{if .Committer}}
|
||||
<td class="text right aligned">{{TimeSince .Committer.When ctx.Locale}}</td>
|
||||
{{else}}
|
||||
<td class="text right aligned">{{TimeSince .Author.When ctx.Locale}}</td>
|
||||
{{end}}
|
||||
<td class="text right aligned tw-py-0">
|
||||
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
|
||||
{{if not $.PageIsWiki}}{{/* at the moment, wiki doesn't support "view at history point*/}}
|
||||
{{$viewCommitLink := printf "%s/src/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
||||
{{if $.FileName}}{{$viewCommitLink = printf "%s/%s" $viewCommitLink (PathEscapeSegments $.FileName)}}{{end}}
|
||||
<a class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}" href="{{$viewCommitLink}}">{{svg "octicon-file-code"}}</a>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{{$buttonText := ctx.Locale.Tr "repo.watch"}}
|
||||
{{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}}
|
||||
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
|
||||
{{if $.IsWatchingRepo}}{{svg "octicon-eye-closed"}}{{else}}{{svg "octicon-eye"}}{{end}}
|
||||
{{svg "octicon-eye"}}
|
||||
<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
|
||||
</button>
|
||||
<a hx-boost="false" class="ui basic label" href="{{.RepoLink}}/watchers">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
|
||||
<div class="ui container">
|
||||
{{$notificationUnreadCount := call .NotificationUnreadCount}}
|
||||
<div class="tw-flex tw-items-center tw-justify-between tw-mb-4">
|
||||
<div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]">
|
||||
<div class="small-menu-items ui compact tiny menu">
|
||||
<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
|
||||
{{ctx.Locale.Tr "notification.unread"}}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="ui attached segment">
|
||||
{{if or .allowAdopt .allowDelete}}
|
||||
{{if .Dirs}}
|
||||
<div class="ui middle aligned divided list">
|
||||
<div class="ui list">
|
||||
{{range $dirI, $dir := .Dirs}}
|
||||
{{$repo := index $.ReposMap $dir}}
|
||||
<div class="item {{if not $repo}}tw-py-1{{end}}">{{/* if not repo, then there are "adapt" buttons, so the padding shouldn't be that default large*/}}
|
||||
|
|
|
@ -49,6 +49,17 @@ func testGitPush(t *testing.T, u *url.URL) {
|
|||
})
|
||||
})
|
||||
|
||||
t.Run("Push branch with options", func(t *testing.T) {
|
||||
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
||||
branchName := "branch-with-options"
|
||||
doGitCreateBranch(gitPath, branchName)(t)
|
||||
doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t)
|
||||
pushed = append(pushed, branchName)
|
||||
|
||||
return pushed, deleted
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Delete branches", func(t *testing.T) {
|
||||
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
||||
doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
|
||||
.run-list-item-right {
|
||||
flex: 0 0 15%;
|
||||
flex: 0 0 min(20%, 130px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
--height-loading: 16rem;
|
||||
--min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
|
||||
--tab-size: 4;
|
||||
--checkbox-size: 16px; /* height and width of checkbox and radio inputs */
|
||||
--checkbox-size: 15px; /* height and width of checkbox and radio inputs */
|
||||
--page-spacing: 16px; /* space between page elements */
|
||||
}
|
||||
|
||||
:root * {
|
||||
|
@ -44,7 +45,7 @@ html, body {
|
|||
}
|
||||
|
||||
body {
|
||||
line-height: 1.4285rem;
|
||||
line-height: 20px;
|
||||
font-family: var(--fonts-regular);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-body);
|
||||
|
@ -305,14 +306,6 @@ a.label,
|
|||
background-color: var(--color-label-bg);
|
||||
}
|
||||
|
||||
/* fix Fomantic's line-height causing vertical scrollbars to appear */
|
||||
ul.ui.list li,
|
||||
ol.ui.list li,
|
||||
.ui.list > .item,
|
||||
.ui.list .list > .item {
|
||||
line-height: var(--line-height-default);
|
||||
}
|
||||
|
||||
.ui.menu {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -456,21 +449,6 @@ ol.ui.list li,
|
|||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .header,
|
||||
.ui.list > .item .header {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .content,
|
||||
.ui.list > .item > .content {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .description,
|
||||
.ui.list > .item .description {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* replace item margin on secondary menu items with gap and remove both the
|
||||
negative margins on the menu as well as margin on the items */
|
||||
.ui.secondary.menu {
|
||||
|
@ -589,10 +567,6 @@ img.ui.avatar,
|
|||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.ui.divided.list > .item {
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.ui.error.message .header,
|
||||
.ui.warning.message .header {
|
||||
color: inherit;
|
||||
|
@ -609,11 +583,14 @@ img.ui.avatar,
|
|||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* add padding to all content when there is no .secondary.nav. this uses padding instead of
|
||||
margin because with the negative margin on .ui.grid we would have to set margin-top: 0,
|
||||
but that does not work universally for all pages */
|
||||
/* add margin to all pages when there is no .secondary.nav */
|
||||
.page-content > :first-child:not(.secondary-nav) {
|
||||
padding-top: 14px;
|
||||
margin-top: var(--page-spacing);
|
||||
}
|
||||
/* if .ui.grid is the first child the first grid-column has 'padding-top: 1rem' which we need
|
||||
to compensate here */
|
||||
.page-content > :first-child.ui.grid {
|
||||
margin-top: calc(var(--page-spacing) - 1rem);
|
||||
}
|
||||
|
||||
.ui.pagination.menu .active.item {
|
||||
|
|
|
@ -22,34 +22,27 @@
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.project-column .issue-card {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.project-column-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.project-column-header.dark-label {
|
||||
color: var(--color-project-board-dark-label) !important;
|
||||
}
|
||||
|
||||
.project-column-header.dark-label .project-column-title {
|
||||
color: var(--color-project-board-dark-label) !important;
|
||||
}
|
||||
|
||||
.project-column-header.light-label {
|
||||
color: var(--color-project-board-light-label) !important;
|
||||
}
|
||||
|
||||
.project-column-header.light-label .project-column-title {
|
||||
color: var(--color-project-board-light-label) !important;
|
||||
}
|
||||
|
||||
.project-column-title {
|
||||
background: none !important;
|
||||
line-height: 1.25 !important;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.project-column-title,
|
||||
.project-column-issue-count {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.project-column > .cards {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
@ -64,6 +57,8 @@
|
|||
|
||||
.project-column > .divider {
|
||||
margin: 5px 0;
|
||||
border-color: currentcolor;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.project-column:first-child {
|
||||
|
|
|
@ -249,21 +249,6 @@ textarea:focus,
|
|||
.user.signup form .optional .title {
|
||||
margin-left: 250px !important;
|
||||
}
|
||||
.user.activate form .inline.field > input,
|
||||
.user.forgot.password form .inline.field > input,
|
||||
.user.reset.password form .inline.field > input,
|
||||
.user.link-account form .inline.field > input,
|
||||
.user.signin form .inline.field > input,
|
||||
.user.signup form .inline.field > input,
|
||||
.user.activate form .inline.field > textarea,
|
||||
.user.forgot.password form .inline.field > textarea,
|
||||
.user.reset.password form .inline.field > textarea,
|
||||
.user.link-account form .inline.field > textarea,
|
||||
.user.signin form .inline.field > textarea,
|
||||
.user.signup form .inline.field > textarea,
|
||||
.oauth-login-link {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
@ -310,14 +295,7 @@ textarea:focus,
|
|||
.user.reset.password form .inline.field > label,
|
||||
.user.link-account form .inline.field > label,
|
||||
.user.signin form .inline.field > label,
|
||||
.user.signup form .inline.field > label,
|
||||
.user.activate form input,
|
||||
.user.forgot.password form input,
|
||||
.user.reset.password form input,
|
||||
.user.link-account form input,
|
||||
.user.signin form input,
|
||||
.user.signup form input,
|
||||
.oauth-login-link {
|
||||
.user.signup form .inline.field > label {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
@ -435,9 +413,9 @@ textarea:focus,
|
|||
.repository.new.repo form label,
|
||||
.repository.new.migrate form label,
|
||||
.repository.new.fork form label,
|
||||
.repository.new.repo form input,
|
||||
.repository.new.migrate form input,
|
||||
.repository.new.fork form input,
|
||||
.repository.new.repo form .inline.field > input,
|
||||
.repository.new.migrate form .inline.field > input,
|
||||
.repository.new.fork form .inline.field > input,
|
||||
.repository.new.fork form .field a,
|
||||
.repository.new.repo form .selection.dropdown,
|
||||
.repository.new.migrate form .selection.dropdown,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
@import "./modules/header.css";
|
||||
@import "./modules/input.css";
|
||||
@import "./modules/label.css";
|
||||
@import "./modules/list.css";
|
||||
@import "./modules/segment.css";
|
||||
@import "./modules/grid.css";
|
||||
@import "./modules/message.css";
|
||||
|
|
|
@ -432,13 +432,13 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.markup code:not(.code-inner),
|
||||
.markup code,
|
||||
.markup tt {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
white-space: break-spaces;
|
||||
background-color: var(--color-markup-code-block);
|
||||
background-color: var(--color-markup-code-inline);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
|
@ -508,7 +508,7 @@
|
|||
line-height: 10px;
|
||||
color: var(--color-text-light);
|
||||
vertical-align: middle;
|
||||
background-color: var(--color-markup-code-block);
|
||||
background-color: var(--color-markup-code-inline);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: inset 0 -1px 0 var(--color-secondary);
|
||||
|
|
|
@ -20,7 +20,7 @@ input[type="radio"] {
|
|||
.ui.checkbox input[type="checkbox"],
|
||||
.ui.checkbox input[type="radio"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
width: var(--checkbox-size);
|
||||
height: var(--checkbox-size);
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
.flex-container {
|
||||
display: flex !important;
|
||||
gap: 16px;
|
||||
gap: var(--page-spacing);
|
||||
margin-top: var(--page-spacing);
|
||||
}
|
||||
|
||||
.flex-container-nav {
|
||||
|
|
|
@ -48,8 +48,11 @@
|
|||
cursor: default;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 2.67142857em;
|
||||
opacity: 0.5;
|
||||
border-radius: 0 0.28571429rem 0.28571429rem 0;
|
||||
pointer-events: none;
|
||||
|
@ -58,6 +61,8 @@
|
|||
|
||||
.ui.icon.input > i.icon.is-loading {
|
||||
position: absolute !important;
|
||||
height: 28px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.ui.icon.input > i.icon.is-loading > * {
|
||||
|
@ -78,7 +83,7 @@
|
|||
|
||||
.ui[class*="left icon"].input > i.icon {
|
||||
right: auto;
|
||||
left: 8px;
|
||||
left: 1px;
|
||||
border-radius: 0.28571429rem 0 0 0.28571429rem;
|
||||
}
|
||||
.ui[class*="left icon"].input > i.circular.icon {
|
||||
|
|
187
web_src/css/modules/list.css
Normal file
187
web_src/css/modules/list.css
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* based on Fomantic UI list module, with just the parts extracted that we use. If you find any
|
||||
unused rules here after refactoring, please remove them. */
|
||||
|
||||
.ui.list {
|
||||
list-style-type: none;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.ui.list:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ui.list:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.list > .item,
|
||||
.ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
}
|
||||
|
||||
.ui.list > .list > .item::after,
|
||||
.ui.list > .item::after {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.ui.list .list:not(.icon) {
|
||||
clear: both;
|
||||
margin: 0;
|
||||
padding: 0.75em 0 0.25em 0.5em;
|
||||
}
|
||||
|
||||
.ui.list .list > .item {
|
||||
padding: 0.14285714em 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > i.icon,
|
||||
.ui.list > .item > i.icon {
|
||||
display: table-cell;
|
||||
min-width: 1.55em;
|
||||
padding-top: 0;
|
||||
transition: color 0.1s ease;
|
||||
padding-right: 0.28571429em;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > i.icon:only-child,
|
||||
.ui.list > .item > i.icon:only-child {
|
||||
display: inline-block;
|
||||
min-width: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .image,
|
||||
.ui.list > .item > .image {
|
||||
display: table-cell;
|
||||
background-color: transparent;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > .image:not(:only-child):not(img),
|
||||
.ui.list > .item > .image:not(:only-child):not(img) {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
.ui.list .list > .item > .image img,
|
||||
.ui.list > .item > .image img {
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > img.image,
|
||||
.ui.list .list > .item > .image:only-child,
|
||||
.ui.list > .item > img.image,
|
||||
.ui.list > .item > .image:only-child {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .content,
|
||||
.ui.list > .item > .content {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.ui.list .list > .item > .image + .content,
|
||||
.ui.list .list > .item > i.icon + .content,
|
||||
.ui.list > .item > .image + .content,
|
||||
.ui.list > .item > i.icon + .content {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
padding: 0 0 0 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > img.image + .content,
|
||||
.ui.list > .item > img.image + .content {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.ui.list .list > .item > .content > .list,
|
||||
.ui.list > .item > .content > .list {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .item .header,
|
||||
.ui.list > .item .header {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-family: var(--fonts-regular);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .description,
|
||||
.ui.list > .item .description {
|
||||
display: block;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui.list > .item a,
|
||||
.ui.list .list > .item a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui.menu .ui.list > .item,
|
||||
.ui.menu .ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
background-color: transparent;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding: 0.21428571em 0;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item::before,
|
||||
.ui.menu .ui.list > .item::before {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item:first-child,
|
||||
.ui.menu .ui.list > .item:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item:last-child,
|
||||
.ui.menu .ui.list > .item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .disabled.item,
|
||||
.ui.list > .disabled.item {
|
||||
pointer-events: none;
|
||||
opacity: var(--opacity-disabled);
|
||||
}
|
||||
|
||||
.ui.list .list > a.item:hover > .icons,
|
||||
.ui.list > a.item:hover > .icons,
|
||||
.ui.list .list > a.item:hover > i.icon,
|
||||
.ui.list > a.item:hover > i.icon {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.divided.list > .item {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
.ui.divided.list .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .item .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .list > .item:first-child,
|
||||
.ui.divided.list > .item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .list > .item:first-child {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.ui.relaxed.list > .item:not(:first-child) {
|
||||
padding-top: 0.42857143em;
|
||||
}
|
||||
.ui.relaxed.list > .item:not(:last-child) {
|
||||
padding-bottom: 0.42857143em;
|
||||
}
|
|
@ -2273,8 +2273,21 @@
|
|||
height: 0.5em;
|
||||
}
|
||||
|
||||
.labels-list {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2.5px;
|
||||
}
|
||||
|
||||
.labels-list a {
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.labels-list .label {
|
||||
margin: 2px 0;
|
||||
padding: 0 6px;
|
||||
margin: 0 !important;
|
||||
min-height: 20px;
|
||||
display: inline-flex !important;
|
||||
line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
|
||||
}
|
||||
|
@ -2855,6 +2868,7 @@ tbody.commit-list {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
|
|
@ -34,23 +34,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
#issue-list .flex-item-title .labels-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
#issue-list .flex-item-title .labels-list a {
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#issue-list .flex-item-title .labels-list .label {
|
||||
padding: 0 6px;
|
||||
margin: 0;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
#issue-list .flex-item-body .branches {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
--color-console-fg-subtle: #bec4c8;
|
||||
--color-console-bg: #171b1e;
|
||||
--color-console-border: #2e353b;
|
||||
--color-console-hover-bg: #e8e8ff16;
|
||||
--color-console-hover-bg: #292d31;
|
||||
--color-console-active-bg: #2e353b;
|
||||
--color-console-menu-bg: #252b30;
|
||||
--color-console-menu-border: #424b51;
|
||||
|
@ -204,8 +204,9 @@
|
|||
--color-active: #e8e8ff24;
|
||||
--color-menu: #151a1e;
|
||||
--color-card: #151a1e;
|
||||
--color-markup-table-row: #e8e8ff06;
|
||||
--color-markup-code-block: #e8e8ff16;
|
||||
--color-markup-table-row: #e8e8ff0f;
|
||||
--color-markup-code-block: #e8e8ff12;
|
||||
--color-markup-code-inline: #e8e8ff28;
|
||||
--color-button: #151a1e;
|
||||
--color-code-bg: #14171a;
|
||||
--color-shadow: #00001758;
|
||||
|
@ -214,8 +215,6 @@
|
|||
--color-placeholder-text: var(--color-text-light-3);
|
||||
--color-editor-line-highlight: var(--color-primary-light-5);
|
||||
--color-project-board-bg: var(--color-secondary-light-2);
|
||||
--color-project-board-dark-label: #0e1011;
|
||||
--color-project-board-light-label: #dde0e2;
|
||||
--color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */
|
||||
--color-reaction-bg: #e8e8ff12;
|
||||
--color-reaction-hover-bg: var(--color-primary-light-4);
|
||||
|
|
|
@ -63,12 +63,12 @@
|
|||
/* console colors - used for actions console and console files */
|
||||
--color-console-fg: #f8f8f9;
|
||||
--color-console-fg-subtle: #bec4c8;
|
||||
--color-console-bg: #181b1d;
|
||||
--color-console-border: #313538;
|
||||
--color-console-hover-bg: #ffffff16;
|
||||
--color-console-active-bg: #313538;
|
||||
--color-console-menu-bg: #272b2e;
|
||||
--color-console-menu-border: #464a4d;
|
||||
--color-console-bg: #171b1e;
|
||||
--color-console-border: #2e353b;
|
||||
--color-console-hover-bg: #292d31;
|
||||
--color-console-active-bg: #2e353b;
|
||||
--color-console-menu-bg: #252b30;
|
||||
--color-console-menu-border: #424b51;
|
||||
/* named colors */
|
||||
--color-red: #db2828;
|
||||
--color-orange: #f2711c;
|
||||
|
@ -204,8 +204,9 @@
|
|||
--color-active: #00001714;
|
||||
--color-menu: #f8f9fb;
|
||||
--color-card: #f8f9fb;
|
||||
--color-markup-table-row: #00001708;
|
||||
--color-markup-code-block: #00001710;
|
||||
--color-markup-table-row: #0030600a;
|
||||
--color-markup-code-block: #00306010;
|
||||
--color-markup-code-inline: #00306012;
|
||||
--color-button: #f8f9fb;
|
||||
--color-code-bg: #fafdff;
|
||||
--color-shadow: #00001726;
|
||||
|
@ -214,8 +215,6 @@
|
|||
--color-placeholder-text: var(--color-text-light-3);
|
||||
--color-editor-line-highlight: var(--color-primary-light-6);
|
||||
--color-project-board-bg: var(--color-secondary-light-4);
|
||||
--color-project-board-dark-label: #0e1114;
|
||||
--color-project-board-light-label: #eaeef2;
|
||||
--color-caret: var(--color-text-dark);
|
||||
--color-reaction-bg: #0000170a;
|
||||
--color-reaction-hover-bg: var(--color-primary-light-5);
|
||||
|
|
977
web_src/fomantic/build/semantic.css
generated
977
web_src/fomantic/build/semantic.css
generated
|
@ -6477,983 +6477,6 @@ select.ui.dropdown {
|
|||
/*******************************
|
||||
Site Overrides
|
||||
*******************************/
|
||||
/*!
|
||||
* # Fomantic-UI - List
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
*
|
||||
*
|
||||
* Released under the MIT license
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
|
||||
/*******************************
|
||||
List
|
||||
*******************************/
|
||||
|
||||
ul.ui.list,
|
||||
ol.ui.list,
|
||||
.ui.list {
|
||||
list-style-type: none;
|
||||
margin: 1em 0;
|
||||
padding: 0 0;
|
||||
}
|
||||
|
||||
ul.ui.list:first-child,
|
||||
ol.ui.list:first-child,
|
||||
.ui.list:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
ul.ui.list:last-child,
|
||||
ol.ui.list:last-child,
|
||||
.ui.list:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Content
|
||||
*******************************/
|
||||
|
||||
/* List Item */
|
||||
|
||||
ul.ui.list li,
|
||||
ol.ui.list li,
|
||||
.ui.list > .item,
|
||||
.ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding: 0.21428571em 0;
|
||||
line-height: 1.14285714em;
|
||||
}
|
||||
|
||||
ul.ui.list > li:first-child:after,
|
||||
ol.ui.list > li:first-child:after,
|
||||
.ui.list > .list > .item:after,
|
||||
.ui.list > .item:after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
ul.ui.list li:first-child,
|
||||
ol.ui.list li:first-child,
|
||||
.ui.list .list > .item:first-child,
|
||||
.ui.list > .item:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
ul.ui.list li:last-child,
|
||||
ol.ui.list li:last-child,
|
||||
.ui.list .list > .item:last-child,
|
||||
.ui.list > .item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Child List */
|
||||
|
||||
ul.ui.list ul,
|
||||
ol.ui.list ol,
|
||||
.ui.list .list:not(.icon) {
|
||||
clear: both;
|
||||
margin: 0;
|
||||
padding: 0.75em 0 0.25em 0.5em;
|
||||
}
|
||||
|
||||
/* Child Item */
|
||||
|
||||
ul.ui.list ul li,
|
||||
ol.ui.list ol li,
|
||||
.ui.list .list > .item {
|
||||
padding: 0.14285714em 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Icon */
|
||||
|
||||
.ui.list .list > .item > i.icon,
|
||||
.ui.list > .item > i.icon {
|
||||
display: table-cell;
|
||||
min-width: 1.55em;
|
||||
margin: 0;
|
||||
padding-top: 0;
|
||||
transition: color 0.1s ease;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > i.icon:not(.loading),
|
||||
.ui.list > .item > i.icon:not(.loading) {
|
||||
padding-right: 0.28571429em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > i.icon:only-child,
|
||||
.ui.list > .item > i.icon:only-child {
|
||||
display: inline-block;
|
||||
min-width: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* Image */
|
||||
|
||||
.ui.list .list > .item > .image,
|
||||
.ui.list > .item > .image {
|
||||
display: table-cell;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .image:not(:only-child):not(img),
|
||||
.ui.list > .item > .image:not(:only-child):not(img) {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .image img,
|
||||
.ui.list > .item > .image img {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > img.image,
|
||||
.ui.list .list > .item > .image:only-child,
|
||||
.ui.list > .item > img.image,
|
||||
.ui.list > .item > .image:only-child {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
|
||||
.ui.list .list > .item > .content,
|
||||
.ui.list > .item > .content {
|
||||
line-height: 1.14285714em;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .image + .content,
|
||||
.ui.list .list > .item > i.icon + .content,
|
||||
.ui.list > .item > .image + .content,
|
||||
.ui.list > .item > i.icon + .content {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
padding: 0 0 0 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > i.loading.icon + .content,
|
||||
.ui.list > .item > i.loading.icon + .content {
|
||||
padding-left: calc(0.2857142857142857em + 0.5em);
|
||||
}
|
||||
|
||||
.ui.list .list > .item > img.image + .content,
|
||||
.ui.list > .item > img.image + .content {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .content > .list,
|
||||
.ui.list > .item > .content > .list {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.ui.list .list > .item .header,
|
||||
.ui.list > .item .header {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-family: var(--fonts-regular);
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
/* Description */
|
||||
|
||||
.ui.list .list > .item .description,
|
||||
.ui.list > .item .description {
|
||||
display: block;
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Child Link */
|
||||
|
||||
.ui.list > .item a,
|
||||
.ui.list .list > .item a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Linking Item */
|
||||
|
||||
.ui.list .list > a.item,
|
||||
.ui.list > a.item {
|
||||
cursor: pointer;
|
||||
color: #4183C4;
|
||||
}
|
||||
|
||||
.ui.list .list > a.item:hover,
|
||||
.ui.list > a.item:hover {
|
||||
color: #1e70bf;
|
||||
}
|
||||
|
||||
/* Linked Item Icons */
|
||||
|
||||
.ui.list .list > a.item > i.icons,
|
||||
.ui.list > a.item > i.icons,
|
||||
.ui.list .list > a.item > i.icon,
|
||||
.ui.list > a.item > i.icon {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Header Link */
|
||||
|
||||
.ui.list .list > .item a.header,
|
||||
.ui.list > .item a.header {
|
||||
cursor: pointer;
|
||||
color: #4183C4 !important;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > a.header:hover,
|
||||
.ui.list > .item > a.header:hover {
|
||||
color: #1e70bf !important;
|
||||
}
|
||||
|
||||
/* Floated Content */
|
||||
|
||||
.ui[class*="left floated"].list {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ui[class*="right floated"].list {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ui.list .list > .item [class*="left floated"],
|
||||
.ui.list > .item [class*="left floated"] {
|
||||
float: left;
|
||||
margin: 0 1em 0 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .item [class*="right floated"],
|
||||
.ui.list > .item [class*="right floated"] {
|
||||
float: right;
|
||||
margin: 0 0 0 1em;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Coupling
|
||||
*******************************/
|
||||
|
||||
.ui.menu .ui.list > .item,
|
||||
.ui.menu .ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
background-color: transparent;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding: 0.21428571em 0;
|
||||
line-height: 1.14285714em;
|
||||
}
|
||||
|
||||
.ui.menu .ui.list .list > .item:before,
|
||||
.ui.menu .ui.list > .item:before {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.ui.menu .ui.list .list > .item:first-child,
|
||||
.ui.menu .ui.list > .item:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ui.menu .ui.list .list > .item:last-child,
|
||||
.ui.menu .ui.list > .item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Types
|
||||
*******************************/
|
||||
|
||||
/*-------------------
|
||||
Horizontal
|
||||
--------------------*/
|
||||
|
||||
.ui.horizontal.list {
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.ui.horizontal.list > .item {
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ui.horizontal.list:not(.celled) > .item:last-child {
|
||||
margin-right: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.ui.horizontal.list .list:not(.icon) {
|
||||
padding-left: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.horizontal.list > .item > .image,
|
||||
.ui.horizontal.list .list > .item > .image,
|
||||
.ui.horizontal.list > .item > i.icon,
|
||||
.ui.horizontal.list .list > .item > i.icon,
|
||||
.ui.horizontal.list > .item > .content,
|
||||
.ui.horizontal.list .list > .item > .content {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Padding on all elements */
|
||||
|
||||
.ui.horizontal.list > .item:first-child,
|
||||
.ui.horizontal.list > .item:last-child {
|
||||
padding-top: 0.21428571em;
|
||||
padding-bottom: 0.21428571em;
|
||||
}
|
||||
|
||||
/* Horizontal List */
|
||||
|
||||
.ui.horizontal.list > .item > i.icon,
|
||||
.ui.horizontal.list .item > i.icons > i.icon {
|
||||
margin: 0;
|
||||
padding: 0 0.25em 0 0;
|
||||
}
|
||||
|
||||
.ui.horizontal.list > .item > .image + .content,
|
||||
.ui.horizontal.list > .item > i.icon,
|
||||
.ui.horizontal.list > .item > i.icon + .content {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ui.horizontal.list > .item > .image {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
States
|
||||
*******************************/
|
||||
|
||||
/*-------------------
|
||||
Disabled
|
||||
--------------------*/
|
||||
|
||||
.ui.list .list > .disabled.item,
|
||||
.ui.list > .disabled.item {
|
||||
pointer-events: none;
|
||||
color: rgba(40, 40, 40, 0.3) !important;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Hover
|
||||
--------------------*/
|
||||
|
||||
.ui.list .list > a.item:hover > .icons,
|
||||
.ui.list > a.item:hover > .icons,
|
||||
.ui.list .list > a.item:hover > i.icon,
|
||||
.ui.list > a.item:hover > i.icon {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Variations
|
||||
*******************************/
|
||||
|
||||
/*-------------------
|
||||
Aligned
|
||||
--------------------*/
|
||||
|
||||
.ui.list[class*="top aligned"] .image,
|
||||
.ui.list[class*="top aligned"] .content,
|
||||
.ui.list [class*="top aligned"] {
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
.ui.list[class*="middle aligned"] .image,
|
||||
.ui.list[class*="middle aligned"] .content,
|
||||
.ui.list [class*="middle aligned"] {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.ui.list[class*="bottom aligned"] .image,
|
||||
.ui.list[class*="bottom aligned"] .content,
|
||||
.ui.list [class*="bottom aligned"] {
|
||||
vertical-align: bottom !important;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Link
|
||||
--------------------*/
|
||||
|
||||
.ui.link.list .item,
|
||||
.ui.link.list a.item,
|
||||
.ui.link.list .item a:not(.ui) {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
transition: 0.1s color ease;
|
||||
}
|
||||
|
||||
.ui.link.list.list a.item:hover,
|
||||
.ui.link.list.list .item a:not(.ui):hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.ui.link.list.list a.item:active,
|
||||
.ui.link.list.list .item a:not(.ui):active {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.ui.link.list.list .active.item,
|
||||
.ui.link.list.list .active.item a:not(.ui) {
|
||||
color: rgba(0, 0, 0, 0.95);
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Selection
|
||||
--------------------*/
|
||||
|
||||
.ui.selection.list .list > .item,
|
||||
.ui.selection.list > .item {
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
padding: 0.5em 0.5em;
|
||||
margin: 0;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 0.5em;
|
||||
transition: 0.1s color ease, 0.1s padding-left ease, 0.1s background-color ease;
|
||||
}
|
||||
|
||||
.ui.selection.list .list > .item:last-child,
|
||||
.ui.selection.list > .item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.selection.list .list > .item:hover,
|
||||
.ui.selection.list > .item:hover {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.ui.selection.list .list > .item:active,
|
||||
.ui.selection.list > .item:active {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.ui.selection.list .list > .item.active,
|
||||
.ui.selection.list > .item.active {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: rgba(0, 0, 0, 0.95);
|
||||
}
|
||||
|
||||
/* Celled / Divided Selection List */
|
||||
|
||||
.ui.celled.selection.list .list > .item,
|
||||
.ui.divided.selection.list .list > .item,
|
||||
.ui.celled.selection.list > .item,
|
||||
.ui.divided.selection.list > .item {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Animated
|
||||
--------------------*/
|
||||
|
||||
.ui.animated.list > .item {
|
||||
transition: 0.25s color ease 0.1s, 0.25s padding-left ease 0.1s, 0.25s background-color ease 0.1s;
|
||||
}
|
||||
|
||||
.ui.animated.list:not(.horizontal) > .item:hover {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Fitted
|
||||
--------------------*/
|
||||
|
||||
.ui.fitted.list:not(.selection) .list > .item,
|
||||
.ui.fitted.list:not(.selection) > .item {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.ui.fitted.selection.list .list > .item,
|
||||
.ui.fitted.selection.list > .item {
|
||||
margin-left: -0.5em;
|
||||
margin-right: -0.5em;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Bulleted
|
||||
--------------------*/
|
||||
|
||||
ul.ui.list,
|
||||
.ui.bulleted.list {
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
|
||||
ul.ui.list li,
|
||||
.ui.bulleted.list .list > .item,
|
||||
.ui.bulleted.list > .item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul.ui.list li:before,
|
||||
.ui.bulleted.list .list > .item:before,
|
||||
.ui.bulleted.list > .item:before {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: auto;
|
||||
left: auto;
|
||||
font-weight: normal;
|
||||
margin-left: -1.25rem;
|
||||
content: '\2022';
|
||||
opacity: 1;
|
||||
color: inherit;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
ul.ui.list li:before,
|
||||
.ui.bulleted.list .list > a.item:before,
|
||||
.ui.bulleted.list > a.item:before {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
ul.ui.list ul,
|
||||
.ui.bulleted.list .list:not(.icon) {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
/* Horizontal Bulleted */
|
||||
|
||||
ul.ui.horizontal.bulleted.list,
|
||||
.ui.horizontal.bulleted.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul.ui.horizontal.bulleted.list li,
|
||||
.ui.horizontal.bulleted.list > .item {
|
||||
margin-left: 1.75rem;
|
||||
}
|
||||
|
||||
ul.ui.horizontal.bulleted.list li:first-child,
|
||||
.ui.horizontal.bulleted.list > .item:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul.ui.horizontal.bulleted.list li::before,
|
||||
.ui.horizontal.bulleted.list > .item::before {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
ul.ui.horizontal.bulleted.list li:first-child::before,
|
||||
.ui.horizontal.bulleted.list > .item:first-child::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Ordered
|
||||
--------------------*/
|
||||
|
||||
ol.ui.list,
|
||||
.ui.ordered.list,
|
||||
.ui.ordered.list .list:not(.icon),
|
||||
ol.ui.list ol {
|
||||
counter-reset: ordered;
|
||||
margin-left: 1.25rem;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ol.ui.list li,
|
||||
.ui.ordered.list .list > .item,
|
||||
.ui.ordered.list > .item {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol.ui.list li:before,
|
||||
.ui.ordered.list .list > .item:before,
|
||||
.ui.ordered.list > .item:before {
|
||||
position: absolute;
|
||||
top: auto;
|
||||
left: auto;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
margin-left: -1.25rem;
|
||||
counter-increment: ordered;
|
||||
content: counters(ordered, ".") " ";
|
||||
text-align: right;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
vertical-align: middle;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Value */
|
||||
|
||||
.ui.ordered.list .list > .item[data-value]:before,
|
||||
.ui.ordered.list > .item[data-value]:before {
|
||||
content: attr(data-value);
|
||||
}
|
||||
|
||||
ol.ui.list li[value]:before {
|
||||
content: attr(value);
|
||||
}
|
||||
|
||||
/* Child Lists */
|
||||
|
||||
ol.ui.list ol,
|
||||
.ui.ordered.list .list:not(.icon) {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
ol.ui.list ol li:before,
|
||||
.ui.ordered.list .list > .item:before {
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
||||
/* Horizontal Ordered */
|
||||
|
||||
ol.ui.horizontal.list,
|
||||
.ui.ordered.horizontal.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ol.ui.horizontal.list li:before,
|
||||
.ui.ordered.horizontal.list .list > .item:before,
|
||||
.ui.ordered.horizontal.list > .item:before {
|
||||
position: static;
|
||||
margin: 0 0.5em 0 0;
|
||||
}
|
||||
|
||||
/* Suffixed Ordered */
|
||||
|
||||
ol.ui.suffixed.list li:before,
|
||||
.ui.suffixed.ordered.list .list > .item:before,
|
||||
.ui.suffixed.ordered.list > .item:before {
|
||||
content: counters(ordered, ".") ".";
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Divided
|
||||
--------------------*/
|
||||
|
||||
.ui.divided.list > .item {
|
||||
border-top: 1px solid rgba(34, 36, 38, 0.15);
|
||||
}
|
||||
|
||||
.ui.divided.list .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ui.divided.list .item .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ui.divided.list .list > .item:first-child,
|
||||
.ui.divided.list > .item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* Sub Menu */
|
||||
|
||||
.ui.divided.list:not(.horizontal) .list > .item:first-child {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
/* Divided bulleted */
|
||||
|
||||
.ui.divided.bulleted.list:not(.horizontal),
|
||||
.ui.divided.bulleted.list .list:not(.icon) {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ui.divided.bulleted.list > .item:not(.horizontal) {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
/* Divided Ordered */
|
||||
|
||||
.ui.divided.ordered.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ui.divided.ordered.list .list > .item,
|
||||
.ui.divided.ordered.list > .item {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.ui.divided.ordered.list .item .list:not(.icon) {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-bottom: 0.21428571em;
|
||||
}
|
||||
|
||||
.ui.divided.ordered.list .item .list > .item {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
/* Divided Selection */
|
||||
|
||||
.ui.divided.selection.list .list > .item,
|
||||
.ui.divided.selection.list > .item {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Divided horizontal */
|
||||
|
||||
.ui.divided.horizontal.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ui.divided.horizontal.list > .item {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.ui.divided.horizontal.list > .item:not(:last-child) {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.ui.divided.horizontal.list > .item {
|
||||
border-top: none;
|
||||
border-right: 1px solid rgba(34, 36, 38, 0.15);
|
||||
margin: 0;
|
||||
line-height: 0.6;
|
||||
}
|
||||
|
||||
.ui.horizontal.divided.list > .item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Celled
|
||||
--------------------*/
|
||||
|
||||
.ui.celled.list > .item,
|
||||
.ui.celled.list > .list {
|
||||
border-top: 1px solid rgba(34, 36, 38, 0.15);
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.ui.celled.list > .item:last-child {
|
||||
border-bottom: 1px solid rgba(34, 36, 38, 0.15);
|
||||
}
|
||||
|
||||
/* Padding on all elements */
|
||||
|
||||
.ui.celled.list > .item:first-child,
|
||||
.ui.celled.list > .item:last-child {
|
||||
padding-top: 0.21428571em;
|
||||
padding-bottom: 0.21428571em;
|
||||
}
|
||||
|
||||
/* Sub Menu */
|
||||
|
||||
.ui.celled.list .item .list > .item {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.ui.celled.list .list > .item:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
/* Celled Bulleted */
|
||||
|
||||
.ui.celled.bulleted.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ui.celled.bulleted.list .list > .item,
|
||||
.ui.celled.bulleted.list > .item {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.ui.celled.bulleted.list .item .list:not(.icon) {
|
||||
margin-left: -1.25rem;
|
||||
margin-right: -1.25rem;
|
||||
padding-bottom: 0.21428571em;
|
||||
}
|
||||
|
||||
/* Celled Ordered */
|
||||
|
||||
.ui.celled.ordered.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ui.celled.ordered.list .list > .item,
|
||||
.ui.celled.ordered.list > .item {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.ui.celled.ordered.list .item .list:not(.icon) {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-bottom: 0.21428571em;
|
||||
}
|
||||
|
||||
.ui.celled.ordered.list .list > .item {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
/* Celled Horizontal */
|
||||
|
||||
.ui.horizontal.celled.list {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ui.horizontal.celled.list .list > .item,
|
||||
.ui.horizontal.celled.list > .item {
|
||||
border-top: none;
|
||||
border-left: 1px solid rgba(34, 36, 38, 0.15);
|
||||
margin: 0;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
line-height: 0.6;
|
||||
}
|
||||
|
||||
.ui.horizontal.celled.list .list > .item:last-child,
|
||||
.ui.horizontal.celled.list > .item:last-child {
|
||||
border-bottom: none;
|
||||
border-right: 1px solid rgba(34, 36, 38, 0.15);
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Relaxed
|
||||
--------------------*/
|
||||
|
||||
.ui.relaxed.list:not(.horizontal) > .item:not(:first-child) {
|
||||
padding-top: 0.42857143em;
|
||||
}
|
||||
|
||||
.ui.relaxed.list:not(.horizontal) > .item:not(:last-child) {
|
||||
padding-bottom: 0.42857143em;
|
||||
}
|
||||
|
||||
.ui.horizontal.relaxed.list .list > .item:not(:first-child),
|
||||
.ui.horizontal.relaxed.list > .item:not(:first-child) {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.ui.horizontal.relaxed.list .list > .item:not(:last-child),
|
||||
.ui.horizontal.relaxed.list > .item:not(:last-child) {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
/* Very Relaxed */
|
||||
|
||||
.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:first-child) {
|
||||
padding-top: 0.85714286em;
|
||||
}
|
||||
|
||||
.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:last-child) {
|
||||
padding-bottom: 0.85714286em;
|
||||
}
|
||||
|
||||
.ui.horizontal[class*="very relaxed"].list .list > .item:not(:first-child),
|
||||
.ui.horizontal[class*="very relaxed"].list > .item:not(:first-child) {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.ui.horizontal[class*="very relaxed"].list .list > .item:not(:last-child),
|
||||
.ui.horizontal[class*="very relaxed"].list > .item:not(:last-child) {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
/*-------------------
|
||||
Sizes
|
||||
--------------------*/
|
||||
|
||||
.ui.list {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.ui.mini.list {
|
||||
font-size: 0.78571429em;
|
||||
}
|
||||
|
||||
.ui.mini.horizontal.list .list > .item,
|
||||
.ui.mini.horizontal.list > .item {
|
||||
font-size: 0.78571429rem;
|
||||
}
|
||||
|
||||
.ui.tiny.list {
|
||||
font-size: 0.85714286em;
|
||||
}
|
||||
|
||||
.ui.tiny.horizontal.list .list > .item,
|
||||
.ui.tiny.horizontal.list > .item {
|
||||
font-size: 0.85714286rem;
|
||||
}
|
||||
|
||||
.ui.small.list {
|
||||
font-size: 0.92857143em;
|
||||
}
|
||||
|
||||
.ui.small.horizontal.list .list > .item,
|
||||
.ui.small.horizontal.list > .item {
|
||||
font-size: 0.92857143rem;
|
||||
}
|
||||
|
||||
.ui.large.list {
|
||||
font-size: 1.14285714em;
|
||||
}
|
||||
|
||||
.ui.large.horizontal.list .list > .item,
|
||||
.ui.large.horizontal.list > .item {
|
||||
font-size: 1.14285714rem;
|
||||
}
|
||||
|
||||
.ui.big.list {
|
||||
font-size: 1.28571429em;
|
||||
}
|
||||
|
||||
.ui.big.horizontal.list .list > .item,
|
||||
.ui.big.horizontal.list > .item {
|
||||
font-size: 1.28571429rem;
|
||||
}
|
||||
|
||||
.ui.huge.list {
|
||||
font-size: 1.42857143em;
|
||||
}
|
||||
|
||||
.ui.huge.horizontal.list .list > .item,
|
||||
.ui.huge.horizontal.list > .item {
|
||||
font-size: 1.42857143rem;
|
||||
}
|
||||
|
||||
.ui.massive.list {
|
||||
font-size: 1.71428571em;
|
||||
}
|
||||
|
||||
.ui.massive.horizontal.list .list > .item,
|
||||
.ui.massive.horizontal.list > .item {
|
||||
font-size: 1.71428571rem;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Theme Overrides
|
||||
*******************************/
|
||||
|
||||
/*******************************
|
||||
User Variable Overrides
|
||||
*******************************/
|
||||
/*
|
||||
* # Fomantic - Menu
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
"dimmer",
|
||||
"dropdown",
|
||||
"form",
|
||||
"list",
|
||||
"menu",
|
||||
"modal",
|
||||
"search",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import {SvgIcon} from '../svg.js';
|
||||
import {useLightTextOnBackground} from '../utils/color.js';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import {contrastColor} from '../utils/color.js';
|
||||
import {GET} from '../modules/fetch.js';
|
||||
|
||||
const {appSubUrl, i18n} = window.config;
|
||||
|
@ -59,16 +58,11 @@ export default {
|
|||
},
|
||||
|
||||
labels() {
|
||||
return this.issue.labels.map((label) => {
|
||||
let textColor;
|
||||
const {r, g, b} = tinycolor(label.color).toRgb();
|
||||
if (useLightTextOnBackground(r, g, b)) {
|
||||
textColor = '#eeeeee';
|
||||
} else {
|
||||
textColor = '#111111';
|
||||
}
|
||||
return {name: label.name, color: `#${label.color}`, textColor};
|
||||
});
|
||||
return this.issue.labels.map((label) => ({
|
||||
name: label.name,
|
||||
color: `#${label.color}`,
|
||||
textColor: contrastColor(`#${label.color}`),
|
||||
}));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
@ -108,7 +102,7 @@ export default {
|
|||
<p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
|
||||
<p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
|
||||
<p>{{ body }}</p>
|
||||
<div>
|
||||
<div class="labels-list">
|
||||
<div
|
||||
v-for="label in labels"
|
||||
:key="label.name"
|
||||
|
|
|
@ -517,8 +517,16 @@ export function initRepositoryActionView() {
|
|||
|
||||
.action-commit-summary {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
margin: 0 0 0 28px;
|
||||
margin-left: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.action-commit-summary {
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================ */
|
||||
|
@ -531,6 +539,14 @@ export function initRepositoryActionView() {
|
|||
top: 12px;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
background: var(--color-body);
|
||||
z-index: 2; /* above .job-info-header */
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.action-view-left {
|
||||
position: static; /* can not sticky because multiple jobs would overlap into right view */
|
||||
}
|
||||
}
|
||||
|
||||
.job-artifacts-title {
|
||||
|
@ -692,7 +708,9 @@ export function initRepositoryActionView() {
|
|||
position: sticky;
|
||||
top: 0;
|
||||
height: 60px;
|
||||
z-index: 1;
|
||||
z-index: 1; /* above .job-step-container */
|
||||
background: var(--color-console-bg);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.job-info-header:has(+ .job-step-container) {
|
||||
|
@ -730,7 +748,7 @@ export function initRepositoryActionView() {
|
|||
|
||||
.job-step-container .job-step-summary.step-expandable:hover {
|
||||
color: var(--color-console-fg);
|
||||
background-color: var(--color-console-hover-bg);
|
||||
background: var(--color-console-hover-bg);
|
||||
}
|
||||
|
||||
.job-step-container .job-step-summary .step-summary-msg {
|
||||
|
@ -748,17 +766,15 @@ export function initRepositoryActionView() {
|
|||
top: 60px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: 767.98px) {
|
||||
.action-view-body {
|
||||
flex-direction: column;
|
||||
}
|
||||
.action-view-left, .action-view-right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action-view-left {
|
||||
max-width: none;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import $ from 'jquery';
|
||||
import {useLightTextOnBackground} from '../utils/color.js';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import {contrastColor} from '../utils/color.js';
|
||||
import {createSortable} from '../modules/sortable.js';
|
||||
import {POST, DELETE, PUT} from '../modules/fetch.js';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
function updateIssueCount(cards) {
|
||||
const parent = cards.parentElement;
|
||||
|
@ -65,14 +65,11 @@ async function initRepoProjectSortable() {
|
|||
boardColumns = mainBoard.getElementsByClassName('project-column');
|
||||
for (let i = 0; i < boardColumns.length; i++) {
|
||||
const column = boardColumns[i];
|
||||
if (parseInt($(column).data('sorting')) !== i) {
|
||||
if (parseInt(column.getAttribute('data-sorting')) !== i) {
|
||||
try {
|
||||
await PUT($(column).data('url'), {
|
||||
data: {
|
||||
sorting: i,
|
||||
color: rgbToHex(window.getComputedStyle($(column)[0]).backgroundColor),
|
||||
},
|
||||
});
|
||||
const bgColor = column.style.backgroundColor; // will be rgb() string
|
||||
const color = bgColor ? tinycolor(bgColor).toHexString() : '';
|
||||
await PUT(column.getAttribute('data-url'), {data: {sorting: i, color}});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
@ -102,16 +99,10 @@ export function initRepoProject() {
|
|||
|
||||
for (const modal of document.getElementsByClassName('edit-project-column-modal')) {
|
||||
const projectHeader = modal.closest('.project-column-header');
|
||||
const projectTitleLabel = projectHeader?.querySelector('.project-column-title');
|
||||
const projectTitleLabel = projectHeader?.querySelector('.project-column-title-label');
|
||||
const projectTitleInput = modal.querySelector('.project-column-title-input');
|
||||
const projectColorInput = modal.querySelector('#new_project_column_color');
|
||||
const boardColumn = modal.closest('.project-column');
|
||||
const bgColor = boardColumn?.style.backgroundColor;
|
||||
|
||||
if (bgColor) {
|
||||
setLabelColor(projectHeader, rgbToHex(bgColor));
|
||||
}
|
||||
|
||||
modal.querySelector('.edit-project-column-button')?.addEventListener('click', async function (e) {
|
||||
e.preventDefault();
|
||||
try {
|
||||
|
@ -126,10 +117,21 @@ export function initRepoProject() {
|
|||
} finally {
|
||||
projectTitleLabel.textContent = projectTitleInput?.value;
|
||||
projectTitleInput.closest('form')?.classList.remove('dirty');
|
||||
if (projectColorInput?.value) {
|
||||
setLabelColor(projectHeader, projectColorInput.value);
|
||||
const dividers = boardColumn.querySelectorAll(':scope > .divider');
|
||||
if (projectColorInput.value) {
|
||||
const color = contrastColor(projectColorInput.value);
|
||||
boardColumn.style.setProperty('background', projectColorInput.value, 'important');
|
||||
boardColumn.style.setProperty('color', color, 'important');
|
||||
for (const divider of dividers) {
|
||||
divider.style.setProperty('color', color);
|
||||
}
|
||||
} else {
|
||||
boardColumn.style.removeProperty('background');
|
||||
boardColumn.style.removeProperty('color');
|
||||
for (const divider of dividers) {
|
||||
divider.style.removeProperty('color');
|
||||
}
|
||||
}
|
||||
boardColumn.style = `background: ${projectColorInput.value} !important`;
|
||||
$('.ui.modal').modal('hide');
|
||||
}
|
||||
});
|
||||
|
@ -182,24 +184,3 @@ export function initRepoProject() {
|
|||
createNewColumn(url, $columnTitle, $projectColorInput);
|
||||
});
|
||||
}
|
||||
|
||||
function setLabelColor(label, color) {
|
||||
const {r, g, b} = tinycolor(color).toRgb();
|
||||
if (useLightTextOnBackground(r, g, b)) {
|
||||
label.classList.remove('dark-label');
|
||||
label.classList.add('light-label');
|
||||
} else {
|
||||
label.classList.remove('light-label');
|
||||
label.classList.add('dark-label');
|
||||
}
|
||||
}
|
||||
|
||||
function rgbToHex(rgb) {
|
||||
rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+).*\)$/);
|
||||
return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
|
||||
}
|
||||
|
||||
function hex(x) {
|
||||
const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user