Issue due date api (#3890)
* Implemented basic api endpoint to manage deadlines * Fixed checking for permissions * Updating a deadline from the ui is now entirely done via the api * cleanup * Cosmetics * fixed lint + fmt * Added swagger model definition for deadline response * Updated gitea-sdk * Updated gitea-sdk * More cleanup * Generate swagger json * Merge branch 'master' of https://github.com/go-gitea/gitea into issue-due-date-api # Conflicts: # public/swagger.v1.json * Fixed permission to update a deadline via api * Re-added form to change a deadline * Added client-side validation + not ignore error messages from the api * Added locale for error message * Merge branch 'master' of https://github.com/go-gitea/gitea # Conflicts: # models/issue_comment.go * Proper date validation * Fixed indention * moved css to css file * added documentation for error codes * after merge cleanup * Added swagger description * DO NOTHING BUT TRIGGER THAT F*CKIN CI SO IT PICKS UP THE LATEST COMMIT AS IT SHOULD * DO NOTHING BUT TRIGGER THAT F*CKIN CI SO IT PICKS UP THE LATEST COMMIT AS IT SHOULD * regenerated stylesheets
This commit is contained in:
parent
55d9ddf24a
commit
ef6813abc9
12 changed files with 258 additions and 90 deletions
|
@ -781,6 +781,7 @@ issues.due_date_added = "added the due date %s %s"
|
|||
issues.due_date_modified = "modified the due date to %s from %s %s"
|
||||
issues.due_date_remove = "removed the due date %s %s"
|
||||
issues.due_date_overdue = "Overdue"
|
||||
issues.due_date_invalid = "The due date is invalid or out of range. Please use the format yyyy-mm-dd."
|
||||
|
||||
pulls.desc = Enable merge requests and code reviews.
|
||||
pulls.new = New Pull Request
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2447,14 +2447,44 @@ function initTopicbar() {
|
|||
}
|
||||
});
|
||||
}
|
||||
function toggleDuedateForm() {
|
||||
$('#add_deadline_form').fadeToggle(150);
|
||||
function toggleDeadlineForm() {
|
||||
$('#deadlineForm').fadeToggle(150);
|
||||
}
|
||||
|
||||
function deleteDueDate(url) {
|
||||
$.post(url, {
|
||||
'_csrf': csrf,
|
||||
},function( data ) {
|
||||
window.location.reload();
|
||||
function setDeadline() {
|
||||
var deadline = $('#deadlineDate').val();
|
||||
updateDeadline(deadline);
|
||||
}
|
||||
|
||||
function updateDeadline(deadlineString) {
|
||||
$('#deadline-err-invalid-date').hide();
|
||||
$('#deadline-loader').addClass('loading');
|
||||
|
||||
var realDeadline = null;
|
||||
if (deadlineString !== '') {
|
||||
|
||||
var newDate = Date.parse(deadlineString)
|
||||
|
||||
if (isNaN(newDate)) {
|
||||
$('#deadline-loader').removeClass('loading');
|
||||
$('#deadline-err-invalid-date').show();
|
||||
return false;
|
||||
}
|
||||
realDeadline = new Date(newDate);
|
||||
}
|
||||
|
||||
$.ajax($('#update-issue-deadline-form').attr('action') + '/deadline', {
|
||||
data: JSON.stringify({
|
||||
'due_date': realDeadline,
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
success: function () {
|
||||
window.location.reload();
|
||||
},
|
||||
error: function () {
|
||||
$('#deadline-loader').removeClass('loading');
|
||||
$('#deadline-err-invalid-date').show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -98,6 +98,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#deadlineForm input{
|
||||
width: 12.8rem;
|
||||
border-radius: 4px 0 0 4px;
|
||||
border-right: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.header-wrapper {
|
||||
background-color: #FAFAFA;
|
||||
|
|
95
public/swagger.v1.json
vendored
95
public/swagger.v1.json
vendored
|
@ -2320,6 +2320,68 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/issues/{index}/deadline": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"issue"
|
||||
],
|
||||
"summary": "Set an issue deadline. If set to null, the deadline is deleted.",
|
||||
"operationId": "issueEditIssueDeadline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "owner of the repo",
|
||||
"name": "owner",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of the repo",
|
||||
"name": "repo",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "index of the issue to create or update a deadline on",
|
||||
"name": "index",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/EditDeadlineOption"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"$ref": "#/responses/IssueDeadline"
|
||||
},
|
||||
"403": {
|
||||
"description": "Not repo writer",
|
||||
"schema": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Issue not found",
|
||||
"schema": {
|
||||
"$ref": "#/responses/empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/issues/{index}/labels": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -6145,6 +6207,21 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
|
||||
},
|
||||
"EditDeadlineOption": {
|
||||
"description": "EditDeadlineOption options for creating a deadline",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"due_date"
|
||||
],
|
||||
"properties": {
|
||||
"due_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "Deadline"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
|
||||
},
|
||||
"EditHookOption": {
|
||||
"description": "EditHookOption options when modify one hook",
|
||||
"type": "object",
|
||||
|
@ -6635,6 +6712,18 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
|
||||
},
|
||||
"IssueDeadline": {
|
||||
"description": "IssueDeadline represents an issue deadline",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"due_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "Deadline"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
|
||||
},
|
||||
"IssueLabelsOption": {
|
||||
"description": "IssueLabelsOption a collection of labels",
|
||||
"type": "object",
|
||||
|
@ -7635,6 +7724,12 @@
|
|||
"$ref": "#/definitions/Issue"
|
||||
}
|
||||
},
|
||||
"IssueDeadline": {
|
||||
"description": "IssueDeadline",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/IssueDeadline"
|
||||
}
|
||||
},
|
||||
"IssueList": {
|
||||
"description": "IssueList",
|
||||
"schema": {
|
||||
|
|
|
@ -447,6 +447,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Combo("").Get(repo.ListTrackedTimes).
|
||||
Post(reqToken(), bind(api.AddTimeOption{}), repo.AddTime)
|
||||
})
|
||||
|
||||
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
|
||||
})
|
||||
}, mustEnableIssues)
|
||||
m.Group("/labels", func() {
|
||||
|
|
|
@ -278,7 +278,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||
|
||||
// Update the deadline
|
||||
var deadlineUnix util.TimeStamp
|
||||
if form.Deadline != nil && !form.Deadline.IsZero() {
|
||||
if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.IsWriter() {
|
||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
||||
}
|
||||
|
||||
|
@ -338,3 +338,72 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||
}
|
||||
ctx.JSON(201, issue.APIFormat())
|
||||
}
|
||||
|
||||
// UpdateIssueDeadline updates an issue deadline
|
||||
func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline
|
||||
// ---
|
||||
// summary: Set an issue deadline. If set to null, the deadline is deleted.
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue to create or update a deadline on
|
||||
// type: integer
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EditDeadlineOption"
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/IssueDeadline"
|
||||
// "403":
|
||||
// description: Not repo writer
|
||||
// schema:
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// description: Issue not found
|
||||
// schema:
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if models.IsErrIssueNotExist(err) {
|
||||
ctx.Status(404)
|
||||
} else {
|
||||
ctx.Error(500, "GetIssueByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.IsWriter() {
|
||||
ctx.Status(403)
|
||||
return
|
||||
}
|
||||
|
||||
var deadlineUnix util.TimeStamp
|
||||
if form.Deadline != nil && !form.Deadline.IsZero() {
|
||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
||||
}
|
||||
|
||||
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
|
||||
ctx.Error(500, "UpdateIssueDeadline", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(201, api.IssueDeadline{Deadline: form.Deadline})
|
||||
}
|
||||
|
|
|
@ -77,3 +77,10 @@ type swaggerResponseTrackedTimeList struct {
|
|||
// in:body
|
||||
Body []api.TrackedTime `json:"body"`
|
||||
}
|
||||
|
||||
// IssueDeadline
|
||||
// swagger:response IssueDeadline
|
||||
type swaggerIssueDeadline struct {
|
||||
// in:body
|
||||
Body api.IssueDeadline `json:"body"`
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ type swaggerParameterBodies struct {
|
|||
CreateIssueOption api.CreateIssueOption
|
||||
// in:body
|
||||
EditIssueOption api.EditIssueOption
|
||||
// in:body
|
||||
EditDeadlineOption api.EditDeadlineOption
|
||||
|
||||
// in:body
|
||||
CreateIssueCommentOption api.CreateIssueCommentOption
|
||||
|
|
|
@ -1490,51 +1490,3 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
|
|||
"html": html,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateDeadline adds or updates a deadline
|
||||
func UpdateDeadline(ctx *context.Context, form auth.DeadlineForm) {
|
||||
issue := GetActionIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.ServerError("ChangeIssueDeadline", errors.New(ctx.GetErrMsg()))
|
||||
return
|
||||
}
|
||||
|
||||
// Make unix of deadline string
|
||||
deadline, err := time.ParseInLocation("2006-01-02", form.DateString, time.Local)
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.invalid_due_date_format"))
|
||||
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
|
||||
return
|
||||
}
|
||||
|
||||
if err = models.UpdateIssueDeadline(issue, util.TimeStamp(deadline.Unix()), ctx.User); err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.error_modifying_due_date"))
|
||||
}
|
||||
|
||||
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveDeadline removes a deadline
|
||||
func RemoveDeadline(ctx *context.Context) {
|
||||
issue := GetActionIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.ServerError("RemoveIssueDeadline", errors.New(ctx.GetErrMsg()))
|
||||
return
|
||||
}
|
||||
|
||||
if err := models.UpdateIssueDeadline(issue, 0, ctx.User); err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.error_removing_due_date"))
|
||||
}
|
||||
|
||||
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -532,8 +532,6 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
})
|
||||
})
|
||||
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
|
||||
m.Post("/deadline/update", reqRepoWriter, bindIgnErr(auth.DeadlineForm{}), repo.UpdateDeadline)
|
||||
m.Post("/deadline/delete", reqRepoWriter, repo.RemoveDeadline)
|
||||
})
|
||||
|
||||
m.Post("/labels", reqRepoWriter, repo.UpdateIssueLabel)
|
||||
|
|
|
@ -211,38 +211,43 @@
|
|||
|
||||
<div class="ui divider"></div>
|
||||
<span class="text"><strong>{{.i18n.Tr "repo.issues.due_date"}}</strong></span>
|
||||
{{if ne .Issue.DeadlineUnix 0}}
|
||||
<p>
|
||||
<span class="octicon octicon-calendar"></span>
|
||||
{{.Issue.DeadlineUnix.FormatShort}}
|
||||
{{if .Issue.IsOverdue}}
|
||||
<span style="color: red;">{{.i18n.Tr "repo.issues.due_date_overdue"}}</span>
|
||||
{{end}}
|
||||
{{if and .IsSigned .IsRepositoryWriter}}
|
||||
<br/>
|
||||
<a style="cursor:pointer;" onclick="toggleDuedateForm();"><i class="edit icon"></i>Edit</a> -
|
||||
<a style="cursor:pointer;" onclick="deleteDueDate('{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/delete');"><i class="remove icon"></i>Remove</a>
|
||||
{{end}}
|
||||
</p>
|
||||
{{else}}
|
||||
<p><i>{{.i18n.Tr "repo.issues.due_date_not_set"}}</i></p>
|
||||
{{end}}
|
||||
|
||||
{{if and .IsSigned .IsRepositoryWriter}}
|
||||
<form method="POST" {{if ne .Issue.DeadlineUnix 0}}style="display: none;"{{end}} id="add_deadline_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/update" class="ui action input fluid">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<div class="ui fluid action input">
|
||||
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if ne .Issue.DeadlineUnix 0 }}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="date" style="min-width: 13.9rem;border-radius: 4px 0 0 4px;border-right: 0;white-space: nowrap;">
|
||||
<button class="ui green icon button">
|
||||
{{if ne .Issue.DeadlineUnix 0}}
|
||||
<i class="edit icon"></i>
|
||||
{{else}}
|
||||
<i class="plus icon"></i>
|
||||
<div class="ui form" id="deadline-loader">
|
||||
<div class="ui negative message" id="deadline-err-invalid-date" style="display: none;">
|
||||
<i class="close icon"></i>
|
||||
{{.i18n.Tr "repo.issues.due_date_invalid"}}
|
||||
</div>
|
||||
{{if ne .Issue.DeadlineUnix 0}}
|
||||
<p>
|
||||
<span class="octicon octicon-calendar"></span>
|
||||
{{.Issue.DeadlineUnix.FormatShort}}
|
||||
{{if .Issue.IsOverdue}}
|
||||
<span style="color: red;">{{.i18n.Tr "repo.issues.due_date_overdue"}}</span>
|
||||
{{end}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if and .IsSigned .IsRepositoryWriter}}
|
||||
<br/>
|
||||
<a style="cursor:pointer;" onclick="toggleDeadlineForm();"><i class="edit icon"></i>Edit</a> -
|
||||
<a style="cursor:pointer;" onclick="updateDeadline('');"><i class="remove icon"></i>Remove</a>
|
||||
{{end}}
|
||||
</p>
|
||||
{{else}}
|
||||
<p><i>{{.i18n.Tr "repo.issues.due_date_not_set"}}</i></p>
|
||||
{{end}}
|
||||
|
||||
{{if and .IsSigned .IsRepositoryWriter}}
|
||||
<div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm">
|
||||
<form class="ui fluid action input" action="{{AppSubUrl}}/api/v1/repos/{{.Repository.Owner.Name}}/{{.Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form" onsubmit="setDeadline();return false;">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
|
||||
<button class="ui green icon button">
|
||||
{{if ne .Issue.DeadlineUnix 0}}
|
||||
<i class="edit icon"></i>
|
||||
{{else}}
|
||||
<i class="plus icon"></i>
|
||||
{{end}}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue