Skip to content

Commit f8536d7

Browse files
committed
fix
1 parent ef86f4e commit f8536d7

File tree

14 files changed

+101
-149
lines changed

14 files changed

+101
-149
lines changed

models/actions/schedule_spec_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,17 @@ import (
77
"testing"
88
"time"
99

10+
"code.gitea.io/gitea/modules/test"
11+
1012
"github.com/stretchr/testify/assert"
1113
"github.com/stretchr/testify/require"
1214
)
1315

1416
func TestActionScheduleSpec_Parse(t *testing.T) {
1517
// Mock the local timezone is not UTC
16-
local := time.Local
1718
tz, err := time.LoadLocation("Asia/Shanghai")
1819
require.NoError(t, err)
19-
defer func() {
20-
time.Local = local
21-
}()
22-
time.Local = tz
20+
defer test.MockVariableValue(&time.Local, tz)()
2321

2422
now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
2523
require.NoError(t, err)

routers/api/v1/repo/issue.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"code.gitea.io/gitea/modules/timeutil"
2727
"code.gitea.io/gitea/modules/web"
2828
"code.gitea.io/gitea/routers/api/v1/utils"
29+
"code.gitea.io/gitea/routers/common"
2930
"code.gitea.io/gitea/services/context"
3031
"code.gitea.io/gitea/services/convert"
3132
issue_service "code.gitea.io/gitea/services/issue"
@@ -1046,18 +1047,11 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
10461047
return
10471048
}
10481049

1049-
var deadlineUnix timeutil.TimeStamp
1050-
var deadline time.Time
1051-
if form.Deadline != nil && !form.Deadline.IsZero() {
1052-
deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
1053-
23, 59, 59, 0, time.Local)
1054-
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
1055-
}
1056-
1050+
deadlineUnix, _ := common.ParseAPIDeadlineToEndOfDay(form.Deadline)
10571051
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
10581052
ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
10591053
return
10601054
}
10611055

1062-
ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
1056+
ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: deadlineUnix.AsTimePtr()})
10631057
}

