- // Copyright 2017 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package repo
-
- import (
- "fmt"
- "net/http"
- "time"
-
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/convert"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- )
-
- // ListTrackedTimes list all the tracked times of an issue
- func ListTrackedTimes(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/times issue issueTrackedTimes
- // ---
- // summary: List an issue's tracked times
- // 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
- // type: integer
- // format: int64
- // required: true
- // - name: user
- // in: query
- // description: optional filter by user (available for issue managers)
- // type: string
- // - name: since
- // in: query
- // description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // - name: before
- // in: query
- // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // - name: page
- // in: query
- // description: page number of results to return (1-based)
- // type: integer
- // - name: limit
- // in: query
- // description: page size of results
- // type: integer
- // responses:
- // "200":
- // "$ref": "#/responses/TrackedTimeList"
- // "404":
- // "$ref": "#/responses/notFound"
-
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.NotFound("Timetracker is disabled")
- return
- }
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
- if err != nil {
- if models.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
- }
- return
- }
-
- opts := &models.FindTrackedTimesOptions{
- ListOptions: utils.GetListOptions(ctx),
- RepositoryID: ctx.Repo.Repository.ID,
- IssueID: issue.ID,
- }
-
- qUser := ctx.FormTrim("user")
- if qUser != "" {
- user, err := models.GetUserByName(qUser)
- if models.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "User does not exist", err)
- } else if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
- return
- }
- opts.UserID = user.ID
- }
-
- if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
- return
- }
-
- cantSetUser := !ctx.User.IsAdmin &&
- opts.UserID != ctx.User.ID &&
- !ctx.IsUserRepoWriter([]models.UnitType{models.UnitTypeIssues})
-
- if cantSetUser {
- if opts.UserID == 0 {
- opts.UserID = ctx.User.ID
- } else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
- return
- }
- }
-
- count, err := models.CountTrackedTimes(opts)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
-
- trackedTimes, err := models.GetTrackedTimes(opts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
- return
- }
- if err = trackedTimes.LoadAttributes(); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
- return
- }
-
- ctx.SetTotalCountHeader(count)
- ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
- }
-
- // AddTime add time manual to the given issue
- func AddTime(ctx *context.APIContext) {
- // swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime
- // ---
- // summary: Add tracked time to a issue
- // 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
- // type: integer
- // format: int64
- // required: true
- // - name: body
- // in: body
- // schema:
- // "$ref": "#/definitions/AddTimeOption"
- // responses:
- // "200":
- // "$ref": "#/responses/TrackedTime"
- // "400":
- // "$ref": "#/responses/error"
- // "403":
- // "$ref": "#/responses/forbidden"
- form := web.GetForm(ctx).(*api.AddTimeOption)
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
- if err != nil {
- if models.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
- }
- return
- }
-
- if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
- return
- }
- ctx.Status(http.StatusForbidden)
- return
- }
-
- user := ctx.User
- if form.User != "" {
- if (ctx.IsUserRepoAdmin() && ctx.User.Name != form.User) || ctx.User.IsAdmin {
- //allow only RepoAdmin, Admin and User to add time
- user, err = models.GetUserByName(form.User)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
- }
- }
- }
-
- created := time.Time{}
- if !form.Created.IsZero() {
- created = form.Created
- }
-
- trackedTime, err := models.AddTime(user, issue, form.Time, created)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "AddTime", err)
- return
- }
- if err = trackedTime.LoadAttributes(); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
- return
- }
- ctx.JSON(http.StatusOK, convert.ToTrackedTime(trackedTime))
- }
-
- // ResetIssueTime reset time manual to the given issue
- func ResetIssueTime(ctx *context.APIContext) {
- // swagger:operation Delete /repos/{owner}/{repo}/issues/{index}/times issue issueResetTime
- // ---
- // summary: Reset a tracked time of an issue
- // 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 add tracked time to
- // type: integer
- // format: int64
- // required: true
- // responses:
- // "204":
- // "$ref": "#/responses/empty"
- // "400":
- // "$ref": "#/responses/error"
- // "403":
- // "$ref": "#/responses/forbidden"
-
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
- if err != nil {
- if models.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
- }
- return
- }
-
- if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
- return
- }
- ctx.Status(http.StatusForbidden)
- return
- }
-
- err = models.DeleteIssueUserTimes(issue, ctx.User)
- if err != nil {
- if models.IsErrNotExist(err) {
- ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
- } else {
- ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
- }
- return
- }
- ctx.Status(204)
- }
-
- // DeleteTime delete a specific time by id
- func DeleteTime(ctx *context.APIContext) {
- // swagger:operation Delete /repos/{owner}/{repo}/issues/{index}/times/{id} issue issueDeleteTime
- // ---
- // summary: Delete specific tracked time
- // 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
- // type: integer
- // format: int64
- // required: true
- // - name: id
- // in: path
- // description: id of time to delete
- // type: integer
- // format: int64
- // required: true
- // responses:
- // "204":
- // "$ref": "#/responses/empty"
- // "400":
- // "$ref": "#/responses/error"
- // "403":
- // "$ref": "#/responses/forbidden"
-
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
- if err != nil {
- if models.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
- }
- return
- }
-
- if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
- return
- }
- ctx.Status(http.StatusForbidden)
- return
- }
-
- time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
- if err != nil {
- if models.IsErrNotExist(err) {
- ctx.NotFound(err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err)
- return
- }
- if time.Deleted {
- ctx.NotFound(fmt.Errorf("tracked time [%d] already deleted", time.ID))
- return
- }
-
- if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
- //Only Admin and User itself can delete their time
- ctx.Status(http.StatusForbidden)
- return
- }
-
- err = models.DeleteTime(time)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteTime", err)
- return
- }
- ctx.Status(http.StatusNoContent)
- }
-
- // ListTrackedTimesByUser lists all tracked times of the user
- func ListTrackedTimesByUser(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/times/{user} repository userTrackedTimes
- // ---
- // summary: List a user's tracked times in a repo
- // deprecated: true
- // 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: user
- // in: path
- // description: username of user
- // type: string
- // required: true
- // responses:
- // "200":
- // "$ref": "#/responses/TrackedTimeList"
- // "400":
- // "$ref": "#/responses/error"
- // "403":
- // "$ref": "#/responses/forbidden"
-
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
- return
- }
- user, err := models.GetUserByName(ctx.Params(":timetrackingusername"))
- if err != nil {
- if models.IsErrUserNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
- }
- return
- }
- if user == nil {
- ctx.NotFound()
- return
- }
-
- if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
- return
- }
-
- opts := &models.FindTrackedTimesOptions{
- UserID: user.ID,
- RepositoryID: ctx.Repo.Repository.ID,
- }
-
- trackedTimes, err := models.GetTrackedTimes(opts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
- return
- }
- if err = trackedTimes.LoadAttributes(); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
- return
- }
- ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
- }
-
- // ListTrackedTimesByRepository lists all tracked times of the repository
- func ListTrackedTimesByRepository(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/times repository repoTrackedTimes
- // ---
- // summary: List a repo's tracked times
- // 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: user
- // in: query
- // description: optional filter by user (available for issue managers)
- // type: string
- // - name: since
- // in: query
- // description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // - name: before
- // in: query
- // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // - name: page
- // in: query
- // description: page number of results to return (1-based)
- // type: integer
- // - name: limit
- // in: query
- // description: page size of results
- // type: integer
- // responses:
- // "200":
- // "$ref": "#/responses/TrackedTimeList"
- // "400":
- // "$ref": "#/responses/error"
- // "403":
- // "$ref": "#/responses/forbidden"
-
- if !ctx.Repo.Repository.IsTimetrackerEnabled() {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
- return
- }
-
- opts := &models.FindTrackedTimesOptions{
- ListOptions: utils.GetListOptions(ctx),
- RepositoryID: ctx.Repo.Repository.ID,
- }
-
- // Filters
- qUser := ctx.FormTrim("user")
- if qUser != "" {
- user, err := models.GetUserByName(qUser)
- if models.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "User does not exist", err)
- } else if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
- return
- }
- opts.UserID = user.ID
- }
-
- var err error
- if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
- return
- }
-
- cantSetUser := !ctx.User.IsAdmin &&
- opts.UserID != ctx.User.ID &&
- !ctx.IsUserRepoWriter([]models.UnitType{models.UnitTypeIssues})
-
- if cantSetUser {
- if opts.UserID == 0 {
- opts.UserID = ctx.User.ID
- } else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
- return
- }
- }
-
- count, err := models.CountTrackedTimes(opts)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
-
- trackedTimes, err := models.GetTrackedTimes(opts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
- return
- }
- if err = trackedTimes.LoadAttributes(); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
- return
- }
-
- ctx.SetTotalCountHeader(count)
- ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
- }
-
- // ListMyTrackedTimes lists all tracked times of the current user
- func ListMyTrackedTimes(ctx *context.APIContext) {
- // swagger:operation GET /user/times user userCurrentTrackedTimes
- // ---
- // summary: List the current user's tracked times
- // parameters:
- // - name: page
- // in: query
- // description: page number of results to return (1-based)
- // type: integer
- // - name: limit
- // in: query
- // description: page size of results
- // type: integer
- // produces:
- // - application/json
- // parameters:
- // - name: since
- // in: query
- // description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // - name: before
- // in: query
- // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
- // type: string
- // format: date-time
- // responses:
- // "200":
- // "$ref": "#/responses/TrackedTimeList"
-
- opts := &models.FindTrackedTimesOptions{
- ListOptions: utils.GetListOptions(ctx),
- UserID: ctx.User.ID,
- }
-
- var err error
- if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
- return
- }
-
- count, err := models.CountTrackedTimes(opts)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
-
- trackedTimes, err := models.GetTrackedTimes(opts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
- return
- }
-
- if err = trackedTimes.LoadAttributes(); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
- return
- }
-
- ctx.SetTotalCountHeader(count)
- ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
- }
|