Skip to content

Commit 5ccecb4

Browse files
jonasfranzlafriks
authored andcommitted
Feature: Timetracking (#2211)
* Added comment's hashtag to url for mail notifications. * Added explanation to return statement + documentation. * Replacing in-line link generation with HTMLURL. (+gofmt) * Replaced action-based model with nil-based model. (+gofmt) * Replaced mailIssueActionToParticipants with mailIssueCommentToParticipants. * Updating comment for mailIssueCommentToParticipants * Added link to comment in "Dashboard" * Deleting feed entry if a comment is going to be deleted * Added migration * Added improved migration to add a CommentID column to action. * Added improved links to comments in feed entries. * Fixes #1956 by filtering for deleted comments that are referenced in actions. * Introducing "IsDeleted" column to action. * Adding design draft (not functional) * Adding database models for stopwatches and trackedtimes * See #967 * Adding design draft (not functional) * Adding translations and improving design * Implementing stopwatch (for timetracking) * Make UI functional * Add hints in timeline for time tracking events * Implementing timetracking feature * Adding "Add time manual" option * Improved stopwatch * Created report of total spent time by user * Only showing total time spent if theire is something to show. * Adding license headers. * Improved error handling for "Add Time Manual" * Adding @sapks 's changes, refactoring * Adding API for feature tracking * Adding unit test * Adding DISABLE/ENABLE option to Repository settings page * Improving translations * Applying @sapk 's changes * Removing repo_unit and using IssuesSetting for disabling/enabling timetracker * Adding DEFAULT_ENABLE_TIMETRACKER to config, installation and admin menu * Improving documentation * Fixing vendor/ folder * Changing timtracking routes by adding subgroups /times and /times/stopwatch (Proposed by @lafriks ) * Restricting write access to timetracking based on the repo settings (Proposed by @lafriks ) * Fixed minor permissions bug. * Adding CanUseTimetracker and IsTimetrackerEnabled in ctx.Repo * Allow assignees and authors to track there time too. * Fixed some build-time-errors + logical errors. * Removing unused Get...ByID functions * Moving IsTimetrackerEnabled from context.Repository to models.Repository * Adding a seperate file for issue related repo functions * Adding license headers * Fixed GetUserByParams return 404 * Moving /users/:username/times to /repos/:username/:reponame/times/:username for security reasons * Adding /repos/:username/times to get all tracked times of the repo * Updating sdk-dependency * Updating swagger.v1.json * Adding warning if user has already a running stopwatch (auto-timetracker) * Replacing GetTrackedTimesBy... with GetTrackedTimes(options FindTrackedTimesOptions) * Changing code.gitea.io/sdk back to code.gitea.io/sdk * Correcting spelling mistake * Updating vendor.json * Changing GET stopwatch/toggle to POST stopwatch/toggle * Changing GET stopwatch/cancel to POST stopwatch/cancel * Added migration for stopwatches/timetracking * Fixed some access bugs for read-only users * Added default allow only contributors to track time value to config * Fixed migration by chaging x.Iterate to x.Find * Resorted imports * Moved Add Time Manually form to repo_form.go * Removed "Seconds" field from Add Time Manually * Resorted imports * Improved permission checking * Fixed some bugs * Added integration test * gofmt * Adding integration test by @lafriks * Added created_unix to comment fixtures * Using last event instead of a fixed event * Adding another integration test by @lafriks * Fixing bug Timetracker enabled causing error 500 at sidebar.tpl * Fixed a refactoring bug that resulted in hiding "HasUserStopwatch" warning. * Returning TrackedTime instead of AddTimeOption at AddTime. * Updating SDK from go-gitea/go-sdk#69 * Resetting Go-SDK back to default repository * Fixing test-vendor by changing ini back to original repository * Adding "tags" to swagger spec * govendor sync * Removed duplicate * Formatting templates * Adding IsTimetrackingEnabled checks to API * Improving translations / english texts * Improving documentation * Updating swagger spec * Fixing integration test caused be translation-changes * Removed encoding issues in local_en-US.ini. * "Added" copyright line * Moved unit.IssuesConfig().EnableTimetracker into a != nil check * Removed some other encoding issues in local_en-US.ini * Improved javascript by checking if data-context exists * Replaced manual comment creation with CreateComment * Removed unnecessary code * Improved error checking * Small cosmetic changes * Replaced int>string>duration parsing with int>duration parsing * Fixed encoding issues * Removed unused imports Signed-off-by: Jonas Franz <[email protected]>
1 parent 69dfe43 commit 5ccecb4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1522
-71
lines changed

conf/app.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ DEFAULT_KEEP_EMAIL_PRIVATE = false
265265
; Default value for AllowCreateOrganization
266266
; New user will have rights set to create organizations depending on this setting
267267
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
268+
; Default value for EnableTimetracking
269+
; Repositories will use timetracking by default depending on this setting
270+
DEFAULT_ENABLE_TIMETRACKING = true
271+
; Default value for AllowOnlyContributorsToTrackTime
272+
; Only users with write permissions could track time if this is true
273+
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
268274
; Default value for the domain part of the user's email address in the git log
269275
; if he has set KeepEmailPrivate true. The user's email replaced with a
270276
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.

integrations/html_helper.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,13 @@ func (doc *HTMLDoc) GetInputValueByName(name string) string {
4040
func (doc *HTMLDoc) GetCSRF() string {
4141
return doc.GetInputValueByName("_csrf")
4242
}
43+
44+
// AssertElement check if element by selector exists or does not exist depending on checkExists
45+
func (doc *HTMLDoc) AssertElement(t testing.TB, selector string, checkExists bool) {
46+
sel := doc.doc.Find(selector)
47+
if checkExists {
48+
assert.Equal(t, 1, sel.Length())
49+
} else {
50+
assert.Equal(t, 0, sel.Length())
51+
}
52+
}

integrations/timetracking_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"net/http"
9+
"path"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestViewTimetrackingControls(t *testing.T) {
16+
prepareTestEnv(t)
17+
session := loginUser(t, "user2")
18+
testViewTimetrackingControls(t, session, "user2", "repo1", "1", true)
19+
//user2/repo1
20+
}
21+
22+
func TestNotViewTimetrackingControls(t *testing.T) {
23+
prepareTestEnv(t)
24+
session := loginUser(t, "user5")
25+
testViewTimetrackingControls(t, session, "user2", "repo1", "1", false)
26+
//user2/repo1
27+
}
28+
func TestViewTimetrackingControlsDisabled(t *testing.T) {
29+
prepareTestEnv(t)
30+
session := loginUser(t, "user2")
31+
testViewTimetrackingControls(t, session, "user3", "repo3", "1", false)
32+
}
33+
34+
func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo, issue string, canTrackTime bool) {
35+
req := NewRequest(t, "GET", path.Join(user, repo, "issues", issue))
36+
resp := session.MakeRequest(t, req, http.StatusOK)
37+
38+
htmlDoc := NewHTMLParser(t, resp.Body)
39+
40+
htmlDoc.AssertElement(t, ".timetrack .start-add .start", canTrackTime)
41+
htmlDoc.AssertElement(t, ".timetrack .start-add .add-time", canTrackTime)
42+
43+
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "issues", issue, "times", "stopwatch", "toggle"), map[string]string{
44+
"_csrf": htmlDoc.GetCSRF(),
45+
})
46+
if canTrackTime {
47+
resp = session.MakeRequest(t, req, http.StatusSeeOther)
48+
49+
req = NewRequest(t, "GET", RedirectURL(t, resp))
50+
resp = session.MakeRequest(t, req, http.StatusOK)
51+
htmlDoc = NewHTMLParser(t, resp.Body)
52+
53+
events := htmlDoc.doc.Find(".event > span.text")
54+
assert.Contains(t, events.Last().Text(), "started working")
55+
56+
htmlDoc.AssertElement(t, ".timetrack .stop-cancel .stop", true)
57+
htmlDoc.AssertElement(t, ".timetrack .stop-cancel .cancel", true)
58+
59+
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "issues", issue, "times", "stopwatch", "toggle"), map[string]string{
60+
"_csrf": htmlDoc.GetCSRF(),
61+
})
62+
resp = session.MakeRequest(t, req, http.StatusSeeOther)
63+
64+
req = NewRequest(t, "GET", RedirectURL(t, resp))
65+
resp = session.MakeRequest(t, req, http.StatusOK)
66+
htmlDoc = NewHTMLParser(t, resp.Body)
67+
68+
events = htmlDoc.doc.Find(".event > span.text")
69+
assert.Contains(t, events.Last().Text(), "stopped working")
70+
htmlDoc.AssertElement(t, ".event .detail .octicon-clock", true)
71+
} else {
72+
session.MakeRequest(t, req, http.StatusNotFound)
73+
}
74+
}