routers/api/v1/repo/milestone.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"code.gitea.io/gitea/modules/timeutil"
1616
"code.gitea.io/gitea/modules/web"
1717
"code.gitea.io/gitea/routers/api/v1/utils"
18+
"code.gitea.io/gitea/routers/common"
1819
"code.gitea.io/gitea/services/context"
1920
"code.gitea.io/gitea/services/convert"
2021
)
@@ -224,9 +225,7 @@ func EditMilestone(ctx *context.APIContext) {
224225
if form.Description != nil {
225226
milestone.Content = *form.Description
226227
}
227-
if form.Deadline != nil && !form.Deadline.IsZero() {
228-
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
229-
}
228+
milestone.DeadlineUnix, _ = common.ParseAPIDeadlineToEndOfDay(form.Deadline)
230229

231230
oldIsClosed := milestone.IsClosed
232231
if form.State != nil {

routers/common/deadline.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package common
5+
6+
import (
7+
"time"
8+
9+
"code.gitea.io/gitea/modules/setting"
10+
"code.gitea.io/gitea/modules/timeutil"
11+
)
12+
13+
func ParseDeadlineDateToEndOfDay(date string) (timeutil.TimeStamp, error) {
14+
if date == "" {
15+
return 0, nil
16+
}
17+
deadline, err := time.ParseInLocation("2006-01-02", date, setting.DefaultUILocation)
18+
if err != nil {
19+
return 0, err
20+
}
21+
deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
22+
return timeutil.TimeStamp(deadline.Unix()), nil
23+
}
24+
25+
func ParseAPIDeadlineToEndOfDay(t *time.Time) (timeutil.TimeStamp, error) {
26+
if t == nil || t.IsZero() || t.Unix() == 0 {
27+
return 0, nil
28+
}
29+
deadline := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, setting.DefaultUILocation)
30+
return timeutil.TimeStamp(deadline.Unix()), nil
31+
}

routers/web/repo/issue.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"sort"
1818
"strconv"
1919
"strings"
20-
"time"
2120

2221
activities_model "code.gitea.io/gitea/models/activities"
2322
"code.gitea.io/gitea/models/db"
@@ -45,9 +44,9 @@ import (
4544
api "code.gitea.io/gitea/modules/structs"
4645
"code.gitea.io/gitea/modules/templates"
4746
"code.gitea.io/gitea/modules/templates/vars"
48-
"code.gitea.io/gitea/modules/timeutil"
4947
"code.gitea.io/gitea/modules/util"
5048
"code.gitea.io/gitea/modules/web"
49+
"code.gitea.io/gitea/routers/common"
5150
"code.gitea.io/gitea/routers/utils"
5251
shared_user "code.gitea.io/gitea/routers/web/shared/user"
5352
asymkey_service "code.gitea.io/gitea/services/asymkey"
@@ -2329,7 +2328,6 @@ func UpdateIssueContent(ctx *context.Context) {
23292328

23302329
// UpdateIssueDeadline updates an issue deadline
23312330
func UpdateIssueDeadline(ctx *context.Context) {
2332-
form := web.GetForm(ctx).(*api.EditDeadlineOption)
23332331
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
23342332
if err != nil {
23352333
if issues_model.IsErrIssueNotExist(err) {
@@ -2345,20 +2343,13 @@ func UpdateIssueDeadline(ctx *context.Context) {
23452343
return
23462344
}
23472345

2348-
var deadlineUnix timeutil.TimeStamp
2349-
var deadline time.Time
2350-
if form.Deadline != nil && !form.Deadline.IsZero() {
2351-
deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
2352-
23, 59, 59, 0, time.Local)
2353-
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
2354-
}
2355-
2346+
deadlineUnix, _ := common.ParseDeadlineDateToEndOfDay(ctx.FormString("deadline"))
23562347
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
23572348
ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
23582349
return
23592350
}
23602351

2361-
ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
2352+
ctx.JSONRedirect("")
23622353
}
23632354

23642355
// UpdateIssueMilestone change issue's milestone

routers/web/repo/milestone.go

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"net/http"
99
"net/url"
10-
"time"
1110

1211
"code.gitea.io/gitea/models/db"
1312
issues_model "code.gitea.io/gitea/models/issues"
@@ -18,6 +17,7 @@ import (
1817
"code.gitea.io/gitea/modules/setting"
1918
"code.gitea.io/gitea/modules/timeutil"
2019
"code.gitea.io/gitea/modules/web"
20+
"code.gitea.io/gitea/routers/common"
2121
"code.gitea.io/gitea/services/context"
2222
"code.gitea.io/gitea/services/forms"
2323
"code.gitea.io/gitea/services/issue"
@@ -134,24 +134,18 @@ func NewMilestonePost(ctx *context.Context) {
134134
return
135135
}
136136

137-
var deadlineUnix int64
138-
if len(form.Deadline) > 0 {
139-
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
140-
if err != nil {
141-
ctx.Data["Err_Deadline"] = true
142-
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
143-
return
144-
}
145-
146-
deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
147-
deadlineUnix = deadline.Unix()
137+
deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
138+
if err != nil {
139+
ctx.Data["Err_Deadline"] = true
140+
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
141+
return
148142
}
149143

150144
if err := issues_model.NewMilestone(ctx, &issues_model.Milestone{
151145
RepoID: ctx.Repo.Repository.ID,
152146
Name: form.Title,
153147
Content: form.Content,
154-
DeadlineUnix: timeutil.TimeStamp(deadlineUnix),
148+
DeadlineUnix: deadlineUnix,
155149
}); err != nil {
156150
ctx.ServerError("NewMilestone", err)
157151
return
@@ -196,17 +190,11 @@ func EditMilestonePost(ctx *context.Context) {
196190
return
197191
}
198192

199-
var deadlineUnix int64
200-
if len(form.Deadline) > 0 {
201-
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
202-
if err != nil {
203-
ctx.Data["Err_Deadline"] = true
204-
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
205-
return
206-
}
207-
208-
deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
209-
deadlineUnix = deadline.Unix()
193+
deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
194+
if err != nil {
195+
ctx.Data["Err_Deadline"] = true
196+
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
197+
return
210198
}
211199

212200
m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":id"))

routers/web/web.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ func registerRoutes(m *web.Router) {
12081208
m.Group("/{index}", func() {
12091209
m.Post("/title", repo.UpdateIssueTitle)
12101210
m.Post("/content", repo.UpdateIssueContent)
1211-
m.Post("/deadline", web.Bind(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline)
1211+
m.Post("/deadline", repo.UpdateIssueDeadline)
12121212
m.Post("/watch", repo.IssueWatch)
12131213
m.Post("/ref", repo.UpdateIssueRef)
12141214
m.Post("/pin", reqRepoAdmin, repo.IssuePinOrUnpin)

templates/repo/issue/milestone_new.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
<div class="field {{if .Err_Deadline}}error{{end}}">
3131
<label>
3232
{{ctx.Locale.Tr "repo.milestones.due_date"}}
33-
<a id="clear-date">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
33+
<a id="milestone-clear-deadline">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
3434
</label>
35-
<input type="date" id="deadline" name="deadline" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
35+
<input type="date" name="deadline" class="tw-w-auto" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
3636
</div>
3737
<div class="field">
3838
<label>{{ctx.Locale.Tr "repo.milestones.desc"}}</label>

templates/repo/issue/view_content/sidebar.tmpl

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -358,44 +358,31 @@
358358

359359
<div class="divider"></div>
360360
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.due_date"}}</strong></span>
361-
<div class="ui form" id="deadline-loader">
362-
<div class="ui negative message tw-hidden" id="deadline-err-invalid-date">
363-
{{svg "octicon-x" 16 "close icon"}}
364-
{{ctx.Locale.Tr "repo.issues.due_date_invalid"}}
365-
</div>
366-
{{if ne .Issue.DeadlineUnix 0}}
367-
<p>
368-
<div class="tw-flex tw-justify-between tw-items-center">
369-
<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
370-
{{svg "octicon-calendar" 16 "tw-mr-2"}}
371-
{{DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
372-
</div>
373-
<div>
374-
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
375-
<a class="issue-due-edit muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil" 16 "tw-mr-1"}}</a>
376-
<a class="issue-due-remove muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_remove"}}">{{svg "octicon-trash"}}</a>
377-
{{end}}
378-
</div>
361+
<div class="ui form tw-mt-2">
362+
{{if .Issue.DeadlineUnix}}
363+
<div class="tw-flex tw-justify-between tw-items-center tw-gap-2">
364+
<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
365+
{{svg "octicon-calendar"}} {{DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
366+
</div>
367+
<div class="flex-text-block">
368+
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
369+
<a class="issue-due-edit muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil"}}</a>
370+
<a class="issue-due-remove muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_remove"}}">{{svg "octicon-trash"}}</a>
371+
{{end}}
379372
</div>
380-
</p>
373+
</div>
381374
{{else}}
382-
<p>{{ctx.Locale.Tr "repo.issues.due_date_not_set"}}</p>
375+
{{ctx.Locale.Tr "repo.issues.due_date_not_set"}}
383376
{{end}}
384377

385378
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
386-
<div {{if ne .Issue.DeadlineUnix 0}} class="tw-hidden"{{end}} id="deadlineForm">
387-
<form class="ui fluid action input issue-due-form" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline" method="post" id="update-issue-deadline-form">
388-
{{$.CsrfTokenHtml}}
389-
<input required placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.FormatDate}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
390-
<button class="ui icon button">
391-
{{if ne .Issue.DeadlineUnix 0}}
392-
{{svg "octicon-pencil"}}
393-
{{else}}
394-
{{svg "octicon-plus"}}
395-
{{end}}
396-
</button>
397-
</form>
398-
</div>
379+
<form class="ui fluid action input issue-due-form form-fetch-action tw-mt-2 {{if .Issue.DeadlineUnix}}tw-hidden{{end}}"
380+
method="post" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline"
381+
>
382+
{{$.CsrfTokenHtml}}
383+
<input required type="date" name="deadline" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}" {{if .Issue.DeadlineUnix}}value="{{.Issue.DeadlineUnix.FormatDate}}"{{end}}>
384+
<button class="ui icon button">{{Iif .Issue.DeadlineUnix (svg "octicon-pencil") (svg "octicon-plus")}}</button>
385+
</form>
399386
{{end}}
400387
</div>
401388

tests/integration/api_issue_milestone_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"net/http"
99
"testing"
10-
"time"
1110

1211
auth_model "code.gitea.io/gitea/models/auth"
1312
issues_model "code.gitea.io/gitea/models/issues"
@@ -60,15 +59,15 @@ func TestAPIIssuesMilestone(t *testing.T) {
6059
DecodeJSON(t, resp, &apiMilestone)
6160
assert.Equal(t, "wow", apiMilestone.Title)
6261
assert.Equal(t, structs.StateClosed, apiMilestone.State)
63-
assert.Equal(t, (*time.Time)(nil), apiMilestone.Deadline)
62+
assert.Nil(t, apiMilestone.Deadline)
6463

6564
var apiMilestones []structs.Milestone
6665
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s", owner.Name, repo.Name, "all")).
6766
AddTokenAuth(token)
6867
resp = MakeRequest(t, req, http.StatusOK)
6968
DecodeJSON(t, resp, &apiMilestones)
7069
assert.Len(t, apiMilestones, 4)
71-
assert.Equal(t, (*time.Time)(nil), apiMilestones[0].Deadline)
70+
assert.Nil(t, apiMilestones[0].Deadline)
7271

7372
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%s", owner.Name, repo.Name, apiMilestones[2].Title)).
7473
AddTokenAuth(token)

web_src/js/features/repo-issue-sidebar.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {POST} from '../modules/fetch.ts';
33
import {updateIssuesMeta} from './repo-common.ts';
44
import {svg} from '../svg.ts';
55
import {htmlEscape} from 'escape-goat';
6+
import {toggleElem} from '../utils/dom.ts';
67

78
// if there are draft comments, confirm before reloading, to avoid losing comments
89
function reloadConfirmDraftComment() {
@@ -258,8 +259,22 @@ function selectItem(select_id, input_id) {
258259
});
259260
}
260261

262+
function initRepoIssueDue() {
263+
const form = document.querySelector<HTMLFormElement>('.issue-due-form');
264+
if (!form) return;
265+
const deadline = form.querySelector<HTMLInputElement>('input[name=deadline]');
266+
document.querySelector('.issue-due-edit')?.addEventListener('click', () => {
267+
toggleElem(form);
268+
});
269+
document.querySelector('.issue-due-remove')?.addEventListener('click', () => {
270+
deadline.value = '';
271+
form.dispatchEvent(new Event('submit', {cancelable: true, bubbles: true}));
272+
});
273+
}
274+
261275
export function initRepoIssueSidebar() {
262276
initBranchSelector();
277+
initRepoIssueDue();
263278

264279
// Init labels and assignees
265280
initListSubmits('select-label', 'labels');

0 commit comments

Comments
 (0)