mirror of https://github.com/go-gitea/gitea
Compare commits
93 Commits
11b99774c1
...
eca8c7d4b1
Author | SHA1 | Date |
---|---|---|
Illya Marchenko | eca8c7d4b1 | |
silverwind | dcc3c17e5c | |
GiteaBot | 27861d711b | |
silverwind | c93eefb42b | |
stuzer05 | 0a8fd353ef | |
stuzer05 | 12553d7dc2 | |
stuzer05 | b97139b232 | |
stuzer05 | b452e13f35 | |
stuzer05 | e92bedc81d | |
stuzer05 | 1e22cc242f | |
stuzer05 | 93d09fa8fc | |
stuzer05 | 0820db04ed | |
stuzer05 | 09723c5e0b | |
stuzer05 | c272512d9a | |
stuzer05 | 0c4b2dfa8f | |
stuzer05 | 805af19ef1 | |
stuzer05 | bb5ca4c40b | |
stuzer05 | a737a8c10b | |
stuzer05 | 92dc2cd22c | |
stuzer05 | 39b8b1929d | |
stuzer05 | a999055954 | |
stuzer05 | a6fa4c3d04 | |
stuzer05 | 2a9009fb02 | |
stuzer05 | 3037d6cdde | |
stuzer05 | 349b959022 | |
stuzer05 | 64de74d540 | |
stuzer05 | fa662ec087 | |
stuzer05 | 62094d8cf3 | |
スツゼル | e933a89bd9 | |
stuzer05 | db49783fa8 | |
stuzer05 | a45c1e9c86 | |
stuzer05 | 721069d766 | |
stuzer05 | 3310440ed1 | |
スツゼル | 598e2d5bed | |
silverwind | b211b9e66d | |
stuzer05 | ffaa4babcb | |
stuzer05 | cef496af2a | |
stuzer05 | 015ad01513 | |
stuzer05 | 748bd67814 | |
スツゼル | 3924cb0062 | |
stuzer05 | e9afd60d6b | |
stuzer05 | 40e1373dce | |
stuzer05 | bf4fa112d0 | |
stuzer05 | fc93006f50 | |
スツゼル | 879d96f077 | |
スツゼル | fd5adc55f3 | |
stuzer05 | 57a3664eb2 | |
stuzer05 | bf323cf26c | |
スツゼル | fb8126035a | |
stuzer05 | deddce59cf | |
stuzer05 | 79e18c6711 | |
stuzer05 | 49a176d37c | |
stuzer05 | 8ef3a478aa | |
stuzer05 | 37e8e8ddaf | |
stuzer05 | e20e23b60a | |
スツゼル | 8c0bf885b2 | |
stuzer05 | b9cdc7c670 | |
stuzer05 | 7be748f63b | |
stuzer05 | 29dd61722b | |
stuzer05 | 875087061f | |
スツゼル | 775c663805 | |
stuzer05 | db3c697178 | |
stuzer05 | 5eea230714 | |
stuzer05 | 0ab85af1ce | |
stuzer05 | 5c4dc8739c | |
stuzer05 | 79f507b81f | |
stuzer05 | 7a570440f1 | |
stuzer05 | f463765fec | |
stuzer05 | e15549501d | |
stuzer05 | 1cff1a9e05 | |
stuzer05 | f7427d8b50 | |
stuzer05 | 1b7ba4189b | |
stuzer05 | 11b9719b5f | |
stuzer05 | 0f5b609f9d | |
stuzer05 | 09c05e8b76 | |
stuzer05 | d247b0ffd1 | |
stuzer05 | 870bb922cc | |
stuzer05 | b062fc9849 | |
stuzer05 | 2fc2f63c6c | |
スツゼル | 4e1aed8a61 | |
stuzer05 | f33b0a0773 | |
stuzer05 | e187364d7a | |
スツゼル | f7a4c9e0aa | |
スツゼル | 5f3edad64f | |
stuzer05 | 4be8c50a6f | |
stuzer05 | d75b7acac6 | |
stuzer05 | c248042c8e | |
stuzer05 | 7ed86ff00a | |
stuzer05 | d9446629c3 | |
stuzer05 | a8778f4db7 | |
stuzer05 | c783692f6c | |
stuzer05 | d11ba9f46c | |
stuzer05 | 1363205f29 |
5
Makefile
5
Makefile
|
@ -908,8 +908,9 @@ webpack: $(WEBPACK_DEST)
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||||
@$(MAKE) -s node-check node_modules
|
@$(MAKE) -s node-check node_modules
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
npx webpack
|
@echo "Running webpack..."
|
||||||
|
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
||||||
@touch $(WEBPACK_DEST)
|
@touch $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: svg
|
.PHONY: svg
|
||||||
|
|
|
@ -112,6 +112,8 @@ const (
|
||||||
|
|
||||||
CommentTypePin // 36 pin Issue
|
CommentTypePin // 36 pin Issue
|
||||||
CommentTypeUnpin // 37 unpin Issue
|
CommentTypeUnpin // 37 unpin Issue
|
||||||
|
|
||||||
|
CommentTypeChangeTimeEstimate // 38 Change time estimate
|
||||||
)
|
)
|
||||||
|
|
||||||
var commentStrings = []string{
|
var commentStrings = []string{
|
||||||
|
@ -151,6 +153,7 @@ var commentStrings = []string{
|
||||||
"change_issue_ref",
|
"change_issue_ref",
|
||||||
"pull_scheduled_merge",
|
"pull_scheduled_merge",
|
||||||
"pull_cancel_scheduled_merge",
|
"pull_cancel_scheduled_merge",
|
||||||
|
"change_time_estimate",
|
||||||
"pin",
|
"pin",
|
||||||
"unpin",
|
"unpin",
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,9 @@ type Issue struct {
|
||||||
|
|
||||||
// For view issue page.
|
// For view issue page.
|
||||||
ShowRole RoleDescriptor `xorm:"-"`
|
ShowRole RoleDescriptor `xorm:"-"`
|
||||||
|
|
||||||
|
// Time estimate
|
||||||
|
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -909,3 +912,33 @@ func insertIssue(ctx context.Context, issue *Issue) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeIssueTimeEstimate changes the plan time of this issue, as the given user.
|
||||||
|
func ChangeIssueTimeEstimate(issue *Issue, doer *user_model.User, timeEstimate int64) (err error) {
|
||||||
|
ctx, committer, err := db.TxContext(db.DefaultContext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, TimeEstimate: timeEstimate}, "time_estimate"); err != nil {
|
||||||
|
return fmt.Errorf("updateIssueCols: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
|
return fmt.Errorf("loadRepo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &CreateCommentOptions{
|
||||||
|
Type: CommentTypeChangeTimeEstimate,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
Content: fmt.Sprintf("%d", timeEstimate),
|
||||||
|
}
|
||||||
|
if _, err = CreateComment(ctx, opts); err != nil {
|
||||||
|
return fmt.Errorf("createComment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
||||||
|
|
|
@ -586,6 +586,8 @@ var migrations = []Migration{
|
||||||
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
||||||
// v298 -> v299
|
// v298 -> v299
|
||||||
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
|
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
|
||||||
|
// v300 -> v301
|
||||||
|
NewMigration("Add TimeEstimate to issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
|
||||||
|
type Issue struct {
|
||||||
|
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync(new(Issue))
|
||||||
|
}
|
|
@ -65,12 +65,14 @@ func NewFuncMap() template.FuncMap {
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// time / number / format
|
// time / number / format
|
||||||
"FileSize": base.FileSize,
|
"FileSize": base.FileSize,
|
||||||
"CountFmt": base.FormatNumberSI,
|
"CountFmt": base.FormatNumberSI,
|
||||||
"TimeSince": timeutil.TimeSince,
|
"TimeSince": timeutil.TimeSince,
|
||||||
"TimeSinceUnix": timeutil.TimeSinceUnix,
|
"TimeSinceUnix": timeutil.TimeSinceUnix,
|
||||||
"DateTime": timeutil.DateTime,
|
"DateTime": timeutil.DateTime,
|
||||||
"Sec2Time": util.SecToTime,
|
"Sec2Time": util.SecToTime,
|
||||||
|
"SecToTimeExact": util.SecToTimeExact,
|
||||||
|
"TimeEstimateToStr": util.TimeEstimateToStr,
|
||||||
"LoadTimes": func(startTime time.Time) string {
|
"LoadTimes": func(startTime time.Time) string {
|
||||||
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
||||||
},
|
},
|
||||||
|
|
|
@ -66,6 +66,43 @@ func SecToTime(durationVal any) string {
|
||||||
return strings.TrimRight(formattedTime, " ")
|
return strings.TrimRight(formattedTime, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SecToTimeExact(duration int64, withSeconds bool) string {
|
||||||
|
formattedTime := ""
|
||||||
|
|
||||||
|
// The following four variables are calculated by taking
|
||||||
|
// into account the previously calculated variables, this avoids
|
||||||
|
// pitfalls when using remainders. As that could lead to incorrect
|
||||||
|
// results when the calculated number equals the quotient number.
|
||||||
|
remainingDays := duration / (60 * 60 * 24)
|
||||||
|
years := remainingDays / 365
|
||||||
|
remainingDays -= years * 365
|
||||||
|
months := remainingDays * 12 / 365
|
||||||
|
remainingDays -= months * 365 / 12
|
||||||
|
weeks := remainingDays / 7
|
||||||
|
remainingDays -= weeks * 7
|
||||||
|
days := remainingDays
|
||||||
|
|
||||||
|
// The following three variables are calculated without depending
|
||||||
|
// on the previous calculated variables.
|
||||||
|
hours := (duration / 3600) % 24
|
||||||
|
minutes := (duration / 60) % 60
|
||||||
|
seconds := duration % 60
|
||||||
|
|
||||||
|
// Show exact time information
|
||||||
|
formattedTime = formatTime(years, "year", formattedTime)
|
||||||
|
formattedTime = formatTime(months, "month", formattedTime)
|
||||||
|
formattedTime = formatTime(weeks, "week", formattedTime)
|
||||||
|
formattedTime = formatTime(days, "day", formattedTime)
|
||||||
|
formattedTime = formatTime(hours, "hour", formattedTime)
|
||||||
|
formattedTime = formatTime(minutes, "minute", formattedTime)
|
||||||
|
if withSeconds {
|
||||||
|
formattedTime = formatTime(seconds, "second", formattedTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The formatTime() function always appends a space at the end. This will be trimmed
|
||||||
|
return strings.TrimRight(formattedTime, " ")
|
||||||
|
}
|
||||||
|
|
||||||
// formatTime appends the given value to the existing forammattedTime. E.g:
|
// formatTime appends the given value to the existing forammattedTime. E.g:
|
||||||
// formattedTime = "1 year"
|
// formattedTime = "1 year"
|
||||||
// input: value = 3, name = "month"
|
// input: value = 3, name = "month"
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2022 Gitea. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Time estimate match regex
|
||||||
|
rTimeEstimateOnlyHours = regexp.MustCompile(`^([\d]+)$`)
|
||||||
|
rTimeEstimateWeeks = regexp.MustCompile(`([\d]+)w`)
|
||||||
|
rTimeEstimateDays = regexp.MustCompile(`([\d]+)d`)
|
||||||
|
rTimeEstimateHours = regexp.MustCompile(`([\d]+)h`)
|
||||||
|
rTimeEstimateMinutes = regexp.MustCompile(`([\d]+)m`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// TimeEstimateFromStr returns time estimate in seconds from formatted string
|
||||||
|
func TimeEstimateFromStr(timeStr string) int64 {
|
||||||
|
timeTotal := 0
|
||||||
|
|
||||||
|
// If single number entered, assume hours
|
||||||
|
timeStrMatches := rTimeEstimateOnlyHours.FindStringSubmatch(timeStr)
|
||||||
|
if len(timeStrMatches) > 0 {
|
||||||
|
raw, _ := strconv.Atoi(timeStrMatches[1])
|
||||||
|
timeTotal += raw * (60 * 60)
|
||||||
|
} else {
|
||||||
|
// Find time weeks
|
||||||
|
timeStrMatches = rTimeEstimateWeeks.FindStringSubmatch(timeStr)
|
||||||
|
if len(timeStrMatches) > 0 {
|
||||||
|
raw, _ := strconv.Atoi(timeStrMatches[1])
|
||||||
|
timeTotal += raw * (60 * 60 * 24 * 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find time days
|
||||||
|
timeStrMatches = rTimeEstimateDays.FindStringSubmatch(timeStr)
|
||||||
|
if len(timeStrMatches) > 0 {
|
||||||
|
raw, _ := strconv.Atoi(timeStrMatches[1])
|
||||||
|
timeTotal += raw * (60 * 60 * 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find time hours
|
||||||
|
timeStrMatches = rTimeEstimateHours.FindStringSubmatch(timeStr)
|
||||||
|
if len(timeStrMatches) > 0 {
|
||||||
|
raw, _ := strconv.Atoi(timeStrMatches[1])
|
||||||
|
timeTotal += raw * (60 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find time minutes
|
||||||
|
timeStrMatches = rTimeEstimateMinutes.FindStringSubmatch(timeStr)
|
||||||
|
if len(timeStrMatches) > 0 {
|
||||||
|
raw, _ := strconv.Atoi(timeStrMatches[1])
|
||||||
|
timeTotal += raw * (60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(timeTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeEstimateStr returns formatted time estimate string from seconds (e.g. "2w 4d 12h 5m")
|
||||||
|
func TimeEstimateToStr(amount int64) string {
|
||||||
|
var timeParts []string
|
||||||
|
|
||||||
|
timeSeconds := float64(amount)
|
||||||
|
|
||||||
|
// Format weeks
|
||||||
|
weeks := math.Floor(timeSeconds / (60 * 60 * 24 * 7))
|
||||||
|
if weeks > 0 {
|
||||||
|
timeParts = append(timeParts, fmt.Sprintf("%dw", int64(weeks)))
|
||||||
|
}
|
||||||
|
timeSeconds -= weeks * (60 * 60 * 24 * 7)
|
||||||
|
|
||||||
|
// Format days
|
||||||
|
days := math.Floor(timeSeconds / (60 * 60 * 24))
|
||||||
|
if days > 0 {
|
||||||
|
timeParts = append(timeParts, fmt.Sprintf("%dd", int64(days)))
|
||||||
|
}
|
||||||
|
timeSeconds -= days * (60 * 60 * 24)
|
||||||
|
|
||||||
|
// Format hours
|
||||||
|
hours := math.Floor(timeSeconds / (60 * 60))
|
||||||
|
if hours > 0 {
|
||||||
|
timeParts = append(timeParts, fmt.Sprintf("%dh", int64(hours)))
|
||||||
|
}
|
||||||
|
timeSeconds -= hours * (60 * 60)
|
||||||
|
|
||||||
|
// Format minutes
|
||||||
|
minutes := math.Floor(timeSeconds / (60))
|
||||||
|
if minutes > 0 {
|
||||||
|
timeParts = append(timeParts, fmt.Sprintf("%dm", int64(minutes)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(timeParts, " ")
|
||||||
|
}
|
|
@ -1480,6 +1480,11 @@ issues.add_assignee_at = `was assigned by <b>%s</b> %s`
|
||||||
issues.remove_assignee_at = `was unassigned by <b>%s</b> %s`
|
issues.remove_assignee_at = `was unassigned by <b>%s</b> %s`
|
||||||
issues.remove_self_assignment = `removed their assignment %s`
|
issues.remove_self_assignment = `removed their assignment %s`
|
||||||
issues.change_title_at = `changed title from <b><strike>%s</strike></b> to <b>%s</b> %s`
|
issues.change_title_at = `changed title from <b><strike>%s</strike></b> to <b>%s</b> %s`
|
||||||
|
issues.time_estimate = `Time Estimate`
|
||||||
|
issues.add_time_estimate = `3w 4d 12h`
|
||||||
|
issues.change_time_estimate_at = `changed time estimate to <b>%s</b> %s`
|
||||||
|
issues.remove_time_estimate = `removed time estimate %s`
|
||||||
|
issues.time_estimate_invalid = `Time estimate format is invalid`
|
||||||
issues.change_ref_at = `changed reference from <b><strike>%s</strike></b> to <b>%s</b> %s`
|
issues.change_ref_at = `changed reference from <b><strike>%s</strike></b> to <b>%s</b> %s`
|
||||||
issues.remove_ref_at = `removed reference <b>%s</b> %s`
|
issues.remove_ref_at = `removed reference <b>%s</b> %s`
|
||||||
issues.add_ref_at = `added reference <b>%s</b> %s`
|
issues.add_ref_at = `added reference <b>%s</b> %s`
|
||||||
|
@ -1650,20 +1655,20 @@ issues.start_tracking_history = `started working %s`
|
||||||
issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed
|
issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed
|
||||||
issues.tracking_already_started = `You have already started time tracking on <a href="%s">another issue</a>!`
|
issues.tracking_already_started = `You have already started time tracking on <a href="%s">another issue</a>!`
|
||||||
issues.stop_tracking = Stop Timer
|
issues.stop_tracking = Stop Timer
|
||||||
issues.stop_tracking_history = `stopped working %s`
|
issues.stop_tracking_history = `worked for <b>%s</b> %s`
|
||||||
issues.cancel_tracking = Discard
|
issues.cancel_tracking = Discard
|
||||||
issues.cancel_tracking_history = `canceled time tracking %s`
|
issues.cancel_tracking_history = `canceled time tracking %s`
|
||||||
issues.add_time = Manually Add Time
|
issues.add_time = Manually Add Time
|
||||||
issues.del_time = Delete this time log
|
issues.del_time = Delete this time log
|
||||||
issues.add_time_short = Add Time
|
issues.add_time_short = Add Time
|
||||||
issues.add_time_cancel = Cancel
|
issues.add_time_cancel = Cancel
|
||||||
issues.add_time_history = `added spent time %s`
|
issues.add_time_history = `added spent time <b>%s</b> %s`
|
||||||
issues.del_time_history= `deleted spent time %s`
|
issues.del_time_history= `deleted spent time <b>%s</b> %s`
|
||||||
issues.add_time_hours = Hours
|
issues.add_time_hours = Hours
|
||||||
issues.add_time_minutes = Minutes
|
issues.add_time_minutes = Minutes
|
||||||
issues.add_time_sum_to_small = No time was entered.
|
issues.add_time_sum_to_small = No time was entered.
|
||||||
issues.time_spent_total = Total Time Spent
|
issues.time_spent_total = Total Time Spent
|
||||||
issues.time_spent_from_all_authors = `Total Time Spent: %s`
|
issues.time_spent_from_all_authors = `Total Time Spent:`
|
||||||
issues.due_date = Due Date
|
issues.due_date = Due Date
|
||||||
issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'."
|
issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'."
|
||||||
issues.error_modifying_due_date = "Failed to modify the due date."
|
issues.error_modifying_due_date = "Failed to modify the due date."
|
||||||
|
|
|
@ -436,6 +436,7 @@ oauth_signin_submit=Vincular conta
|
||||||
oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador.
|
oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador.
|
||||||
oauth.signin.error.access_denied=O pedido de autorização foi negado.
|
oauth.signin.error.access_denied=O pedido de autorização foi negado.
|
||||||
oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde.
|
oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde.
|
||||||
|
oauth_callback_unable_auto_reg=O registo automático está habilitado, mas o fornecedor OAuth2 %[1]s sinalizou campos em falta: %[2]s, por isso não foi possível criar uma conta automaticamente. Crie ou vincule uma conta ou contacte o administrador do sítio.
|
||||||
openid_connect_submit=Estabelecer ligação
|
openid_connect_submit=Estabelecer ligação
|
||||||
openid_connect_title=Estabelecer ligação a uma conta existente
|
openid_connect_title=Estabelecer ligação a uma conta existente
|
||||||
openid_connect_desc=O URI do OpenID escolhido é desconhecido. Associe-o a uma nova conta aqui.
|
openid_connect_desc=O URI do OpenID escolhido é desconhecido. Associe-o a uma nova conta aqui.
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
|
@ -13,6 +13,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -1771,6 +1772,9 @@ func ViewIssue(ctx *context.Context) {
|
||||||
comment.Content = comment.Content[1:]
|
comment.Content = comment.Content[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if comment.Type == issues_model.CommentTypeChangeTimeEstimate {
|
||||||
|
timeSec, _ := util.ToInt64(comment.Content)
|
||||||
|
comment.Content = util.SecToTimeExact(timeSec, timeSec < 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
if comment.Type == issues_model.CommentTypeClose || comment.Type == issues_model.CommentTypeMergePull {
|
if comment.Type == issues_model.CommentTypeClose || comment.Type == issues_model.CommentTypeMergePull {
|
||||||
|
@ -2208,6 +2212,57 @@ func UpdateIssueTitle(ctx *context.Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateIssueTimeEstimate change issue's planned time
|
||||||
|
var (
|
||||||
|
rTimeEstimateStr = regexp.MustCompile(`^([\d]+w)?\s?([\d]+d)?\s?([\d]+h)?\s?([\d]+m)?$`)
|
||||||
|
rTimeEstimateStrHoursOnly = regexp.MustCompile(`^([\d]+)$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateIssueTimeEstimate(ctx *context.Context) {
|
||||||
|
issue := GetActionIssue(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
|
||||||
|
ctx.Error(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := issue.Link()
|
||||||
|
|
||||||
|
timeStr := ctx.FormString("time_estimate")
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if !rTimeEstimateStr.MatchString(timeStr) && !rTimeEstimateStrHoursOnly.MatchString(timeStr) {
|
||||||
|
ctx.Flash.Error(ctx.Tr("repo.issues.time_estimate_invalid"))
|
||||||
|
ctx.Redirect(url, http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total := util.TimeEstimateFromStr(timeStr)
|
||||||
|
|
||||||
|
// User entered something wrong
|
||||||
|
if total == 0 && len(timeStr) != 0 {
|
||||||
|
ctx.Flash.Error(ctx.Tr("repo.issues.time_estimate_invalid"))
|
||||||
|
ctx.Redirect(url, http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No time changed
|
||||||
|
if issue.TimeEstimate == total {
|
||||||
|
ctx.Redirect(url, http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := issue_service.ChangeTimeEstimate(issue, ctx.Doer, total); err != nil {
|
||||||
|
ctx.ServerError("ChangeTimeEstimate", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(url, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateIssueRef change issue's ref (branch)
|
// UpdateIssueRef change issue's ref (branch)
|
||||||
func UpdateIssueRef(ctx *context.Context) {
|
func UpdateIssueRef(ctx *context.Context) {
|
||||||
issue := GetActionIssue(ctx)
|
issue := GetActionIssue(ctx)
|
||||||
|
|
|
@ -34,7 +34,7 @@ func AddTimeManually(c *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
total := time.Duration(form.Hours)*time.Hour + time.Duration(form.Minutes)*time.Minute
|
total := util.TimeEstimateFromStr(form.TimeString)
|
||||||
|
|
||||||
if total <= 0 {
|
if total <= 0 {
|
||||||
c.Flash.Error(c.Tr("repo.issues.add_time_sum_to_small"))
|
c.Flash.Error(c.Tr("repo.issues.add_time_sum_to_small"))
|
||||||
|
@ -42,7 +42,7 @@ func AddTimeManually(c *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := issues_model.AddTime(c, c.Doer, issue, int64(total.Seconds()), time.Now()); err != nil {
|
if _, err := issues_model.AddTime(c, c.Doer, issue, total, time.Now()); err != nil {
|
||||||
c.ServerError("AddTime", err)
|
c.ServerError("AddTime", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1202,6 +1202,7 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Post("/cancel", repo.CancelStopwatch)
|
m.Post("/cancel", repo.CancelStopwatch)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
m.Post("/time_estimate", repo.UpdateIssueTimeEstimate)
|
||||||
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeIssueReaction)
|
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeIssueReaction)
|
||||||
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
|
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
|
||||||
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
|
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
|
||||||
|
|
|
@ -76,6 +76,11 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
|
||||||
// so we check for the "|" delimeter and convert new to legacy format on demand
|
// so we check for the "|" delimeter and convert new to legacy format on demand
|
||||||
c.Content = util.SecToTime(c.Content[1:])
|
c.Content = util.SecToTime(c.Content[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Type == issues_model.CommentTypeChangeTimeEstimate {
|
||||||
|
timeSec, _ := util.ToInt64(c.Content)
|
||||||
|
c.Content = util.SecToTimeExact(timeSec, timeSec < 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
comment := &api.TimelineComment{
|
comment := &api.TimelineComment{
|
||||||
|
|
|
@ -877,8 +877,7 @@ func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) bi
|
||||||
|
|
||||||
// AddTimeManuallyForm form that adds spent time manually.
|
// AddTimeManuallyForm form that adds spent time manually.
|
||||||
type AddTimeManuallyForm struct {
|
type AddTimeManuallyForm struct {
|
||||||
Hours int `binding:"Range(0,1000)"`
|
TimeString string
|
||||||
Minutes int `binding:"Range(0,1000)"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
|
|
|
@ -43,6 +43,7 @@ var hiddenCommentTypeGroups = hiddenCommentTypeGroupsType{
|
||||||
/*14*/ issues_model.CommentTypeAddTimeManual,
|
/*14*/ issues_model.CommentTypeAddTimeManual,
|
||||||
/*15*/ issues_model.CommentTypeCancelTracking,
|
/*15*/ issues_model.CommentTypeCancelTracking,
|
||||||
/*26*/ issues_model.CommentTypeDeleteTimeManual,
|
/*26*/ issues_model.CommentTypeDeleteTimeManual,
|
||||||
|
/*38*/ issues_model.CommentTypeChangeTimeEstimate,
|
||||||
},
|
},
|
||||||
"deadline": {
|
"deadline": {
|
||||||
/*16*/ issues_model.CommentTypeAddedDeadline,
|
/*16*/ issues_model.CommentTypeAddedDeadline,
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -23,64 +21,11 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/references"
|
"code.gitea.io/gitea/modules/references"
|
||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
secondsByMinute = float64(time.Minute / time.Second) // seconds in a minute
|
|
||||||
secondsByHour = 60 * secondsByMinute // seconds in an hour
|
|
||||||
secondsByDay = 8 * secondsByHour // seconds in a day
|
|
||||||
secondsByWeek = 5 * secondsByDay // seconds in a week
|
|
||||||
secondsByMonth = 4 * secondsByWeek // seconds in a month
|
|
||||||
)
|
|
||||||
|
|
||||||
var reDuration = regexp.MustCompile(`(?i)^(?:(\d+([\.,]\d+)?)(?:mo))?(?:(\d+([\.,]\d+)?)(?:w))?(?:(\d+([\.,]\d+)?)(?:d))?(?:(\d+([\.,]\d+)?)(?:h))?(?:(\d+([\.,]\d+)?)(?:m))?$`)
|
|
||||||
|
|
||||||
// timeLogToAmount parses time log string and returns amount in seconds
|
|
||||||
func timeLogToAmount(str string) int64 {
|
|
||||||
matches := reDuration.FindAllStringSubmatch(str, -1)
|
|
||||||
if len(matches) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
match := matches[0]
|
|
||||||
|
|
||||||
var a int64
|
|
||||||
|
|
||||||
// months
|
|
||||||
if len(match[1]) > 0 {
|
|
||||||
mo, _ := strconv.ParseFloat(strings.Replace(match[1], ",", ".", 1), 64)
|
|
||||||
a += int64(mo * secondsByMonth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// weeks
|
|
||||||
if len(match[3]) > 0 {
|
|
||||||
w, _ := strconv.ParseFloat(strings.Replace(match[3], ",", ".", 1), 64)
|
|
||||||
a += int64(w * secondsByWeek)
|
|
||||||
}
|
|
||||||
|
|
||||||
// days
|
|
||||||
if len(match[5]) > 0 {
|
|
||||||
d, _ := strconv.ParseFloat(strings.Replace(match[5], ",", ".", 1), 64)
|
|
||||||
a += int64(d * secondsByDay)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hours
|
|
||||||
if len(match[7]) > 0 {
|
|
||||||
h, _ := strconv.ParseFloat(strings.Replace(match[7], ",", ".", 1), 64)
|
|
||||||
a += int64(h * secondsByHour)
|
|
||||||
}
|
|
||||||
|
|
||||||
// minutes
|
|
||||||
if len(match[9]) > 0 {
|
|
||||||
d, _ := strconv.ParseFloat(strings.Replace(match[9], ",", ".", 1), 64)
|
|
||||||
a += int64(d * secondsByMinute)
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func issueAddTime(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, time time.Time, timeLog string) error {
|
func issueAddTime(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, time time.Time, timeLog string) error {
|
||||||
amount := timeLogToAmount(timeLog)
|
amount := util.TimeEstimateFromStr(timeLog)
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,13 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeTimeEstimate changes the time estimate of this issue, as the given user.
|
||||||
|
func ChangeTimeEstimate(issue *issues_model.Issue, doer *user_model.User, timeEstimate int64) (err error) {
|
||||||
|
issue.TimeEstimate = timeEstimate
|
||||||
|
|
||||||
|
return issues_model.ChangeIssueTimeEstimate(issue, doer, timeEstimate)
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeIssueRef changes the branch of this issue, as the given user.
|
// ChangeIssueRef changes the branch of this issue, as the given user.
|
||||||
func ChangeIssueRef(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, ref string) error {
|
func ChangeIssueRef(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, ref string) error {
|
||||||
oldRef := issue.Ref
|
oldRef := issue.Ref
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
{{end -}}
|
{{end -}}
|
||||||
{{- range .ReviewComments}}
|
{{- range .ReviewComments}}
|
||||||
<hr>
|
<hr>
|
||||||
{{$.locale.Tr "mail.issue.in_tree_path" .TreePath}}
|
{{ctx.Locale.Tr "mail.issue.in_tree_path" .TreePath}}
|
||||||
<div class="review">
|
<div class="review">
|
||||||
<pre>{{.Patch}}</pre>
|
<pre>{{.Patch}}</pre>
|
||||||
<div>{{.RenderedContent}}</div>
|
<div>{{.RenderedContent}}</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{if $.IsSplitStyle}}
|
{{if $.IsSplitStyle}}
|
||||||
{{range $k, $line := $.section.Lines}}
|
{{range $k, $line := $.section.Lines}}
|
||||||
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
|
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
|
||||||
{{if eq .GetType 4}}
|
{{if eq .GetType 4}}
|
||||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
|
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
|
||||||
<div class="tw-flex">
|
<div class="tw-flex">
|
||||||
|
@ -26,17 +26,17 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
|
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
|
||||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
|
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
|
||||||
<td class="blob-excerpt lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
<td class="lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
||||||
<td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
|
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
|
||||||
<td class="blob-excerpt lines-code lines-code-old">{{/*
|
<td class="lines-code lines-code-old">{{/*
|
||||||
*/}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
|
*/}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
|
||||||
*/}}<code class="code-inner"></code>{{/*
|
*/}}<code class="code-inner"></code>{{/*
|
||||||
*/}}{{end}}{{/*
|
*/}}{{end}}{{/*
|
||||||
*/}}</td>
|
*/}}</td>
|
||||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
|
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
|
||||||
<td class="blob-excerpt lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
<td class="lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
||||||
<td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
|
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
|
||||||
<td class="blob-excerpt lines-code lines-code-new">{{/*
|
<td class="lines-code lines-code-new">{{/*
|
||||||
*/}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
|
*/}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
|
||||||
*/}}<code class="code-inner"></code>{{/*
|
*/}}<code class="code-inner"></code>{{/*
|
||||||
*/}}{{end}}{{/*
|
*/}}{{end}}{{/*
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{range $k, $line := $.section.Lines}}
|
{{range $k, $line := $.section.Lines}}
|
||||||
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
|
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
|
||||||
{{if eq .GetType 4}}
|
{{if eq .GetType 4}}
|
||||||
<td colspan="2" class="lines-num">
|
<td colspan="2" class="lines-num">
|
||||||
<div class="tw-flex">
|
<div class="tw-flex">
|
||||||
|
@ -72,9 +72,9 @@
|
||||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
|
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
|
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
|
||||||
<td class="blob-excerpt lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
<td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
|
||||||
<td class="blob-excerpt lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
|
<td class="lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
|
||||||
<td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
|
<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST,
|
26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST,
|
||||||
29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED
|
29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED
|
||||||
32 = DISMISSED_REVIEW, 33 = COMMENT_TYPE_CHANGE_ISSUE_REF, 34 = PR_SCHEDULE_TO_AUTO_MERGE,
|
32 = DISMISSED_REVIEW, 33 = COMMENT_TYPE_CHANGE_ISSUE_REF, 34 = PR_SCHEDULE_TO_AUTO_MERGE,
|
||||||
35 = CANCEL_SCHEDULED_AUTO_MERGE_PR, 36 = PIN_ISSUE, 37 = UNPIN_ISSUE -->
|
35 = CANCEL_SCHEDULED_AUTO_MERGE_PR, 36 = PIN_ISSUE, 37 = UNPIN_ISSUE,
|
||||||
|
38 = COMMENT_TYPE_CHANGE_TIME_ESTIMATE -->
|
||||||
{{if eq .Type 0}}
|
{{if eq .Type 0}}
|
||||||
<div class="timeline-item comment" id="{{.HashTag}}">
|
<div class="timeline-item comment" id="{{.HashTag}}">
|
||||||
{{if .OriginalAuthor}}
|
{{if .OriginalAuthor}}
|
||||||
|
@ -249,18 +250,18 @@
|
||||||
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{template "shared/user/authorlink" .Poster}}
|
||||||
{{ctx.Locale.Tr "repo.issues.stop_tracking_history" $createdStr}}
|
|
||||||
</span>
|
{{$timeStr := ""}}
|
||||||
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
|
|
||||||
<div class="detail flex-text-block">
|
|
||||||
{{svg "octicon-clock"}}
|
|
||||||
{{if .RenderedContent}}
|
{{if .RenderedContent}}
|
||||||
{{/* compatibility with time comments made before v1.21 */}}
|
{{/* compatibility with time comments made before v1.21 */}}
|
||||||
<span class="text grey muted-links">{{.RenderedContent}}</span>
|
{{$timeStr = .RenderedContent}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="text grey muted-links">{{.Content|Sec2Time}}</span>
|
{{$timeStr = .Content|Sec2Time}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
|
||||||
|
{{ctx.Locale.Tr "repo.issues.stop_tracking_history" $timeStr $createdStr | SafeHTML}}
|
||||||
|
</span>
|
||||||
|
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 14}}
|
{{else if eq .Type 14}}
|
||||||
<div class="timeline-item event" id="{{.HashTag}}">
|
<div class="timeline-item event" id="{{.HashTag}}">
|
||||||
|
@ -268,18 +269,18 @@
|
||||||
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{template "shared/user/authorlink" .Poster}}
|
||||||
{{ctx.Locale.Tr "repo.issues.add_time_history" $createdStr}}
|
|
||||||
</span>
|
{{$timeStr := ""}}
|
||||||
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
|
|
||||||
<div class="detail flex-text-block">
|
|
||||||
{{svg "octicon-clock"}}
|
|
||||||
{{if .RenderedContent}}
|
{{if .RenderedContent}}
|
||||||
{{/* compatibility with time comments made before v1.21 */}}
|
{{/* compatibility with time comments made before v1.21 */}}
|
||||||
<span class="text grey muted-links">{{.RenderedContent}}</span>
|
{{$timeStr = .RenderedContent}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="text grey muted-links">{{.Content|Sec2Time}}</span>
|
{{$timeStr = .Content|Sec2Time}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
|
||||||
|
{{ctx.Locale.Tr "repo.issues.add_time_history" $timeStr $createdStr | SafeHTML}}
|
||||||
|
</span>
|
||||||
|
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 15}}
|
{{else if eq .Type 15}}
|
||||||
<div class="timeline-item event" id="{{.HashTag}}">
|
<div class="timeline-item event" id="{{.HashTag}}">
|
||||||
|
@ -680,6 +681,15 @@
|
||||||
{{else}}{{ctx.Locale.Tr "repo.issues.unpin_comment" $createdStr}}{{end}}
|
{{else}}{{ctx.Locale.Tr "repo.issues.unpin_comment" $createdStr}}{{end}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{else if eq .Type 38}}
|
||||||
|
<div class="timeline-item event" id="{{.HashTag}}">
|
||||||
|
<span class="badge">{{svg "octicon-clock"}}</span>
|
||||||
|
{{template "shared/user/avatarlink" dict "Context" $.Context "user" .Poster}}
|
||||||
|
<span class="text grey muted-links">
|
||||||
|
{{template "shared/user/authorlink" .Poster}}
|
||||||
|
{{ctx.Locale.Tr "repo.issues.change_time_estimate_at" .Content $createdStr | SafeHTML}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -278,8 +278,22 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Repository.IsTimetrackerEnabled $.Context}}
|
{{if .Repository.IsTimetrackerEnabled $.Context}}
|
||||||
{{if and .CanUseTimetracker (not .Repository.IsArchived)}}
|
{{if and .CanUseTimetracker (not .Repository.IsArchived)}}
|
||||||
<div class="divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui timetrack">
|
<div>
|
||||||
|
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.time_estimate"}}</strong></span>
|
||||||
|
|
||||||
|
<form method="post" id="set_time_estimate_form" class="gt-mt-3" action="{{.Issue.Link}}/time_estimate">
|
||||||
|
{{$.CsrfTokenHtml}}
|
||||||
|
<div class="ui input fluid">
|
||||||
|
<input name="time_estimate" placeholder='{{ctx.Locale.Tr "repo.issues.add_time_estimate"}}' value="{{TimeEstimateToStr .Issue.TimeEstimate}}" data-value="{{$.Issue.TimeEstimate}}" type="text" >
|
||||||
|
</div>
|
||||||
|
<button class="ui fluid button green tooltip tw-mt-1">
|
||||||
|
{{ctx.Locale.Tr "repo.issues.save"}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="ui timetrack">
|
||||||
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.tracker"}}</strong></span>
|
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.tracker"}}</strong></span>
|
||||||
<div class="tw-mt-2">
|
<div class="tw-mt-2">
|
||||||
<form method="post" action="{{.Issue.Link}}/times/stopwatch/toggle" id="toggle_stopwatch_form">
|
<form method="post" action="{{.Issue.Link}}/times/stopwatch/toggle" id="toggle_stopwatch_form">
|
||||||
|
@ -311,9 +325,8 @@
|
||||||
<div class="header">{{ctx.Locale.Tr "repo.issues.add_time"}}</div>
|
<div class="header">{{ctx.Locale.Tr "repo.issues.add_time"}}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form method="post" id="add_time_manual_form" action="{{.Issue.Link}}/times/add" class="ui input fluid tw-gap-2">
|
<form method="post" id="add_time_manual_form" action="{{.Issue.Link}}/times/add" class="ui input fluid tw-gap-2">
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<input placeholder='{{ctx.Locale.Tr "repo.issues.add_time_hours"}}' type="number" name="hours">
|
<input placeholder='{{ctx.Locale.Tr "repo.issues.add_time_estimate"}}' type="text" name="time_string">
|
||||||
<input placeholder='{{ctx.Locale.Tr "repo.issues.add_time_minutes"}}' type="number" name="minutes" class="ui compact">
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
@ -332,8 +345,9 @@
|
||||||
{{if .WorkingUsers}}
|
{{if .WorkingUsers}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui comments">
|
<div class="ui comments">
|
||||||
<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time)}}</strong></span>
|
<div class="text"><strong>{{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" | SafeHTML}}</strong></div>
|
||||||
<div>
|
<div>{{SecToTimeExact .Issue.TotalTrackedTime false}}</div>
|
||||||
|
<div class="gt-mt-3">
|
||||||
{{range $user, $trackedtime := .WorkingUsers}}
|
{{range $user, $trackedtime := .WorkingUsers}}
|
||||||
<div class="comment tw-mt-2">
|
<div class="comment tw-mt-2">
|
||||||
<a class="avatar">
|
<a class="avatar">
|
||||||
|
|
|
@ -73,8 +73,7 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
|
||||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||||
|
|
||||||
events = htmlDoc.doc.Find(".event > span.text")
|
events = htmlDoc.doc.Find(".event > span.text")
|
||||||
assert.Contains(t, events.Last().Text(), "stopped working")
|
assert.Contains(t, events.Last().Text(), "worked for ")
|
||||||
htmlDoc.AssertElement(t, ".event .detail .octicon-clock", true)
|
|
||||||
} else {
|
} else {
|
||||||
session.MakeRequest(t, req, http.StatusNotFound)
|
session.MakeRequest(t, req, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2377,7 +2377,7 @@ tbody.commit-list {
|
||||||
|
|
||||||
.tag-code,
|
.tag-code,
|
||||||
.tag-code td,
|
.tag-code td,
|
||||||
.tag-code .blob-excerpt {
|
.tag-code.line-expanded {
|
||||||
background-color: var(--color-box-body-highlight);
|
background-color: var(--color-box-body-highlight);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
@ -2393,8 +2393,8 @@ tbody.commit-list {
|
||||||
padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blob-excerpt {
|
.line-expanded {
|
||||||
background-color: var(--color-secondary-alpha-30);
|
background-color: var(--color-secondary-alpha-20);
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-keyword {
|
.issue-keyword {
|
||||||
|
@ -2553,11 +2553,9 @@ tbody.commit-list {
|
||||||
|
|
||||||
.code-diff-unified .add-code,
|
.code-diff-unified .add-code,
|
||||||
.code-diff-unified .add-code td,
|
.code-diff-unified .add-code td,
|
||||||
.code-diff-split .add-code .lines-num-new,
|
|
||||||
.code-diff-split .add-code .lines-type-marker-new,
|
.code-diff-split .add-code .lines-type-marker-new,
|
||||||
.code-diff-split .add-code .lines-escape-new,
|
.code-diff-split .add-code .lines-escape-new,
|
||||||
.code-diff-split .add-code .lines-code-new,
|
.code-diff-split .add-code .lines-code-new,
|
||||||
.code-diff-split .del-code .add-code.lines-num-new,
|
|
||||||
.code-diff-split .del-code .add-code.lines-type-marker-new,
|
.code-diff-split .del-code .add-code.lines-type-marker-new,
|
||||||
.code-diff-split .del-code .add-code.lines-escape-new,
|
.code-diff-split .del-code .add-code.lines-escape-new,
|
||||||
.code-diff-split .del-code .add-code.lines-code-new {
|
.code-diff-split .del-code .add-code.lines-code-new {
|
||||||
|
@ -2565,17 +2563,33 @@ tbody.commit-list {
|
||||||
border-color: var(--color-diff-added-row-border);
|
border-color: var(--color-diff-added-row-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-diff-split .del-code .lines-num-new,
|
|
||||||
.code-diff-split .del-code .lines-type-marker-new,
|
.code-diff-split .del-code .lines-type-marker-new,
|
||||||
.code-diff-split .del-code .lines-code-new,
|
.code-diff-split .del-code .lines-code-new,
|
||||||
.code-diff-split .del-code .lines-escape-new,
|
.code-diff-split .del-code .lines-escape-new,
|
||||||
.code-diff-split .add-code .lines-num-old,
|
|
||||||
.code-diff-split .add-code .lines-escape-old,
|
.code-diff-split .add-code .lines-escape-old,
|
||||||
.code-diff-split .add-code .lines-type-marker-old,
|
.code-diff-split .add-code .lines-type-marker-old,
|
||||||
.code-diff-split .add-code .lines-code-old {
|
.code-diff-split .add-code .lines-code-old {
|
||||||
background: var(--color-diff-inactive);
|
background: var(--color-diff-inactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-diff-split .add-code .lines-num.lines-num-old,
|
||||||
|
.code-diff-split .del-code .lines-num.lines-num-new {
|
||||||
|
background: var(--color-diff-inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-diff-unified .del-code .lines-num,
|
||||||
|
.code-diff-split .del-code .lines-num {
|
||||||
|
background: var(--color-diff-removed-linenum-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-diff-unified .add-code .lines-num,
|
||||||
|
.code-diff-split .add-code .lines-num,
|
||||||
|
.code-diff-split .del-code .add-code.lines-num {
|
||||||
|
background: var(--color-diff-added-linenum-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
.code-diff-split tbody tr td:nth-child(5),
|
.code-diff-split tbody tr td:nth-child(5),
|
||||||
.code-diff-split tbody tr td.add-comment-right {
|
.code-diff-split tbody tr td.add-comment-right {
|
||||||
border-left: 1px solid var(--color-secondary);
|
border-left: 1px solid var(--color-secondary);
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
/* red/green colorblind-friendly colors */
|
/* red/green colorblind-friendly colors */
|
||||||
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
|
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
|
||||||
:root {
|
:root {
|
||||||
--color-diff-added-word-bg: #388bfd66;
|
--color-diff-added-linenum-bg: #1979fd46;
|
||||||
--color-diff-added-row-bg: #388bfd26;
|
--color-diff-added-row-bg: #1979fd20;
|
||||||
|
--color-diff-added-word-bg: #1979fd66;
|
||||||
--color-diff-removed-word-bg: #db6d2866;
|
--color-diff-removed-linenum-bg: #c8622146;
|
||||||
--color-diff-removed-row-bg: #db6d2826;
|
--color-diff-removed-row-bg: #c8622120;
|
||||||
|
--color-diff-removed-word-bg: #c8622166;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,14 +143,16 @@
|
||||||
--color-grey-light: #818f9e;
|
--color-grey-light: #818f9e;
|
||||||
--color-gold: #b1983b;
|
--color-gold: #b1983b;
|
||||||
--color-white: #ffffff;
|
--color-white: #ffffff;
|
||||||
--color-diff-removed-word-bg: #6f3333;
|
--color-diff-added-linenum-bg: #274227;
|
||||||
--color-diff-added-word-bg: #3c653c;
|
--color-diff-added-row-bg: #203224;
|
||||||
--color-diff-removed-row-bg: #3c2626;
|
|
||||||
--color-diff-moved-row-bg: #818044;
|
|
||||||
--color-diff-added-row-bg: #283e2d;
|
|
||||||
--color-diff-removed-row-border: #634343;
|
|
||||||
--color-diff-moved-row-border: #bcca6f;
|
|
||||||
--color-diff-added-row-border: #314a37;
|
--color-diff-added-row-border: #314a37;
|
||||||
|
--color-diff-added-word-bg: #3c653c;
|
||||||
|
--color-diff-moved-row-bg: #818044;
|
||||||
|
--color-diff-moved-row-border: #bcca6f;
|
||||||
|
--color-diff-removed-linenum-bg: #482121;
|
||||||
|
--color-diff-removed-row-bg: #301e1e;
|
||||||
|
--color-diff-removed-row-border: #634343;
|
||||||
|
--color-diff-removed-word-bg: #6f3333;
|
||||||
--color-diff-inactive: #22282d;
|
--color-diff-inactive: #22282d;
|
||||||
--color-error-border: #a04141;
|
--color-error-border: #a04141;
|
||||||
--color-error-bg: #522;
|
--color-error-bg: #522;
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
/* red/green colorblind-friendly colors */
|
/* red/green colorblind-friendly colors */
|
||||||
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
|
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
|
||||||
:root {
|
:root {
|
||||||
--color-diff-added-word-bg: #54aeff66;
|
--color-diff-added-linenum-bg: #54aeff4d;
|
||||||
--color-diff-added-row-bg: #ddf4ff80;
|
--color-diff-added-row-bg: #ddf4ff80;
|
||||||
|
--color-diff-added-word-bg: #54aeff66;
|
||||||
--color-diff-removed-word-bg: #ffb77c80;
|
--color-diff-removed-linenum-bg: #ffb77c4d;
|
||||||
--color-diff-removed-row-bg: #fff1e580;
|
--color-diff-removed-row-bg: #fff1e580;
|
||||||
|
--color-diff-removed-word-bg: #ffb77c80;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,14 +143,16 @@
|
||||||
--color-grey-light: #7c838a;
|
--color-grey-light: #7c838a;
|
||||||
--color-gold: #a1882b;
|
--color-gold: #a1882b;
|
||||||
--color-white: #ffffff;
|
--color-white: #ffffff;
|
||||||
--color-diff-removed-word-bg: #fdb8c0;
|
--color-diff-added-linenum-bg: #d1f8d9;
|
||||||
--color-diff-added-word-bg: #acf2bd;
|
|
||||||
--color-diff-removed-row-bg: #ffeef0;
|
|
||||||
--color-diff-moved-row-bg: #f1f8d1;
|
|
||||||
--color-diff-added-row-bg: #e6ffed;
|
--color-diff-added-row-bg: #e6ffed;
|
||||||
--color-diff-removed-row-border: #f1c0c0;
|
|
||||||
--color-diff-moved-row-border: #d0e27f;
|
|
||||||
--color-diff-added-row-border: #e6ffed;
|
--color-diff-added-row-border: #e6ffed;
|
||||||
|
--color-diff-added-word-bg: #acf2bd;
|
||||||
|
--color-diff-moved-row-bg: #f1f8d1;
|
||||||
|
--color-diff-moved-row-border: #d0e27f;
|
||||||
|
--color-diff-removed-linenum-bg: #ffcecb;
|
||||||
|
--color-diff-removed-row-bg: #ffeef0;
|
||||||
|
--color-diff-removed-row-border: #f1c0c0;
|
||||||
|
--color-diff-removed-word-bg: #fdb8c0;
|
||||||
--color-diff-inactive: #f0f2f4;
|
--color-diff-inactive: #f0f2f4;
|
||||||
--color-error-border: #e0b4b4;
|
--color-error-border: #e0b4b4;
|
||||||
--color-error-bg: #fff6f6;
|
--color-error-bg: #fff6f6;
|
||||||
|
|
Loading…
Reference in New Issue