models/error.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,50 @@ func (err ErrCommentNotExist) Error() string {
768768
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
769769
}
770770

771+
// _________ __ __ .__
772+
// / _____// |_ ____ ________ _ _______ _/ |_ ____ | |__
773+
// \_____ \\ __\/ _ \\____ \ \/ \/ /\__ \\ __\/ ___\| | \
774+
// / \| | ( <_> ) |_> > / / __ \| | \ \___| Y \
775+
// /_______ /|__| \____/| __/ \/\_/ (____ /__| \___ >___| /
776+
// \/ |__| \/ \/ \/
777+
778+
// ErrStopwatchNotExist represents a "Stopwatch Not Exist" kind of error.
779+
type ErrStopwatchNotExist struct {
780+
ID int64
781+
}
782+
783+
// IsErrStopwatchNotExist checks if an error is a ErrStopwatchNotExist.
784+
func IsErrStopwatchNotExist(err error) bool {
785+
_, ok := err.(ErrStopwatchNotExist)
786+
return ok
787+
}
788+
789+
func (err ErrStopwatchNotExist) Error() string {
790+
return fmt.Sprintf("stopwatch does not exist [id: %d]", err.ID)
791+
}
792+
793+
// ___________ __ .______________.__
794+
// \__ ___/___________ ____ | | __ ____ __| _/\__ ___/|__| _____ ____
795+
// | | \_ __ \__ \ _/ ___\| |/ // __ \ / __ | | | | |/ \_/ __ \
796+
// | | | | \// __ \\ \___| <\ ___// /_/ | | | | | Y Y \ ___/
797+
// |____| |__| (____ /\___ >__|_ \\___ >____ | |____| |__|__|_| /\___ >
798+
// \/ \/ \/ \/ \/ \/ \/
799+
800+
// ErrTrackedTimeNotExist represents a "TrackedTime Not Exist" kind of error.
801+
type ErrTrackedTimeNotExist struct {
802+
ID int64
803+
}
804+
805+
// IsErrTrackedTimeNotExist checks if an error is a ErrTrackedTimeNotExist.
806+
func IsErrTrackedTimeNotExist(err error) bool {
807+
_, ok := err.(ErrTrackedTimeNotExist)
808+
return ok
809+
}
810+
811+
func (err ErrTrackedTimeNotExist) Error() string {
812+
return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID)
813+
}
814+
771815
// .____ ___. .__
772816
// | | _____ \_ |__ ____ | |
773817
// | | \__ \ | __ \_/ __ \| |

