Add UI to delete tracked times (#14100)
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
		
					parent
					
						
							
								6a696b93b1
							
						
					
				
			
			
				commit
				
					
						d38ae597e1
					
				
			
		
					 13 changed files with 123 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -136,6 +136,8 @@ type Comment struct {
 | 
			
		|||
	MilestoneID      int64
 | 
			
		||||
	OldMilestone     *Milestone `xorm:"-"`
 | 
			
		||||
	Milestone        *Milestone `xorm:"-"`
 | 
			
		||||
	TimeID           int64
 | 
			
		||||
	Time             *TrackedTime `xorm:"-"`
 | 
			
		||||
	AssigneeID       int64
 | 
			
		||||
	RemovedAssignee  bool
 | 
			
		||||
	Assignee         *User `xorm:"-"`
 | 
			
		||||
| 
						 | 
				
			
			@ -541,6 +543,16 @@ func (c *Comment) LoadDepIssueDetails() (err error) {
 | 
			
		|||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadTime loads the associated time for a CommentTypeAddTimeManual
 | 
			
		||||
func (c *Comment) LoadTime() error {
 | 
			
		||||
	if c.Time != nil || c.TimeID == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	c.Time, err = GetTrackedTimeByID(c.TimeID)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Comment) loadReactions(e Engine, repo *Repository) (err error) {
 | 
			
		||||
	if c.Reactions != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -692,6 +704,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 | 
			
		|||
		MilestoneID:      opts.MilestoneID,
 | 
			
		||||
		OldProjectID:     opts.OldProjectID,
 | 
			
		||||
		ProjectID:        opts.ProjectID,
 | 
			
		||||
		TimeID:           opts.TimeID,
 | 
			
		||||
		RemovedAssignee:  opts.RemovedAssignee,
 | 
			
		||||
		AssigneeID:       opts.AssigneeID,
 | 
			
		||||
		AssigneeTeamID:   opts.AssigneeTeamID,
 | 
			
		||||
| 
						 | 
				
			
			@ -859,6 +872,7 @@ type CreateCommentOptions struct {
 | 
			
		|||
	MilestoneID      int64
 | 
			
		||||
	OldProjectID     int64
 | 
			
		||||
	ProjectID        int64
 | 
			
		||||
	TimeID           int64
 | 
			
		||||
	AssigneeID       int64
 | 
			
		||||
	AssigneeTeamID   int64
 | 
			
		||||
	RemovedAssignee  bool
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,7 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
 | 
			
		|||
			Repo:    issue.Repo,
 | 
			
		||||
			Content: SecToTime(timediff),
 | 
			
		||||
			Type:    CommentTypeStopTracking,
 | 
			
		||||
			TimeID:  tt.ID,
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,7 @@ func AddTime(user *User, issue *Issue, amount int64, created time.Time) (*Tracke
 | 
			
		|||
		Doer:    user,
 | 
			
		||||
		Content: SecToTime(amount),
 | 
			
		||||
		Type:    CommentTypeAddTimeManual,
 | 
			
		||||
		TimeID:  t.ID,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -292,6 +292,8 @@ var migrations = []Migration{
 | 
			
		|||
	NewMigration("Add Sorting to ProjectBoard table", addSortingColToProjectBoard),
 | 
			
		||||
	// v172 -> v173
 | 
			
		||||
	NewMigration("Add sessions table for go-chi/session", addSessionTable),
 | 
			
		||||
	// v173 -> v174
 | 
			
		||||
	NewMigration("Add time_id column to Comment", addTimeIDCommentColumn),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCurrentDBVersion returns the current db version
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								models/migrations/v173.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								models/migrations/v173.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
// Copyright 2021 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 migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"xorm.io/xorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func addTimeIDCommentColumn(x *xorm.Engine) error {
 | 
			
		||||
	type Comment struct {
 | 
			
		||||
		TimeID int64
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := x.Sync2(new(Comment)); err != nil {
 | 
			
		||||
		return fmt.Errorf("Sync2: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1163,6 +1163,7 @@ issues.stop_tracking_history = `stopped working %s`
 | 
			
		|||
issues.cancel_tracking = Discard
 | 
			
		||||
issues.cancel_tracking_history = `cancelled time tracking %s`
 | 
			
		||||
issues.add_time = Manually Add Time
 | 
			
		||||
issues.del_time = Delete this time log
 | 
			
		||||
issues.add_time_short = Add Time
 | 
			
		||||
issues.add_time_cancel = Cancel
 | 
			
		||||
issues.add_time_history = `added spent time %s`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1416,6 +1416,10 @@ func ViewIssue(ctx *context.Context) {
 | 
			
		|||
				ctx.ServerError("LoadPushCommits", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else if comment.Type == models.CommentTypeAddTimeManual ||
 | 
			
		||||
			comment.Type == models.CommentTypeStopTracking {
 | 
			
		||||
			// drop error since times could be pruned from DB..
 | 
			
		||||
			_ = comment.LoadTime()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,13 +10,13 @@ import (
 | 
			
		|||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	auth "code.gitea.io/gitea/modules/forms"
 | 
			
		||||
	"code.gitea.io/gitea/modules/forms"
 | 
			
		||||
	"code.gitea.io/gitea/modules/web"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AddTimeManually tracks time manually
 | 
			
		||||
func AddTimeManually(c *context.Context) {
 | 
			
		||||
	form := web.GetForm(c).(*auth.AddTimeManuallyForm)
 | 
			
		||||
	form := web.GetForm(c).(*forms.AddTimeManuallyForm)
 | 
			
		||||
	issue := GetActionIssue(c)
 | 
			
		||||
	if c.Written() {
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -48,3 +48,39 @@ func AddTimeManually(c *context.Context) {
 | 
			
		|||
 | 
			
		||||
	c.Redirect(url, http.StatusSeeOther)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteTime deletes tracked time
 | 
			
		||||
func DeleteTime(c *context.Context) {
 | 
			
		||||
	issue := GetActionIssue(c)
 | 
			
		||||
	if c.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !c.Repo.CanUseTimetracker(issue, c.User) {
 | 
			
		||||
		c.NotFound("CanUseTimetracker", nil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, err := models.GetTrackedTimeByID(c.ParamsInt64(":timeid"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if models.IsErrNotExist(err) {
 | 
			
		||||
			c.NotFound("time not found", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// only OP or admin may delete
 | 
			
		||||
	if !c.IsSigned || (!c.IsUserSiteAdmin() && c.User.ID != t.UserID) {
 | 
			
		||||
		c.Error(http.StatusForbidden, "not allowed")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = models.DeleteTime(t); err != nil {
 | 
			
		||||
		c.ServerError("DeleteTime", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Flash.Success(c.Tr("repo.issues.del_time_history", models.SecToTime(t.Time)))
 | 
			
		||||
	c.Redirect(issue.HTMLURL())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -723,6 +723,7 @@ func RegisterRoutes(m *web.Route) {
 | 
			
		|||
				m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
 | 
			
		||||
				m.Group("/times", func() {
 | 
			
		||||
					m.Post("/add", bindIgnErr(auth.AddTimeManuallyForm{}), repo.AddTimeManually)
 | 
			
		||||
					m.Post("/{timeid}/delete", repo.DeleteTime)
 | 
			
		||||
					m.Group("/stopwatch", func() {
 | 
			
		||||
						m.Post("/toggle", repo.IssueStopwatch)
 | 
			
		||||
						m.Post("/cancel", repo.CancelStopwatch)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -276,6 +276,7 @@
 | 
			
		|||
				<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
 | 
			
		||||
				{{$.i18n.Tr "repo.issues.stop_tracking_history"  $createdStr | Safe}}
 | 
			
		||||
			</span>
 | 
			
		||||
			{{ template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" . }}
 | 
			
		||||
			<div class="detail">
 | 
			
		||||
				{{svg "octicon-clock"}}
 | 
			
		||||
				<span class="text grey">{{.Content}}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -291,6 +292,7 @@
 | 
			
		|||
				<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
 | 
			
		||||
				{{$.i18n.Tr "repo.issues.add_time_history"  $createdStr | Safe}}
 | 
			
		||||
			</span>
 | 
			
		||||
			{{ template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" . }}
 | 
			
		||||
			<div class="detail">
 | 
			
		||||
				{{svg "octicon-clock"}}
 | 
			
		||||
				<span class="text grey">{{.Content}}</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								templates/repo/issue/view_content/comments_delete_time.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								templates/repo/issue/view_content/comments_delete_time.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
{{ if .comment.Time }} {{/* compatibility with time comments made before v1.14 */}}
 | 
			
		||||
	{{ if (not .comment.Time.Deleted) }}
 | 
			
		||||
		{{ if (or .ctx.IsAdmin (and .ctx.IsSigned (eq .ctx.SignedUserID .comment.PosterID))) }}
 | 
			
		||||
			<span class="ui float right">
 | 
			
		||||
				<div class="ui mini modal issue-delete-time-modal" data-id="{{.comment.Time.ID}}">
 | 
			
		||||
					<form method="POST" class="delete-time-form" action="{{.ctx.RepoLink}}/issues/{{.ctx.Issue.Index}}/times/{{.comment.TimeID}}/delete">
 | 
			
		||||
						{{.ctx.CsrfTokenHtml}}
 | 
			
		||||
					</form>
 | 
			
		||||
					<div class="header">{{.ctx.i18n.Tr "repo.issues.del_time"}}</div>
 | 
			
		||||
					<div class="actions">
 | 
			
		||||
						<div class="ui red approve button">{{.ctx.i18n.Tr "repo.issues.context.delete"}}</div>
 | 
			
		||||
						<div class="ui cancel button">{{.ctx.i18n.Tr "repo.issues.add_time_cancel"}}</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<button class="ui icon button compact mini issue-delete-time poping up" data-id="{{.comment.Time.ID}}" data-content="{{.ctx.i18n.Tr "repo.issues.del_time"}}" data-position="top right" data-variation="tiny inverted">
 | 
			
		||||
					{{svg "octicon-trashcan"}}
 | 
			
		||||
				</button>
 | 
			
		||||
			</span>
 | 
			
		||||
		{{end}}
 | 
			
		||||
	{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
| 
						 | 
				
			
			@ -348,7 +348,7 @@
 | 
			
		|||
							{{end}}
 | 
			
		||||
							<div class="ui buttons two fluid">
 | 
			
		||||
								<button class="ui button poping up issue-start-time" data-content='{{.i18n.Tr "repo.issues.start_tracking"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.start_tracking_short"}}</button>
 | 
			
		||||
								<div class="ui mini modal">
 | 
			
		||||
								<div class="ui mini modal issue-start-time-modal">
 | 
			
		||||
									<div class="header">{{.i18n.Tr "repo.issues.add_time"}}</div>
 | 
			
		||||
									<div class="content">
 | 
			
		||||
										<form method="POST" id="add_time_manual_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/times/add" class="ui action input fluid">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3203,12 +3203,17 @@ function initVueApp() {
 | 
			
		|||
 | 
			
		||||
function initIssueTimetracking() {
 | 
			
		||||
  $(document).on('click', '.issue-add-time', () => {
 | 
			
		||||
    $('.mini.modal').modal({
 | 
			
		||||
    $('.issue-start-time-modal').modal({
 | 
			
		||||
      duration: 200,
 | 
			
		||||
      onApprove() {
 | 
			
		||||
        $('#add_time_manual_form').trigger('submit');
 | 
			
		||||
      }
 | 
			
		||||
    }).modal('show');
 | 
			
		||||
    $('.issue-start-time-modal input').on('keydown', (e) => {
 | 
			
		||||
      if ((e.keyCode || e.key) === 13) {
 | 
			
		||||
        $('#add_time_manual_form').trigger('submit');
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  $(document).on('click', '.issue-start-time, .issue-stop-time', () => {
 | 
			
		||||
    $('#toggle_stopwatch_form').trigger('submit');
 | 
			
		||||
| 
						 | 
				
			
			@ -3216,6 +3221,15 @@ function initIssueTimetracking() {
 | 
			
		|||
  $(document).on('click', '.issue-cancel-time', () => {
 | 
			
		||||
    $('#cancel_stopwatch_form').trigger('submit');
 | 
			
		||||
  });
 | 
			
		||||
  $(document).on('click', 'button.issue-delete-time', function () {
 | 
			
		||||
    const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`;
 | 
			
		||||
    $(sel).modal({
 | 
			
		||||
      duration: 200,
 | 
			
		||||
      onApprove() {
 | 
			
		||||
        $(`${sel} form`).trigger('submit');
 | 
			
		||||
      }
 | 
			
		||||
    }).modal('show');
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function initFilterBranchTagDropdown(selector) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue