Compare commits

...

12 Commits

Author SHA1 Message Date
silverwind
8f4dafcd4e
Rework header bar on issue, pull requests and milestone (#24420)
- Make search bar dynamic full width via flexbox
- Make all buttons `small` so font size is the same for all elements in
the header
- Remove primary color from search field, add SVG icon like on Code tab
- Fix button vertical padding being enlarged by SVG icons

[View diff without
whitespace](https://github.com/go-gitea/gitea/pull/24420/files?diff=unified&w=1)

<img width="1226" alt="Screenshot 2023-04-29 at 11 58 53"
src="https://user-images.githubusercontent.com/115237/235296851-74848267-664f-4c1f-b94c-a1b94196ff75.png">
<img width="1219" alt="Screenshot 2023-04-29 at 11 59 39"
src="https://user-images.githubusercontent.com/115237/235296852-bcfde5ed-8658-43c2-b7e5-3ad84611e76f.png">

Mobile:
<img width="437" alt="Screenshot 2023-04-29 at 11 59 52"
src="https://user-images.githubusercontent.com/115237/235296860-99263373-7b27-4540-868c-a93e70f281ca.png">
<img width="433" alt="Screenshot 2023-04-29 at 12 00 00"
src="https://user-images.githubusercontent.com/115237/235296862-6cf64317-a864-405a-a00f-b5ab620349f5.png">
2023-04-29 23:33:25 -04:00
GiteaBot
0f52beb6b7 [skip ci] Updated translations via Crowdin 2023-04-30 00:25:54 +00:00
Yarden Shoham
cc84c58aff
Remove unused setting time.FORMAT (#24430)
It's loaded and then never used.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-29 22:51:43 +02:00
KN4CK3R
a18919bba6
Fix user-cards format (#24428)
Fixes #24418
2023-04-29 15:43:01 -04:00
yp05327
cc64a92560
Add follow organization and fix the logic of following page (#24345)
![image](https://user-images.githubusercontent.com/18380374/234740589-066f2e5c-30c7-4fc3-a539-066100e1f138.png)

![image](https://user-images.githubusercontent.com/18380374/234740605-88efe55d-7eaa-422e-ab86-0b5a402ca11c.png)

Maybe we can fix user card tmpl in #24319?
Or maybe a list is better here

![image](https://user-images.githubusercontent.com/18380374/234451417-7f93df20-4b19-4abb-a62d-4c67e1aa2220.png)

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-29 15:13:58 -04:00
Yarden Shoham
94d6b5b09d
Add "Updated" column for admin repositories list (#24429)
- Closes #12454

# Before

![image](https://user-images.githubusercontent.com/20454870/235314351-82f5a414-7827-4029-8779-a837283a5a05.png)
# After

![image](https://user-images.githubusercontent.com/20454870/235314376-ccf4bb95-6823-4fce-9b9a-a23da2351769.png)

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
2023-04-29 20:40:10 +02:00
Matthew Walowski
f766b00293
Add ability to specify '--not' from GetAllCommits (#24409)
For my specific use case, I'd like to get all commits that are on one
branch but NOT on the other branch.

For instance, I'd like to get all the commits on `Branch1` that are not
also on `master` (I.e. all commits that were made after `Branch1` was
created).

This PR adds a `not` query param that gets passed down to the `git log`
command to allow the user to exclude items from `GetAllCommits`.

See [git
documentation](https://git-scm.com/docs/git-log#Documentation/git-log.txt---not)

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-29 08:34:14 -04:00
wxiaoguang
241b74f6c5
Improve template helper (#24417)
It seems that we really need the "context function" soon. So we should
clean up the helper functions first.

Major changes:

* Improve StringUtils and add JsonUtils
* Remove one-time-use helper functions like CompareLink
* Move other code (no change) to util_avatar/util_render/util_misc (no
need to propose changes for them)

I have tested the changed templates:


![image](https://user-images.githubusercontent.com/2114189/235283862-608dbf6b-2da3-4d06-8157-b523ca93edb4.png)


![image](https://user-images.githubusercontent.com/2114189/235283888-1dfc0471-e622-4d64-9d76-7859819580d3.png)


![image](https://user-images.githubusercontent.com/2114189/235283903-d559f14d-4abb-4a50-915f-2b9cbc381a7a.png)


![image](https://user-images.githubusercontent.com/2114189/235283955-b7b5adea-aca3-4758-b38a-3aae3f7c6048.png)

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-29 08:02:29 -04:00
wxiaoguang
5a5ab8ef5a
Start cleaning the messy ".ui.left / .ui.right", improve label list page, fix stackable menu (#24393)
Since 2015/2016, there is a global pollution: ".ui.left" / ".ui.right".

Fomantic UI doesn't work this way, it just conflicts with many Fomantic
definitions.

This PR starts the cleaning work of such techinical debts.

And, the "label list" page has been quite messy for long time, for
example, why "li" appears in "div" ......

And fix #24296

<details>


![image](https://user-images.githubusercontent.com/2114189/235051281-54c5374c-b5fd-4b5f-9aa2-02d4bb2d9112.png)


![image](https://user-images.githubusercontent.com/2114189/235055703-2ba042e0-4db7-4e63-8646-02f390d496b5.png)


![image](https://user-images.githubusercontent.com/2114189/235056310-4f6ffdc2-5758-4927-8fb8-314d9fb72a6b.png)


![image](https://user-images.githubusercontent.com/2114189/235058400-dab1c9ec-3325-4671-8345-aee6b0b68042.png)


![image](https://user-images.githubusercontent.com/2114189/235058424-85509532-b9bc-43ad-b00f-a87184c60f22.png)

</details>
2023-04-29 07:35:59 -04:00
Hester Gong
72e956b79a
Improve protected branch setting page (#24379)
Main changes:

1. Change html structure of protected branch page, use [`grouped
fields`](https://fomantic-ui.com/collections/form.html#grouped-fields)
instead of `fields` for better margin, and wrap `grouped fields` around
related `field`s, remove unnecessary `<div id="protection_box"
class="fields">` outer div

2. Changed some order of field to make them more categorized, used `ui
dividing header` for categorization and fine tune css.

Before:

<img width="1907" alt="Screen Shot 2023-04-27 at 14 56 19"
src="https://user-images.githubusercontent.com/17645053/234783731-bce8a7ce-dfc9-4d47-a3a8-b962ebea9467.png">
<img width="1849" alt="Screen Shot 2023-04-27 at 14 56 30"
src="https://user-images.githubusercontent.com/17645053/234783740-c47d314e-5e2d-4854-98fd-c88f85ef3584.png">
<img width="1872" alt="Screen Shot 2023-04-27 at 14 56 36"
src="https://user-images.githubusercontent.com/17645053/234783745-18e35a75-07e8-451d-b001-f9bcf16fcab5.png">

After:


https://user-images.githubusercontent.com/17645053/235114568-da010aad-7654-4410-ab8c-5d0fce7edadb.mov



3. Changed "Enable Merge Whitelist" to radio checkbox, and added "Enable
Merge" radio checkbox, which are exclusive

Before:

<img width="926" alt="Screen Shot 2023-04-28 at 13 08 29"
src="https://user-images.githubusercontent.com/17645053/235059233-75790f7a-e5ea-4e1c-82c6-509fef8b84b3.png">

After:

<img width="942" alt="Screen Shot 2023-04-28 at 13 09 28"
src="https://user-images.githubusercontent.com/17645053/235059367-852d1f61-8407-4126-8c79-315b9c1ffada.png">


4. Add a link to set default branch on branch list page (with reference
to github)


https://user-images.githubusercontent.com/17645053/234787404-61c1c7b6-aabf-429f-a109-5b690e4e0b5a.mov

5. Removed dead codes.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2023-04-29 06:44:52 -04:00
silverwind
fc62992518
Skip known flaky queue tests on CI environment (#24419)
Random CI failures are annoying. It's better to just skip the affected
tests so maintainers can use their valuable time for more productive
topics.

Related: https://github.com/go-gitea/gitea/issues/23608
Related: https://github.com/go-gitea/gitea/issues/23977
Related: https://github.com/go-gitea/gitea/issues/18703
2023-04-29 11:53:11 +02:00
Earl Warren
9cf721e446
getting the tag list does not require being signed in (#24413)
Fixes: https://codeberg.org/forgejo/forgejo/issues/681
2023-04-29 00:40:55 -04:00
82 changed files with 1318 additions and 1254 deletions

View File

@ -1899,11 +1899,6 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Specifies the format for fully outputted dates. Defaults to RFC1123
;; Special supported values are ANSIC, UnixDate, RubyDate, RFC822, RFC822Z, RFC850, RFC1123, RFC1123Z, RFC3339, RFC3339Nano, Kitchen, Stamp, StampMilli, StampMicro and StampNano
;; For more information about the format see http://golang.org/pkg/time/#pkg-constants
;FORMAT =
;;
;; Location the UI time display i.e. Asia/Shanghai ;; Location the UI time display i.e. Asia/Shanghai
;; Empty means server's location setting ;; Empty means server's location setting
;DEFAULT_UI_LOCATION = ;DEFAULT_UI_LOCATION =

View File

@ -1207,7 +1207,6 @@ in this mapping or the filetype using heuristics.
## Time (`time`) ## Time (`time`)
- `FORMAT`: Time format to display on UI. i.e. RFC1123 or 2006-01-02 15:04:05
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai - `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
## Task (`task`) ## Task (`task`)

View File

@ -346,7 +346,7 @@ func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListO
Select("`user`.*"). Select("`user`.*").
Join("LEFT", "follow", "`user`.id=follow.follow_id"). Join("LEFT", "follow", "`user`.id=follow.follow_id").
Where("follow.user_id=?", u.ID). Where("follow.user_id=?", u.ID).
And("`user`.type=?", UserTypeIndividual). And("`user`.type IN (?, ?)", UserTypeIndividual, UserTypeOrganization).
And(isUserVisibleToViewerCond(viewer)) And(isUserVisibleToViewerCond(viewer))
if listOptions.Page != 0 { if listOptions.Page != 0 {
@ -1210,23 +1210,25 @@ func isUserVisibleToViewerCond(viewer *User) builder.Cond {
return builder.Neq{ return builder.Neq{
"`user`.visibility": structs.VisibleTypePrivate, "`user`.visibility": structs.VisibleTypePrivate,
}.Or( }.Or(
// viewer's following
builder.In("`user`.id", builder.In("`user`.id",
builder. builder.
Select("`follow`.user_id"). Select("`follow`.user_id").
From("follow"). From("follow").
Where(builder.Eq{"`follow`.follow_id": viewer.ID})), Where(builder.Eq{"`follow`.follow_id": viewer.ID})),
builder.In("`user`.id", // viewer's org user
builder.
Select("`team_user`.uid").
From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.id = `t2`.id").
Where(builder.Eq{"`t2`.uid": viewer.ID})),
builder.In("`user`.id", builder.In("`user`.id",
builder. builder.
Select("`team_user`.uid"). Select("`team_user`.uid").
From("team_user"). From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id"). Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id").
Where(builder.Eq{"`t2`.uid": viewer.ID}))) Where(builder.Eq{"`t2`.uid": viewer.ID})),
// viewer's org
builder.In("`user`.id",
builder.
Select("`team_user`.org_id").
From("team_user").
Where(builder.Eq{"`team_user`.uid": viewer.ID})))
} }
// IsUserVisibleToViewer check if viewer is able to see user profile // IsUserVisibleToViewer check if viewer is able to see user profile

View File

@ -187,8 +187,8 @@ func (c *Commit) CommitsCount() (int64, error) {
} }
// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize // CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize
func (c *Commit) CommitsByRange(page, pageSize int) ([]*Commit, error) { func (c *Commit) CommitsByRange(page, pageSize int, not string) ([]*Commit, error) {
return c.repo.commitsByRange(c.ID, page, pageSize) return c.repo.commitsByRange(c.ID, page, pageSize, not)
} }
// CommitsBefore returns all the commits before current revision // CommitsBefore returns all the commits before current revision

View File

@ -90,14 +90,22 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
return commits[0], nil return commits[0], nil
} }
func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) { func (repo *Repository) commitsByRange(id SHA1, page, pageSize int, not string) ([]*Commit, error) {
stdout, _, err := NewCommand(repo.Ctx, "log"). cmd := NewCommand(repo.Ctx, "log").
AddOptionFormat("--skip=%d", (page-1)*pageSize).AddOptionFormat("--max-count=%d", pageSize).AddArguments(prettyLogFormat). AddOptionFormat("--skip=%d", (page-1)*pageSize).
AddDynamicArguments(id.String()). AddOptionFormat("--max-count=%d", pageSize).
RunStdBytes(&RunOpts{Dir: repo.Path}) AddArguments(prettyLogFormat).
AddDynamicArguments(id.String())
if not != "" {
cmd.AddOptionValues("--not", not)
}
stdout, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return repo.parsePrettyFormatLogToList(stdout) return repo.parsePrettyFormatLogToList(stdout)
} }

View File

@ -4,6 +4,7 @@
package queue package queue
import ( import (
"os"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -101,6 +102,9 @@ func TestChannelQueue_Batch(t *testing.T) {
} }
func TestChannelQueue_Pause(t *testing.T) { func TestChannelQueue_Pause(t *testing.T) {
if os.Getenv("CI") != "" {
t.Skip("Skipping because test is flaky on CI")
}
lock := sync.Mutex{} lock := sync.Mutex{}
var queue Queue var queue Queue
var err error var err error

View File

@ -4,6 +4,7 @@
package queue package queue
import ( import (
"os"
"strconv" "strconv"
"sync" "sync"
"testing" "testing"
@ -15,6 +16,10 @@ import (
) )
func TestPersistableChannelUniqueQueue(t *testing.T) { func TestPersistableChannelUniqueQueue(t *testing.T) {
if os.Getenv("CI") != "" {
t.Skip("Skipping because test is flaky on CI")
}
tmpDir := t.TempDir() tmpDir := t.TempDir()
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)

View File

@ -9,45 +9,10 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
var ( // DefaultUILocation is the location on the UI, so that we can display the time on UI.
// Time settings var DefaultUILocation = time.Local
TimeFormat string
// UILocation is the location on the UI, so that we can display the time on UI.
DefaultUILocation = time.Local
)
func loadTimeFrom(rootCfg ConfigProvider) { func loadTimeFrom(rootCfg ConfigProvider) {
timeFormatKey := rootCfg.Section("time").Key("FORMAT").MustString("")
if timeFormatKey != "" {
TimeFormat = map[string]string{
"ANSIC": time.ANSIC,
"UnixDate": time.UnixDate,
"RubyDate": time.RubyDate,
"RFC822": time.RFC822,
"RFC822Z": time.RFC822Z,
"RFC850": time.RFC850,
"RFC1123": time.RFC1123,
"RFC1123Z": time.RFC1123Z,
"RFC3339": time.RFC3339,
"RFC3339Nano": time.RFC3339Nano,
"Kitchen": time.Kitchen,
"Stamp": time.Stamp,
"StampMilli": time.StampMilli,
"StampMicro": time.StampMicro,
"StampNano": time.StampNano,
}[timeFormatKey]
// When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
if len(TimeFormat) == 0 {
TimeFormat = timeFormatKey
TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat)
if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" {
log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat)
log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05")
}
log.Trace("Custom TimeFormat: %s", TimeFormat)
}
}
zone := rootCfg.Section("time").Key("DEFAULT_UI_LOCATION").String() zone := rootCfg.Section("time").Key("DEFAULT_UI_LOCATION").String()
if zone != "" { if zone != "" {
var err error var err error

View File

@ -5,46 +5,25 @@
package templates package templates
import ( import (
"bytes"
"context" "context"
"encoding/hex"
"fmt" "fmt"
"html" "html"
"html/template" "html/template"
"math"
"mime"
"net/url" "net/url"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
"time" "time"
"unicode"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/avatars"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
system_model "code.gitea.io/gitea/models/system" system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
gitea_html "code.gitea.io/gitea/modules/html"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/templates/eval" "code.gitea.io/gitea/modules/templates/eval"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff" "code.gitea.io/gitea/services/gitdiff"
"github.com/editorconfig/editorconfig-core-go/v2"
) )
// Used from static.go && dynamic.go // Used from static.go && dynamic.go
@ -53,6 +32,8 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
// NewFuncMap returns functions for injecting to templates // NewFuncMap returns functions for injecting to templates
func NewFuncMap() []template.FuncMap { func NewFuncMap() []template.FuncMap {
return []template.FuncMap{map[string]interface{}{ return []template.FuncMap{map[string]interface{}{
"DumpVar": dumpVar,
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// html/template related functions // html/template related functions
"dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names. "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
@ -63,6 +44,7 @@ func NewFuncMap() []template.FuncMap {
"JSEscape": template.JSEscapeString, "JSEscape": template.JSEscapeString,
"Str2html": Str2html, // TODO: rename it to SanitizeHTML "Str2html": Str2html, // TODO: rename it to SanitizeHTML
"URLJoin": util.URLJoin, "URLJoin": util.URLJoin,
"DotEscape": DotEscape,
"PathEscape": url.PathEscape, "PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments, "PathEscapeSegments": util.PathEscapeSegments,
@ -70,30 +52,7 @@ func NewFuncMap() []template.FuncMap {
// utils // utils
"StringUtils": NewStringUtils, "StringUtils": NewStringUtils,
"SliceUtils": NewSliceUtils, "SliceUtils": NewSliceUtils,
"JsonUtils": NewJsonUtils,
// -----------------------------------------------------------------
// string / json
// TODO: move string helper functions to StringUtils
"Join": strings.Join,
"DotEscape": DotEscape,
"EllipsisString": base.EllipsisString,
"DumpVar": dumpVar,
"Json": func(in interface{}) string {
out, err := json.Marshal(in)
if err != nil {
return ""
}
return string(out)
},
"JsonPrettyPrint": func(in string) string {
var out bytes.Buffer
err := json.Indent(&out, []byte(in), "", " ")
if err != nil {
return ""
}
return out.String()
},
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// svg / avatar / icon // svg / avatar / icon
@ -107,31 +66,7 @@ func NewFuncMap() []template.FuncMap {
"MigrationIcon": MigrationIcon, "MigrationIcon": MigrationIcon,
"ActionIcon": ActionIcon, "ActionIcon": ActionIcon,
"SortArrow": func(normSort, revSort, urlSort string, isDefault bool) template.HTML { "SortArrow": SortArrow,
// if needed
if len(normSort) == 0 || len(urlSort) == 0 {
return ""
}
if len(urlSort) == 0 && isDefault {
// if sort is sorted as default add arrow tho this table header
if isDefault {
return svg.RenderHTML("octicon-triangle-down", 16)
}
} else {
// if sort arg is in url test if it correlates with column header sort arguments
// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
if urlSort == normSort {
// the table is sorted with this header normal
return svg.RenderHTML("octicon-triangle-up", 16)
} else if urlSort == revSort {
// the table is sorted with this header reverse
return svg.RenderHTML("octicon-triangle-down", 16)
}
}
// the table is NOT sorted with this header
return ""
},
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// time / number / format // time / number / format
@ -242,32 +177,9 @@ func NewFuncMap() []template.FuncMap {
"ReactionToEmoji": ReactionToEmoji, "ReactionToEmoji": ReactionToEmoji,
"RenderNote": RenderNote, "RenderNote": RenderNote,
"RenderMarkdownToHtml": func(ctx context.Context, input string) template.HTML { "RenderMarkdownToHtml": RenderMarkdownToHtml,
output, err := markdown.RenderString(&markup.RenderContext{ "RenderLabel": RenderLabel,
Ctx: ctx, "RenderLabels": RenderLabels,
URLPrefix: setting.AppSubURL,
}, input)
if err != nil {
log.Error("RenderString: %v", err)
}
return template.HTML(output)
},
"RenderLabel": func(ctx context.Context, label *issues_model.Label) template.HTML {
return template.HTML(RenderLabel(ctx, label))
},
"RenderLabels": func(ctx context.Context, labels []*issues_model.Label, repoLink string) template.HTML {
htmlCode := `<span class="labels-list">`
for _, label := range labels {
// Protect against nil value in labels - shouldn't happen but would cause a panic if so
if label == nil {
continue
}
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ",
repoLink, label.ID, RenderLabel(ctx, label))
}
htmlCode += "</span>"
return template.HTML(htmlCode)
},
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// misc // misc
@ -278,124 +190,11 @@ func NewFuncMap() []template.FuncMap {
"CommentMustAsDiff": gitdiff.CommentMustAsDiff, "CommentMustAsDiff": gitdiff.CommentMustAsDiff,
"MirrorRemoteAddress": mirrorRemoteAddress, "MirrorRemoteAddress": mirrorRemoteAddress,
"ParseDeadline": func(deadline string) []string { "FilenameIsImage": FilenameIsImage,
return strings.Split(deadline, "|") "TabSizeClass": TabSizeClass,
},
"FilenameIsImage": func(filename string) bool {
mimeType := mime.TypeByExtension(filepath.Ext(filename))
return strings.HasPrefix(mimeType, "image/")
},
"TabSizeClass": func(ec interface{}, filename string) string {
var (
value *editorconfig.Editorconfig
ok bool
)
if ec != nil {
if value, ok = ec.(*editorconfig.Editorconfig); !ok || value == nil {
return "tab-size-8"
}
def, err := value.GetDefinitionForFilename(filename)
if err != nil {
log.Error("tab size class: getting definition for filename: %v", err)
return "tab-size-8"
}
if def.TabWidth > 0 {
return fmt.Sprintf("tab-size-%d", def.TabWidth)
}
}
return "tab-size-8"
},
"SubJumpablePath": func(str string) []string {
var path []string
index := strings.LastIndex(str, "/")
if index != -1 && index != len(str) {
path = append(path, str[0:index+1], str[index+1:])
} else {
path = append(path, str)
}
return path
},
"CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string {
var curBranch string
if repo.ID != baseRepo.ID {
curBranch += fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name))
}
curBranch += util.PathEscapeSegments(branchName)
return fmt.Sprintf("%s/compare/%s...%s",
baseRepo.Link(),
util.PathEscapeSegments(baseRepo.DefaultBranch),
curBranch,
)
},
}} }}
} }
// AvatarHTML creates the HTML for an avatar
func AvatarHTML(src string, size int, class, name string) template.HTML {
sizeStr := fmt.Sprintf(`%d`, size)
if name == "" {
name = "avatar"
}
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
}
// Avatar renders user avatars. args: user, size (int), class (string)
func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
switch t := item.(type) {
case *user_model.User:
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *repo_model.Collaborator:
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *organization.Organization:
src := t.AsUser().AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.AsUser().DisplayName())
}
}
return template.HTML("")
}
// AvatarByAction renders user avatars from action. args: action, size (int), class (string)
func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML {
action.LoadActUser(ctx)
return Avatar(ctx, action.ActUser, others...)
}
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
src := repo.RelAvatarLink()
if src != "" {
return AvatarHTML(src, size, class, repo.FullName())
}
return template.HTML("")
}
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, name)
}
return template.HTML("")
}
// Safe render raw as HTML // Safe render raw as HTML
func Safe(raw string) template.HTML { func Safe(raw string) template.HTML {
return template.HTML(raw) return template.HTML(raw)
@ -411,342 +210,6 @@ func DotEscape(raw string) string {
return strings.ReplaceAll(raw, ".", "\u200d.\u200d") return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
} }
// RenderCommitMessage renders commit message with XSS-safe and special links.
func RenderCommitMessage(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
return RenderCommitMessageLink(ctx, msg, urlPrefix, "", metas)
}
// RenderCommitMessageLink renders commit message as a XXS-safe link to the provided
// default url, handling for special links.
func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
// we can safely assume that it will not return any error, since there
// shouldn't be any special HTML.
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
DefaultLink: urlDefault,
Metas: metas,
}, cleanMsg)
if err != nil {
log.Error("RenderCommitMessage: %v", err)
return ""
}
msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n")
if len(msgLines) == 0 {
return template.HTML("")
}
return template.HTML(msgLines[0])
}
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to
// the provided default url, handling for special links without email to links.
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
msgLine = msgLine[:lineEnd]
}
msgLine = strings.TrimRightFunc(msgLine, unicode.IsSpace)
if len(msgLine) == 0 {
return template.HTML("")
}
// we can safely assume that it will not return any error, since there
// shouldn't be any special HTML.
renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
DefaultLink: urlDefault,
Metas: metas,
}, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("RenderCommitMessageSubject: %v", err)
return template.HTML("")
}
return template.HTML(renderedMessage)
}
// RenderCommitBody extracts the body of a commit message without its title.
func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
msgLine := strings.TrimRightFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
msgLine = msgLine[lineEnd+1:]
} else {
return template.HTML("")
}
msgLine = strings.TrimLeftFunc(msgLine, unicode.IsSpace)
if len(msgLine) == 0 {
return template.HTML("")
}
renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("RenderCommitMessage: %v", err)
return ""
}
return template.HTML(renderedMessage)
}
// Match text that is between back ticks.
var codeMatcher = regexp.MustCompile("`([^`]+)`")
// RenderCodeBlock renders "`…`" as highlighted "<code>" block.
// Intended for issue and PR titles, these containers should have styles for "<code>" elements
func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
htmlWithCodeTags := codeMatcher.ReplaceAllString(string(htmlEscapedTextToRender), "<code>$1</code>") // replace with HTML <code> tags
return template.HTML(htmlWithCodeTags)
}
// RenderIssueTitle renders issue/pull title with defined post processors
func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML {
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, template.HTMLEscapeString(text))
if err != nil {
log.Error("RenderIssueTitle: %v", err)
return template.HTML("")
}
return template.HTML(renderedText)
}
// RenderLabel renders a label
func RenderLabel(ctx context.Context, label *issues_model.Label) string {
labelScope := label.ExclusiveScope()
textColor := "#111"
if label.UseLightTextColor() {
textColor = "#eee"
}
description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
if labelScope == "" {
// Regular label
return fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</div>",
textColor, label.Color, description, RenderEmoji(ctx, label.Name))
}
// Scoped label
scopeText := RenderEmoji(ctx, labelScope)
itemText := RenderEmoji(ctx, label.Name[len(labelScope)+1:])
itemColor := label.Color
scopeColor := label.Color
if r, g, b, err := label.ColorRGB(); err == nil {
// Make scope and item background colors slightly darker and lighter respectively.
// More contrast needed with higher luminance, empirically tweaked.
luminance := (0.299*r + 0.587*g + 0.114*b) / 255
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)
lighten := contrast + math.Max(contrast-luminance, 0.0)
// Compute factor to keep RGB values proportional.
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)
scopeBytes := []byte{
uint8(math.Min(math.Round(r*darkenFactor), 255)),
uint8(math.Min(math.Round(g*darkenFactor), 255)),
uint8(math.Min(math.Round(b*darkenFactor), 255)),
}
itemBytes := []byte{
uint8(math.Min(math.Round(r*lightenFactor), 255)),
uint8(math.Min(math.Round(g*lightenFactor), 255)),
uint8(math.Min(math.Round(b*lightenFactor), 255)),
}
itemColor = "#" + hex.EncodeToString(itemBytes)
scopeColor = "#" + hex.EncodeToString(scopeBytes)
}
return fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important''>%s</div>"+
"</span>",
description,
textColor, scopeColor, scopeText,
textColor, itemColor, itemText)
}
// RenderEmoji renders html text with emoji post processors
func RenderEmoji(ctx context.Context, text string) template.HTML {
renderedText, err := markup.RenderEmoji(&markup.RenderContext{Ctx: ctx},
template.HTMLEscapeString(text))
if err != nil {
log.Error("RenderEmoji: %v", err)
return template.HTML("")
}
return template.HTML(renderedText)
}
// ReactionToEmoji renders emoji for use in reactions
func ReactionToEmoji(reaction string) template.HTML {
val := emoji.FromCode(reaction)
if val != nil {
return template.HTML(val.Emoji)
}
val = emoji.FromAlias(reaction)
if val != nil {
return template.HTML(val.Emoji)
}
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
}
// RenderNote renders the contents of a git-notes file as a commit message.
func RenderNote(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, cleanMsg)
if err != nil {
log.Error("RenderNote: %v", err)
return ""
}
return template.HTML(fullMessage)
}
// IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
func IsMultilineCommitMessage(msg string) bool {
return strings.Count(strings.TrimSpace(msg), "\n") >= 1
}
// Actioner describes an action
type Actioner interface {
GetOpType() activities_model.ActionType
GetActUserName() string
GetRepoUserName() string
GetRepoName() string
GetRepoPath() string
GetRepoLink() string
GetBranch() string
GetContent() string
GetCreate() time.Time
GetIssueInfos() []string
}
// ActionIcon accepts an action operation type and returns an icon class name.
func ActionIcon(opType activities_model.ActionType) string {
switch opType {
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
return "repo"
case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
return "git-commit"
case activities_model.ActionCreateIssue:
return "issue-opened"
case activities_model.ActionCreatePullRequest:
return "git-pull-request"
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
return "comment-discussion"
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
return "git-merge"
case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
return "issue-closed"
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
return "issue-reopened"
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
return "mirror"
case activities_model.ActionApprovePullRequest:
return "check"
case activities_model.ActionRejectPullRequest:
return "diff"
case activities_model.ActionPublishRelease:
return "tag"
case activities_model.ActionPullReviewDismissed:
return "x"
default:
return "question"
}
}
// ActionContent2Commits converts action content to push commits
func ActionContent2Commits(act Actioner) *repository.PushCommits {
push := repository.NewPushCommits()
if act == nil || act.GetContent() == "" {
return push
}
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
}
if push.Len == 0 {
push.Len = len(push.Commits)
}
return push
}
// DiffLineTypeToStr returns diff line type name
func DiffLineTypeToStr(diffType int) string {
switch diffType {
case 2:
return "add"
case 3:
return "del"
case 4:
return "tag"
}
return "same"
}
// MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
func MigrationIcon(hostname string) string {
switch hostname {
case "github.com":
return "octicon-mark-github"
default:
return "gitea-git"
}
}
type remoteAddress struct {
Address string
Username string
Password string
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
a := remoteAddress{}
remoteURL := m.OriginalURL
if ignoreOriginalURL || remoteURL == "" {
var err error
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return a
}
}
u, err := giturl.Parse(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return a
}
if u.Scheme != "ssh" && u.Scheme != "file" {
if u.User != nil {
a.Username = u.User.Username()
a.Password, _ = u.User.Password()
}
u.User = nil
}
a.Address = u.String()
return a
}
// Eval the expression and return the result, see the comment of eval.Expr for details. // Eval the expression and return the result, see the comment of eval.Expr for details.
// To use this helper function in templates, pass each token as a separate parameter. // To use this helper function in templates, pass each token as a separate parameter.
// //

View File

@ -0,0 +1,84 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"context"
"fmt"
"html"
"html/template"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/avatars"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
gitea_html "code.gitea.io/gitea/modules/html"
"code.gitea.io/gitea/modules/setting"
)
// AvatarHTML creates the HTML for an avatar
func AvatarHTML(src string, size int, class, name string) template.HTML {
sizeStr := fmt.Sprintf(`%d`, size)
if name == "" {
name = "avatar"
}
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
}
// Avatar renders user avatars. args: user, size (int), class (string)
func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
switch t := item.(type) {
case *user_model.User:
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *repo_model.Collaborator:
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *organization.Organization:
src := t.AsUser().AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.AsUser().DisplayName())
}
}
return template.HTML("")
}
// AvatarByAction renders user avatars from action. args: action, size (int), class (string)
func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML {
action.LoadActUser(ctx)
return Avatar(ctx, action.ActUser, others...)
}
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
src := repo.RelAvatarLink()
if src != "" {
return AvatarHTML(src, size, class, repo.FullName())
}
return template.HTML("")
}
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, name)
}
return template.HTML("")
}

View File

@ -0,0 +1,35 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"bytes"
"code.gitea.io/gitea/modules/json"
)
type JsonUtils struct{} //nolint:revive
var jsonUtils = JsonUtils{}
func NewJsonUtils() *JsonUtils { //nolint:revive
return &jsonUtils
}
func (su *JsonUtils) EncodeToString(v any) string {
out, err := json.Marshal(v)
if err != nil {
return ""
}
return string(out)
}
func (su *JsonUtils) PrettyIndent(s string) string {
var out bytes.Buffer
err := json.Indent(&out, []byte(s), "", " ")
if err != nil {
return ""
}
return out.String()
}

View File

@ -0,0 +1,209 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"context"
"fmt"
"html/template"
"mime"
"path/filepath"
"strings"
"time"
activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/svg"
"github.com/editorconfig/editorconfig-core-go/v2"
)
func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML {
// if needed
if len(normSort) == 0 || len(urlSort) == 0 {
return ""
}
if len(urlSort) == 0 && isDefault {
// if sort is sorted as default add arrow tho this table header
if isDefault {
return svg.RenderHTML("octicon-triangle-down", 16)
}
} else {
// if sort arg is in url test if it correlates with column header sort arguments
// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
if urlSort == normSort {
// the table is sorted with this header normal
return svg.RenderHTML("octicon-triangle-up", 16)
} else if urlSort == revSort {
// the table is sorted with this header reverse
return svg.RenderHTML("octicon-triangle-down", 16)
}
}
// the table is NOT sorted with this header
return ""
}
// IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
func IsMultilineCommitMessage(msg string) bool {
return strings.Count(strings.TrimSpace(msg), "\n") >= 1
}
// Actioner describes an action
type Actioner interface {
GetOpType() activities_model.ActionType
GetActUserName() string
GetRepoUserName() string
GetRepoName() string
GetRepoPath() string
GetRepoLink() string
GetBranch() string
GetContent() string
GetCreate() time.Time
GetIssueInfos() []string
}
// ActionIcon accepts an action operation type and returns an icon class name.
func ActionIcon(opType activities_model.ActionType) string {
switch opType {
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
return "repo"
case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
return "git-commit"
case activities_model.ActionCreateIssue:
return "issue-opened"
case activities_model.ActionCreatePullRequest:
return "git-pull-request"
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
return "comment-discussion"
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
return "git-merge"
case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
return "issue-closed"
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
return "issue-reopened"
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
return "mirror"
case activities_model.ActionApprovePullRequest:
return "check"
case activities_model.ActionRejectPullRequest:
return "diff"
case activities_model.ActionPublishRelease:
return "tag"
case activities_model.ActionPullReviewDismissed:
return "x"
default:
return "question"
}
}
// ActionContent2Commits converts action content to push commits
func ActionContent2Commits(act Actioner) *repository.PushCommits {
push := repository.NewPushCommits()
if act == nil || act.GetContent() == "" {
return push
}
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
}
if push.Len == 0 {
push.Len = len(push.Commits)
}
return push
}
// DiffLineTypeToStr returns diff line type name
func DiffLineTypeToStr(diffType int) string {
switch diffType {
case 2:
return "add"
case 3:
return "del"
case 4:
return "tag"
}
return "same"
}
// MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
func MigrationIcon(hostname string) string {
switch hostname {
case "github.com":
return "octicon-mark-github"
default:
return "gitea-git"
}
}
type remoteAddress struct {
Address string
Username string
Password string
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
a := remoteAddress{}
remoteURL := m.OriginalURL
if ignoreOriginalURL || remoteURL == "" {
var err error
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return a
}
}
u, err := giturl.Parse(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return a
}
if u.Scheme != "ssh" && u.Scheme != "file" {
if u.User != nil {
a.Username = u.User.Username()
a.Password, _ = u.User.Password()
}
u.User = nil
}
a.Address = u.String()
return a
}
func FilenameIsImage(filename string) bool {
mimeType := mime.TypeByExtension(filepath.Ext(filename))
return strings.HasPrefix(mimeType, "image/")
}
func TabSizeClass(ec interface{}, filename string) string {
var (
value *editorconfig.Editorconfig
ok bool
)
if ec != nil {
if value, ok = ec.(*editorconfig.Editorconfig); !ok || value == nil {
return "tab-size-8"
}
def, err := value.GetDefinitionForFilename(filename)
if err != nil {
log.Error("tab size class: getting definition for filename: %v", err)
return "tab-size-8"
}
if def.TabWidth > 0 {
return fmt.Sprintf("tab-size-%d", def.TabWidth)
}
}
return "tab-size-8"
}

View File

@ -0,0 +1,254 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"context"
"encoding/hex"
"fmt"
"html/template"
"math"
"net/url"
"regexp"
"strings"
"unicode"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
)
// RenderCommitMessage renders commit message with XSS-safe and special links.
func RenderCommitMessage(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
return RenderCommitMessageLink(ctx, msg, urlPrefix, "", metas)
}
// RenderCommitMessageLink renders commit message as a XXS-safe link to the provided
// default url, handling for special links.
func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
// we can safely assume that it will not return any error, since there
// shouldn't be any special HTML.
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
DefaultLink: urlDefault,
Metas: metas,
}, cleanMsg)
if err != nil {
log.Error("RenderCommitMessage: %v", err)
return ""
}
msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n")
if len(msgLines) == 0 {
return template.HTML("")
}
return template.HTML(msgLines[0])
}
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to
// the provided default url, handling for special links without email to links.
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
msgLine = msgLine[:lineEnd]
}
msgLine = strings.TrimRightFunc(msgLine, unicode.IsSpace)
if len(msgLine) == 0 {
return template.HTML("")
}
// we can safely assume that it will not return any error, since there
// shouldn't be any special HTML.
renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
DefaultLink: urlDefault,
Metas: metas,
}, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("RenderCommitMessageSubject: %v", err)
return template.HTML("")
}
return template.HTML(renderedMessage)
}
// RenderCommitBody extracts the body of a commit message without its title.
func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
msgLine := strings.TrimRightFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
msgLine = msgLine[lineEnd+1:]
} else {
return template.HTML("")
}
msgLine = strings.TrimLeftFunc(msgLine, unicode.IsSpace)
if len(msgLine) == 0 {
return template.HTML("")
}
renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("RenderCommitMessage: %v", err)
return ""
}
return template.HTML(renderedMessage)
}
// Match text that is between back ticks.
var codeMatcher = regexp.MustCompile("`([^`]+)`")
// RenderCodeBlock renders "`…`" as highlighted "<code>" block.
// Intended for issue and PR titles, these containers should have styles for "<code>" elements
func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
htmlWithCodeTags := codeMatcher.ReplaceAllString(string(htmlEscapedTextToRender), "<code>$1</code>") // replace with HTML <code> tags
return template.HTML(htmlWithCodeTags)
}
// RenderIssueTitle renders issue/pull title with defined post processors
func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML {
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, template.HTMLEscapeString(text))
if err != nil {
log.Error("RenderIssueTitle: %v", err)
return template.HTML("")
}
return template.HTML(renderedText)
}
// RenderLabel renders a label
func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML {
labelScope := label.ExclusiveScope()
textColor := "#111"
if label.UseLightTextColor() {
textColor = "#eee"
}
description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
if labelScope == "" {
// Regular label
s := fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</div>",
textColor, label.Color, description, RenderEmoji(ctx, label.Name))
return template.HTML(s)
}
// Scoped label
scopeText := RenderEmoji(ctx, labelScope)
itemText := RenderEmoji(ctx, label.Name[len(labelScope)+1:])
itemColor := label.Color
scopeColor := label.Color
if r, g, b, err := label.ColorRGB(); err == nil {
// Make scope and item background colors slightly darker and lighter respectively.
// More contrast needed with higher luminance, empirically tweaked.
luminance := (0.299*r + 0.587*g + 0.114*b) / 255
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)
lighten := contrast + math.Max(contrast-luminance, 0.0)
// Compute factor to keep RGB values proportional.
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)
scopeBytes := []byte{
uint8(math.Min(math.Round(r*darkenFactor), 255)),
uint8(math.Min(math.Round(g*darkenFactor), 255)),
uint8(math.Min(math.Round(b*darkenFactor), 255)),
}
itemBytes := []byte{
uint8(math.Min(math.Round(r*lightenFactor), 255)),
uint8(math.Min(math.Round(g*lightenFactor), 255)),
uint8(math.Min(math.Round(b*lightenFactor), 255)),
}
itemColor = "#" + hex.EncodeToString(itemBytes)
scopeColor = "#" + hex.EncodeToString(scopeBytes)
}
s := fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important''>%s</div>"+
"</span>",
description,
textColor, scopeColor, scopeText,
textColor, itemColor, itemText)
return template.HTML(s)
}
// RenderEmoji renders html text with emoji post processors
func RenderEmoji(ctx context.Context, text string) template.HTML {
renderedText, err := markup.RenderEmoji(&markup.RenderContext{Ctx: ctx},
template.HTMLEscapeString(text))
if err != nil {
log.Error("RenderEmoji: %v", err)
return template.HTML("")
}
return template.HTML(renderedText)
}
// ReactionToEmoji renders emoji for use in reactions
func ReactionToEmoji(reaction string) template.HTML {
val := emoji.FromCode(reaction)
if val != nil {
return template.HTML(val.Emoji)
}
val = emoji.FromAlias(reaction)
if val != nil {
return template.HTML(val.Emoji)
}
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
}
// RenderNote renders the contents of a git-notes file as a commit message.
func RenderNote(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
Ctx: ctx,
URLPrefix: urlPrefix,
Metas: metas,
}, cleanMsg)
if err != nil {
log.Error("RenderNote: %v", err)
return ""
}
return template.HTML(fullMessage)
}
func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //nolint:revive
output, err := markdown.RenderString(&markup.RenderContext{
Ctx: ctx,
URLPrefix: setting.AppSubURL,
}, input)
if err != nil {
log.Error("RenderString: %v", err)
}
return template.HTML(output)
}
func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink string) template.HTML {
htmlCode := `<span class="labels-list">`
for _, label := range labels {
// Protect against nil value in labels - shouldn't happen but would cause a panic if so
if label == nil {
continue
}
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ",
repoLink, label.ID, RenderLabel(ctx, label))
}
htmlCode += "</span>"
return template.HTML(htmlCode)
}