models/fixtures/comment.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
issue_id: 1 # in repo_id 1
66
label_id: 1
77
content: "1"
8+
created_unix: 946684810
89
-
910
id: 2
1011
type: 0 # comment
1112
poster_id: 3 # user not watching (see watch.yml)
1213
issue_id: 1 # in repo_id 1
1314
content: "good work!"
15+
created_unix: 946684811
1416
-
1517
id: 3
1618
type: 0 # comment
1719
poster_id: 5 # user not watching (see watch.yml)
1820
issue_id: 1 # in repo_id 1
1921
content: "meh..."
22+
created_unix: 946684812

models/fixtures/issue.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,16 @@
5757
content: content5
5858
is_closed: true
5959
is_pull: false
60+
-
61+
id: 6
62+
repo_id: 3
63+
index: 1
64+
poster_id: 1
65+
assignee_id: 1
66+
name: issue6
67+
content: content6
68+
is_closed: false
69+
is_pull: false
70+
num_comments: 0
71+
created_unix: 946684800
72+
updated_unix: 978307200

models/fixtures/repo_unit.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
repo_id: 1
1212
type: 2
1313
index: 1
14-
config: "{}"
14+
config: "{\"EnableTimetracker\":true,\"AllowOnlyContributorsToTrackTime\":true}"
1515
created_unix: 946684810
1616

1717
-
@@ -51,7 +51,7 @@
5151
repo_id: 3
5252
type: 2
5353
index: 1
54-
config: "{}"
54+
config: "{\"EnableTimetracker\":false,\"AllowOnlyContributorsToTrackTime\":false}"
5555
created_unix: 946684810
5656

5757
-

models/fixtures/repository.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
lower_name: repo3
3030
name: repo3
3131
is_private: true
32-
num_issues: 0
32+
num_issues: 1
3333
num_closed_issues: 0
3434
num_pulls: 0
3535
num_closed_pulls: 0

models/fixtures/stopwatch.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-
2+
id: 1
3+
user_id: 1
4+
issue_id: 1
5+
created_unix: 1500988502
6+
7+
-
8+
id: 2
9+
user_id: 2
10+
issue_id: 2
11+
created_unix: 1500988502

models/fixtures/tracked_time.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-
2+
id: 1
3+
user_id: 1
4+
issue_id: 1
5+
time: 400
6+
created_unix: 946684800
7+
8+
-
9+
id: 2
10+
user_id: 2
11+
issue_id: 2
12+
time: 3661
13+
created_unix: 946684801
14+
15+
-
16+
id: 3
17+
user_id: 2
18+
issue_id: 2
19+
time: 1
20+
created_unix: 946684802
21+
22+
-
23+
id: 4
24+
user_id: -1
25+
issue_id: 4
26+
time: 1
27+
created_unix: 946684802
28+
29+
-
30+
id: 5
31+
user_id: 2
32+
issue_id: 5
33+
time: 1
34+
created_unix: 946684802

models/issue_comment.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ const (
5252
CommentTypeChangeTitle
5353
// Delete Branch
5454
CommentTypeDeleteBranch
55+
// Start a stopwatch for time tracking
56+
CommentTypeStartTracking
57+
// Stop a stopwatch for time tracking
58+
CommentTypeStopTracking
59+
// Add time manual for time tracking
60+
CommentTypeAddTimeManual
61+
// Cancel a stopwatch for time tracking
62+
CommentTypeCancelTracking
5563
)
5664

5765
// CommentTag defines comment tag type
@@ -672,7 +680,6 @@ func DeleteComment(comment *Comment) error {
672680
return err
673681
}
674682
}
675-
676683
if _, err := sess.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
677684
return err
678685
}

0 commit comments

Comments
 (0)