mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-17 00:01:00 -04:00
Compare commits
4 Commits
e9991b1f06
...
b301cb17a3
Author | SHA1 | Date | |
---|---|---|---|
|
b301cb17a3 | ||
|
e259daeff8 | ||
|
edb618c136 | ||
|
43cf04c031 |
@ -1,12 +1,18 @@
|
||||
plugins:
|
||||
- stylelint-declaration-strict-value
|
||||
|
||||
ignoreFiles:
|
||||
- "**/*.go"
|
||||
|
||||
overrides:
|
||||
- files: ["**/*.less"]
|
||||
customSyntax: postcss-less
|
||||
- files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console/*"]
|
||||
rules:
|
||||
scale-unlimited/declaration-strict-value: null
|
||||
- files: ["**/chroma/*", "**/codemirror/*"]
|
||||
rules:
|
||||
block-no-empty: null
|
||||
|
||||
rules:
|
||||
alpha-value-notation: null
|
||||
|
11
cmd/admin.go
11
cmd/admin.go
@ -7,6 +7,7 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
@ -469,11 +470,19 @@ func runAddOauth(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
config := parseOAuth2Config(c)
|
||||
if config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
||||
}
|
||||
}
|
||||
|
||||
return auth_model.CreateSource(&auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
Cfg: config,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -239,6 +239,32 @@ func (org *Organization) CustomAvatarRelativePath() string {
|
||||
return org.Avatar
|
||||
}
|
||||
|
||||
// UnitPermission returns unit permission
|
||||
func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
|
||||
if doer != nil {
|
||||
teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
if err != nil {
|
||||
log.Error("GetUserOrgTeams: %v", err)
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
|
||||
if err := teams.LoadUnits(ctx); err != nil {
|
||||
log.Error("LoadUnits: %v", err)
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
|
||||
if len(teams) > 0 {
|
||||
return teams.UnitMaxAccess(unitType)
|
||||
}
|
||||
}
|
||||
|
||||
if org.Visibility.IsPublic() {
|
||||
return perm.AccessModeRead
|
||||
}
|
||||
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
|
||||
// CreateOrganization creates record of a new organization.
|
||||
func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
|
||||
if !owner.CanCreateOrganization() {
|
||||
|
@ -393,6 +393,11 @@ func (u *User) IsOrganization() bool {
|
||||
return u.Type == UserTypeOrganization
|
||||
}
|
||||
|
||||
// IsIndividual returns true if user is actually a individual user.
|
||||
func (u *User) IsIndividual() bool {
|
||||
return u.Type == UserTypeIndividual
|
||||
}
|
||||
|
||||
// DisplayName returns full name if it's not empty,
|
||||
// returns username otherwise.
|
||||
func (u *User) DisplayName() string {
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@ -31,29 +30,34 @@ type Organization struct {
|
||||
}
|
||||
|
||||
func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool {
|
||||
if ctx.Doer == nil {
|
||||
return false
|
||||
}
|
||||
return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite
|
||||
return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite
|
||||
}
|
||||
|
||||
func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType unit.Type) perm.AccessMode {
|
||||
if doerID > 0 {
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.Organization.ID, doerID)
|
||||
if err != nil {
|
||||
log.Error("GetUserOrgTeams: %v", err)
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
if len(teams) > 0 {
|
||||
return teams.UnitMaxAccess(unitType)
|
||||
}
|
||||
}
|
||||
func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool {
|
||||
return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead
|
||||
}
|
||||
|
||||
if org.Organization.Visibility == structs.VisibleTypePublic {
|
||||
return perm.AccessModeRead
|
||||
}
|
||||
func GetOrganizationByParams(ctx *Context) {
|
||||
orgName := ctx.Params(":org")
|
||||
|
||||
return perm.AccessModeNone
|
||||
var err error
|
||||
|
||||
ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
|
||||
if err != nil {
|
||||
if organization.IsErrOrgNotExist(err) {
|
||||
redirectUserID, err := user_model.LookupUserRedirect(orgName)
|
||||
if err == nil {
|
||||
RedirectToUser(ctx, orgName, redirectUserID)
|
||||
} else if user_model.IsErrUserRedirectNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", err)
|
||||
} else {
|
||||
ctx.ServerError("LookupUserRedirect", err)
|
||||
}
|
||||
} else {
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// HandleOrgAssignment handles organization assignment
|
||||
@ -77,25 +81,26 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||
requireTeamAdmin = args[3]
|
||||
}
|
||||
|
||||
orgName := ctx.Params(":org")
|
||||
|
||||
var err error
|
||||
ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
|
||||
if err != nil {
|
||||
if organization.IsErrOrgNotExist(err) {
|
||||
redirectUserID, err := user_model.LookupUserRedirect(orgName)
|
||||
if err == nil {
|
||||
RedirectToUser(ctx, orgName, redirectUserID)
|
||||
} else if user_model.IsErrUserRedirectNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", err)
|
||||
} else {
|
||||
ctx.ServerError("LookupUserRedirect", err)
|
||||
|
||||
if ctx.ContextUser == nil {
|
||||
// if Organization is not defined, get it from params
|
||||
if ctx.Org.Organization == nil {
|
||||
GetOrganizationByParams(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
}
|
||||
} else if ctx.ContextUser.IsOrganization() {
|
||||
if ctx.Org == nil {
|
||||
ctx.Org = &Organization{}
|
||||
}
|
||||
ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
|
||||
} else {
|
||||
// ContextUser is an individual User
|
||||
return
|
||||
}
|
||||
|
||||
org := ctx.Org.Organization
|
||||
|
||||
// Handle Visibility
|
||||
@ -156,6 +161,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||
}
|
||||
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
|
||||
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
|
||||
ctx.Data["IsProjectEnabled"] = true
|
||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
ctx.Data["IsPublicMember"] = func(uid int64) bool {
|
||||
@ -231,6 +237,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
|
||||
ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
|
||||
ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
|
||||
}
|
||||
|
||||
// OrgAssignment returns a middleware to handle organization assignment
|
||||
|
@ -2806,6 +2806,8 @@ auths.still_in_used = The authentication source is still in use. Convert or dele
|
||||
auths.deletion_success = The authentication source has been deleted.
|
||||
auths.login_source_exist = The authentication source '%s' already exists.
|
||||
auths.login_source_of_type_exist = An authentication source of this type already exists.
|
||||
auths.unable_to_initialize_openid = Unable to initialize OpenID Connect Provider: %s
|
||||
auths.invalid_openIdConnectAutoDiscoveryURL = Invalid Auto Discovery URL (this must be a valid URL starting with http:// or https://)
|
||||
|
||||
config.server_config = Server Configuration
|
||||
config.app_name = Site Title
|
||||
|
@ -271,6 +271,15 @@ func NewAuthSourcePost(ctx *context.Context) {
|
||||
}
|
||||
case auth.OAuth2:
|
||||
config = parseOAuth2Config(form)
|
||||
oauth2Config := config.(*oauth2.Source)
|
||||
if oauth2Config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthNew, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
case auth.SSPI:
|
||||
var err error
|
||||
config, err = parseSSPIConfig(ctx, form)
|
||||
@ -305,6 +314,10 @@ func NewAuthSourcePost(ctx *context.Context) {
|
||||
if auth.IsErrSourceAlreadyExist(err) {
|
||||
ctx.Data["Err_Name"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthNew, form)
|
||||
} else if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
unwrapped := err.(oauth2.ErrOpenIDConnectInitialize).Unwrap()
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.unable_to_initialize_openid", unwrapped), tplAuthNew, form)
|
||||
} else {
|
||||
ctx.ServerError("auth.CreateSource", err)
|
||||
}
|
||||
@ -389,6 +402,15 @@ func EditAuthSourcePost(ctx *context.Context) {
|
||||
}
|
||||
case auth.OAuth2:
|
||||
config = parseOAuth2Config(form)
|
||||
oauth2Config := config.(*oauth2.Source)
|
||||
if oauth2Config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthEdit, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
case auth.SSPI:
|
||||
config, err = parseSSPIConfig(ctx, form)
|
||||
if err != nil {
|
||||
@ -408,6 +430,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
||||
if err := auth.UpdateSource(source); err != nil {
|
||||
if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Flash.Error(err.Error(), true)
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.HTML(http.StatusOK, tplAuthEdit)
|
||||
} else {
|
||||
ctx.ServerError("UpdateSource", err)
|
||||
|
@ -156,6 +156,7 @@ func Home(ctx *context.Context) {
|
||||
pager.SetDefaultParams(ctx)
|
||||
pager.AddParam(ctx, "language", "Language")
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||
|
||||
ctx.HTML(http.StatusOK, tplOrgHome)
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ func NewProject(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
||||
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
|
||||
ctx.Data["PageIsViewProjects"] = true
|
||||
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
||||
shared_user.RenderUserHeader(ctx)
|
||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
)
|
||||
|
||||
func RenderUserHeader(ctx *context.Context) {
|
||||
ctx.Data["IsProjectEnabled"] = true
|
||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ func CodeSearch(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["IsProjectEnabled"] = true
|
||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
ctx.Data["Title"] = ctx.Tr("explore.code")
|
||||
|
@ -288,6 +288,7 @@ func Profile(ctx *context.Context) {
|
||||
pager.AddParam(ctx, "language", "Language")
|
||||
}
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.Data["IsProjectEnabled"] = true
|
||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
|
||||
|
@ -690,6 +690,21 @@ func RegisterRoutes(m *web.Route) {
|
||||
}
|
||||
}
|
||||
|
||||
reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||
return func(ctx *context.Context) {
|
||||
if ctx.ContextUser == nil {
|
||||
ctx.NotFound(unitType.String(), nil)
|
||||
return
|
||||
}
|
||||
if ctx.ContextUser.IsOrganization() {
|
||||
if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode {
|
||||
ctx.NotFound(unitType.String(), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() {
|
||||
m.Group("/{org}", func() {
|
||||
@ -869,8 +884,10 @@ func RegisterRoutes(m *web.Route) {
|
||||
}
|
||||
|
||||
m.Group("/projects", func() {
|
||||
m.Get("", org.Projects)
|
||||
m.Get("/{id}", org.ViewProject)
|
||||
m.Group("", func() {
|
||||
m.Get("", org.Projects)
|
||||
m.Get("/{id}", org.ViewProject)
|
||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead))
|
||||
m.Group("", func() { //nolint:dupl
|
||||
m.Get("/new", org.NewProject)
|
||||
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
|
||||
@ -890,25 +907,18 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Post("/move", org.MoveIssues)
|
||||
})
|
||||
})
|
||||
}, reqSignIn, func(ctx *context.Context) {
|
||||
if ctx.ContextUser == nil {
|
||||
ctx.NotFound("NewProject", nil)
|
||||
return
|
||||
}
|
||||
if ctx.ContextUser.IsOrganization() {
|
||||
if !ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) {
|
||||
ctx.NotFound("NewProject", nil)
|
||||
return
|
||||
}
|
||||
} else if ctx.ContextUser.ID != ctx.Doer.ID {
|
||||
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite), func(ctx *context.Context) {
|
||||
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
|
||||
ctx.NotFound("NewProject", nil)
|
||||
return
|
||||
}
|
||||
})
|
||||
}, repo.MustEnableProjects)
|
||||
|
||||
m.Get("/code", user.CodeSearch)
|
||||
}, context_service.UserAssignmentWeb())
|
||||
m.Group("", func() {
|
||||
m.Get("/code", user.CodeSearch)
|
||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead))
|
||||
}, context_service.UserAssignmentWeb(), context.OrgAssignment())
|
||||
|
||||
// ***** Release Attachment Download without Signin
|
||||
m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload)
|
||||
|
@ -36,6 +36,10 @@ func (err ErrOpenIDConnectInitialize) Error() string {
|
||||
return fmt.Sprintf("Failed to initialize OpenID Connect Provider with name '%s' with url '%s': %v", err.ProviderName, err.OpenIDConnectAutoDiscoveryURL, err.Cause)
|
||||
}
|
||||
|
||||
func (err ErrOpenIDConnectInitialize) Unwrap() error {
|
||||
return err.Cause
|
||||
}
|
||||
|
||||
// wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2
|
||||
// inside oauth2: import cycle not allowed models -> modules/auth/oauth2 -> models
|
||||
func wrapOpenIDConnectInitializeError(err error, providerName string, source *Source) error {
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
org_model "code.gitea.io/gitea/models/organization"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
)
|
||||
@ -57,14 +56,6 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{}))
|
||||
} else {
|
||||
errCb(http.StatusInternalServerError, "GetUserByName", err)
|
||||
}
|
||||
} else {
|
||||
if ctx.ContextUser.IsOrganization() {
|
||||
if ctx.Org == nil {
|
||||
ctx.Org = &context.Organization{}
|
||||
}
|
||||
ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser)
|
||||
ctx.Data["Org"] = ctx.Org.Organization
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
<label for="oauth2_icon_url">{{.locale.Tr "admin.auths.oauth2_icon_url"}}</label>
|
||||
<input id="oauth2_icon_url" name="oauth2_icon_url" value="{{.oauth2_icon_url}}">
|
||||
</div>
|
||||
<div class="open_id_connect_auto_discovery_url required field">
|
||||
<div class="open_id_connect_auto_discovery_url required field{{if .Err_DiscoveryURL}} error{{end}}">
|
||||
<label for="open_id_connect_auto_discovery_url">{{.locale.Tr "admin.auths.openIdConnectAutoDiscoveryURL"}}</label>
|
||||
<input id="open_id_connect_auto_discovery_url" name="open_id_connect_auto_discovery_url" value="{{.open_id_connect_auto_discovery_url}}">
|
||||
</div>
|
||||
|
@ -3,16 +3,18 @@
|
||||
<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
|
||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
||||
</a>
|
||||
{{if and .IsProjectEnabled .CanReadProjects}}
|
||||
<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects">
|
||||
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
||||
</a>
|
||||
{{if .IsPackageEnabled}}
|
||||
{{end}}
|
||||
{{if and .IsPackageEnabled .CanReadPackages}}
|
||||
<a class="item" href="{{$.Org.HomeLink}}/-/packages">
|
||||
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .IsRepoIndexerEnabled}}
|
||||
<a class="{{if $.PageIsOrgCode}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code">
|
||||
{{if and .IsRepoIndexerEnabled .CanReadCode}}
|
||||
<a class="item" href="{{$.Org.HomeLink}}/-/code">
|
||||
{{svg "octicon-code"}} {{$.locale.Tr "org.code"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -63,7 +63,7 @@
|
||||
{{end}}
|
||||
{{template "repo/sub_menu" .}}
|
||||
<div class="repo-button-row gt-df gt-ac gt-sb gt-fw">
|
||||
<div class="gt-df gt-ac">
|
||||
<div class="gt-df gt-ac gt-fw gt-gap-y-3">
|
||||
{{template "repo/branch_dropdown" dict "root" .}}
|
||||
{{$n := len .TreeNames}}
|
||||
{{$l := Subtract $n 1}}
|
||||
@ -99,20 +99,16 @@
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</button>
|
||||
{{end}}
|
||||
{{if and (eq $n 0) (.Repository.IsTemplate)}}
|
||||
<a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
|
||||
{{.locale.Tr "repo.use_template"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if ne $n 0}}
|
||||
<span class="ui breadcrumb repo-path gt-ml-2"><a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="gt-df gt-ac">
|
||||
{{if eq $n 0}}
|
||||
{{if .Repository.IsTemplate}}
|
||||
<div class="ui tiny primary buttons">
|
||||
<a href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}" class="ui button">
|
||||
{{.locale.Tr "repo.use_template"}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<!-- Only show clone panel in repository home page -->
|
||||
{{if eq $n 0}}
|
||||
<div class="ui action tiny input" id="clone-panel">
|
||||
|
@ -22,15 +22,17 @@
|
||||
<a class="item" href="{{.ContextUser.HomeLink}}">
|
||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
||||
</a>
|
||||
{{if and .IsProjectEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects))}}
|
||||
<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
|
||||
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
||||
</a>
|
||||
{{if (not .UnitPackagesGlobalDisabled)}}
|
||||
{{end}}
|
||||
{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}}
|
||||
<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
|
||||
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .IsRepoIndexerEnabled}}
|
||||
{{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}}
|
||||
<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
|
||||
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
|
||||
</a>
|
||||
|
@ -2869,7 +2869,7 @@
|
||||
}
|
||||
|
||||
.repo-button-row > * {
|
||||
margin-top: 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.wiki .repo-button-row {
|
||||
|
79
web_src/less/chroma/chroma-style-diff.go
Normal file
79
web_src/less/chroma/chroma-style-diff.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build ignore
|
||||
|
||||
/*
|
||||
This tool is used to compare the CSS names in a chroma builtin styles with the Gitea theme CSS names.
|
||||
|
||||
It outputs the difference between the two sets of CSS names, eg:
|
||||
|
||||
```
|
||||
CSS names not in builtin:
|
||||
.chroma .ln
|
||||
----
|
||||
Builtin CSS names not in file:
|
||||
.chroma .vm
|
||||
```
|
||||
|
||||
Developers could use this tool to re-sync the CSS names in the Gitea theme.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
println("Usage: chroma-style-diff css-or-less-file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
|
||||
// a simple CSS parser to collect CSS names
|
||||
content = regexp.MustCompile("//.*\r?\n").ReplaceAllString(content, "\n")
|
||||
content = regexp.MustCompile("/\\*.*?\\*/").ReplaceAllString(content, "")
|
||||
matches := regexp.MustCompile("\\s*([-.#:\\w\\s]+)\\s*\\{[^}]*}").FindAllStringSubmatch(content, -1)
|
||||
|
||||
cssNames := map[string]bool{}
|
||||
for _, matchGroup := range matches {
|
||||
cssName := strings.TrimSpace(matchGroup[1])
|
||||
cssNames[cssName] = true
|
||||
}
|
||||
|
||||
// collect Chroma builtin CSS names
|
||||
builtin := map[string]bool{}
|
||||
for tokenType, cssName := range chroma.StandardTypes {
|
||||
if tokenType > 0 && cssName != "" {
|
||||
builtin[".chroma ."+cssName] = true
|
||||
}
|
||||
}
|
||||
|
||||
// show the diff
|
||||
println("CSS names not in builtin:")
|
||||
for cssName := range cssNames {
|
||||
if !builtin[cssName] {
|
||||
println(cssName)
|
||||
}
|
||||
}
|
||||
println("----")
|
||||
println("Builtin CSS names not in file:")
|
||||
for cssName := range builtin {
|
||||
if !cssNames[cssName] {
|
||||
println(cssName)
|
||||
}
|
||||
}
|
||||
}
|
@ -7,17 +7,19 @@
|
||||
.chroma .cpf { color: #649bc4; } /* CommentPreprocFile */
|
||||
.chroma .cs { color: #9075cd; } /* CommentSpecial */
|
||||
.chroma .dl { color: #649bc4; } /* LiteralStringDelimiter */
|
||||
.chroma .fm {} /* NameFunctionMagic */
|
||||
.chroma .g {} /* Generic */
|
||||
.chroma .gd { color: #ffffff; background-color: #5f3737; } /* GenericDeleted */
|
||||
.chroma .ge { color: #ddee30; } /* GenericEmph */
|
||||
.chroma .gh { color: #ffaa10; } /* GenericHeading */
|
||||
.chroma .gi { color: #ffffff; background-color: #3a523a; } /* GenericInserted */
|
||||
.chroma .gl {} /* GenericUnderline */
|
||||
.chroma .go { color: #777e94; } /* GenericOutput */
|
||||
.chroma .gp { color: #ebdbb2; } /* GenericPrompt */
|
||||
.chroma .gr { color: #ff4433; } /* GenericError */
|
||||
.chroma .gs { color: #ebdbb2; } /* GenericStrong */
|
||||
.chroma .gt { color: #ff7540; } /* GenericTraceback */
|
||||
.chroma .gu { color: #b8bb26; } /* GenericSubheading */
|
||||
.chroma .hl { background-color: #3f424d; } /* LineHighlight */
|
||||
.chroma .il { color: #649bc4; } /* LiteralNumberIntegerLong */
|
||||
.chroma .k { color: #ff7540; } /* Keyword */
|
||||
.chroma .kc { color: #649bc4; } /* KeywordConstant */
|
||||
@ -25,16 +27,16 @@
|
||||
.chroma .kn { color: #ffaa10; } /* KeywordNamespace */
|
||||
.chroma .kp { color: #5f8700; } /* KeywordPseudo */
|
||||
.chroma .kr { color: #ff7540; } /* KeywordReserved */
|
||||
.chroma .kt { color: #fabd2f; } /* KeywordType */
|
||||
.chroma .ln { color: #7f8699; } /* LineNumbers */
|
||||
.chroma .lnt { color: #7f8699; } /* LineNumbersTable */
|
||||
.chroma .kt { color: #ff7b72; } /* KeywordType */
|
||||
.chroma .l {} /* Literal */
|
||||
.chroma .ld {} /* LiteralDate */
|
||||
.chroma .m { color: #649bc4; } /* LiteralNumber */
|
||||
.chroma .mb { color: #649bc4; } /* LiteralNumberBin */
|
||||
.chroma .mf { color: #649bc4; } /* LiteralNumberFloat */
|
||||
.chroma .mh { color: #649bc4; } /* LiteralNumberHex */
|
||||
.chroma .mi { color: #649bc4; } /* LiteralNumberInteger */
|
||||
.chroma .mo { color: #649bc4; } /* LiteralNumberOct */
|
||||
.chroma .n { color: #fabd2f; } /* Name */
|
||||
.chroma .n { color: #c9d1d9; } /* Name */
|
||||
.chroma .na { color: #b8bb26; } /* NameAttribute */
|
||||
.chroma .nb { color: #fabd2f; } /* NameBuiltin */
|
||||
.chroma .nc { color: #ffaa10; } /* NameClass */
|
||||
@ -51,6 +53,7 @@
|
||||
.chroma .o { color: #ff7540; } /* Operator */
|
||||
.chroma .ow { color: #5f8700; } /* OperatorWord */
|
||||
.chroma .p { color: #d2d4db; } /* Punctuation */
|
||||
.chroma .py {} /* NameProperty */
|
||||
.chroma .s { color: #b8bb26; } /* LiteralString */
|
||||
.chroma .s1 { color: #b8bb26; } /* LiteralStringSingle */
|
||||
.chroma .s2 { color: #b8bb26; } /* LiteralStringDouble */
|
||||
@ -67,4 +70,5 @@
|
||||
.chroma .vc { color: #ff7540; } /* NameVariableClass */
|
||||
.chroma .vg { color: #ffaa10; } /* NameVariableGlobal */
|
||||
.chroma .vi { color: #ffaa10; } /* NameVariableInstance */
|
||||
.chroma .vm {} /* NameVariableMagic */
|
||||
.chroma .w { color: #7f8699; } /* TextWhitespace */
|
||||
|
@ -7,16 +7,19 @@
|
||||
.chroma .cpf { color: #4c4dbc; } /* CommentPreprocFile */
|
||||
.chroma .cs { color: #999999; } /* CommentSpecial */
|
||||
.chroma .dl { color: #106303; } /* LiteralStringDelimiter */
|
||||
.chroma .fm {} /* NameFunctionMagic */
|
||||
.chroma .g {} /* Generic */
|
||||
.chroma .gd { color: #000000; background-color: #ffdddd; } /* GenericDeleted */
|
||||
.chroma .ge { color: #000000; } /* GenericEmph */
|
||||
.chroma .gh { color: #999999; } /* GenericHeading */
|
||||
.chroma .gi { color: #000000; background-color: #ddffdd; } /* GenericInserted */
|
||||
.chroma .gl {} /* GenericUnderline */
|
||||
.chroma .go { color: #888888; } /* GenericOutput */
|
||||
.chroma .gp { color: #555555; } /* GenericPrompt */
|
||||
.chroma .gr { color: #aa0000; } /* GenericError */
|
||||
.chroma .gs {} /* GenericStrong */
|
||||
.chroma .gt { color: #aa0000; } /* GenericTraceback */
|
||||
.chroma .gu { color: #aaaaaa; } /* GenericSubheading */
|
||||
.chroma .hl { background-color: #e5e5e5; } /* LineHighlight */
|
||||
.chroma .il { color: #009999; } /* LiteralNumberIntegerLong */
|
||||
.chroma .k { color: #d73a49; } /* Keyword */
|
||||
.chroma .kc { color: #d73a49; } /* KeywordConstant */
|
||||
@ -25,14 +28,15 @@
|
||||
.chroma .kp { color: #d73a49; } /* KeywordPseudo */
|
||||
.chroma .kr { color: #d73a49; } /* KeywordReserved */
|
||||
.chroma .kt { color: #445588; } /* KeywordType */
|
||||
.chroma .ln { color: #7f7f7f; } /* LineNumbers */
|
||||
.chroma .lnt { color: #7f7f7f; } /* LineNumbersTable */
|
||||
.chroma .l {} /* Literal */
|
||||
.chroma .ld {} /* LiteralDate */
|
||||
.chroma .m { color: #009999; } /* LiteralNumber */
|
||||
.chroma .mb { color: #009999; } /* LiteralNumberBin */
|
||||
.chroma .mf { color: #009999; } /* LiteralNumberFloat */
|
||||
.chroma .mh { color: #009999; } /* LiteralNumberHex */
|
||||
.chroma .mi { color: #009999; } /* LiteralNumberInteger */
|
||||
.chroma .mo { color: #009999; } /* LiteralNumberOct */
|
||||
.chroma .n {} /* Name */
|
||||
.chroma .na { color: #d73a49; } /* NameAttribute */
|
||||
.chroma .nb { color: #005cc5; } /* NameBuiltin */
|
||||
.chroma .nc { color: #445588; } /* NameClass */
|
||||
@ -48,6 +52,8 @@
|
||||
.chroma .nx { color: #24292e; } /* NameOther */
|
||||
.chroma .o { color: #d73a49; } /* Operator */
|
||||
.chroma .ow { color: #d73a49; } /* OperatorWord */
|
||||
.chroma .p {} /* Punctuation */
|
||||
.chroma .py {} /* NameProperty */
|
||||
.chroma .s { color: #106303; } /* LiteralString */
|
||||
.chroma .s1 { color: #cc7a00; } /* LiteralStringSingle */
|
||||
.chroma .s2 { color: #106303; } /* LiteralStringDouble */
|
||||
@ -64,4 +70,5 @@
|
||||
.chroma .vc { color: #008080; } /* NameVariableClass */
|
||||
.chroma .vg { color: #008080; } /* NameVariableGlobal */
|
||||
.chroma .vi { color: #008080; } /* NameVariableInstance */
|
||||
.chroma .vm {} /* NameVariableMagic */
|
||||
.chroma .w { color: #bbbbbb; } /* TextWhitespace */
|
||||
|
@ -172,6 +172,27 @@
|
||||
.gt-py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
|
||||
.gt-py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; }
|
||||
|
||||
.gt-gap-0 { gap: 0 !important; }
|
||||
.gt-gap-1 { gap: .125rem !important; }
|
||||
.gt-gap-2 { gap: .25rem !important; }
|
||||
.gt-gap-3 { gap: .5rem !important; }
|
||||
.gt-gap-4 { gap: 1rem !important; }
|
||||
.gt-gap-5 { gap: 2rem !important; }
|
||||
|
||||
.gt-gap-x-0 { column-gap: 0 !important; }
|
||||
.gt-gap-x-1 { column-gap: .125rem !important; }
|
||||
.gt-gap-x-2 { column-gap: .25rem !important; }
|
||||
.gt-gap-x-3 { column-gap: .5rem !important; }
|
||||
.gt-gap-x-4 { column-gap: 1rem !important; }
|
||||
.gt-gap-x-5 { column-gap: 2rem !important; }
|
||||
|
||||
.gt-gap-y-0 { row-gap: 0 !important; }
|
||||
.gt-gap-y-1 { row-gap: .125rem !important; }
|
||||
.gt-gap-y-2 { row-gap: .25rem !important; }
|
||||
.gt-gap-y-3 { row-gap: .5rem !important; }
|
||||
.gt-gap-y-4 { row-gap: 1rem !important; }
|
||||
.gt-gap-y-5 { row-gap: 2rem !important; }
|
||||
|
||||
.gt-content-center { align-content: center !important; }
|
||||
|
||||
@media @mediaSm {
|
||||
|
@ -1,3 +1,4 @@
|
||||
@import "../chroma/base.less";
|
||||
@import "../chroma/dark.less";
|
||||
@import "../codemirror/dark.less";
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user