mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	times Add filters (#9373)
(extend #9200) * add query param for GET functions (created Bevore & after) * add test * generalize func GetQueryBeforeSince Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		@@ -44,6 +44,18 @@ func TestAPIGetTrackedTimes(t *testing.T) {
 | 
				
			|||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, user.Name, apiTimes[i].UserName)
 | 
							assert.Equal(t, user.Name, apiTimes[i].UserName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// test filter
 | 
				
			||||||
 | 
						since := "2000-01-01T00%3A00%3A02%2B00%3A00"  //946684802
 | 
				
			||||||
 | 
						before := "2000-01-01T00%3A00%3A12%2B00%3A00" //946684812
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?since=%s&before=%s&token=%s", user2.Name, issue2.Repo.Name, issue2.Index, since, before, token)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var filterAPITimes api.TrackedTimeList
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &filterAPITimes)
 | 
				
			||||||
 | 
						assert.Len(t, filterAPITimes, 2)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(3), filterAPITimes[0].ID)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(6), filterAPITimes[1].ID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAPIDeleteTrackedTime(t *testing.T) {
 | 
					func TestAPIDeleteTrackedTime(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,10 +100,12 @@ func (tl TrackedTimeList) APIFormat() api.TrackedTimeList {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
 | 
					// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
 | 
				
			||||||
type FindTrackedTimesOptions struct {
 | 
					type FindTrackedTimesOptions struct {
 | 
				
			||||||
	IssueID      int64
 | 
						IssueID           int64
 | 
				
			||||||
	UserID       int64
 | 
						UserID            int64
 | 
				
			||||||
	RepositoryID int64
 | 
						RepositoryID      int64
 | 
				
			||||||
	MilestoneID  int64
 | 
						MilestoneID       int64
 | 
				
			||||||
 | 
						CreatedAfterUnix  int64
 | 
				
			||||||
 | 
						CreatedBeforeUnix int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToCond will convert each condition into a xorm-Cond
 | 
					// ToCond will convert each condition into a xorm-Cond
 | 
				
			||||||
@@ -121,6 +123,12 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond {
 | 
				
			|||||||
	if opts.MilestoneID != 0 {
 | 
						if opts.MilestoneID != 0 {
 | 
				
			||||||
		cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID})
 | 
							cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if opts.CreatedAfterUnix != 0 {
 | 
				
			||||||
 | 
							cond = cond.And(builder.Gte{"tracked_time.created_unix": opts.CreatedAfterUnix})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.CreatedBeforeUnix != 0 {
 | 
				
			||||||
 | 
							cond = cond.And(builder.Lte{"tracked_time.created_unix": opts.CreatedBeforeUnix})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return cond
 | 
						return cond
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -654,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
				m.Group("/times", func() {
 | 
									m.Group("/times", func() {
 | 
				
			||||||
					m.Combo("").Get(repo.ListTrackedTimesByRepository)
 | 
										m.Combo("").Get(repo.ListTrackedTimesByRepository)
 | 
				
			||||||
					m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
 | 
										m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
 | 
				
			||||||
				}, mustEnableIssues)
 | 
									}, mustEnableIssues, reqToken())
 | 
				
			||||||
				m.Group("/issues", func() {
 | 
									m.Group("/issues", func() {
 | 
				
			||||||
					m.Combo("").Get(repo.ListIssues).
 | 
										m.Combo("").Get(repo.ListIssues).
 | 
				
			||||||
						Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
 | 
											Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
 | 
				
			||||||
@@ -688,12 +688,12 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
							m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
 | 
												m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
						m.Group("/times", func() {
 | 
											m.Group("/times", func() {
 | 
				
			||||||
							m.Combo("", reqToken()).
 | 
												m.Combo("").
 | 
				
			||||||
								Get(repo.ListTrackedTimes).
 | 
													Get(repo.ListTrackedTimes).
 | 
				
			||||||
								Post(bind(api.AddTimeOption{}), repo.AddTime).
 | 
													Post(bind(api.AddTimeOption{}), repo.AddTime).
 | 
				
			||||||
								Delete(repo.ResetIssueTime)
 | 
													Delete(repo.ResetIssueTime)
 | 
				
			||||||
							m.Delete("/:id", reqToken(), repo.DeleteTime)
 | 
												m.Delete("/:id", repo.DeleteTime)
 | 
				
			||||||
						})
 | 
											}, reqToken())
 | 
				
			||||||
						m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
 | 
											m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
 | 
				
			||||||
						m.Group("/stopwatch", func() {
 | 
											m.Group("/stopwatch", func() {
 | 
				
			||||||
							m.Post("/start", reqToken(), repo.StartIssueStopwatch)
 | 
												m.Post("/start", reqToken(), repo.StartIssueStopwatch)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,15 @@
 | 
				
			|||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/routers/api/v1/utils"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListTrackedTimes list all the tracked times of an issue
 | 
					// ListTrackedTimes list all the tracked times of an issue
 | 
				
			||||||
@@ -37,6 +40,16 @@ func ListTrackedTimes(ctx *context.APIContext) {
 | 
				
			|||||||
	//   type: integer
 | 
						//   type: integer
 | 
				
			||||||
	//   format: int64
 | 
						//   format: int64
 | 
				
			||||||
	//   required: true
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - 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:
 | 
						// responses:
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/TrackedTimeList"
 | 
						//     "$ref": "#/responses/TrackedTimeList"
 | 
				
			||||||
@@ -62,6 +75,11 @@ func ListTrackedTimes(ctx *context.APIContext) {
 | 
				
			|||||||
		IssueID:      issue.ID,
 | 
							IssueID:      issue.ID,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
 | 
				
			||||||
 | 
							ctx.InternalServerError(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
 | 
						if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
 | 
				
			||||||
		opts.UserID = ctx.User.ID
 | 
							opts.UserID = ctx.User.ID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -141,7 +159,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
 | 
				
			|||||||
			//allow only RepoAdmin, Admin and User to add time
 | 
								//allow only RepoAdmin, Admin and User to add time
 | 
				
			||||||
			user, err = models.GetUserByName(form.User)
 | 
								user, err = models.GetUserByName(form.User)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				ctx.Error(500, "GetUserByName", err)
 | 
									ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -195,33 +213,33 @@ func ResetIssueTime(ctx *context.APIContext) {
 | 
				
			|||||||
	//   "400":
 | 
						//   "400":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
	//   "403":
 | 
						//   "403":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/forbidden"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
						issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrIssueNotExist(err) {
 | 
							if models.IsErrIssueNotExist(err) {
 | 
				
			||||||
			ctx.NotFound(err)
 | 
								ctx.NotFound(err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Error(500, "GetIssueByIndex", err)
 | 
								ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
 | 
						if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
 | 
				
			||||||
		if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
							if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
				
			||||||
			ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
 | 
								ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Status(403)
 | 
							ctx.Status(http.StatusForbidden)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = models.DeleteIssueUserTimes(issue, ctx.User)
 | 
						err = models.DeleteIssueUserTimes(issue, ctx.User)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrNotExist(err) {
 | 
							if models.IsErrNotExist(err) {
 | 
				
			||||||
			ctx.Error(404, "DeleteIssueUserTimes", err)
 | 
								ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Error(500, "DeleteIssueUserTimes", err)
 | 
								ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -266,52 +284,53 @@ func DeleteTime(ctx *context.APIContext) {
 | 
				
			|||||||
	//   "400":
 | 
						//   "400":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
	//   "403":
 | 
						//   "403":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/forbidden"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
						issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrIssueNotExist(err) {
 | 
							if models.IsErrIssueNotExist(err) {
 | 
				
			||||||
			ctx.NotFound(err)
 | 
								ctx.NotFound(err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Error(500, "GetIssueByIndex", err)
 | 
								ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
 | 
						if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
 | 
				
			||||||
		if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
							if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
				
			||||||
			ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
 | 
								ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Status(403)
 | 
							ctx.Status(http.StatusForbidden)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
 | 
						time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(500, "GetTrackedTimeByID", err)
 | 
							ctx.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
 | 
						if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
 | 
				
			||||||
		//Only Admin and User itself can delete their time
 | 
							//Only Admin and User itself can delete their time
 | 
				
			||||||
		ctx.Status(403)
 | 
							ctx.Status(http.StatusForbidden)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = models.DeleteTime(time)
 | 
						err = models.DeleteTime(time)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(500, "DeleteTime", err)
 | 
							ctx.Error(http.StatusInternalServerError, "DeleteTime", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Status(204)
 | 
						ctx.Status(http.StatusNoContent)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListTrackedTimesByUser  lists all tracked times of the user
 | 
					// ListTrackedTimesByUser  lists all tracked times of the user
 | 
				
			||||||
func ListTrackedTimesByUser(ctx *context.APIContext) {
 | 
					func ListTrackedTimesByUser(ctx *context.APIContext) {
 | 
				
			||||||
	// swagger:operation GET /repos/{owner}/{repo}/times/{user} user userTrackedTimes
 | 
						// swagger:operation GET /repos/{owner}/{repo}/times/{user} repository userTrackedTimes
 | 
				
			||||||
	// ---
 | 
						// ---
 | 
				
			||||||
	// summary: List a user's tracked times in a repo
 | 
						// summary: List a user's tracked times in a repo
 | 
				
			||||||
 | 
						// deprecated: true
 | 
				
			||||||
	// produces:
 | 
						// produces:
 | 
				
			||||||
	// - application/json
 | 
						// - application/json
 | 
				
			||||||
	// parameters:
 | 
						// parameters:
 | 
				
			||||||
@@ -335,6 +354,8 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 | 
				
			|||||||
	//     "$ref": "#/responses/TrackedTimeList"
 | 
						//     "$ref": "#/responses/TrackedTimeList"
 | 
				
			||||||
	//   "400":
 | 
						//   "400":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
 | 
						//   "403":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/forbidden"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
						if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
				
			||||||
		ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
 | 
							ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
 | 
				
			||||||
@@ -353,9 +374,23 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 | 
				
			|||||||
		ctx.NotFound()
 | 
							ctx.NotFound()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{
 | 
					
 | 
				
			||||||
 | 
						if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
 | 
				
			||||||
 | 
							ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
 | 
				
			||||||
 | 
							ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := models.FindTrackedTimesOptions{
 | 
				
			||||||
		UserID:       user.ID,
 | 
							UserID:       user.ID,
 | 
				
			||||||
		RepositoryID: ctx.Repo.Repository.ID})
 | 
							RepositoryID: ctx.Repo.Repository.ID,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trackedTimes, err := models.GetTrackedTimes(opts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
 | 
							ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -385,11 +420,27 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 | 
				
			|||||||
	//   description: name of the repo
 | 
						//   description: name of the repo
 | 
				
			||||||
	//   type: string
 | 
						//   type: string
 | 
				
			||||||
	//   required: true
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: user
 | 
				
			||||||
 | 
						//   in: query
 | 
				
			||||||
 | 
						//   description: optional filter by user
 | 
				
			||||||
 | 
						//   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
 | 
				
			||||||
	// responses:
 | 
						// responses:
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/TrackedTimeList"
 | 
						//     "$ref": "#/responses/TrackedTimeList"
 | 
				
			||||||
	//   "400":
 | 
						//   "400":
 | 
				
			||||||
	//     "$ref": "#/responses/error"
 | 
						//     "$ref": "#/responses/error"
 | 
				
			||||||
 | 
						//   "403":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/forbidden"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
						if !ctx.Repo.Repository.IsTimetrackerEnabled() {
 | 
				
			||||||
		ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
 | 
							ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
 | 
				
			||||||
@@ -400,8 +451,30 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 | 
				
			|||||||
		RepositoryID: ctx.Repo.Repository.ID,
 | 
							RepositoryID: ctx.Repo.Repository.ID,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Filters
 | 
				
			||||||
 | 
						qUser := strings.Trim(ctx.Query("user"), " ")
 | 
				
			||||||
 | 
						if qUser != "" {
 | 
				
			||||||
 | 
							user, err := models.GetUserByName(qUser)
 | 
				
			||||||
 | 
							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.InternalServerError(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
 | 
						if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
 | 
				
			||||||
		opts.UserID = ctx.User.ID
 | 
							if opts.UserID == 0 {
 | 
				
			||||||
 | 
								opts.UserID = ctx.User.ID
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trackedTimes, err := models.GetTrackedTimes(opts)
 | 
						trackedTimes, err := models.GetTrackedTimes(opts)
 | 
				
			||||||
@@ -423,18 +496,39 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
 | 
				
			|||||||
	// summary: List the current user's tracked times
 | 
						// summary: List the current user's tracked times
 | 
				
			||||||
	// produces:
 | 
						// produces:
 | 
				
			||||||
	// - application/json
 | 
						// - 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:
 | 
						// responses:
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/TrackedTimeList"
 | 
						//     "$ref": "#/responses/TrackedTimeList"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{UserID: ctx.User.ID})
 | 
						opts := models.FindTrackedTimesOptions{UserID: ctx.User.ID}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
 | 
				
			||||||
 | 
							ctx.InternalServerError(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trackedTimes, err := models.GetTrackedTimes(opts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
 | 
							ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = trackedTimes.LoadAttributes(); err != nil {
 | 
						if err = trackedTimes.LoadAttributes(); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 | 
							ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
 | 
						ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package utils
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "code.gitea.io/gitea/modules/context"
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UserID user ID of authenticated user, or 0 if not authenticated
 | 
					// UserID user ID of authenticated user, or 0 if not authenticated
 | 
				
			||||||
func UserID(ctx *context.APIContext) int64 {
 | 
					func UserID(ctx *context.APIContext) int64 {
 | 
				
			||||||
@@ -13,3 +18,29 @@ func UserID(ctx *context.APIContext) int64 {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return ctx.User.ID
 | 
						return ctx.User.ID
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
 | 
				
			||||||
 | 
					func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err error) {
 | 
				
			||||||
 | 
						qCreatedBefore := strings.Trim(ctx.Query("before"), " ")
 | 
				
			||||||
 | 
						if qCreatedBefore != "" {
 | 
				
			||||||
 | 
							createdBefore, err := time.Parse(time.RFC3339, qCreatedBefore)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, 0, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !createdBefore.IsZero() {
 | 
				
			||||||
 | 
								before = createdBefore.Unix()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCreatedAfter := strings.Trim(ctx.Query("since"), " ")
 | 
				
			||||||
 | 
						if qCreatedAfter != "" {
 | 
				
			||||||
 | 
							createdAfter, err := time.Parse(time.RFC3339, qCreatedAfter)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, 0, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !createdAfter.IsZero() {
 | 
				
			||||||
 | 
								since = createdAfter.Unix()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return before, since, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4433,6 +4433,20 @@
 | 
				
			|||||||
            "name": "index",
 | 
					            "name": "index",
 | 
				
			||||||
            "in": "path",
 | 
					            "in": "path",
 | 
				
			||||||
            "required": true
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "since",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "before",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
@@ -4543,7 +4557,7 @@
 | 
				
			|||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "403": {
 | 
					          "403": {
 | 
				
			||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/forbidden"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -4601,7 +4615,7 @@
 | 
				
			|||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "403": {
 | 
					          "403": {
 | 
				
			||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/forbidden"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -6419,6 +6433,26 @@
 | 
				
			|||||||
            "name": "repo",
 | 
					            "name": "repo",
 | 
				
			||||||
            "in": "path",
 | 
					            "in": "path",
 | 
				
			||||||
            "required": true
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "optional filter by user",
 | 
				
			||||||
 | 
					            "name": "user",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "since",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "before",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
@@ -6427,6 +6461,9 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "400": {
 | 
					          "400": {
 | 
				
			||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "403": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/forbidden"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -6437,10 +6474,11 @@
 | 
				
			|||||||
          "application/json"
 | 
					          "application/json"
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "tags": [
 | 
					        "tags": [
 | 
				
			||||||
          "user"
 | 
					          "repository"
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "summary": "List a user's tracked times in a repo",
 | 
					        "summary": "List a user's tracked times in a repo",
 | 
				
			||||||
        "operationId": "userTrackedTimes",
 | 
					        "operationId": "userTrackedTimes",
 | 
				
			||||||
 | 
					        "deprecated": true,
 | 
				
			||||||
        "parameters": [
 | 
					        "parameters": [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "type": "string",
 | 
					            "type": "string",
 | 
				
			||||||
@@ -6470,6 +6508,9 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "400": {
 | 
					          "400": {
 | 
				
			||||||
            "$ref": "#/responses/error"
 | 
					            "$ref": "#/responses/error"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "403": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/forbidden"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -7685,6 +7726,22 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "summary": "List the current user's tracked times",
 | 
					        "summary": "List the current user's tracked times",
 | 
				
			||||||
        "operationId": "userCurrentTrackedTimes",
 | 
					        "operationId": "userCurrentTrackedTimes",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "since",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "format": "date-time",
 | 
				
			||||||
 | 
					            "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
 | 
				
			||||||
 | 
					            "name": "before",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
          "200": {
 | 
					          "200": {
 | 
				
			||||||
            "$ref": "#/responses/TrackedTimeList"
 | 
					            "$ref": "#/responses/TrackedTimeList"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user