Compare commits

...

10 Commits

Author SHA1 Message Date
wxiaoguang
affdd40296
Make issue title edit buttons focusable and fix incorrect ajax requests (#22807)
Replace #19922 , which is stale since my last review:
https://github.com/go-gitea/gitea/pull/19922#pullrequestreview-1003546506
and https://github.com/go-gitea/gitea/pull/19922#issuecomment-1153181546

Close #19769

Changes:
1. Use `<button>` instead of `<div>` for buttons
2. Prevent default event handler in `initGlobalButtonClickOnEnter`
3. Fix the incorrect call to `pullrequest_targetbranch_change`
4. Add a slight margin-left to the input element to make UI look better

The logic in repo-issue.js is not ideal, but this PR isn't going to
touch the logic.

This is also an example for future developers to understand how to make
buttons work properly.

### Before


![image](https://user-images.githubusercontent.com/2114189/217262515-ec0462f7-7051-46a5-bfa2-2f6c6a807b7d.png)

### After

* Add a slight margin-left.
* The `Cancel` button is focused.


![image](https://user-images.githubusercontent.com/2114189/217264891-934c9c8d-d190-4866-98b5-666cea57e28d.png)

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2023-02-09 12:11:16 -05:00
Brecht Van Lommel
bdd2c9d2ef
Fix update by rebase being wrongly disabled by protected base branch (#22825)
The branch this is force pushing to is the head branch in the head repo,
so it should be checking if that is protected, not the base.
2023-02-09 12:08:42 -05:00
Brecht Van Lommel
137fcc989b
Fix inconsistent Filter Project name in issue list (#22827)
Use Project instead of Filter Project like the other filter menus.
2023-02-09 11:39:31 -05:00
wxiaoguang
24a9caa2f3
Fix more HTMLURL in templates (#22831)
I haven't tested `runs_list.tmpl` but I think it could be right.

After this PR, besides the `<meta .. HTMLURL>` in html head, the only
explicit HTMLURL usage is in `pull_merge_instruction.tmpl`, which
doesn't affect users too much and it's difficult to fix at the moment.

There are still many usages of `AppUrl` in the templates (eg: the
package help manual), they are similar problems as the HTMLURL in
pull_merge_instruction, and they might be fixed together in the future.

Diff without space:
https://github.com/go-gitea/gitea/pull/22831/files?diff=unified&w=1
2023-02-09 11:31:30 -05:00
John Olheiser
0c190e396d
Fix unmatched div in project filter (#22832)
(Note that the below screenshots aren't the same repo, the former is try
and the latter is local)

Before

![div-before](https://user-images.githubusercontent.com/42128690/217723899-a15da77f-a196-4b23-a157-e7f1e1979610.png)

After

![div-after](https://user-images.githubusercontent.com/42128690/217723878-e54235bc-a7d7-425e-bd0d-47d1814f18ba.png)

Signed-off-by: jolheiser <john.olheiser@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2023-02-09 11:15:07 -05:00
wxiaoguang
cef8f50286
Improve AppUrl/ROOT_URL checking (#22836)
After some PRs:
* #21986
* #22795
* #22808
* #22831
* #22839

Users won't be affected by the ROOT_URL problem in most cases. Close
#19345

This PR improves AppUrl/ROOT_URL checking, only check it on the admin
page, and the message is also updated.

Feel free to suggest about more English-native messages.



![image](https://user-images.githubusercontent.com/2114189/217811809-7d44ddb7-2c4a-46d0-a5db-8ae6ee65f8c3.png)
2023-02-09 11:14:45 -05:00
Jason Song
e253888a0e
Fix isAllowed of escapeStreamer (#22814)
The use of `sort.Search` is wrong: The slice should be sorted, and
`return >= 0` doen't mean it exists, see the
[manual](https://pkg.go.dev/sort#Search).

Could be fixed like this if we really need it:

```diff
diff --git a/modules/charset/escape_stream.go b/modules/charset/escape_stream.go
index 823b63513..fcf1ffbc1 100644
--- a/modules/charset/escape_stream.go
+++ b/modules/charset/escape_stream.go
@@ -20,6 +20,9 @@ import (
 var defaultWordRegexp = regexp.MustCompile(`(-?\d*\.\d\w*)|([^\` + "`" + `\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\x00-\x1f]+)`)

 func NewEscapeStreamer(locale translation.Locale, next HTMLStreamer, allowed ...rune) HTMLStreamer {
+       sort.Slice(allowed, func(i, j int) bool {
+               return allowed[i] < allowed[j]
+       })
        return &escapeStreamer{
                escaped:                 &EscapeStatus{},
                PassthroughHTMLStreamer: *NewPassthroughStreamer(next),
@@ -284,14 +287,8 @@ func (e *escapeStreamer) runeTypes(runes ...rune) (types []runeType, confusables
 }

 func (e *escapeStreamer) isAllowed(r rune) bool {
-       if len(e.allowed) == 0 {
-               return false
-       }
-       if len(e.allowed) == 1 {
-               return e.allowed[0] == r
-       }
-
-       return sort.Search(len(e.allowed), func(i int) bool {
+       i := sort.Search(len(e.allowed), func(i int) bool {
                return e.allowed[i] >= r
-       }) >= 0
+       })
+       return i < len(e.allowed) && e.allowed[i] == r
 }
```

But I don't think so, a map is better to do it.
2023-02-09 20:51:36 +08:00
wxiaoguang
29aea3642f
Make clone URL use current page's host (#22808)
Follow #21986

Even if the ROOT_URL is incorrect, the clone URL on the UI should be
correct.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-09 17:29:13 +08:00
yp05327
7ae10cb7f1
change org_type.go to visible_type.go and fix the notes (#22752)
It seems `VisibleType` is only designed for org at first. But it is also
used by user's visibility now.
So I think `org_type.go` can be changed to `visible_type.go`.
2023-02-09 06:40:34 +00:00
silverwind
90cf07a2c8
Improve notification and stopwatch styles (#22169)
- Add dot-style indicators to notification and time tracker
- Slightly reduce whitespace between right-aligned icons
- Move notification icon to right on mobile
- Switch menu icon to SVG

<img width="270" alt="Screenshot 2022-12-19 at 19 40 32"
src="https://user-images.githubusercontent.com/115237/208496795-ce8734a0-f109-47b7-8eb8-96931e867b23.png">
<img width="607" alt="Screenshot 2022-12-19 at 19 41 04"
src="https://user-images.githubusercontent.com/115237/208496797-2ff68197-f520-4174-927e-ead15addd63e.png">

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-09 13:42:18 +08:00
20 changed files with 187 additions and 146 deletions

View File

@ -277,26 +277,26 @@ func CommentTypeIsRef(t CommentType) bool {
return t == CommentTypeCommentRef || t == CommentTypePullRef || t == CommentTypeIssueRef return t == CommentTypeCommentRef || t == CommentTypePullRef || t == CommentTypeIssueRef
} }
// RefCommentHTMLURL returns the HTML URL for the comment that created this reference // RefCommentLink returns the relative URL for the comment that created this reference
func (c *Comment) RefCommentHTMLURL() string { func (c *Comment) RefCommentLink() string {
// Edge case for when the reference is inside the title or the description of the referring issue // Edge case for when the reference is inside the title or the description of the referring issue
if c.RefCommentID == 0 { if c.RefCommentID == 0 {
return c.RefIssueHTMLURL() return c.RefIssueLink()
} }
if err := c.LoadRefComment(); err != nil { // Silently dropping errors :unamused: if err := c.LoadRefComment(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefComment(%d): %v", c.RefCommentID, err) log.Error("LoadRefComment(%d): %v", c.RefCommentID, err)
return "" return ""
} }
return c.RefComment.HTMLURL() return c.RefComment.Link()
} }
// RefIssueHTMLURL returns the HTML URL of the issue where this reference was created // RefIssueLink returns the relative URL of the issue where this reference was created
func (c *Comment) RefIssueHTMLURL() string { func (c *Comment) RefIssueLink() string {
if err := c.LoadRefIssue(); err != nil { // Silently dropping errors :unamused: if err := c.LoadRefIssue(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefIssue(%d): %v", c.RefCommentID, err) log.Error("LoadRefIssue(%d): %v", c.RefCommentID, err)
return "" return ""
} }
return c.RefIssue.HTMLURL() return c.RefIssue.Link()
} }
// RefIssueTitle returns the title of the issue where this reference was created // RefIssueTitle returns the title of the issue where this reference was created

View File

@ -6,7 +6,6 @@ package charset
import ( import (
"fmt" "fmt"
"regexp" "regexp"
"sort"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@ -20,12 +19,16 @@ import (
var defaultWordRegexp = regexp.MustCompile(`(-?\d*\.\d\w*)|([^\` + "`" + `\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\x00-\x1f]+)`) var defaultWordRegexp = regexp.MustCompile(`(-?\d*\.\d\w*)|([^\` + "`" + `\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\x00-\x1f]+)`)
func NewEscapeStreamer(locale translation.Locale, next HTMLStreamer, allowed ...rune) HTMLStreamer { func NewEscapeStreamer(locale translation.Locale, next HTMLStreamer, allowed ...rune) HTMLStreamer {
allowedM := make(map[rune]bool, len(allowed))
for _, v := range allowed {
allowedM[v] = true
}
return &escapeStreamer{ return &escapeStreamer{
escaped: &EscapeStatus{}, escaped: &EscapeStatus{},
PassthroughHTMLStreamer: *NewPassthroughStreamer(next), PassthroughHTMLStreamer: *NewPassthroughStreamer(next),
locale: locale, locale: locale,
ambiguousTables: AmbiguousTablesForLocale(locale), ambiguousTables: AmbiguousTablesForLocale(locale),
allowed: allowed, allowed: allowedM,
} }
} }
@ -34,7 +37,7 @@ type escapeStreamer struct {
escaped *EscapeStatus escaped *EscapeStatus
locale translation.Locale locale translation.Locale
ambiguousTables []*AmbiguousTable ambiguousTables []*AmbiguousTable
allowed []rune allowed map[rune]bool
} }
func (e *escapeStreamer) EscapeStatus() *EscapeStatus { func (e *escapeStreamer) EscapeStatus() *EscapeStatus {
@ -256,7 +259,7 @@ func (e *escapeStreamer) runeTypes(runes ...rune) (types []runeType, confusables
runeCounts.numBrokenRunes++ runeCounts.numBrokenRunes++
case r == ' ' || r == '\t' || r == '\n': case r == ' ' || r == '\t' || r == '\n':
runeCounts.numBasicRunes++ runeCounts.numBasicRunes++
case e.isAllowed(r): case e.allowed[r]:
if r > 0x7e || r < 0x20 { if r > 0x7e || r < 0x20 {
types[i] = nonBasicASCIIRuneType types[i] = nonBasicASCIIRuneType
runeCounts.numNonConfusingNonBasicRunes++ runeCounts.numNonConfusingNonBasicRunes++
@ -282,16 +285,3 @@ func (e *escapeStreamer) runeTypes(runes ...rune) (types []runeType, confusables
} }
return types, confusables, runeCounts return types, confusables, runeCounts
} }
func (e *escapeStreamer) isAllowed(r rune) bool {
if len(e.allowed) == 0 {
return false
}
if len(e.allowed) == 1 {
return e.allowed[0] == r
}
return sort.Search(len(e.allowed), func(i int) bool {
return e.allowed[i] >= r
}) >= 0
}

View File

@ -3,7 +3,7 @@
package structs package structs
// VisibleType defines the visibility (Organization only) // VisibleType defines the visibility of user and org
type VisibleType int type VisibleType int
const ( const (
@ -13,11 +13,11 @@ const (
// VisibleTypeLimited Visible for every connected user // VisibleTypeLimited Visible for every connected user
VisibleTypeLimited VisibleTypeLimited
// VisibleTypePrivate Visible only for organization's members // VisibleTypePrivate Visible only for self or admin user
VisibleTypePrivate VisibleTypePrivate
) )
// VisibilityModes is a map of org Visibility types // VisibilityModes is a map of Visibility types
var VisibilityModes = map[string]VisibleType{ var VisibilityModes = map[string]VisibleType{
"public": VisibleTypePublic, "public": VisibleTypePublic,
"limited": VisibleTypeLimited, "limited": VisibleTypeLimited,

View File

@ -72,6 +72,10 @@ func NewFuncMap() []template.FuncMap {
return setting.StaticURLPrefix + "/assets" return setting.StaticURLPrefix + "/assets"
}, },
"AppUrl": func() string { "AppUrl": func() string {
// The usage of AppUrl should be avoided as much as possible,
// because the AppURL(ROOT_URL) may not match user's visiting site and the ROOT_URL in app.ini may be incorrect.
// And it's difficult for Gitea to guess absolute URL correctly with zero configuration,
// because Gitea doesn't know whether the scheme is HTTP or HTTPS unless the reverse proxy could tell Gitea.
return setting.AppURL return setting.AppURL
}, },
"AppVer": func() string { "AppVer": func() string {

View File

@ -106,7 +106,7 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
BaseBranch: pull.HeadBranch, BaseBranch: pull.HeadBranch,
} }
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.BaseRepoID, pull.BaseBranch) pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
if err != nil { if err != nil {
return false, false, err return false, false, err
} }

View File

@ -3,22 +3,24 @@
{{if .IsSigned}} {{if .IsSigned}}
{{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}} {{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}}
{{end}} {{end}}
<div class="item brand" style="justify-content: space-between;"> <div class="item brand sb">
<a href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{.locale.Tr "dashboard"}}{{else}}{{.locale.Tr "home"}}{{end}}"> <a href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{.locale.Tr "dashboard"}}{{else}}{{.locale.Tr "home"}}{{end}}">
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{.locale.Tr "logo"}}" aria-hidden="true"> <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{.locale.Tr "logo"}}" aria-hidden="true">
</a> </a>
{{if .IsSigned}} <div class="df ac">
<a href="{{AppSubUrl}}/notifications" class="tooltip mobile-only" data-content='{{.locale.Tr "notifications"}}'> {{if .IsSigned}}
<span class="text black"> <a href="{{AppSubUrl}}/notifications" class="tooltip mobile-only mr-4 mt-3" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}">
<span class="fitted">{{svg "octicon-bell"}}</span> <span class="fitted item">
<span class="ui red label mini{{if not $notificationUnreadCount}} hidden{{end}} notification_count"> {{svg "octicon-bell"}}
{{$notificationUnreadCount}} <span class="notification_count{{if not $notificationUnreadCount}} hidden{{end}}">
{{$notificationUnreadCount}}
</span>
</span> </span>
</span> </a>
</a> {{end}}
{{end}} <div class="ui icon button mobile-only" id="navbar-expand-toggle">
<div class="ui basic icon button mobile-only" id="navbar-expand-toggle"> {{svg "octicon-three-bars"}}
<i class="sidebar icon"></i> </div>
</div> </div>
</div> </div>
@ -78,12 +80,10 @@
{{else if .IsSigned}} {{else if .IsSigned}}
<div class="right stackable menu"> <div class="right stackable menu">
{{if EnableTimetracking}} {{if EnableTimetracking}}
<a class="active-stopwatch-trigger item ui label {{if not .ActiveStopwatch}}hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}"> <a class="active-stopwatch-trigger item ui mx-0{{if not .ActiveStopwatch}} hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}">
<span class="text"> <span class="fitted relative">
<span class="fitted item"> {{svg "octicon-stopwatch"}}
{{svg "octicon-stopwatch"}} <span class="header-stopwatch-dot"></span>
<span class="red" style="position:absolute; right:-0.6em; top:-0.6em;">{{svg "octicon-dot-fill"}}</span>
</span>
<span class="sr-mobile-only">{{.locale.Tr "active_stopwatch"}}</span> <span class="sr-mobile-only">{{.locale.Tr "active_stopwatch"}}</span>
</span> </span>
</a> </a>
@ -118,16 +118,16 @@
</div> </div>
{{end}} {{end}}
<a href="{{AppSubUrl}}/notifications" class="item tooltip not-mobile" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}"> <a href="{{AppSubUrl}}/notifications" class="item tooltip not-mobile mx-0" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}">
<span class="text"> <span class="fitted item">
<span class="fitted">{{svg "octicon-bell"}}</span> {{svg "octicon-bell"}}
<span class="ui red label {{if not $notificationUnreadCount}}hidden{{end}} notification_count"> <span class="notification_count{{if not $notificationUnreadCount}} hidden{{end}}">
{{$notificationUnreadCount}} {{$notificationUnreadCount}}
</span> </span>
</span> </span>
</a> </a>
<div class="ui dropdown jump item tooltip" data-content="{{.locale.Tr "create_new"}}"> <div class="ui dropdown jump item tooltip mx-0" data-content="{{.locale.Tr "create_new"}}">
<span class="text"> <span class="text">
<span class="fitted">{{svg "octicon-plus"}}</span> <span class="fitted">{{svg "octicon-plus"}}</span>
<span class="sr-mobile-only">{{.locale.Tr "create_new"}}</span> <span class="sr-mobile-only">{{.locale.Tr "create_new"}}</span>
@ -150,7 +150,7 @@
</div><!-- end content create new menu --> </div><!-- end content create new menu -->
</div><!-- end dropdown menu create new --> </div><!-- end dropdown menu create new -->
<div class="ui dropdown jump item tooltip" tabindex="-1" data-content="{{.locale.Tr "user_profile_and_more"}}"> <div class="ui dropdown jump item tooltip mx-0" tabindex="-1" data-content="{{.locale.Tr "user_profile_and_more"}}">
<span class="text"> <span class="text">
{{avatar .SignedUser 24 "tiny"}} {{avatar .SignedUser 24 "tiny"}}
<span class="sr-only">{{.locale.Tr "user_profile_and_more"}}</span> <span class="sr-only">{{.locale.Tr "user_profile_and_more"}}</span>

View File

@ -238,7 +238,7 @@
{{end}} {{end}}
<div class="right floated"> <div class="right floated">
{{range .Assignees}} {{range .Assignees}}
<a class="tooltip" target="_blank" href="{{.HTMLURL}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini mr-3"}}</a> <a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini mr-3"}}</a>
{{end}} {{end}}
</div> </div>
</div> </div>

View File

@ -6,7 +6,7 @@
</div> </div>
<div class="issue-item-main f1 fc df"> <div class="issue-item-main f1 fc df">
<div class="issue-item-top-row"> <div class="issue-item-top-row">
<a class="index ml-0 mr-2" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> <a class="index ml-0 mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{.Title}} {{.Title}}
</a> </a>
<span class="ui label"> <span class="ui label">

View File

@ -17,7 +17,14 @@
const btn = isSSH ? sshBtn : httpsBtn; const btn = isSSH ? sshBtn : httpsBtn;
if (!btn) return; if (!btn) return;
const link = btn.getAttribute('data-link'); let link = btn.getAttribute('data-link');
if (link.startsWith('http://') || link.startsWith('https://')) {
// use current protocol/host as the clone link
const url = new URL(link);
url.protocol = window.location.protocol;
url.host = window.location.host;
link = url.toString();
}
for (const el of document.getElementsByClassName('js-clone-url')) { for (const el of document.getElementsByClassName('js-clone-url')) {
el[el.nodeName === 'INPUT' ? 'value' : 'textContent'] = link; el[el.nodeName === 'INPUT' ? 'value' : 'textContent'] = link;
} }

View File

@ -77,13 +77,13 @@
<!-- Project --> <!-- Project -->
<div class="ui{{if not (or .OpenProjects .ClosedProjects)}} disabled{{end}} dropdown jump item"> <div class="ui{{if not (or .OpenProjects .ClosedProjects)}} disabled{{end}} dropdown jump item">
<span class="text"> <span class="text">
{{.locale.Tr "repo.issues.filter_projects"}} {{.locale.Tr "repo.issues.filter_project"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span> </span>
<div class="menu"> <div class="menu">
<div class="ui icon search input"> <div class="ui icon search input">
<i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_projects"}}"> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_project"}}">
</div> </div>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_all"}}</a> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_all"}}</a>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_none"}}</a> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_none"}}</a>
@ -276,7 +276,6 @@
{{.Title}} {{.Title}}
</div> </div>
{{end}} {{end}}
</div>
{{end}} {{end}}
</div> </div>
</div> </div>

View File

@ -151,12 +151,12 @@
{{if eq .RefAction 3}}<del>{{end}} {{if eq .RefAction 3}}<del>{{end}}
<span class="text grey muted-links"> <span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}} {{template "shared/user/authorlink" .Poster}}
{{$.locale.Tr $refTr (.EventTag|Escape) $createdStr (.RefCommentHTMLURL|Escape) $refFrom | Safe}} {{$.locale.Tr $refTr (.EventTag|Escape) $createdStr (.RefCommentLink|Escape) $refFrom | Safe}}
</span> </span>
{{if eq .RefAction 3}}</del>{{end}} {{if eq .RefAction 3}}</del>{{end}}
<div class="detail"> <div class="detail">
<span class="text grey muted-links"><a href="{{.RefIssueHTMLURL}}"><b>{{.RefIssueTitle}}</b> {{.RefIssueIdent}}</a></span> <span class="text grey muted-links"><a href="{{.RefIssueLink}}"><b>{{.RefIssueTitle}}</b> {{.RefIssueIdent}}</a></span>
</div> </div>
</div> </div>
{{else if eq .Type 4}} {{else if eq .Type 4}}

View File

@ -303,79 +303,78 @@
{{$hasPendingPullRequestMergeTip = $.locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}} {{$hasPendingPullRequestMergeTip = $.locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}}
{{end}} {{end}}
<div class="ui divider"></div> <div class="ui divider"></div>
<script> <script type="module">
(() => { const issueUrl = window.location.origin + {{$.Issue.Link}};
const defaultMergeTitle = {{.DefaultMergeMessage}}; const defaultMergeTitle = {{.DefaultMergeMessage}};
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}}; const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
const defaultMergeMessage = {{if .DefaultMergeBody}}{{.DefaultMergeBody}}{{else}}'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}}{{end}}; const defaultMergeMessage = {{if .DefaultMergeBody}}{{.DefaultMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}};
const defaultSquashMergeMessage = {{if .DefaultSquashMergeBody}}{{.DefaultSquashMergeBody}}{{else}}'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}}{{end}}; const defaultSquashMergeMessage = {{if .DefaultSquashMergeBody}}{{.DefaultSquashMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}};
const mergeForm = { const mergeForm = {
'baseLink': {{.Link}}, 'baseLink': {{.Link}},
'textCancel': {{$.locale.Tr "cancel"}}, 'textCancel': {{$.locale.Tr "cancel"}},
'textDeleteBranch': {{$.locale.Tr "repo.branch.delete" .HeadTarget}}, 'textDeleteBranch': {{$.locale.Tr "repo.branch.delete" .HeadTarget}},
'textAutoMergeButtonWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_button_when_succeed"}}, 'textAutoMergeButtonWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_button_when_succeed"}},
'textAutoMergeWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_when_succeed"}}, 'textAutoMergeWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_when_succeed"}},
'textAutoMergeCancelSchedule': {{$.locale.Tr "repo.pulls.auto_merge_cancel_schedule"}}, 'textAutoMergeCancelSchedule': {{$.locale.Tr "repo.pulls.auto_merge_cancel_schedule"}},
'textClearMergeMessage': {{$.locale.Tr "repo.pulls.clear_merge_message"}}, 'textClearMergeMessage': {{$.locale.Tr "repo.pulls.clear_merge_message"}},
'textClearMergeMessageHint': {{$.locale.Tr "repo.pulls.clear_merge_message_hint"}}, 'textClearMergeMessageHint': {{$.locale.Tr "repo.pulls.clear_merge_message_hint"}},
'canMergeNow': {{$canMergeNow}}, 'canMergeNow': {{$canMergeNow}},
'allOverridableChecksOk': {{not $notAllOverridableChecksOk}}, 'allOverridableChecksOk': {{not $notAllOverridableChecksOk}},
'emptyCommit': {{.Issue.PullRequest.IsEmpty}}, 'emptyCommit': {{.Issue.PullRequest.IsEmpty}},
'pullHeadCommitID': {{.PullHeadCommitID}}, 'pullHeadCommitID': {{.PullHeadCommitID}},
'isPullBranchDeletable': {{.IsPullBranchDeletable}}, 'isPullBranchDeletable': {{.IsPullBranchDeletable}},
'defaultMergeStyle': {{.MergeStyle}}, 'defaultMergeStyle': {{.MergeStyle}},
'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, 'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}},
'mergeMessageFieldPlaceHolder': {{$.locale.Tr "repo.editor.commit_message_desc"}}, 'mergeMessageFieldPlaceHolder': {{$.locale.Tr "repo.editor.commit_message_desc"}},
'defaultMergeMessage': defaultMergeMessage, 'defaultMergeMessage': defaultMergeMessage,
'hasPendingPullRequestMerge': {{.HasPendingPullRequestMerge}}, 'hasPendingPullRequestMerge': {{.HasPendingPullRequestMerge}},
'hasPendingPullRequestMergeTip': {{$hasPendingPullRequestMergeTip}}, 'hasPendingPullRequestMergeTip': {{$hasPendingPullRequestMergeTip}},
}; };
const generalHideAutoMerge = mergeForm.canMergeNow && mergeForm.allOverridableChecksOk; // if this PR can be merged now, then hide the auto merge const generalHideAutoMerge = mergeForm.canMergeNow && mergeForm.allOverridableChecksOk; // if this PR can be merged now, then hide the auto merge
mergeForm['mergeStyles'] = [ mergeForm['mergeStyles'] = [
{ {
'name': 'merge', 'name': 'merge',
'allowed': {{$prUnit.PullRequestsConfig.AllowMerge}}, 'allowed': {{$prUnit.PullRequestsConfig.AllowMerge}},
'textDoMerge': {{$.locale.Tr "repo.pulls.merge_pull_request"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.merge_pull_request"}},
'mergeTitleFieldText': defaultMergeTitle, 'mergeTitleFieldText': defaultMergeTitle,
'mergeMessageFieldText': defaultMergeMessage, 'mergeMessageFieldText': defaultMergeMessage,
'hideAutoMerge': generalHideAutoMerge, 'hideAutoMerge': generalHideAutoMerge,
}, },
{ {
'name': 'rebase', 'name': 'rebase',
'allowed': {{$prUnit.PullRequestsConfig.AllowRebase}}, 'allowed': {{$prUnit.PullRequestsConfig.AllowRebase}},
'textDoMerge': {{$.locale.Tr "repo.pulls.rebase_merge_pull_request"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.rebase_merge_pull_request"}},
'hideMergeMessageTexts': true, 'hideMergeMessageTexts': true,
'hideAutoMerge': generalHideAutoMerge, 'hideAutoMerge': generalHideAutoMerge,
}, },
{ {
'name': 'rebase-merge', 'name': 'rebase-merge',
'allowed': {{$prUnit.PullRequestsConfig.AllowRebaseMerge}}, 'allowed': {{$prUnit.PullRequestsConfig.AllowRebaseMerge}},
'textDoMerge': {{$.locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}},
'mergeTitleFieldText': defaultMergeTitle, 'mergeTitleFieldText': defaultMergeTitle,
'mergeMessageFieldText': defaultMergeMessage, 'mergeMessageFieldText': defaultMergeMessage,
'hideAutoMerge': generalHideAutoMerge, 'hideAutoMerge': generalHideAutoMerge,
}, },
{ {
'name': 'squash', 'name': 'squash',
'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}}, 'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}},
'textDoMerge': {{$.locale.Tr "repo.pulls.squash_merge_pull_request"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.squash_merge_pull_request"}},
'mergeTitleFieldText': defaultSquashMergeTitle, 'mergeTitleFieldText': defaultSquashMergeTitle,
'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage, 'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage,
'hideAutoMerge': generalHideAutoMerge, 'hideAutoMerge': generalHideAutoMerge,
}, },
{ {
'name': 'manually-merged', 'name': 'manually-merged',
'allowed': {{and $prUnit.PullRequestsConfig.AllowManualMerge $.IsRepoAdmin}}, 'allowed': {{and $prUnit.PullRequestsConfig.AllowManualMerge $.IsRepoAdmin}},
'textDoMerge': {{$.locale.Tr "repo.pulls.merge_manually"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.merge_manually"}},
'hideMergeMessageTexts': true, 'hideMergeMessageTexts': true,
'hideAutoMerge': true, 'hideAutoMerge': true,
} }
]; ];
window.config.pageData.pullRequestMergeForm = mergeForm; window.config.pageData.pullRequestMergeForm = mergeForm;
})();
</script> </script>
<div id="pull-request-merge-form"></div> <div id="pull-request-merge-form"></div>

View File

@ -5,6 +5,7 @@
<div class="ui secondary segment"> <div class="ui secondary segment">
{{if eq $.Issue.PullRequest.Flow 0}} {{if eq $.Issue.PullRequest.Flow 0}}
<div>git checkout -b {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}} {{$.Issue.PullRequest.BaseBranch}}</div> <div>git checkout -b {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}} {{$.Issue.PullRequest.BaseBranch}}</div>
{{/* the only legacy HTMLURL used in template, which doesn't affect users too much and is very diffcult to fix, it should be fixed together with other AppUrl usages*/}}
<div>git pull {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.HTMLURL}}{{else}}origin{{end}} {{$.Issue.PullRequest.HeadBranch}}</div> <div>git pull {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.HTMLURL}}{{else}}origin{{end}} {{$.Issue.PullRequest.HeadBranch}}</div>
{{else}} {{else}}
<div>git fetch origin {{$.Issue.PullRequest.GetGitRefName}}:{{$.Issue.PullRequest.HeadBranch}}</div> <div>git fetch origin {{$.Issue.PullRequest.GetGitRefName}}:{{$.Issue.PullRequest.HeadBranch}}</div>

View File

@ -2,20 +2,20 @@
<div class="issue-title" id="issue-title-wrapper"> <div class="issue-title" id="issue-title-wrapper">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
<div class="edit-button"> <div class="edit-button">
<div id="edit-title" class="ui basic secondary not-in-edit button">{{.locale.Tr "repo.issues.edit"}}</div> <button id="edit-title" class="ui basic button secondary not-in-edit">{{.locale.Tr "repo.issues.edit"}}</button>
</div> </div>
{{end}} {{end}}
<h1> <h1>
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}}</span> <span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}}</span>
<span class="index">#{{.Issue.Index}}</span> <span class="index">#{{.Issue.Index}}</span>
<div id="edit-title-input" class="ui input" style="display: none"> <div id="edit-title-input" class="ui input ml-4" style="display: none">
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off"> <input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
</div> </div>
</h1> </h1>
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
<div class="edit-buttons"> <div class="edit-buttons">
<div id="cancel-edit-title" class="ui basic secondary in-edit button" style="display: none">{{.locale.Tr "repo.issues.cancel"}}</div> <button id="cancel-edit-title" class="ui basic button secondary in-edit" style="display: none">{{.locale.Tr "repo.issues.cancel"}}</button>
<div id="save-edit-title" class="ui primary in-edit button" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{.locale.Tr "repo.issues.save"}}</div> <button id="save-edit-title" class="ui primary button in-edit" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{.locale.Tr "repo.issues.save"}}</button>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -1,12 +1,17 @@
import $ from 'jquery'; import $ from 'jquery';
import {checkAppUrl} from '../common-global.js';
const {csrfToken} = window.config; const {csrfToken} = window.config;
export function initAdminCommon() { export function initAdminCommon() {
if ($('.admin').length === 0) { if ($('.page-content.admin').length === 0) {
return; return;
} }
// check whether appUrl(ROOT_URL) is correct, if not, show an error message
// only admin pages need this check because most templates are using relative URLs now
checkAppUrl();
// New user // New user
if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) { if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) {
$('#login_type').on('change', function () { $('#login_type').on('change', function () {

View File

@ -60,6 +60,7 @@ export function initGlobalButtonClickOnEnter() {
$(document).on('keypress', '.ui.button', (e) => { $(document).on('keypress', '.ui.button', (e) => {
if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar
$(e.target).trigger('click'); $(e.target).trigger('click');
e.preventDefault();
} }
}); });
} }
@ -381,9 +382,6 @@ export function checkAppUrl() {
if (curUrl.startsWith(appUrl) || `${curUrl}/` === appUrl) { if (curUrl.startsWith(appUrl) || `${curUrl}/` === appUrl) {
return; return;
} }
if (document.querySelector('.page-content.install')) { showGlobalErrorMessage(`Your ROOT_URL in app.ini is "${appUrl}", it's unlikely matching the site you are visiting.
return; // no need to show the message on the installation page Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification.`);
}
showGlobalErrorMessage(`Your ROOT_URL in app.ini is ${appUrl} but you are visiting ${curUrl}
You should set ROOT_URL correctly, otherwise the web may not work correctly.`);
} }

View File

@ -605,6 +605,7 @@ export function initRepoIssueTitleEdit() {
const targetBranch = $('#pull-target-branch').data('branch'); const targetBranch = $('#pull-target-branch').data('branch');
const $branchTarget = $('#branch_target'); const $branchTarget = $('#branch_target');
if (targetBranch === $branchTarget.text()) { if (targetBranch === $branchTarget.text()) {
window.location.reload();
return false; return false;
} }
$.post(update_url, { $.post(update_url, {
@ -617,19 +618,22 @@ export function initRepoIssueTitleEdit() {
}); });
}; };
const pullrequest_target_update_url = $(this).data('target-update-url'); const pullrequest_target_update_url = $(this).attr('data-target-update-url');
if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) { if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) {
$editInput.val($issueTitle.text()); $editInput.val($issueTitle.text());
pullrequest_targetbranch_change(pullrequest_target_update_url); pullrequest_targetbranch_change(pullrequest_target_update_url);
} else { } else {
$.post($(this).data('update-url'), { $.post($(this).attr('data-update-url'), {
_csrf: csrfToken, _csrf: csrfToken,
title: $editInput.val() title: $editInput.val()
}, (data) => { }, (data) => {
$editInput.val(data.title); $editInput.val(data.title);
$issueTitle.text(data.title); $issueTitle.text(data.title);
pullrequest_targetbranch_change(pullrequest_target_update_url); if (pullrequest_target_update_url) {
window.location.reload(); pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window
} else {
window.location.reload();
}
}); });
} }
return false; return false;

View File

@ -48,7 +48,6 @@ import {
initCommitStatuses, initCommitStatuses,
} from './features/repo-commit.js'; } from './features/repo-commit.js';
import { import {
checkAppUrl,
initFootLanguageMenu, initFootLanguageMenu,
initGlobalButtonClickOnEnter, initGlobalButtonClickOnEnter,
initGlobalButtons, initGlobalButtons,
@ -199,5 +198,4 @@ $(document).ready(() => {
initUserAuthWebAuthnRegister(); initUserAuthWebAuthnRegister();
initUserSettings(); initUserSettings();
initViewedCheckboxListenerFor(); initViewedCheckboxListenerFor();
checkAppUrl();
}); });

View File

@ -1364,6 +1364,7 @@ a.ui.card:hover,
visibility: hidden; visibility: hidden;
} }
.text.primary { color: var(--color-primary) !important; }
.text.red { color: var(--color-red) !important; } .text.red { color: var(--color-red) !important; }
.text.orange { color: var(--color-orange) !important; } .text.orange { color: var(--color-orange) !important; }
.text.yellow { color: var(--color-yellow) !important; } .text.yellow { color: var(--color-yellow) !important; }
@ -2315,6 +2316,13 @@ a.ui.label:hover {
.ui.basic.secondary.buttons .button:active, .ui.basic.secondary.buttons .button:active,
.ui.basic.secondary.button:active { .ui.basic.secondary.button:active {
color: var(--color-secondary-dark-8) !important; color: var(--color-secondary-dark-8) !important;
border-color: var(--color-secondary-dark-1) !important;
}
.ui.basic.secondary.button:focus,
.ui.basic.secondary.buttons .button:focus {
color: var(--color-secondary-dark-8) !important;
border-color: var(--color-secondary-dark-3) !important;
} }
.ui.tertiary.button { .ui.tertiary.button {
@ -2434,6 +2442,33 @@ a.ui.basic.label:hover {
margin-top: 1rem; margin-top: 1rem;
} }
.header-stopwatch-dot {
position: absolute;
left: 8px;
top: -8px;
width: 13px;
height: 13px;
background: var(--color-primary);
border: 2px solid var(--color-header-bar);
border-radius: 100%;
}
.notification_count {
position: absolute;
left: 5px;
top: -8px;
min-width: 1.5em;
text-align: center;
background: var(--color-primary);
border: 2px solid var(--color-header-bar);
color: var(--color-header-bar);
padding: 2px;
border-radius: 1em;
font-size: 10px;
font-weight: 700;
line-height: .7;
}
table th[data-sortt-asc], table th[data-sortt-asc],
table th[data-sortt-desc] { table th[data-sortt-desc] {
&:hover { &:hover {

View File

@ -21,6 +21,7 @@
/* below class names match Tailwind CSS */ /* below class names match Tailwind CSS */
.pointer-events-none { pointer-events: none !important; } .pointer-events-none { pointer-events: none !important; }
.relative { position: relative !important; }
.mono { .mono {
font-family: var(--fonts-monospace) !important; font-family: var(--fonts-monospace) !important;