* 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 stylesheetstags/v1.6.0-dev
@@ -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 |
@@ -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; |
@@ -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> |