View File

@ -3,12 +3,18 @@
package templates package templates
import "strings" import (
"strings"
"code.gitea.io/gitea/modules/base"
)
type StringUtils struct{} type StringUtils struct{}
var stringUtils = StringUtils{}
func NewStringUtils() *StringUtils { func NewStringUtils() *StringUtils {
return &StringUtils{} return &stringUtils
} }
func (su *StringUtils) HasPrefix(s, prefix string) bool { func (su *StringUtils) HasPrefix(s, prefix string) bool {
@ -22,3 +28,11 @@ func (su *StringUtils) Contains(s, substr string) bool {
func (su *StringUtils) Split(s, sep string) []string { func (su *StringUtils) Split(s, sep string) []string {
return strings.Split(s, sep) return strings.Split(s, sep)
} }
func (su *StringUtils) Join(a []string, sep string) string {
return strings.Join(a, sep)
}
func (su *StringUtils) EllipsisString(s string, max int) string {
return base.EllipsisString(s, max)
}

View File

@ -1,33 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package timeutil
import (
"time"
"code.gitea.io/gitea/modules/setting"
)
var langTimeFormats = map[string]string{
"zh-CN": "2006年01月02日 15时04分05秒",
"en-US": time.RFC1123,
"lv-LV": "02.01.2006. 15:04:05",
}
// GetLangTimeFormat represents the default time format for the language
func GetLangTimeFormat(lang string) string {
return langTimeFormats[lang]
}
// GetTimeFormat represents the
func GetTimeFormat(lang string) string {
if setting.TimeFormat == "" {
format := GetLangTimeFormat(lang)
if format == "" {
format = time.RFC1123
}
return format
}
return setting.TimeFormat
}

View File

@ -1901,6 +1901,7 @@ settings.sync_mirror = Synchronize Now
settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute. settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute.
settings.site = Website settings.site = Website
settings.update_settings = Update Settings settings.update_settings = Update Settings
settings.branches.switch_default_branch = Switch Default Branch
settings.branches.update_default_branch = Update Default Branch settings.branches.update_default_branch = Update Default Branch
settings.branches.add_new_rule = Add New Rule settings.branches.add_new_rule = Add New Rule
settings.advanced_settings = Advanced Settings settings.advanced_settings = Advanced Settings
@ -2096,6 +2097,8 @@ settings.event_pull_request_review = Pull Request Reviewed
settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment. settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment.
settings.event_pull_request_sync = Pull Request Synchronized settings.event_pull_request_sync = Pull Request Synchronized
settings.event_pull_request_sync_desc = Pull request synchronized. settings.event_pull_request_sync_desc = Pull request synchronized.
settings.event_pull_request_approvals = Pull Request Approvals
settings.event_pull_request_merge = Pull Request Merge
settings.event_package = Package settings.event_package = Package
settings.event_package_desc = Package created or deleted in a repository. settings.event_package_desc = Package created or deleted in a repository.
settings.branch_filter = Branch filter settings.branch_filter = Branch filter
@ -2151,13 +2154,15 @@ settings.protected_branch.delete_rule = Delete Rule
settings.protected_branch_can_push = Allow push? settings.protected_branch_can_push = Allow push?
settings.protected_branch_can_push_yes = You can push settings.protected_branch_can_push_yes = You can push
settings.protected_branch_can_push_no = You cannot push settings.protected_branch_can_push_no = You cannot push
settings.branch_protection = Branch Protection for Branch '<b>%s</b>' settings.branch_protection = Branch Protection Rules for Branch '<b>%s</b>'
settings.protect_this_branch = Enable Branch Protection settings.protect_this_branch = Enable Branch Protection
settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch. settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch.
settings.protect_disable_push = Disable Push settings.protect_disable_push = Disable Push
settings.protect_disable_push_desc = No pushing will be allowed to this branch. settings.protect_disable_push_desc = No pushing will be allowed to this branch.
settings.protect_enable_push = Enable Push settings.protect_enable_push = Enable Push
settings.protect_enable_push_desc = Anyone with write access will be allowed to push to this branch (but not force push). settings.protect_enable_push_desc = Anyone with write access will be allowed to push to this branch (but not force push).
settings.protect_enable_merge = Enable Merge
settings.protect_enable_merge_desc = Anyone with write access will be allowed to merge the pull requests into this branch.
settings.protect_whitelist_committers = Whitelist Restricted Push settings.protect_whitelist_committers = Whitelist Restricted Push
settings.protect_whitelist_committers_desc = Only whitelisted users or teams will be allowed to push to this branch (but not force push). settings.protect_whitelist_committers_desc = Only whitelisted users or teams will be allowed to push to this branch (but not force push).
settings.protect_whitelist_deploy_keys = Whitelist deploy keys with write access to push. settings.protect_whitelist_deploy_keys = Whitelist deploy keys with write access to push.
@ -2183,6 +2188,7 @@ settings.dismiss_stale_approvals_desc = When new commits that change the content
settings.require_signed_commits = Require Signed Commits settings.require_signed_commits = Require Signed Commits
settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable. settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable.
settings.protect_branch_name_pattern = Protected Branch Name Pattern settings.protect_branch_name_pattern = Protected Branch Name Pattern
settings.protect_patterns = Patterns
settings.protect_protected_file_patterns = "Protected file patterns (separated using semicolon ';'):" settings.protect_protected_file_patterns = "Protected file patterns (separated using semicolon ';'):"
settings.protect_protected_file_patterns_desc = "Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>." settings.protect_protected_file_patterns_desc = "Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>."
settings.protect_unprotected_file_patterns = "Unprotected file patterns (separated using semicolon ';'):" settings.protect_unprotected_file_patterns = "Unprotected file patterns (separated using semicolon ';'):"

View File

@ -1606,7 +1606,10 @@ pulls.tab_files=変更されたファイル
pulls.reopen_to_merge=このプルリクエストをマージする場合は再オープンしてください。 pulls.reopen_to_merge=このプルリクエストをマージする場合は再オープンしてください。
pulls.cant_reopen_deleted_branch=このプルリクエストはブランチが削除されているため、再オープンできません。 pulls.cant_reopen_deleted_branch=このプルリクエストはブランチが削除されているため、再オープンできません。
pulls.merged=マージ済み pulls.merged=マージ済み
pulls.merged_success=プルリクエストは正常にマージ、クローズされました
pulls.closed=プルリクエストはクローズされました
pulls.manually_merged=手動マージ済み pulls.manually_merged=手動マージ済み
pulls.merged_info_text=ブランチ %s を削除できるようになりました。
pulls.is_closed=プルリクエストはクローズされています。 pulls.is_closed=プルリクエストはクローズされています。
pulls.title_wip_desc=`誤ってマージされないようにするには、<a href="#">タイトルの頭に <strong>%s</strong> を付けます</a>。` pulls.title_wip_desc=`誤ってマージされないようにするには、<a href="#">タイトルの頭に <strong>%s</strong> を付けます</a>。`
pulls.cannot_merge_work_in_progress=このプルリクエストは作業中(WIP)としてマーキングされています。 pulls.cannot_merge_work_in_progress=このプルリクエストは作業中(WIP)としてマーキングされています。
@ -2426,6 +2429,7 @@ team_unit_desc=リポジトリのセクションへのアクセスを許可
team_unit_disabled=(無効) team_unit_disabled=(無効)
form.name_reserved=組織名 "%s" は予約されています。 form.name_reserved=組織名 "%s" は予約されています。
form.name_pattern_not_allowed=`"%s" の形式は組織名に使用できません。`
form.create_org_not_allowed=組織を作成する権限がありません。 form.create_org_not_allowed=組織を作成する権限がありません。
settings=設定 settings=設定
@ -3392,8 +3396,12 @@ runs.closed_tab=%d クローズ
runs.commit=コミット runs.commit=コミット
runs.pushed_by=Pushed by runs.pushed_by=Pushed by
runs.invalid_workflow_helper=ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s runs.invalid_workflow_helper=ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s
runs.no_matching_runner_helper=一致するランナーがありません: %s
need_approval_desc=フォークプルリクエストのワークフローを実行するには承認が必要です。 need_approval_desc=フォークプルリクエストのワークフローを実行するには承認が必要です。
[projects] [projects]
type-1.display_name=個人プロジェクト
type-2.display_name=リポジトリ プロジェクト
type-3.display_name=組織プロジェクト

View File

@ -1833,7 +1833,7 @@ activity.new_issues_count_n=questões novas
activity.new_issue_label=Em aberto activity.new_issue_label=Em aberto
activity.title.unresolved_conv_1=%d diálogo não concluído activity.title.unresolved_conv_1=%d diálogo não concluído
activity.title.unresolved_conv_n=%d diálogos não concluídos activity.title.unresolved_conv_n=%d diálogos não concluídos
activity.unresolved_conv_desc=Estas questões e estes pedidos de integração que foram modificados recentemente ainda não foram concluídos. activity.unresolved_conv_desc=Estas questões e estes pedidos de integração, que foram modificados recentemente, ainda não foram concluídos.
activity.unresolved_conv_label=Em aberto activity.unresolved_conv_label=Em aberto
activity.title.releases_1=%d lançamento activity.title.releases_1=%d lançamento
activity.title.releases_n=%d Lançamentos activity.title.releases_n=%d Lançamentos

View File

@ -115,6 +115,10 @@ func GetAllCommits(ctx *context.APIContext) {
// in: query // in: query
// description: page size of results (ignored if used with 'path') // description: page size of results (ignored if used with 'path')
// type: integer // type: integer
// - name: not
// in: query
// description: commits that match the given specifier will not be listed.
// type: string
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/CommitList" // "$ref": "#/responses/CommitList"
@ -181,7 +185,8 @@ func GetAllCommits(ctx *context.APIContext) {
} }
// Query commits // Query commits
commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize) not := ctx.FormString("not")
commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsByRange", err) ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
return return

View File

@ -16,7 +16,7 @@ import (
// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed // ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed
func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) { func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
commits, err := ctx.Repo.Commit.CommitsByRange(0, 10) commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "")
if err != nil { if err != nil {
ctx.ServerError("ShowBranchFeed", err) ctx.ServerError("ShowBranchFeed", err)
return return

View File

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
@ -143,6 +144,11 @@ func Home(ctx *context.Context) {
return return
} }
var isFollowing bool
if ctx.Doer != nil {
isFollowing = user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID)
}
ctx.Data["Repos"] = repos ctx.Data["Repos"] = repos
ctx.Data["Total"] = count ctx.Data["Total"] = count
ctx.Data["MembersTotal"] = membersCount ctx.Data["MembersTotal"] = membersCount
@ -150,6 +156,7 @@ func Home(ctx *context.Context) {
ctx.Data["Teams"] = ctx.Org.Teams ctx.Data["Teams"] = ctx.Org.Teams
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
ctx.Data["PageIsViewRepositories"] = true ctx.Data["PageIsViewRepositories"] = true
ctx.Data["IsFollowing"] = isFollowing
pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)

View File

@ -70,7 +70,7 @@ func Commits(ctx *context.Context) {
} }
// Both `git log branchName` and `git log commitId` work. // Both `git log branchName` and `git log commitId` work.
commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize) commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "")
if err != nil { if err != nil {
ctx.ServerError("CommitsByRange", err) ctx.ServerError("CommitsByRange", err)
return return

View File

@ -130,15 +130,6 @@ func SettingsProtectedBranch(c *context.Context) {
} }
c.Data["branch_status_check_contexts"] = contexts c.Data["branch_status_check_contexts"] = contexts
c.Data["is_context_required"] = func(context string) bool {
for _, c := range rule.StatusCheckContexts {
if c == context {
return true
}
}
return false
}
if c.Repo.Owner.IsOrganization() { if c.Repo.Owner.IsOrganization() {
teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead)
if err != nil { if err != nil {

View File

@ -167,9 +167,7 @@ func Profile(ctx *context.Context) {
language := ctx.FormTrim("language") language := ctx.FormTrim("language")
ctx.Data["Language"] = language ctx.Data["Language"] = language
switch tab { followers, numFollowers, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
case "followers":
items, count, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
PageSize: pagingNum, PageSize: pagingNum,
Page: page, Page: page,
}) })
@ -177,11 +175,8 @@ func Profile(ctx *context.Context) {
ctx.ServerError("GetUserFollowers", err) ctx.ServerError("GetUserFollowers", err)
return return
} }
ctx.Data["Cards"] = items ctx.Data["NumFollowers"] = numFollowers
following, numFollowing, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
total = int(count)
case "following":
items, count, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
PageSize: pagingNum, PageSize: pagingNum,
Page: page, Page: page,
}) })
@ -189,8 +184,14 @@ func Profile(ctx *context.Context) {
ctx.ServerError("GetUserFollowing", err) ctx.ServerError("GetUserFollowing", err)
return return
} }
ctx.Data["Cards"] = items ctx.Data["NumFollowing"] = numFollowing
switch tab {
case "followers":
ctx.Data["Cards"] = followers
total = int(count)
case "following":
ctx.Data["Cards"] = following
total = int(count) total = int(count)
case "activity": case "activity":
date := ctx.FormString("date") date := ctx.FormString("date")

View File

@ -1093,7 +1093,7 @@ func registerRoutes(m *web.Route) {
repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true)) repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
m.Post("/tags/delete", repo.DeleteTag, reqSignIn, m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
}, reqSignIn, context.RepoAssignment, context.UnitTypes()) }, ignSignIn, context.RepoAssignment, context.UnitTypes())
// Releases // Releases
m.Group("/{username}/{reponame}", func() { m.Group("/{username}/{reponame}", func() {

View File

@ -334,7 +334,7 @@
<div class="field"> <div class="field">
<label for="oauth2_scopes">{{.locale.Tr "admin.auths.oauth2_scopes"}}</label> <label for="oauth2_scopes">{{.locale.Tr "admin.auths.oauth2_scopes"}}</label>
<input id="oauth2_scopes" name="oauth2_scopes" value="{{if $cfg.Scopes}}{{Join $cfg.Scopes ","}}{{end}}"> <input id="oauth2_scopes" name="oauth2_scopes" value="{{if $cfg.Scopes}}{{StringUtils.Join $cfg.Scopes ","}}{{end}}">
</div> </div>
<div class="field"> <div class="field">
<label for="oauth2_required_claim_name">{{.locale.Tr "admin.auths.oauth2_required_claim_name"}}</label> <label for="oauth2_required_claim_name">{{.locale.Tr "admin.auths.oauth2_required_claim_name"}}</label>

View File

@ -365,7 +365,7 @@
<dt>{{$.locale.Tr "admin.config.log_mode"}}</dt> <dt>{{$.locale.Tr "admin.config.log_mode"}}</dt>
<dd>{{.Name}} ({{.Provider}})</dd> <dd>{{.Name}} ({{.Provider}})</dd>
<dt>{{$.locale.Tr "admin.config.log_config"}}</dt> <dt>{{$.locale.Tr "admin.config.log_config"}}</dt>
<dd><pre>{{.Config | JsonPrettyPrint}}</pre></dd> <dd><pre>{{JsonUtils.PrettyIndent .Config}}</pre></dd>
{{end}} {{end}}
<div class="ui divider"></div> <div class="ui divider"></div>
<dt>{{$.locale.Tr "admin.config.router_log_mode"}}</dt> <dt>{{$.locale.Tr "admin.config.router_log_mode"}}</dt>
@ -378,7 +378,7 @@
<dt>{{$.locale.Tr "admin.config.log_mode"}}</dt> <dt>{{$.locale.Tr "admin.config.log_mode"}}</dt>
<dd>{{.Name}} ({{.Provider}})</dd> <dd>{{.Name}} ({{.Provider}})</dd>
<dt>{{$.locale.Tr "admin.config.log_config"}}</dt> <dt>{{$.locale.Tr "admin.config.log_config"}}</dt>
<dd><pre>{{.Config | JsonPrettyPrint}}</pre></dd> <dd><pre>{{JsonUtils.PrettyIndent .Config}}</pre></dd>
{{end}} {{end}}
{{else}} {{else}}
<dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd> <dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd>
@ -393,7 +393,7 @@
<dt>{{$.locale.Tr "admin.config.log_mode"}}</dt> <dt>{{$.locale.Tr "admin.config.log_mode"}}</dt>
<dd>{{.Name}} ({{.Provider}})</dd> <dd>{{.Name}} ({{.Provider}})</dd>
<dt>{{$.locale.Tr "admin.config.log_config"}}</dt> <dt>{{$.locale.Tr "admin.config.log_config"}}</dt>
<dd><pre>{{.Config | JsonPrettyPrint}}</pre></dd> <dd><pre>{{JsonUtils.PrettyIndent .Config}}</pre></dd>
{{end}} {{end}}
{{else}} {{else}}
<dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd> <dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd>
@ -412,7 +412,7 @@
<dt>{{$.locale.Tr "admin.config.log_mode"}}</dt> <dt>{{$.locale.Tr "admin.config.log_mode"}}</dt>
<dd>{{.Name}} ({{.Provider}})</dd> <dd>{{.Name}} ({{.Provider}})</dd>
<dt>{{$.locale.Tr "admin.config.log_config"}}</dt> <dt>{{$.locale.Tr "admin.config.log_config"}}</dt>
<dd><pre>{{.Config | JsonPrettyPrint}}</pre></dd> <dd><pre>{{JsonUtils.PrettyIndent .Config}}</pre></dd>
{{end}} {{end}}
{{else}} {{else}}
<dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd> <dd>{{$.locale.Tr "admin.config.routes_to_default_logger"}}</dd>

View File

@ -23,51 +23,51 @@
<tbody> <tbody>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.delete_inactive_accounts"}}</td> <td>{{.locale.Tr "admin.dashboard.delete_inactive_accounts"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="delete_inactive_accounts">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="delete_inactive_accounts">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.delete_repo_archives"}}</td> <td>{{.locale.Tr "admin.dashboard.delete_repo_archives"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="delete_repo_archives">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="delete_repo_archives">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.delete_missing_repos"}}</td> <td>{{.locale.Tr "admin.dashboard.delete_missing_repos"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="delete_missing_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="delete_missing_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.git_gc_repos"}}</td> <td>{{.locale.Tr "admin.dashboard.git_gc_repos"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="git_gc_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="git_gc_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
{{if and (not .SSH.Disabled) (not .SSH.StartBuiltinServer)}} {{if and (not .SSH.Disabled) (not .SSH.StartBuiltinServer)}}
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.resync_all_sshkeys"}}<br> <td>{{.locale.Tr "admin.dashboard.resync_all_sshkeys"}}<br>
{{.locale.Tr "admin.dashboard.resync_all_sshkeys.desc"}}</td> {{.locale.Tr "admin.dashboard.resync_all_sshkeys.desc"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="resync_all_sshkeys">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="resync_all_sshkeys">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.resync_all_sshprincipals"}}<br> <td>{{.locale.Tr "admin.dashboard.resync_all_sshprincipals"}}<br>
{{.locale.Tr "admin.dashboard.resync_all_sshprincipals.desc"}}</td> {{.locale.Tr "admin.dashboard.resync_all_sshprincipals.desc"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="resync_all_sshprincipals">{{svg "octicon-play" 16}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="resync_all_sshprincipals">{{svg "octicon-play" 16}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
{{end}} {{end}}
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.resync_all_hooks"}}</td> <td>{{.locale.Tr "admin.dashboard.resync_all_hooks"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="resync_all_hooks">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="resync_all_hooks">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.reinit_missing_repos"}}</td> <td>{{.locale.Tr "admin.dashboard.reinit_missing_repos"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="reinit_missing_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="reinit_missing_repos">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.sync_external_users"}}</td> <td>{{.locale.Tr "admin.dashboard.sync_external_users"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="sync_external_users">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="sync_external_users">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.repo_health_check"}}</td> <td>{{.locale.Tr "admin.dashboard.repo_health_check"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="repo_health_check">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="repo_health_check">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
<tr> <tr>
<td>{{.locale.Tr "admin.dashboard.delete_generated_repository_avatars"}}</td> <td>{{.locale.Tr "admin.dashboard.delete_generated_repository_avatars"}}</td>
<td><button type="submit" class="ui green button right" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td> <td class="text right"><button type="submit" class="ui green button" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -174,7 +174,7 @@
{{.locale.Tr "admin.monitor.queue.configuration"}} {{.locale.Tr "admin.monitor.queue.configuration"}}
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<pre>{{.Queue.Configuration | JsonPrettyPrint}}</pre> <pre>{{JsonUtils.PrettyIndent .Queue.Configuration}}</pre>
</div> </div>
</div> </div>

View File

@ -33,6 +33,7 @@
{{.locale.Tr "admin.repos.size"}} {{.locale.Tr "admin.repos.size"}}
{{SortArrow "size" "reversesize" $.SortType false}} {{SortArrow "size" "reversesize" $.SortType false}}
</th> </th>
<th>{{.locale.Tr "admin.auths.updated"}}</th>
<th>{{.locale.Tr "admin.users.created"}}</th> <th>{{.locale.Tr "admin.users.created"}}</th>
<th>{{.locale.Tr "admin.notices.op"}}</th> <th>{{.locale.Tr "admin.notices.op"}}</th>
</tr> </tr>
@ -80,6 +81,7 @@
<td>{{.NumForks}}</td> <td>{{.NumForks}}</td>
<td>{{.NumIssues}}</td> <td>{{.NumIssues}}</td>
<td>{{FileSize .Size}}</td> <td>{{FileSize .Size}}</td>
<td>{{DateTime "short" .UpdatedUnix}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td> <td>{{DateTime "short" .CreatedUnix}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td> <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
</tr> </tr>

View File

@ -1,5 +1,5 @@
<footer role="group" aria-label="{{.locale.Tr "aria.footer"}}"> <footer class="page-footer" role="group" aria-label="{{.locale.Tr "aria.footer"}}">
<div class="ui left" role="contentinfo" aria-label="{{.locale.Tr "aria.footer.software"}}"> <div class="left-links" role="contentinfo" aria-label="{{.locale.Tr "aria.footer.software"}}">
<a target="_blank" rel="noopener noreferrer" href="https://gitea.io">{{.locale.Tr "powered_by" "Gitea"}}</a> <a target="_blank" rel="noopener noreferrer" href="https://gitea.io">{{.locale.Tr "powered_by" "Gitea"}}</a>
{{if (or .ShowFooterVersion .PageIsAdmin)}} {{if (or .ShowFooterVersion .PageIsAdmin)}}
{{.locale.Tr "version"}}: {{.locale.Tr "version"}}:
@ -14,7 +14,7 @@
{{.locale.Tr "template"}}{{if .TemplateName}} {{.TemplateName}}{{end}}: <strong>{{call .TemplateLoadTimes}}</strong> {{.locale.Tr "template"}}{{if .TemplateName}} {{.TemplateName}}{{end}}: <strong>{{call .TemplateLoadTimes}}</strong>
{{end}} {{end}}
</div> </div>
<div class="ui right links" role="group" aria-label="{{.locale.Tr "aria.footer.links"}}"> <div class="right-links" role="group" aria-label="{{.locale.Tr "aria.footer.links"}}">
<div class="ui dropdown upward language"> <div class="ui dropdown upward language">
<span>{{svg "octicon-globe"}} {{.locale.LangName}}</span> <span>{{svg "octicon-globe"}} {{.locale.LangName}}</span>
<div class="menu language-menu"> <div class="menu language-menu">

View File

@ -83,7 +83,7 @@
{{template "custom/body_inner_pre" .}} {{template "custom/body_inner_pre" .}}
{{if not .PageIsInstall}} {{if not .PageIsInstall}}
<div class="ui top secondary stackable main menu following bar light no-vertical-tabs"> <div class="ui top secondary stackable main menu following bar light">
{{template "base/head_navbar" .}} {{template "base/head_navbar" .}}
</div><!-- end bar --> </div><!-- end bar -->
{{end}} {{end}}

View File

@ -19,6 +19,18 @@
{{if .Org.Website}}<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer me" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}} {{if .Org.Website}}<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer me" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
</div> </div>
</div> </div>
<div class="right stackable menu">
<form method="post" action="{{.Link}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}&redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<button type="submit" class="ui basic button gt-mr-0">
{{if $.IsFollowing}}
{{.locale.Tr "user.unfollow"}}
{{else}}
{{.locale.Tr "user.follow"}}
{{end}}
</button>
</form>
</div>
</div> </div>
{{template "org/menu" .}} {{template "org/menu" .}}

View File

@ -50,12 +50,12 @@
<div class="ui bottom attached table segment members"> <div class="ui bottom attached table segment members">
{{range .Invites}} {{range .Invites}}
<div class="item"> <div class="item">
{{.Email}}
<form action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/remove_invite" method="post"> <form action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/remove_invite" method="post">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
<input type="hidden" name="iid" value="{{.ID}}"> <input type="hidden" name="iid" value="{{.ID}}">
<button class="ui red button right">{{$.locale.Tr "org.members.remove"}}</button> <button class="ui red button gt-float-right">{{$.locale.Tr "org.members.remove"}}</button>
</form> </form>
{{.Email}}
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -25,8 +25,8 @@
</div> </div>
<div class="inline ui field right"> <div class="inline ui field right">
<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/repositories" method="post"> <form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/repositories" method="post">
<button class="ui red button delete-button right" data-modal-id="org-team-remove-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{.locale.Tr "remove_all"}}</button> <button class="ui green button add-all-button" data-modal-id="org-team-add-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{.locale.Tr "add_all"}}</button>
<button class="ui green button add-all-button right" data-modal-id="org-team-add-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{.locale.Tr "add_all"}}</button> <button class="ui red button delete-button" data-modal-id="org-team-remove-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{.locale.Tr "remove_all"}}</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -22,9 +22,9 @@
<a class="item" href="{{$.Link}}/rules/{{.ID}}"><strong>{{.Type.Name}}</strong></a> <a class="item" href="{{$.Link}}/rules/{{.ID}}"><strong>{{.Type.Name}}</strong></a>
<div><i>{{if .Enabled}}{{$.locale.Tr "enabled"}}{{else}}{{$.locale.Tr "disabled"}}{{end}}</i></div> <div><i>{{if .Enabled}}{{$.locale.Tr "enabled"}}{{else}}{{$.locale.Tr "disabled"}}{{end}}</i></div>
{{if .KeepCount}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:</i> {{if eq .KeepCount 1}}{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}}</div>{{end}} {{if .KeepCount}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:</i> {{if eq .KeepCount 1}}{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}}</div>{{end}}
{{if .KeepPattern}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}:</i> {{EllipsisString .KeepPattern 100}}</div>{{end}} {{if .KeepPattern}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}:</i> {{StringUtils.EllipsisString .KeepPattern 100}}</div>{{end}}
{{if .RemoveDays}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:</i> {{$.locale.Tr "tool.days" .RemoveDays}}</div>{{end}} {{if .RemoveDays}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:</i> {{$.locale.Tr "tool.days" .RemoveDays}}</div>{{end}}
{{if .RemovePattern}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}:</i> {{EllipsisString .RemovePattern 100}}</div>{{end}} {{if .RemovePattern}}<div><i>{{$.locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}:</i> {{StringUtils.EllipsisString .RemovePattern 100}}</div>{{end}}
</div> </div>
</div> </div>
{{else}} {{else}}

View File

@ -3,14 +3,14 @@
{{if .CanWriteProjects}} {{if .CanWriteProjects}}
<div class="navbar"> <div class="navbar">
<div class="ui right"> <div class="ui right">
<a class="ui green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a> <a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
</div> </div>
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
{{end}} {{end}}
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="ui compact tiny menu"> <div class="small-pill-buttons ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=open"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=open">
{{svg "octicon-project-symlink" 16 "gt-mr-3"}} {{svg "octicon-project-symlink" 16 "gt-mr-3"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}} {{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}

View File

@ -3,7 +3,7 @@
<div class="navbar"> <div class="navbar">
{{if and .CanWriteProjects .PageIsEditProject}} {{if and .CanWriteProjects .PageIsEditProject}}
<div class="ui right floated secondary menu"> <div class="ui right floated secondary menu">
<a class="ui green button" href="{{$.HomeLink}}/-/projects/new">{{.locale.Tr "repo.milestones.new"}}</a> <a class="ui small green button" href="{{$.HomeLink}}/-/projects/new">{{.locale.Tr "repo.milestones.new"}}</a>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="column right aligned"> <div class="column right aligned">
{{if .CanWriteProjects}} {{if .CanWriteProjects}}
<a class="ui green button show-modal item" data-modal="#new-board-item">{{.locale.Tr "new_project_column"}}</a> <a class="ui small green button show-modal item" data-modal="#new-board-item">{{.locale.Tr "new_project_column"}}</a>
{{end}} {{end}}
<div class="ui small modal new-board-modal" id="new-board-item"> <div class="ui small modal new-board-modal" id="new-board-item">
<div class="header"> <div class="header">

View File

@ -1,4 +1,4 @@
<div class="ui compact tiny menu"> <div class="small-pill-buttons ui compact tiny menu">
<a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?workflow={{.CurWorkflow}}&state=open"> <a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?workflow={{.CurWorkflow}}&state=open">
{{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{.locale.Tr "actions.runs.open_tab" $.NumOpenActionRuns}} {{.locale.Tr "actions.runs.open_tab" $.NumOpenActionRuns}}

View File

@ -5,8 +5,13 @@
{{template "base/alert" .}} {{template "base/alert" .}}
{{template "repo/sub_menu" .}} {{template "repo/sub_menu" .}}
{{if .DefaultBranchBranch}} {{if .DefaultBranchBranch}}
<h4 class="ui top attached header gt-mt-4"> <h4 class="ui top attached header">
{{.locale.Tr "repo.default_branch"}} {{.locale.Tr "repo.default_branch"}}
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
<a role="button" class="right" href="{{.RepoLink}}/settings/branches" data-tooltip-content="{{.locale.Tr "repo.settings.branches.switch_default_branch"}}">
{{svg "octicon-arrow-switch"}}
</a>
{{end}}
</h4> </h4>
<div class="ui attached table segment"> <div class="ui attached table segment">

View File

@ -3,7 +3,7 @@
{{.locale.Tr "repo.cite_this_repo"}} {{.locale.Tr "repo.cite_this_repo"}}
</div> </div>
<div class="content"> <div class="content">
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins no-vertical-tabs"> <div class="ui stackable secondary menu">
<div class="fitted item"> <div class="fitted item">
<div class="ui action input" id="citation-panel"> <div class="ui action input" id="citation-panel">
{{template "repo/cite/cite_buttons" .}} {{template "repo/cite/cite_buttons" .}}

View File

@ -9,11 +9,11 @@
<b>{{$resolveDoer.Name}}</b> {{$.locale.Tr "repo.issues.review.resolved_by"}} <b>{{$resolveDoer.Name}}</b> {{$.locale.Tr "repo.issues.review.resolved_by"}}
</div> </div>
<div> <div>
<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button show-outdated gt-df gt-ac"> <button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated gt-df gt-ac">
{{svg "octicon-unfold" 16 "gt-mr-3"}} {{svg "octicon-unfold" 16 "gt-mr-3"}}
{{$.locale.Tr "repo.issues.review.show_resolved"}} {{$.locale.Tr "repo.issues.review.show_resolved"}}
</button> </button>
<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button hide-outdated gt-df gt-ac gt-hidden"> <button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button hide-outdated gt-df gt-ac gt-hidden">
{{svg "octicon-fold" 16 "gt-mr-3"}} {{svg "octicon-fold" 16 "gt-mr-3"}}
{{$.locale.Tr "repo.issues.review.hide_resolved"}} {{$.locale.Tr "repo.issues.review.hide_resolved"}}
</button> </button>

View File

@ -151,7 +151,7 @@
{{end}} {{end}}
<div class="ui tabs container"> <div class="ui tabs container">
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}} {{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
<div class="ui tabular stackable menu navbar"> <div class="ui tabular menu navbar gt-overflow-x-auto gt-overflow-y-hidden">
{{if .Permission.CanRead $.UnitTypeCode}} {{if .Permission.CanRead $.UnitTypeCode}}
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}"> <a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}">
{{svg "octicon-code"}} {{.locale.Tr "repo.code"}} {{svg "octicon-code"}} {{.locale.Tr "repo.code"}}
@ -236,21 +236,17 @@
{{template "custom/extra_tabs" .}} {{template "custom/extra_tabs" .}}
{{if .Permission.IsAdmin}} {{if .Permission.IsAdmin}}
<div class="right menu"> <a class="{{if .PageIsRepoSettings}}active {{end}}right item" href="{{.RepoLink}}/settings">
<a class="{{if .PageIsRepoSettings}}active {{end}}item" href="{{.RepoLink}}/settings">
{{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}} {{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}}
</a> </a>
</div>
{{end}} {{end}}
</div> </div>
{{else if .Permission.IsAdmin}} {{else if .Permission.IsAdmin}}
<div class="ui tabular stackable menu navbar"> <div class="ui tabular menu navbar gt-overflow-x-auto gt-overflow-y-hidden">
<div class="right menu"> <a class="{{if .PageIsRepoSettings}}active {{end}}right item" href="{{.RepoLink}}/settings">
<a class="{{if .PageIsRepoSettings}}active {{end}}item" href="{{.RepoLink}}/settings">
{{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}} {{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}}
</a> </a>
</div> </div>
</div>
{{end}} {{end}}
</div> </div>
<div class="ui tabs divider"></div> <div class="ui tabs divider"></div>

View File

@ -14,12 +14,12 @@
<div class="ui repo-search"> <div class="ui repo-search">
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get"> <form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get">
<div class="field"> <div class="field">
<div class="ui action input{{if .CodeIndexerUnavailable}} disabled left icon{{end}}"{{if .CodeIndexerUnavailable}} data-tooltip-content="{{.locale.Tr "repo.search.code_search_unavailable"}}"{{end}}> <div class="ui small action input{{if .CodeIndexerUnavailable}} disabled left icon{{end}}"{{if .CodeIndexerUnavailable}} data-tooltip-content="{{.locale.Tr "repo.search.code_search_unavailable"}}"{{end}}>
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}"> <input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}">
{{if .CodeIndexerUnavailable}} {{if .CodeIndexerUnavailable}}
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-alert"}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-alert"}}</i>
{{end}} {{end}}
<button class="ui icon button"{{if .CodeIndexerUnavailable}} disabled{{end}} type="submit"> <button class="ui small icon button"{{if .CodeIndexerUnavailable}} disabled{{end}} type="submit">
{{svg "octicon-search"}} {{svg "octicon-search"}}
</button> </button>
</div> </div>
@ -68,7 +68,13 @@
{{$l := Eval $n "-" 1}} {{$l := Eval $n "-" 1}}
<!-- If home page, show new pr. If not, show breadcrumb --> <!-- If home page, show new pr. If not, show breadcrumb -->
{{if and (eq $n 0) .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{if and (eq $n 0) .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{CompareLink .BaseRepo .Repository .BranchName}}" {{$cmpBranch := ""}}
{{if ne .Repository.ID .BaseRepo.ID}}
{{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}}
{{end}}
{{$cmpBranch = printf "%s%s" $cmpBranch (.BranchName|PathEscapeSegments)}}
{{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}}
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{$compareLink}}"
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}"> data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}">
{{svg "octicon-git-pull-request"}} {{svg "octicon-git-pull-request"}}
</a> </a>
@ -103,7 +109,17 @@
</a> </a>
{{end}} {{end}}
{{if ne $n 0}} {{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> <span class="ui breadcrumb repo-path gt-ml-2">
<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
{{- range $i, $v := .TreeNames -}}
<span class="divider">/</span>
{{- if eq $i $l -}}
<span class="active section" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</span>
{{- else -}}
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</a></span>
{{- end -}}
{{- end -}}
</span>
{{end}} {{end}}
</div> </div>
<div class="gt-df gt-ac"> <div class="gt-df gt-ac">

View File

@ -20,39 +20,40 @@
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<div class="labelspage">
{{if and (not $.PageIsOrgSettingsLabels) (or $.CanWriteIssues $.CanWritePulls) (eq .NumLabels 0) (not $.Repository.IsArchived)}} {{if and (not $.PageIsOrgSettingsLabels) (or $.CanWriteIssues $.CanWritePulls) (eq .NumLabels 0) (not $.Repository.IsArchived)}}
{{template "repo/issue/labels/label_load_template" .}} {{template "repo/issue/labels/label_load_template" .}}
<div class="ui divider"></div> <div class="ui divider"></div>
{{else if and ($.PageIsOrgSettingsLabels) (eq .NumLabels 0)}} {{else if and ($.PageIsOrgSettingsLabels) (eq .NumLabels 0)}}
{{template "repo/issue/labels/label_load_template" .}} {{template "repo/issue/labels/label_load_template" .}}
<div class="ui divider"></div>
{{end}} {{end}}
<ul class="issue-label-list">
{{range .Labels}} {{range .Labels}}
<li class="item"> <li class="item">
<div class="ui grid middle aligned"> <div class="label-title">
<div class="eight wide column">
{{RenderLabel $.Context .}} {{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}
</div> </div>
<div class="five wide column"> <div class="label-issues">
{{if $.PageIsOrgSettingsLabels}} {{if $.PageIsOrgSettingsLabels}}
<a class="ui left open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> <a class="open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
{{else}} {{else}}
<a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> <a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
{{end}} {{end}}
</div> </div>
<div class="three wide column"> <div class="label-operation">
{{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}} {{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
<a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
{{else if $.PageIsOrgSettingsLabels}} {{else if $.PageIsOrgSettingsLabels}}
<a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
{{end}} {{end}}
</div> </div>
</div>
</li> </li>
{{end}} {{end}}
{{if and (not .PageIsOrgSettingsLabels) (.OrgLabels)}} {{if and (not .PageIsOrgSettingsLabels) (.OrgLabels)}}
<li class="item"> <li class="item">
<div class="ui grid middle aligned"> <div class="ui grid middle aligned">
@ -64,25 +65,19 @@
</div> </div>
</div> </div>
</li> </li>
{{if (not $.PageIsOrgSettingsLabels)}}
<div class="orglabel">
{{range .OrgLabels}} {{range .OrgLabels}}
<li class="item"> <li class="item org-label">
<div class="ui grid middle aligned"> <div class="label-title">
<div class="nine wide column">
{{RenderLabel $.Context .}} {{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}
</div> </div>
<div class="four wide column"> <div class="label-issues">
<a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a> <a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a>
</div>
<div class="three wide column">
</div>
</div> </div>
<div class="label-operation"></div>
</li> </li>
{{end}} {{end}}
</div>
{{end}} {{end}}
{{end}} </ul>
</div>
</div> </div>

View File

@ -2,26 +2,18 @@
<div role="main" aria-label="{{.Title}}" class="page-content repository issue-list"> <div role="main" aria-label="{{.Title}}" class="page-content repository issue-list">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
<div class="ui three column grid issue-list-headers"> <div class="list-header">
<div class="column">
{{template "repo/issue/navbar" .}} {{template "repo/issue/navbar" .}}
</div>
<div class="column center aligned">
{{template "repo/issue/search" .}} {{template "repo/issue/search" .}}
</div>
{{if not .Repository.IsArchived}} {{if not .Repository.IsArchived}}
<div class="column right aligned">
{{if .PageIsIssueList}} {{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{.locale.Tr "repo.issues.new"}}</a> <a class="ui small green button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{.locale.Tr "repo.issues.new"}}</a>
{{else}} {{else}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{.locale.Tr "repo.pulls.new"}}</a> <a class="ui small green button new-pr-button{{if not .PullRequestCtx.Allowed}} disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{.locale.Tr "repo.pulls.new"}}</a>
{{end}} {{end}}
</div>
{{else}} {{else}}
{{if not .PageIsIssueList}} {{if not .PageIsIssueList}}
<div class="column right aligned"> <a class="ui small green small button{{if not .PullRequestCtx.Allowed}} disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{$.locale.Tr "action.compare_commits_general"}}</a>
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{$.locale.Tr "action.compare_commits_general"}}</a>
</div>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>
@ -34,7 +26,7 @@
{{template "repo/issue/openclose" .}} {{template "repo/issue/openclose" .}}
</div> </div>
<div class="ten wide right aligned column"> <div class="ten wide right aligned column">
<div class="ui secondary filter stackable menu labels"> <div class="ui secondary filter menu labels gt-overflow-x-auto gt-overflow-y-hidden">
<!-- Label --> <!-- Label -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item label-filter" style="margin-left: auto"> <div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item label-filter" style="margin-left: auto">
<span class="text"> <span class="text">
@ -176,7 +168,7 @@
{{end}} {{end}}
<!-- Sort --> <!-- Sort -->
<div class="ui dropdown downward type jump item"> <div class="list-header-sort ui small dropdown downward type jump item">
<span class="text"> <span class="text">
{{.locale.Tr "repo.issues.filter_sort"}} {{.locale.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}

View File

@ -6,16 +6,15 @@
{{template "repo/issue/navbar" .}} {{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}} {{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right"> <div class="ui right">
<a class="ui green button" href="{{$.Link}}/new">{{.locale.Tr "repo.milestones.new"}}</a> <a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.milestones.new"}}</a>
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="ui three column stackable grid"> <div class="list-header">
<div class="column"> <div class="small-pill-buttons ui compact tiny menu">
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=open&q={{$.Keyword}}"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=open&q={{$.Keyword}}">
{{svg "octicon-milestone" 16 "gt-mr-3"}} {{svg "octicon-milestone" 16 "gt-mr-3"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}} {{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
@ -25,22 +24,20 @@
{{.locale.PrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}} {{.locale.PrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a> </a>
</div> </div>
</div>
<!-- Search --> <!-- Search -->
<div class="column center aligned"> <form class="list-header-search ui form ignore-dirty">
<form class="ui form ignore-dirty"> <div class="ui small search fluid action input">
<div class="ui search fluid action input">
<input type="hidden" name="state" value="{{$.State}}"> <input type="hidden" name="state" value="{{$.State}}">
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..."> <input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}...">
<button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button> <button class="ui small icon button" type="submit" aria-label="{{.locale.Tr "explore.search"}}">
{{svg "octicon-search"}}
</button>
</div> </div>
</form> </form>
</div>
<div class="column right aligned gt-df gt-ac gt-je">
<!-- Sort --> <!-- Sort -->
<div class="ui dropdown type jump item"> <div class="list-header-sort ui small dropdown type jump item">
<span class="text"> <span class="text">
{{.locale.Tr "repo.issues.filter_sort"}} {{.locale.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
@ -55,7 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- milestone list --> <!-- milestone list -->
<div class="milestone list"> <div class="milestone list">

View File

@ -1,4 +1,4 @@
<h2 class="ui compact small menu header"> <h2 class="ui compact small menu header small-pill-buttons">
<a class="{{if .PageIsLabels}}active {{end}}item" href="{{.RepoLink}}/labels">{{.locale.Tr "repo.labels"}}</a> <a class="{{if .PageIsLabels}}active {{end}}item" href="{{.RepoLink}}/labels">{{.locale.Tr "repo.labels"}}</a>
<a class="{{if .PageIsMilestones}}active {{end}}item" href="{{.RepoLink}}/milestones">{{.locale.Tr "repo.milestones"}}</a> <a class="{{if .PageIsMilestones}}active {{end}}item" href="{{.RepoLink}}/milestones">{{.locale.Tr "repo.milestones"}}</a>
</h2> </h2>

View File

@ -13,7 +13,7 @@
<div class="field"> <div class="field">
<input name="title" id="issue_title" placeholder="{{.locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" tabindex="3" autofocus required maxlength="255" autocomplete="off"> <input name="title" id="issue_title" placeholder="{{.locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" tabindex="3" autofocus required maxlength="255" autocomplete="off">
{{if .PageIsComparePull}} {{if .PageIsComparePull}}
<div class="title_wip_desc" data-wip-prefixes="{{Json .PullRequestWorkInProgressPrefixes}}">{{.locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> <div class="title_wip_desc" data-wip-prefixes="{{JsonUtils.EncodeToString .PullRequestWorkInProgressPrefixes}}">{{.locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div>
{{end}} {{end}}
</div> </div>
{{if .Fields}} {{if .Fields}}

View File

@ -1,4 +1,4 @@
<div class="ui compact tiny menu"> <div class="small-pill-buttons ui compact tiny menu">
<a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&project={{.ProjectID}}&assignee={{.AssigneeID}}&poster={{.PosterID}}"> <a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&project={{.ProjectID}}&assignee={{.AssigneeID}}&poster={{.PosterID}}">
{{if .PageIsPullList}} {{if .PageIsPullList}}
{{svg "octicon-git-pull-request" 16 "gt-mr-3"}} {{svg "octicon-git-pull-request" 16 "gt-mr-3"}}

View File

@ -1,5 +1,5 @@
<form class="ui form ignore-dirty"> <form class="list-header-search ui form ignore-dirty">
<div class="ui search fluid action input"> <div class="ui small search fluid action input">
<input type="hidden" name="type" value="{{$.ViewType}}"> <input type="hidden" name="type" value="{{$.ViewType}}">
<input type="hidden" name="state" value="{{$.State}}"> <input type="hidden" name="state" value="{{$.State}}">
<input type="hidden" name="labels" value="{{.SelectLabels}}"> <input type="hidden" name="labels" value="{{.SelectLabels}}">
@ -8,6 +8,8 @@
<input type="hidden" name="assignee" value="{{$.AssigneeID}}"> <input type="hidden" name="assignee" value="{{$.AssigneeID}}">
<input type="hidden" name="poster" value="{{$.PosterID}}"> <input type="hidden" name="poster" value="{{$.PosterID}}">
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..."> <input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}...">
<button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button> <button class="ui small icon button" type="submit" aria-label="{{.locale.Tr "explore.search"}}">
{{svg "octicon-search"}}
</button>
</div> </div>
</form> </form>

View File

@ -304,10 +304,12 @@
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}} {{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
<span class="text grey muted-links"> <span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}} {{template "shared/user/authorlink" .Poster}}
{{$parsedDeadline := .Content | ParseDeadline}} {{$parsedDeadline := StringUtils.Split .Content "|"}}
{{if eq (len $parsedDeadline) 2}}
{{$from := DateTime "long" (index $parsedDeadline 1)}} {{$from := DateTime "long" (index $parsedDeadline 1)}}
{{$to := DateTime "long" (index $parsedDeadline 0)}} {{$to := DateTime "long" (index $parsedDeadline 0)}}
{{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}} {{$.locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}}
{{end}}
</span> </span>
</div> </div>
{{else if eq .Type 18}} {{else if eq .Type 18}}

View File

@ -6,13 +6,13 @@
{{template "repo/issue/navbar" .}} {{template "repo/issue/navbar" .}}
{{if and .CanWriteProjects (not .Repository.IsArchived)}} {{if and .CanWriteProjects (not .Repository.IsArchived)}}
<div class="ui right"> <div class="ui right">
<a class="ui green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a> <a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="ui compact tiny menu"> <div class="small-pill-buttons ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=open"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=open">
{{svg "octicon-project" 16 "gt-mr-3"}} {{svg "octicon-project" 16 "gt-mr-3"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}} {{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}

View File

@ -6,7 +6,7 @@
{{template "repo/issue/navbar" .}} {{template "repo/issue/navbar" .}}
{{if and .CanWriteProjects .PageIsEditProject}} {{if and .CanWriteProjects .PageIsEditProject}}
<div class="ui right floated secondary menu"> <div class="ui right floated secondary menu">
<a class="ui green button" href="{{$.RepoLink}}/projects/new">{{.locale.Tr "repo.milestones.new"}}</a> <a class="ui small green button" href="{{$.RepoLink}}/projects/new">{{.locale.Tr "repo.milestones.new"}}</a>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -8,8 +8,8 @@
</div> </div>
<div class="column right aligned"> <div class="column right aligned">
{{if and .CanWriteProjects (not .Repository.IsArchived)}} {{if and .CanWriteProjects (not .Repository.IsArchived)}}
<a class="ui green button item" href="{{$.RepoLink}}/issues/new/choose?project={{$.Project.ID}}">{{.locale.Tr "repo.issues.new"}}</a> <a class="ui small green button item" href="{{$.RepoLink}}/issues/new/choose?project={{$.Project.ID}}">{{.locale.Tr "repo.issues.new"}}</a>
<a class="ui green button show-modal item" data-modal="#new-board-item">{{.locale.Tr "new_project_column"}}</a> <a class="ui small green button show-modal item" data-modal="#new-board-item">{{.locale.Tr "new_project_column"}}</a>
{{end}} {{end}}
<div class="ui small modal new-board-modal" id="new-board-item"> <div class="ui small modal new-board-modal" id="new-board-item">
<div class="header"> <div class="header">

View File

@ -20,7 +20,7 @@
<b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong> <b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong>
{{else}} {{else}}
<input id="tag-name" name="tag_name" value="{{.tag_name}}" aria-label="{{.locale.Tr "repo.release.tag_name"}}" placeholder="{{.locale.Tr "repo.release.tag_name"}}" autofocus required maxlength="255"> <input id="tag-name" name="tag_name" value="{{.tag_name}}" aria-label="{{.locale.Tr "repo.release.tag_name"}}" placeholder="{{.locale.Tr "repo.release.tag_name"}}" autofocus required maxlength="255">
<input id="tag-name-editor" type="hidden" data-existing-tags={{Json .Tags}} data-tag-helper={{.locale.Tr "repo.release.tag_helper"}} data-tag-helper-new={{.locale.Tr "repo.release.tag_helper_new"}} data-tag-helper-existing={{.locale.Tr "repo.release.tag_helper_existing"}}> <input id="tag-name-editor" type="hidden" data-existing-tags="{{JsonUtils.EncodeToString .Tags}}" data-tag-helper="{{.locale.Tr "repo.release.tag_helper"}}" data-tag-helper-new="{{.locale.Tr "repo.release.tag_helper_new"}}" data-tag-helper-existing="{{.locale.Tr "repo.release.tag_helper_existing"}}">
<div id="tag-target-selector" class="gt-dib"> <div id="tag-target-selector" class="gt-dib">
<span class="at">@</span> <span class="at">@</span>
<div class="ui selection dropdown"> <div class="ui selection dropdown">

View File

@ -12,18 +12,13 @@
<p> <p>
{{.locale.Tr "repo.settings.default_branch_desc"}} {{.locale.Tr "repo.settings.default_branch_desc"}}
</p> </p>
<form class="ui form" action="{{.Link}}" method="post"> <form class="gt-df" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="action" value="default_branch"> <input type="hidden" name="action" value="default_branch">
{{if not .Repository.IsEmpty}} {{if not .Repository.IsEmpty}}
<div class="required inline field"> <div class="ui dropdown selection gt-f1 gt-mr-3 gt-max-width-24rem">
<div class="ui dropdown selection" tabindex="0"> {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<select name="branch"> <input type="hidden" name="branch" value="{{.Repository.DefaultBranch}}">
<option value="{{.Repository.DefaultBranch}}">{{.Repository.DefaultBranch}}</option>
{{range .Branches}}
<option value="{{.}}">{{.}}</option>
{{end}}
</select>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="default text">{{.Repository.DefaultBranch}}</div> <div class="default text">{{.Repository.DefaultBranch}}</div>
<div class="menu"> <div class="menu">
{{range .Branches}} {{range .Branches}}
@ -32,7 +27,6 @@
</div> </div>
</div> </div>
<button class="ui green button">{{$.locale.Tr "repo.settings.branches.update_default_branch"}}</button> <button class="ui green button">{{$.locale.Tr "repo.settings.branches.update_default_branch"}}</button>
</div>
{{end}} {{end}}
</form> </form>
</div> </div>

View File

@ -5,39 +5,49 @@
{{.locale.Tr "repo.settings.branch_protection" (.Rule.RuleName|Escape) | Str2html}} {{.locale.Tr "repo.settings.branch_protection" (.Rule.RuleName|Escape) | Str2html}}
</h4> </h4>
<div class="ui attached segment branch-protection"> <div class="ui attached segment branch-protection">
<h5 class="ui dividing header">{{.locale.Tr "repo.settings.protect_patterns"}}</h5>
<div class="field"> <div class="field">
<label for="protected_file_patterns">{{.locale.Tr "repo.settings.protect_branch_name_pattern"}}</label> <label>{{.locale.Tr "repo.settings.protect_branch_name_pattern"}}</label>
<input name="rule_name" type="text" value="{{.Rule.RuleName}}"> <input name="rule_name" type="text" value="{{.Rule.RuleName}}">
<input name="rule_id" type="hidden" value="{{.Rule.ID}}"> <input name="rule_id" type="hidden" value="{{.Rule.ID}}">
</div> </div>
<div class="field">
<div class="ui divider"></div> <label>{{.locale.Tr "repo.settings.protect_protected_file_patterns"}}</label>
<input name="protected_file_patterns" type="text" value="{{.Rule.ProtectedFilePatterns}}">
<p class="help gt-ml-0">{{.locale.Tr "repo.settings.protect_protected_file_patterns_desc" | Safe}}</p>
</div>
<div class="field">
<label>{{.locale.Tr "repo.settings.protect_unprotected_file_patterns"}}</label>
<input name="unprotected_file_patterns" type="text" value="{{.Rule.UnprotectedFilePatterns}}">
<p class="help gt-ml-0">{{.locale.Tr "repo.settings.protect_unprotected_file_patterns_desc" | Safe}}</p>
</div>
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<div id="protection_box" class="fields"> <h5 class="ui dividing header">{{.locale.Tr "repo.settings.event_push"}}</h5>
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input name="enable_push" type="radio" value="none" class="disable-whitelist" data-target="#whitelist_box" {{if not .Rule.CanPush}}checked{{end}}> <input name="enable_push" type="radio" value="none" class="toggle-target-disabled" data-target="#whitelist_box" {{if not .Rule.CanPush}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_disable_push"}}</label> <label>{{.locale.Tr "repo.settings.protect_disable_push"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_disable_push_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.protect_disable_push_desc"}}</p>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input name="enable_push" type="radio" value="all" class="disable-whitelist" data-target="#whitelist_box" {{if and (.Rule.CanPush) (not .Rule.EnableWhitelist)}}checked{{end}}> <input name="enable_push" type="radio" value="all" class="toggle-target-disabled" data-target="#whitelist_box" {{if and (.Rule.CanPush) (not .Rule.EnableWhitelist)}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_enable_push"}}</label> <label>{{.locale.Tr "repo.settings.protect_enable_push"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_enable_push_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.protect_enable_push_desc"}}</p>
</div> </div>
</div> </div>
<div class="grouped fields">
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input name="enable_push" type="radio" value="whitelist" class="enable-whitelist" data-target="#whitelist_box" {{if and (.Rule.CanPush) (.Rule.EnableWhitelist)}}checked{{end}}> <input name="enable_push" type="radio" value="whitelist" class="toggle-target-enabled" data-target="#whitelist_box" {{if and (.Rule.CanPush) (.Rule.EnableWhitelist)}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_whitelist_committers"}}</label> <label>{{.locale.Tr "repo.settings.protect_whitelist_committers"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_whitelist_committers_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.protect_whitelist_committers_desc"}}</p>
</div> </div>
</div> </div>
<div id="whitelist_box" class="fields {{if not .Rule.EnableWhitelist}}disabled{{end}}"> <div id="whitelist_box" class="grouped fields {{if not .Rule.EnableWhitelist}}disabled{{end}}">
<div class="whitelist field"> <div class="checkbox-sub-item field">
<label>{{.locale.Tr "repo.settings.protect_whitelist_users"}}</label> <label>{{.locale.Tr "repo.settings.protect_whitelist_users"}}</label>
<div class="ui multiple search selection dropdown"> <div class="ui multiple search selection dropdown">
<input type="hidden" name="whitelist_users" value="{{.whitelist_users}}"> <input type="hidden" name="whitelist_users" value="{{.whitelist_users}}">
@ -52,8 +62,7 @@
</div> </div>
</div> </div>
{{if .Owner.IsOrganization}} {{if .Owner.IsOrganization}}
<br> <div class="checkbox-sub-item field">
<div class="whitelist field">
<label>{{.locale.Tr "repo.settings.protect_whitelist_teams"}}</label> <label>{{.locale.Tr "repo.settings.protect_whitelist_teams"}}</label>
<div class="ui multiple search selection dropdown"> <div class="ui multiple search selection dropdown">
<input type="hidden" name="whitelist_teams" value="{{.whitelist_teams}}"> <input type="hidden" name="whitelist_teams" value="{{.whitelist_teams}}">
@ -69,105 +78,37 @@
</div> </div>
</div> </div>
{{end}} {{end}}
<br> <div class="checkbox-sub-item field">
<div class="whitelist field">
<div class="ui checkbox"> <div class="ui checkbox">
<input type="checkbox" name="whitelist_deploy_keys" {{if .Rule.WhitelistDeployKeys}}checked{{end}}> <input type="checkbox" name="whitelist_deploy_keys" {{if .Rule.WhitelistDeployKeys}}checked{{end}}>
<label for="whitelist_deploy_keys">{{.locale.Tr "repo.settings.protect_whitelist_deploy_keys"}}</label> <label>{{.locale.Tr "repo.settings.protect_whitelist_deploy_keys"}}</label>
</div> </div>
</div> </div>
</div> </div>
<div class="ui divider"></div>
<div class="field">
<div class="ui checkbox">
<input class="enable-whitelist" name="enable_merge_whitelist" type="checkbox" data-target="#merge_whitelist_box" {{if .Rule.EnableMergeWhitelist}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_committers"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_merge_whitelist_committers_desc"}}</p>
</div>
</div>
<div id="merge_whitelist_box" class="fields {{if not .Rule.EnableMergeWhitelist}}disabled{{end}}">
<div class="whitelist field">
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_users"}}</label>
<div class="ui multiple search selection dropdown">
<input type="hidden" name="merge_whitelist_users" value="{{.merge_whitelist_users}}">
<div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div>
<div class="menu">
{{range .Users}}
<div class="item" data-value="{{.ID}}">
{{avatar $.Context . 28 "mini"}}{{template "repo/search_name" .}}
</div>
{{end}}
</div>
</div>
</div>
{{if .Owner.IsOrganization}}
<br>
<div class="whitelist field">
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_teams"}}</label>
<div class="ui multiple search selection dropdown">
<input type="hidden" name="merge_whitelist_teams" value="{{.merge_whitelist_teams}}">
<div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_teams"}}</div>
<div class="menu">
{{range .Teams}}
<div class="item" data-value="{{.ID}}">
{{svg "octicon-people"}}
{{.Name}}
</div>
{{end}}
</div>
</div>
</div>
{{end}}
</div>
<div class="field">
<div class="ui checkbox">
<input class="enable-statuscheck" name="enable_status_check" type="checkbox" data-target="#statuscheck_contexts_box" {{if eq (len .branch_status_check_contexts) 0}}disabled{{end}} {{if .Rule.EnableStatusCheck}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_check_status_contexts"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_check_status_contexts_desc"}}</p>
</div>
</div>
<div id="statuscheck_contexts_box" class="fields {{if not .Rule.EnableStatusCheck}}disabled{{end}}">
<div class="field">
<table class="ui celled table six column">
<thead>
<tr><th>
{{.locale.Tr "repo.settings.protect_check_status_contexts_list"}}
</th>
</tr>
</thead>
<tbody>
{{range $.branch_status_check_contexts}}
<tr><td>
<span class="ui checkbox">
<input class="enable-whitelist" name="status_check_contexts" value="{{.}}" type="checkbox" {{if $.is_context_required}}{{if call $.is_context_required .}}checked{{end}}{{end}}>
</span>
{{.}}
{{if $.is_context_required}}{{if call $.is_context_required .}}<div class="ui label right">Required</div>{{end}}{{end}}
</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
<div class="field">
<label for="required-approvals">{{.locale.Tr "repo.settings.protect_required_approvals"}}</label>
<input name="required_approvals" id="required-approvals" type="number" value="{{.Rule.RequiredApprovals}}">
<p class="help">{{.locale.Tr "repo.settings.protect_required_approvals_desc"}}</p>
</div> </div>
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input class="enable-whitelist" name="enable_approvals_whitelist" type="checkbox" data-target="#approvals_whitelist_box" {{if .Rule.EnableApprovalsWhitelist}}checked{{end}}> <input name="require_signed_commits" type="checkbox" {{if .Rule.RequireSignedCommits}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.require_signed_commits"}}</label>
<p class="help">{{.locale.Tr "repo.settings.require_signed_commits_desc"}}</p>
</div>
</div>
<h5 class="ui dividing header">{{.locale.Tr "repo.settings.event_pull_request_approvals"}}</h5>
<div class="field">
<label>{{.locale.Tr "repo.settings.protect_required_approvals"}}</label>
<input name="required_approvals" type="number" value="{{.Rule.RequiredApprovals}}">
<p class="help gt-ml-0">{{.locale.Tr "repo.settings.protect_required_approvals_desc"}}</p>
</div>
<div class="grouped fields">
<div class="field">
<div class="ui checkbox">
<input name="enable_approvals_whitelist" type="checkbox" class="toggle-target-enabled" data-target="#approvals_whitelist_box" {{if .Rule.EnableApprovalsWhitelist}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_enabled"}}</label> <label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_enabled"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_approvals_whitelist_enabled_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.protect_approvals_whitelist_enabled_desc"}}</p>
</div> </div>
</div> </div>
<div id="approvals_whitelist_box" class="fields {{if not .Rule.EnableApprovalsWhitelist}}disabled{{end}}"> <div id="approvals_whitelist_box" class="grouped fields {{if not .Rule.EnableApprovalsWhitelist}}disabled{{end}}">
<div class="whitelist field"> <div class="checkbox-sub-item field">
<label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_users"}}</label> <label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_users"}}</label>
<div class="ui multiple search selection dropdown"> <div class="ui multiple search selection dropdown">
<input type="hidden" name="approvals_whitelist_users" value="{{.approvals_whitelist_users}}"> <input type="hidden" name="approvals_whitelist_users" value="{{.approvals_whitelist_users}}">
@ -182,8 +123,7 @@
</div> </div>
</div> </div>
{{if .Owner.IsOrganization}} {{if .Owner.IsOrganization}}
<br> <div class="checkbox-sub-item field">
<div class="whitelist field">
<label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_teams"}}</label> <label>{{.locale.Tr "repo.settings.protect_approvals_whitelist_teams"}}</label>
<div class="ui multiple search selection dropdown"> <div class="ui multiple search selection dropdown">
<input type="hidden" name="approvals_whitelist_teams" value="{{.approvals_whitelist_teams}}"> <input type="hidden" name="approvals_whitelist_teams" value="{{.approvals_whitelist_teams}}">
@ -200,53 +140,117 @@
</div> </div>
{{end}} {{end}}
</div> </div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="dismiss_stale_approvals" type="checkbox" {{if .Rule.DismissStaleApprovals}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.dismiss_stale_approvals"}}</label>
<p class="help">{{.locale.Tr "repo.settings.dismiss_stale_approvals_desc"}}</p>
</div>
</div>
<div class="grouped fields">
<div class="field">
<div class="ui checkbox">
<input name="enable_status_check" type="checkbox" class="toggle-target-enabled" data-target="#statuscheck_contexts_box" {{if .Rule.EnableStatusCheck}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_check_status_contexts"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_check_status_contexts_desc"}}</p>
</div>
</div>
<div id="statuscheck_contexts_box" class="checkbox-sub-item field {{if not .Rule.EnableStatusCheck}}disabled{{end}}">
<table class="ui celled table">
<thead>
<tr>
<th>{{.locale.Tr "repo.settings.protect_check_status_contexts_list"}}</th>
</tr>
</thead>
<tbody>
{{range $.branch_status_check_contexts}}
<tr>
<td>
<span class="ui checkbox">
<label>{{.}}</label>
<input name="status_check_contexts" value="{{.}}" type="checkbox" {{if SliceUtils.Contains $.Rule.StatusCheckContexts .}}checked{{end}}>
</span>
</td>
</tr>
{{else}}
<tr><td>N/A</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
<h5 class="ui dividing header">{{.locale.Tr "repo.settings.event_pull_request_merge"}}</h5>
<div class="grouped fields">
<div class="field">
<div class="ui radio checkbox">
<input name="enable_merge_whitelist" type="radio" value="false" class="toggle-target-disabled" data-target="#merge_whitelist_box" {{if not .Rule.EnableMergeWhitelist}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_enable_merge"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_enable_merge_desc"}}</p>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="enable_merge_whitelist" type="radio" value="true" class="toggle-target-enabled" data-target="#merge_whitelist_box" {{if .Rule.EnableMergeWhitelist}}checked{{end}}>
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_committers"}}</label>
<p class="help">{{.locale.Tr "repo.settings.protect_merge_whitelist_committers_desc"}}</p>
</div>
</div>
<div id="merge_whitelist_box" class="grouped fields {{if not .Rule.EnableMergeWhitelist}}disabled{{end}}">
<div class="checkbox-sub-item field">
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_users"}}</label>
<div class="ui multiple search selection dropdown">
<input type="hidden" name="merge_whitelist_users" value="{{.merge_whitelist_users}}">
<div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div>
<div class="menu">
{{range .Users}}
<div class="item" data-value="{{.ID}}">
{{avatar $.Context . 28 "mini"}}{{template "repo/search_name" .}}
</div>
{{end}}
</div>
</div>
</div>
{{if .Owner.IsOrganization}}
<div class="checkbox-sub-item field">
<label>{{.locale.Tr "repo.settings.protect_merge_whitelist_teams"}}</label>
<div class="ui multiple search selection dropdown">
<input type="hidden" name="merge_whitelist_teams" value="{{.merge_whitelist_teams}}">
<div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_teams"}}</div>
<div class="menu">
{{range .Teams}}
<div class="item" data-value="{{.ID}}">
{{svg "octicon-people"}}
{{.Name}}
</div>
{{end}}
</div>
</div>
</div>
{{end}}
</div>
</div>
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input name="block_on_rejected_reviews" type="checkbox" {{if .Rule.BlockOnRejectedReviews}}checked{{end}}> <input name="block_on_rejected_reviews" type="checkbox" {{if .Rule.BlockOnRejectedReviews}}checked{{end}}>
<label for="block_on_rejected_reviews">{{.locale.Tr "repo.settings.block_rejected_reviews"}}</label> <label>{{.locale.Tr "repo.settings.block_rejected_reviews"}}</label>
<p class="help">{{.locale.Tr "repo.settings.block_rejected_reviews_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.block_rejected_reviews_desc"}}</p>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input name="block_on_official_review_requests" type="checkbox" {{if .Rule.BlockOnOfficialReviewRequests}}checked{{end}}> <input name="block_on_official_review_requests" type="checkbox" {{if .Rule.BlockOnOfficialReviewRequests}}checked{{end}}>
<label for="block_on_official_review_requests">{{.locale.Tr "repo.settings.block_on_official_review_requests"}}</label> <label>{{.locale.Tr "repo.settings.block_on_official_review_requests"}}</label>
<p class="help">{{.locale.Tr "repo.settings.block_on_official_review_requests_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.block_on_official_review_requests_desc"}}</p>
</div> </div>
</div> </div>
<div class="field">
<div class="ui checkbox">
<input name="dismiss_stale_approvals" type="checkbox" {{if .Rule.DismissStaleApprovals}}checked{{end}}>
<label for="dismiss_stale_approvals">{{.locale.Tr "repo.settings.dismiss_stale_approvals"}}</label>
<p class="help">{{.locale.Tr "repo.settings.dismiss_stale_approvals_desc"}}</p>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="require_signed_commits" type="checkbox" {{if .Rule.RequireSignedCommits}}checked{{end}}>
<label for="require_signed_commits">{{.locale.Tr "repo.settings.require_signed_commits"}}</label>
<p class="help">{{.locale.Tr "repo.settings.require_signed_commits_desc"}}</p>
</div>
</div>
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input name="block_on_outdated_branch" type="checkbox" {{if .Rule.BlockOnOutdatedBranch}}checked{{end}}> <input name="block_on_outdated_branch" type="checkbox" {{if .Rule.BlockOnOutdatedBranch}}checked{{end}}>
<label for="block_on_outdated_branch">{{.locale.Tr "repo.settings.block_outdated_branch"}}</label> <label>{{.locale.Tr "repo.settings.block_outdated_branch"}}</label>
<p class="help">{{.locale.Tr "repo.settings.block_outdated_branch_desc"}}</p> <p class="help">{{.locale.Tr "repo.settings.block_outdated_branch_desc"}}</p>
</div> </div>
</div> </div>
<div class="field">
<label for="protected_file_patterns">{{.locale.Tr "repo.settings.protect_protected_file_patterns"}}</label>
<input name="protected_file_patterns" id="protected_file_patterns" type="text" value="{{.Rule.ProtectedFilePatterns}}">
<p class="help">{{.locale.Tr "repo.settings.protect_protected_file_patterns_desc" | Safe}}</p>
</div>
<div class="field">
<label for="unprotected_file_patterns">{{.locale.Tr "repo.settings.protect_unprotected_file_patterns"}}</label>
<input name="unprotected_file_patterns" id="unprotected_file_patterns" type="text" value="{{.Rule.UnprotectedFilePatterns}}">
<p class="help">{{.locale.Tr "repo.settings.protect_unprotected_file_patterns_desc" | Safe}}</p>
</div>
</div>
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="field"> <div class="field">

View File

@ -2,7 +2,7 @@
{{$canReadCode := $.Permission.CanRead $.UnitTypeCode}} {{$canReadCode := $.Permission.CanRead $.UnitTypeCode}}
{{if $canReadReleases}} {{if $canReadReleases}}
<h2 class="ui compact small menu header"> <h2 class="ui compact small menu header small-pill-buttons">
<a class="{{if .PageIsReleaseList}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a> <a class="{{if .PageIsReleaseList}}active {{end}}item" href="{{.RepoLink}}/releases">{{.locale.Tr "repo.release.releases"}}</a>
{{if $canReadCode}} {{if $canReadCode}}
<a class="{{if .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a> <a class="{{if .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a>

View File

@ -1,4 +1,4 @@
<div class="ui container user-cards"> <div class="user-cards">
{{if .CardsTitle}} {{if .CardsTitle}}
<h2 class="ui dividing header"> <h2 class="ui dividing header">
{{.CardsTitle}} {{.CardsTitle}}

View File

@ -61,13 +61,15 @@
{{else}} {{else}}
{{if $entry.IsDir}} {{if $entry.IsDir}}
{{$subJumpablePathName := $entry.GetSubJumpablePathName}} {{$subJumpablePathName := $entry.GetSubJumpablePathName}}
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}}
{{svg "octicon-file-directory-fill"}} {{svg "octicon-file-directory-fill"}}
<a class="muted" href="{{$.TreeLink}}/{{PathEscapeSegments $subJumpablePathName}}" title="{{$subJumpablePathName}}"> <a class="muted" href="{{$.TreeLink}}/{{PathEscapeSegments $subJumpablePathName}}" title="{{$subJumpablePathName}}">
{{if eq (len $subJumpablePath) 2}} {{$subJumpablePathFields := StringUtils.Split $subJumpablePathName "/"}}
<span class="color-text-light-2">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}} {{$subJumpablePathFieldLast := (Eval (len $subJumpablePathFields) "-" 1)}}
{{if eq $subJumpablePathFieldLast 0}}
{{$subJumpablePathName}}
{{else}} {{else}}
{{index $subJumpablePath 0}} {{$subJumpablePathPrefixes := slice $subJumpablePathFields 0 $subJumpablePathFieldLast}}
<span class="color-text-light-2">{{StringUtils.Join $subJumpablePathPrefixes "/"}}</span>/{{index $subJumpablePathFields $subJumpablePathFieldLast}}
{{end}} {{end}}
</a> </a>
{{else}} {{else}}

View File

@ -1,6 +1,8 @@
{{template "base/head" .}} {{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository watchers"> <div role="main" aria-label="{{.Title}}" class="page-content repository watchers">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container">
{{template "repo/user_cards" .}} {{template "repo/user_cards" .}}
</div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -37,7 +37,7 @@
</div> </div>
<div class="field" data-tooltip-content="Labels are comma-separated. Whitespace at the beginning, end, and around the commas are ignored."> <div class="field" data-tooltip-content="Labels are comma-separated. Whitespace at the beginning, end, and around the commas are ignored.">
<label for="custom_labels">{{.locale.Tr "actions.runners.custom_labels"}}</label> <label for="custom_labels">{{.locale.Tr "actions.runners.custom_labels"}}</label>
<input id="custom_labels" name="custom_labels" value="{{Join .Runner.CustomLabels `,`}}"> <input id="custom_labels" name="custom_labels" value="{{StringUtils.Join .Runner.CustomLabels `,`}}">
<p class="help">{{.locale.Tr "actions.runners.custom_labels_helper"}}</p> <p class="help">{{.locale.Tr "actions.runners.custom_labels_helper"}}</p>
</div> </div>

View File

@ -3803,6 +3803,12 @@
"description": "page size of results (ignored if used with 'path')", "description": "page size of results (ignored if used with 'path')",
"name": "limit", "name": "limit",
"in": "query" "in": "query"
},
{
"type": "string",
"description": "commits that match the given specifier will not be listed.",
"name": "not",
"in": "query"
} }
], ],
"responses": { "responses": {

View File

@ -60,9 +60,8 @@
</div> </div>
</div> </div>
<div class="twelve wide column content"> <div class="twelve wide column content">
<div class="ui three column stackable grid"> <div class="list-header">
<div class="column"> <div class="small-pill-buttons ui compact tiny menu">
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
{{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{.locale.PrettyNumber .IssueStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}} {{.locale.PrettyNumber .IssueStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
@ -72,22 +71,18 @@
{{.locale.PrettyNumber .IssueStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}} {{.locale.PrettyNumber .IssueStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a> </a>
</div> </div>
</div> <form class="list-header-search ui form ignore-dirty">
<div class="column center aligned"> <div class="ui small search fluid action input">
<form class="ui form ignore-dirty">
<div class="ui search fluid action input">
<input type="hidden" name="type" value="{{$.ViewType}}"> <input type="hidden" name="type" value="{{$.ViewType}}">
<input type="hidden" name="repos" value="[{{range $.RepoIDs}}{{.}}%2C{{end}}]"> <input type="hidden" name="repos" value="[{{range $.RepoIDs}}{{.}}%2C{{end}}]">
<input type="hidden" name="sort" value="{{$.SortType}}"> <input type="hidden" name="sort" value="{{$.SortType}}">
<input type="hidden" name="state" value="{{$.State}}"> <input type="hidden" name="state" value="{{$.State}}">
<input name="q" value="{{$.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..."> <input name="q" value="{{$.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}...">
<button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button> <button class="ui small icon button" type="submit" aria-label="{{.locale.Tr "explore.search"}}">{{svg "octicon-search"}}</button>
</div> </div>
</form> </form>
</div>
<div class="column right aligned gt-df gt-ac gt-je">
<!-- Sort --> <!-- Sort -->
<div class="ui dropdown type jump item"> <div class="list-header-sort ui small dropdown type jump item">
<span class="text gt-whitespace-nowrap"> <span class="text gt-whitespace-nowrap">
{{.locale.Tr "repo.issues.filter_sort"}} {{.locale.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
@ -111,7 +106,6 @@
{{end}} {{end}}
{{end}} {{end}}
</div> </div>
</div>
{{template "shared/issuelist" dict "." . "listType" "dashboard"}} {{template "shared/issuelist" dict "." . "listType" "dashboard"}}
</div> </div>
</div> </div>

View File

@ -34,9 +34,8 @@
</div> </div>
</div> </div>
<div class="twelve wide column content"> <div class="twelve wide column content">
<div class="ui three column stackable grid"> <div class="list-header">
<div class="column"> <div class="small-pill-buttons ui compact tiny menu">
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
{{svg "octicon-milestone" 16 "gt-mr-3"}} {{svg "octicon-milestone" 16 "gt-mr-3"}}
{{.locale.PrettyNumber .MilestoneStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}} {{.locale.PrettyNumber .MilestoneStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
@ -46,22 +45,18 @@
{{.locale.PrettyNumber .MilestoneStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}} {{.locale.PrettyNumber .MilestoneStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a> </a>
</div> </div>
</div> <form class="list-header-search ui form ignore-dirty">
<div class="column center aligned"> <div class="ui small search fluid action input">
<form class="ui form ignore-dirty">
<div class="ui search fluid action input">
<input type="hidden" name="type" value="{{$.ViewType}}"> <input type="hidden" name="type" value="{{$.ViewType}}">
<input type="hidden" name="repos" value="[{{range $.RepoIDs}}{{.}},{{end}}]"> <input type="hidden" name="repos" value="[{{range $.RepoIDs}}{{.}},{{end}}]">
<input type="hidden" name="sort" value="{{$.SortType}}"> <input type="hidden" name="sort" value="{{$.SortType}}">
<input type="hidden" name="state" value="{{$.State}}"> <input type="hidden" name="state" value="{{$.State}}">
<input name="q" value="{{$.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..."> <input name="q" value="{{$.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}...">
<button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button> <button class="ui small icon button" type="submit" aria-label="{{.locale.Tr "explore.search"}}">{{svg "octicon-search"}}</button>
</div> </div>
</form> </form>
</div>
<div class="column right aligned gt-df gt-ac gt-je">
<!-- Sort --> <!-- Sort -->
<div class="ui dropdown type jump item"> <div class="list-header-sort ui dropdown type jump item">
<span class="text"> <span class="text">
{{.locale.Tr "repo.issues.filter_sort"}} {{.locale.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
@ -76,7 +71,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="milestone list"> <div class="milestone list">
{{range .Milestones}} {{range .Milestones}}
<li class="item"> <li class="item">

View File

@ -1,5 +1,5 @@
<div class="dashboard-navbar"> <div class="dashboard-navbar">
<div class="ui secondary stackable menu"> <div class="ui secondary stackable menu g-menu-stackable-scrollable">
<div class="item"> <div class="item">
<div class="ui floating dropdown jump"> <div class="ui floating dropdown jump">
<span class="text truncated-item-container"> <span class="text truncated-item-container">

View File

@ -1,6 +1,6 @@
{{if .HeatmapData}} {{if .HeatmapData}}
<div id="user-heatmap" <div id="user-heatmap"
data-heatmap-data="{{Json .HeatmapData}}" data-heatmap-data="{{JsonUtils.EncodeToString .HeatmapData}}"
data-locale-total-contributions="{{$.locale.Tr "heatmap.number_of_contributions_in_the_last_12_months" ($.locale.PrettyNumber .HeatmapTotalContributions)}}" data-locale-total-contributions="{{$.locale.Tr "heatmap.number_of_contributions_in_the_last_12_months" ($.locale.PrettyNumber .HeatmapTotalContributions)}}"
data-locale-no-contributions="{{.locale.Tr "heatmap.no_contributions"}}" data-locale-no-contributions="{{.locale.Tr "heatmap.no_contributions"}}"
data-locale-more="{{.locale.Tr "heatmap.more"}}" data-locale-more="{{.locale.Tr "heatmap.more"}}"

View File

@ -13,7 +13,7 @@
{{if eq .Status 1}} {{if eq .Status 1}}
<div id="issue-filters" class="ui stackable grid"> <div id="issue-filters" class="ui stackable grid">
<div class="six wide column"> <div class="six wide column">
<div class="ui compact tiny menu"> <div class="small-pill-buttons ui compact tiny menu">
<a class="{{if eq .State "all"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}"> <a class="{{if eq .State "all"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">
{{.locale.Tr "all"}} {{.locale.Tr "all"}}
</a> </a>

View File

@ -22,7 +22,7 @@
<a href="{{.ContextUser.HomeLink}}.rss"><i class="ui text grey gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a> <a href="{{.ContextUser.HomeLink}}.rss"><i class="ui text grey gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
{{end}} {{end}}
<div class="gt-mt-3"> <div class="gt-mt-3">
<a class="muted" href="{{.ContextUser.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "gt-mr-2"}}{{.ContextUser.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.ContextUser.HomeLink}}?tab=following">{{.ContextUser.NumFollowing}} {{.locale.Tr "user.following"}}</a> <a class="muted" href="{{.ContextUser.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "gt-mr-2"}}{{.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.ContextUser.HomeLink}}?tab=following">{{.NumFollowing}} {{.locale.Tr "user.following"}}</a>
</div> </div>
</div> </div>
<div class="extra content gt-word-break"> <div class="extra content gt-word-break">

View File

@ -19,7 +19,7 @@ func TestPullCompare(t *testing.T) {
req := NewRequest(t, "GET", "/user2/repo1/pulls") req := NewRequest(t, "GET", "/user2/repo1/pulls")
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find(".ui.three.column.grid").Find(".ui.green.button").Attr("href") link, exists := htmlDoc.doc.Find(".new-pr-button").Attr("href")
assert.True(t, exists, "The template has changed") assert.True(t, exists, "The template has changed")
req = NewRequest(t, "GET", link) req = NewRequest(t, "GET", link)

View File

@ -769,6 +769,10 @@ a.label,
border-color: var(--color-primary); border-color: var(--color-primary);
} }
.ui.action.input .button {
border-color: var(--color-input-border);
}
/* currently used for search bar dropdowns in repo search and explore code */ /* currently used for search bar dropdowns in repo search and explore code */
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection { .ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
min-width: 10em; min-width: 10em;
@ -778,10 +782,21 @@ a.label,
border-right-color: transparent; border-right-color: transparent;
} }
.ui.action.input:not([class*="left action"]) > input:hover {
border-right-color: transparent;
}
.ui.action.input:not([class*="left action"]) > input:focus { .ui.action.input:not([class*="left action"]) > input:focus {
border-right-color: var(--color-primary); border-right-color: var(--color-primary);
} }
/* fix button enlarged vertically by svg icon */
/* TODO: change to just `.small.button:has(svg)` but may have global side effects */
.ui.action.input .small.button:has(svg) {
padding-top: 7px !important;
padding-bottom: 7px !important;
}
.ui.menu, .ui.menu,
.ui.vertical.menu { .ui.vertical.menu {
background: var(--color-menu); background: var(--color-menu);
@ -830,6 +845,10 @@ a.label,
color: var(--color-text-light-3); color: var(--color-text-light-3);
} }
.ui.menu .item::before {
background: var(--color-secondary);
}
/* sub menu of vertical menu */ /* sub menu of vertical menu */
.ui.vertical.menu .item .menu .item { .ui.vertical.menu .item .menu .item {
color: var(--color-text-light-2); color: var(--color-text-light-2);
@ -1472,10 +1491,12 @@ img.ui.avatar,
color: var(--color-gold) !important; color: var(--color-gold) !important;
} }
/* FIXME: this is a serious pollution, do not use this for "float: left" anymore */
.ui.left:not(.action) { .ui.left:not(.action) {
float: left; float: left;
} }
/* FIXME: this is a serious pollution, do not use this for "float: right" anymore */
.ui.right:not(.action) { .ui.right:not(.action) {
float: right; float: right;
} }
@ -2533,12 +2554,12 @@ a.ui.basic.label:hover {
right: 0.78571429rem; right: 0.78571429rem;
top: 0; top: 0;
bottom: 0; bottom: 0;
height: 30px; display: flex;
margin-top: auto; align-items: center;
margin-bottom: auto;
} }
/* https://github.com/go-gitea/gitea/issues/10210 */ /* if a .top.attached.header is followed by a .segment, add some margin */
.ui.segments ~ .ui.top.attached.header,
.ui.attached.segment ~ .ui.top.attached.header { .ui.attached.segment ~ .ui.top.attached.header {
margin-top: 1rem; margin-top: 1rem;
} }
@ -2660,38 +2681,6 @@ table th[data-sortt-desc] .svg {
background: var(--color-secondary-dark-1) !important; background: var(--color-secondary-dark-1) !important;
} }
.labelspage {
list-style: none;
padding-top: 0;
}
.labelspage .item {
border-bottom: 1px solid var(--color-secondary);
border-top: none;
}
.labelspage .item a {
font-size: 12px;
padding-right: 10px;
color: var(--color-text-light);
}
.labelspage .item a:hover {
color: var(--color-primary-light-2);
}
.labelspage .item a.open-issues {
margin-right: 30px;
}
.labelspage .item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.labelspage .orglabel {
opacity: 0.7;
}
/* https://github.com/go-gitea/gitea/pull/11486 */ /* https://github.com/go-gitea/gitea/pull/11486 */
.ui.sub.header { .ui.sub.header {
@ -2796,21 +2785,16 @@ table th[data-sortt-desc] .svg {
height: 15px; height: 15px;
} }
@media (max-width: 767px) { .g-menu-stackable-scrollable {
.ui.stackable.menu:not(.no-vertical-tabs) {
overflow-y: hidden; overflow-y: hidden;
overflow-x: auto; overflow-x: auto;
flex-direction: row; }
flex-wrap: nowrap !important;
} @media (max-width: 767.98px) {
.ui.stackable.menu:not(.no-vertical-tabs) .item { /* also respect Fomantic's "stackable" definition, use the same breakpoint to reset our styles */
width: initial !important; .g-menu-stackable-scrollable {
} overflow-x: unset;
.ui.stackable.menu:not(.no-vertical-tabs) > .dropdown.item { overflow-y: unset;
position: initial;
}
.ui.stackable.menu:not(.no-vertical-tabs) .menu {
flex-direction: row;
} }
} }

View File

@ -60,22 +60,30 @@ Gitea's private styles use `g-` prefix.
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.gt-max-width-24rem { max-width: 24rem !important; }
/* below class names match Tailwind CSS */ /* below class names match Tailwind CSS */
.gt-break-all { word-break: break-all !important; } .gt-break-all { word-break: break-all !important; }
.gt-content-center { align-content: center !important; } .gt-content-center { align-content: center !important; }
.gt-cursor-default { cursor: default !important; } .gt-cursor-default { cursor: default !important; }
.gt-invisible { visibility: hidden !important; } .gt-invisible { visibility: hidden !important; }
.gt-items-start { align-items: flex-start !important; } .gt-items-start { align-items: flex-start !important; }
.gt-overflow-x-scroll { overflow-x: scroll !important; }
.gt-pointer-events-none { pointer-events: none !important; } .gt-pointer-events-none { pointer-events: none !important; }
.gt-relative { position: relative !important; } .gt-relative { position: relative !important; }
.gt-whitespace-nowrap { white-space: nowrap !important; } .gt-whitespace-nowrap { white-space: nowrap !important; }
.gt-whitespace-pre { white-space: pre !important; } .gt-whitespace-pre { white-space: pre !important; }
.gt-whitespace-pre-wrap { white-space: pre-wrap !important; } .gt-whitespace-pre-wrap { white-space: pre-wrap !important; }
.gt-overflow-x-auto { overflow-x: auto !important; }
.gt-overflow-x-scroll { overflow-x: scroll !important; }
.gt-overflow-y-hidden { overflow-y: hidden !important; }
.gt-w-screen { width: 100vw !important; } .gt-w-screen { width: 100vw !important; }
.gt-h-screen { height: 100vh !important; } .gt-h-screen { height: 100vh !important; }
.gt-float-left { float: left !important; }
.gt-float-right { float: right !important; }
.gt-rounded { border-radius: var(--border-radius) !important; } .gt-rounded { border-radius: var(--border-radius) !important; }
.gt-rounded-top { border-radius: var(--border-radius) var(--border-radius) 0 0 !important; } .gt-rounded-top { border-radius: var(--border-radius) var(--border-radius) 0 0 !important; }
.gt-rounded-bottom { border-radius: 0 0 var(--border-radius) var(--border-radius) !important; } .gt-rounded-bottom { border-radius: 0 0 var(--border-radius) var(--border-radius) !important; }

View File

@ -43,39 +43,38 @@
color: var(--color-green); color: var(--color-green);
} }
footer { .page-footer {
display: flex;
background-color: var(--color-footer); background-color: var(--color-footer);
border-top: 1px solid var(--color-secondary); border-top: 1px solid var(--color-secondary);
line-height: 39px; line-height: 39px;
flex-basis: 40px;
color: var(--color-text-light);
padding: 0 20px; padding: 0 20px;
} }
footer .right.links { .page-footer .left-links {
flex: 1;
}
.page-footer .right-links {
min-width: 180px; /* make sure the menu dropdown doesn't overflow horizontally when language name is short */ min-width: 180px; /* make sure the menu dropdown doesn't overflow horizontally when language name is short */
} }
footer .right.links > a { .page-footer .right-links > a {
border-left: 1px solid var(--color-secondary-dark-1); border-left: 1px solid var(--color-secondary-dark-1);
padding-left: 8px; padding-left: 8px;
margin-left: 5px; margin-left: 5px;
} }
footer .ui.dropdown.language .menu { .page-footer .ui.dropdown.language .menu {
height: 500px; height: 500px;
max-height: calc(100vh - 60px); max-height: calc(100vh - 60px);
overflow-y: auto; overflow-y: auto;
margin-bottom: 10px; margin-bottom: 10px;
} }
@media (max-width: 880px) { @media (max-width: 880px) {
footer .ui.left, .page-footer {
footer .ui.right {
width: 100%;
display: block; display: block;
text-align: center; text-align: center;
float: none;
} }
} }

View File

@ -30,9 +30,11 @@
@import "./install.css"; @import "./install.css";
@import "./form.css"; @import "./form.css";
@import "./repository.css"; @import "./repository.css";
@import "./repository-release-tag.css"; @import "./repository/release-tag.css";
@import "./repository/issue-label.css";
@import "./repository/list-header.css";
@import "./editor.css"; @import "./editor.css";
@import "./editor-markdown.css"; @import "./editor/combomarkdowneditor.css";
@import "./organization.css"; @import "./organization.css";
@import "./user.css"; @import "./user.css";
@import "./dashboard.css"; @import "./dashboard.css";

View File

@ -160,8 +160,8 @@
} }
.repository .ui.tabs.divider { .repository .ui.tabs.divider {
margin-top: 0; margin-top: -1px;
margin-bottom: 20px; margin-bottom: 12px;
} }
.repository #clone-panel #repo-clone-url { .repository #clone-panel #repo-clone-url {
@ -2012,35 +2012,10 @@
margin-top: -3px; margin-top: -3px;
} }
.repository.settings.branches .protected-branches .selection.dropdown { /* if the element is for a checkbox, then it should have a padding-left to align to the checkbox's text */
width: 300px; .repository.settings.branches .branch-protection .ui.checkbox .help,
} .repository.settings.branches .branch-protection .checkbox-sub-item {
padding-left: 26px;
.repository.settings.branches .protected-branches .item {
border: 1px solid var(--color-secondary);
padding: 10px 15px;
}
.repository.settings.branches .protected-branches .item:not(:last-child) {
border-bottom: 0;
}
.repository.settings.branches .branch-protection .help {
margin-left: 26px;
padding-top: 0;
}
.repository.settings.branches .branch-protection .fields {
margin-left: 20px;
display: block;
}
.repository.settings.branches .branch-protection .whitelist {
margin-left: 26px;
}
.repository.settings.branches .branch-protection .whitelist .dropdown img {
display: inline-block;
} }
.repository.settings.webhook .events .column { .repository.settings.webhook .events .column {
@ -3314,18 +3289,6 @@ td.blob-excerpt {
.repository.file.list #repo-files-table .commit-list span.commit-summary { .repository.file.list #repo-files-table .commit-list span.commit-summary {
display: none !important; display: none !important;
} }
.issue-list-headers.ui[class].grid > div:nth-child(1) {
order: 1;
width: 50%;
}
.issue-list-headers.ui[class].grid > div:nth-child(2) {
order: 3;
width: 100%;
}
.issue-list-headers.ui[class].grid > div.column:not(.row):nth-child(3) {
order: 2;
width: 50%;
}
.repository.view.issue .comment-list .timeline, .repository.view.issue .comment-list .timeline,
.repository.view.issue .comment-list .timeline-item { .repository.view.issue .comment-list .timeline-item {
margin-left: 0; margin-left: 0;

View File

@ -0,0 +1,44 @@
.issue-label-list {
list-style: none;
padding: 0;
margin: 0;
}
.issue-label-list .item {
border-bottom: 1px solid var(--color-secondary);
display: flex;
padding: 1em 0;
margin: 0;
}
.issue-label-list .item:first-child {
padding-top: 0;
}
.issue-label-list .item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.issue-label-list .item .label-title {
width: 33%;
}
.issue-label-list .item .label-issues {
width: 33%;
}
.issue-label-list .item .label-operation {
width: 33%;
text-align: right;
}
.issue-label-list .item a {
font-size: 12px;
padding-right: 10px;
color: var(--color-text-light);
}
.issue-label-list .item.org-label {
opacity: 0.7;
}

View File

@ -0,0 +1,36 @@
.list-header {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .5rem;
}
.list-header-sort {
display: flex;
align-items: center;
justify-content: flex-end;
padding-left: 1rem;
padding-right: 1rem;
}
.list-header-search {
display: flex;
flex: 1;
align-items: center;
flex-wrap: wrap;
justify-content: center;
min-width: 200px; /* to enable flexbox wrapping on mobile */
}
.list-header-search .input {
flex: 1;
}
.small-pill-buttons {
min-height: 35.4px !important; /* match .small.button in height */
}
.small-pill-buttons .item {
padding-top: 6px !important;
padding-bottom: 6px !important;
}

View File

@ -1,6 +1,5 @@
import $ from 'jquery'; import $ from 'jquery';
import {createMonaco} from './codeeditor.js'; import {createMonaco} from './codeeditor.js';
import {initRepoCommonFilterSearchDropdown} from './repo-common.js';
const {appSubUrl, csrfToken} = window.config; const {appSubUrl, csrfToken} = window.config;
@ -73,20 +72,13 @@ export function initRepoSettingGitHook() {
} }
export function initRepoSettingBranches() { export function initRepoSettingBranches() {
// Branches if (!$('.repository.settings.branches').length) return;
if ($('.repository.settings.branches').length > 0) { $('.toggle-target-enabled').on('change', function () {
initRepoCommonFilterSearchDropdown('.protected-branches .dropdown'); const $target = $($(this).attr('data-target'));
$('.enable-protection, .enable-whitelist, .enable-statuscheck').on('change', function () { $target.toggleClass('disabled', !this.checked);
if (this.checked) {
$($(this).data('target')).removeClass('disabled');
} else {
$($(this).data('target')).addClass('disabled');
}
}); });
$('.disable-whitelist').on('change', function () { $('.toggle-target-disabled').on('change', function () {
if (this.checked) { const $target = $($(this).attr('data-target'));
$($(this).data('target')).addClass('disabled'); if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable
}
}); });
}
} }