Merge branch 'main' into feature/remove-signin-tabs

This commit is contained in:
Joshua Nazareth 2024-04-09 15:44:49 +05:30 committed by GitHub
commit bc282f73cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
102 changed files with 1030 additions and 1678 deletions

View File

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

View File

@ -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 {

View File

@ -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))

View File

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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

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

View File

@ -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" {

View File

@ -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()

View File

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

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

@ -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)),

View File

@ -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"
}

View File

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

View File

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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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:
Informationmeans 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 Providermeans the person or organisation providing the Information under this licence.
Information Provider means the person or organisation providing the Information under this licence.
Licensormeans any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majestys 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 Majestys 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.
Useas 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.
Youmeans 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 Majestys 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 Controllers 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 Controllers 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.

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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: _______________________________________

View File

@ -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.

View File

@ -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

View File

@ -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=すべて

View File

@ -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 {

View File

@ -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) {

View File

@ -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

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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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(),
})

View File

@ -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"}
}
}

View File

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

View File

@ -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:

View File

@ -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:

View File

@ -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) {

View File

@ -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")

View File

@ -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)

View File

@ -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),

View File

@ -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),
})
}

View File

@ -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 {

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

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

View File

@ -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 {

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

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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),
})

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -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}}">

View File

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

View File

@ -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>

View File

@ -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">

View File

@ -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"}}

View File

@ -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*/}}

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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,

View File

@ -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";

View File

@ -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);

View File

@ -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);

View File

@ -2,7 +2,8 @@
.flex-container {
display: flex !important;
gap: 16px;
gap: var(--page-spacing);
margin-top: var(--page-spacing);
}
.flex-container-nav {

View File

@ -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 {

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

View File

@ -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) {

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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/

View File

@ -26,7 +26,6 @@
"dimmer",
"dropdown",
"form",
"list",
"menu",
"modal",
"search",

View File

@ -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"

View File

@ -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>

View File

@ -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