* 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> | |||