]> source.dussan.org Git - gitea.git/commitdiff
Add global setting how timestamps should be rendered (#28657)
authorYarden Shoham <git@yardenshoham.com>
Tue, 2 Jan 2024 01:25:30 +0000 (03:25 +0200)
committerGitHub <noreply@github.com>
Tue, 2 Jan 2024 01:25:30 +0000 (09:25 +0800)
- Resolves https://github.com/go-gitea/gitea/issues/22493
- Related to https://github.com/go-gitea/gitea/issues/4520

Some admins prefer all timestamps to display the full date instead of
relative time. They can do that now by setting

```ini
[ui]
PREFERRED_TIMESTAMP_TENSE = absolute
```

This setting is set to `mixed` by default, allowing dates to render as
"5 hours ago". Here are some screenshots of the UI with this setting set
to `absolute`:

![image](https://github.com/go-gitea/gitea/assets/20454870/f496457f-6afa-44be-a1e7-249ee5fe0706)

![image](https://github.com/go-gitea/gitea/assets/20454870/c03b14f5-063d-4e13-9780-76ab002d76a9)

![image](https://github.com/go-gitea/gitea/assets/20454870/f4b34e28-1546-4374-9199-c43348844edd)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: delvh <dev.lh@web.de>
custom/conf/app.example.ini
docs/content/administration/config-cheat-sheet.en-us.md
modules/setting/ui.go
modules/timeutil/datetime.go
modules/timeutil/datetime_test.go
modules/timeutil/since.go
web_src/js/components/DiffCommitSelector.vue

index 08f2e0d63fc66f09bea56ab582ef7eed98d47234..d58309f141bd50fdcc58b508afb4666e16a674ce 100644 (file)
@@ -1244,6 +1244,10 @@ LEVEL = Info
 ;; Change the sort type of the explore pages.
 ;; Default is "recentupdate", but you also have "alphabetically", "reverselastlogin", "newest", "oldest".
 ;EXPLORE_PAGING_DEFAULT_SORT = recentupdate
+;;
+;; The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`.
+;; `mixed` means most timestamps are rendered in relative time (i.e. 2 days ago).
+;PREFERRED_TIMESTAMP_TENSE = mixed
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
index beaa8cfb30245cb7e958f1f801354abc91bd80c2..e111ff6db62e0311f0f629181ae5d8dbad553bc4 100644 (file)
@@ -231,6 +231,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `ONLY_SHOW_RELEVANT_REPOS`: **false**: Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
     A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
 - `EXPLORE_PAGING_DEFAULT_SORT`: **recentupdate**: Change the sort type of the explore pages. Valid values are "recentupdate", "alphabetically", "reverselastlogin", "newest" and "oldest"
+- `PREFERRED_TIMESTAMP_TENSE`: **mixed**: The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`. `mixed` means most timestamps are rendered in relative time (i.e. 2 days ago).
 
 ### UI - Admin (`ui.admin`)
 
index f94e6206cd995c6e8afbb9b2dad5eae90b454ae4..2f9eef93c3bc9eb3c21682a9462b6a0d05fd26dd 100644 (file)
@@ -7,33 +7,35 @@ import (
        "time"
 
        "code.gitea.io/gitea/modules/container"
+       "code.gitea.io/gitea/modules/log"
 )
 
 // UI settings
 var UI = struct {
-       ExplorePagingNum      int
-       SitemapPagingNum      int
-       IssuePagingNum        int
-       RepoSearchPagingNum   int
-       MembersPagingNum      int
-       FeedMaxCommitNum      int
-       FeedPagingNum         int
-       PackagesPagingNum     int
-       GraphMaxCommitNum     int
-       CodeCommentLines      int
-       ReactionMaxUserNum    int
-       MaxDisplayFileSize    int64
-       ShowUserEmail         bool
-       DefaultShowFullName   bool
-       DefaultTheme          string
-       Themes                []string
-       Reactions             []string
-       ReactionsLookup       container.Set[string] `ini:"-"`
-       CustomEmojis          []string
-       CustomEmojisMap       map[string]string `ini:"-"`
-       SearchRepoDescription bool
-       OnlyShowRelevantRepos bool
-       ExploreDefaultSort    string `ini:"EXPLORE_PAGING_DEFAULT_SORT"`
+       ExplorePagingNum        int
+       SitemapPagingNum        int
+       IssuePagingNum          int
+       RepoSearchPagingNum     int
+       MembersPagingNum        int
+       FeedMaxCommitNum        int
+       FeedPagingNum           int
+       PackagesPagingNum       int
+       GraphMaxCommitNum       int
+       CodeCommentLines        int
+       ReactionMaxUserNum      int
+       MaxDisplayFileSize      int64
+       ShowUserEmail           bool
+       DefaultShowFullName     bool
+       DefaultTheme            string
+       Themes                  []string
+       Reactions               []string
+       ReactionsLookup         container.Set[string] `ini:"-"`
+       CustomEmojis            []string
+       CustomEmojisMap         map[string]string `ini:"-"`
+       SearchRepoDescription   bool
+       OnlyShowRelevantRepos   bool
+       ExploreDefaultSort      string `ini:"EXPLORE_PAGING_DEFAULT_SORT"`
+       PreferredTimestampTense string
 
        AmbiguousUnicodeDetection bool
 
@@ -67,23 +69,24 @@ var UI = struct {
                Keywords    string
        } `ini:"ui.meta"`
 }{
-       ExplorePagingNum:    20,
-       SitemapPagingNum:    20,
-       IssuePagingNum:      20,
-       RepoSearchPagingNum: 20,
-       MembersPagingNum:    20,
-       FeedMaxCommitNum:    5,
-       FeedPagingNum:       20,
-       PackagesPagingNum:   20,
-       GraphMaxCommitNum:   100,
-       CodeCommentLines:    4,
-       ReactionMaxUserNum:  10,
-       MaxDisplayFileSize:  8388608,
-       DefaultTheme:        `gitea-auto`,
-       Themes:              []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
-       Reactions:           []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
-       CustomEmojis:        []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
-       CustomEmojisMap:     map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
+       ExplorePagingNum:        20,
+       SitemapPagingNum:        20,
+       IssuePagingNum:          20,
+       RepoSearchPagingNum:     20,
+       MembersPagingNum:        20,
+       FeedMaxCommitNum:        5,
+       FeedPagingNum:           20,
+       PackagesPagingNum:       20,
+       GraphMaxCommitNum:       100,
+       CodeCommentLines:        4,
+       ReactionMaxUserNum:      10,
+       MaxDisplayFileSize:      8388608,
+       DefaultTheme:            `gitea-auto`,
+       Themes:                  []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
+       Reactions:               []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
+       CustomEmojis:            []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
+       CustomEmojisMap:         map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
+       PreferredTimestampTense: "mixed",
 
        AmbiguousUnicodeDetection: true,
 
@@ -142,6 +145,10 @@ func loadUIFrom(rootCfg ConfigProvider) {
        UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
        UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
 
+       if UI.PreferredTimestampTense != "mixed" && UI.PreferredTimestampTense != "absolute" {
+               log.Fatal("ui.PREFERRED_TIMESTAMP_TENSE must be either 'mixed' or 'absolute'")
+       }
+
        // OnlyShowRelevantRepos=false is important for many private/enterprise instances,
        // because many private repositories do not have "description/topic", users just want to search by their names.
        UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false)
index 83170b374b54c670702ddd2760095f3a936be527..d254a56a74daecc23a6ae2edeba5c94e6cc7e111 100644 (file)
@@ -7,11 +7,12 @@ import (
        "fmt"
        "html"
        "html/template"
+       "strings"
        "time"
 )
 
 // DateTime renders an absolute time HTML element by datetime.
-func DateTime(format string, datetime any) template.HTML {
+func DateTime(format string, datetime any, attrs ...string) template.HTML {
        if p, ok := datetime.(*time.Time); ok {
                datetime = *p
        }
@@ -48,13 +49,15 @@ func DateTime(format string, datetime any) template.HTML {
                panic(fmt.Sprintf("Unsupported time type %T", datetime))
        }
 
+       extraAttrs := strings.Join(attrs, " ")
+
        switch format {
        case "short":
-               return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
+               return template.HTML(fmt.Sprintf(`<relative-time %s format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="%s">%s</relative-time>`, extraAttrs, datetimeEscaped, textEscaped))
        case "long":
-               return template.HTML(fmt.Sprintf(`<relative-time format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
+               return template.HTML(fmt.Sprintf(`<relative-time %s format="datetime" year="numeric" month="long" day="numeric" weekday="" datetime="%s">%s</relative-time>`, extraAttrs, datetimeEscaped, textEscaped))
        case "full":
-               return template.HTML(fmt.Sprintf(`<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, datetimeEscaped, textEscaped))
+               return template.HTML(fmt.Sprintf(`<relative-time %s format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="%s">%s</relative-time>`, extraAttrs, datetimeEscaped, textEscaped))
        }
        panic(fmt.Sprintf("Unsupported format %s", format))
 }
index f44b7aaae3c1bfec63c5f0407318f684c963d85b..387e6274a785a1e7f10177304b1e73a7d14a1e58 100644 (file)
@@ -29,17 +29,17 @@ func TestDateTime(t *testing.T) {
        assert.EqualValues(t, "-", DateTime("short", TimeStamp(0)))
 
        actual := DateTime("short", "invalid")
-       assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="invalid">invalid</relative-time>`, actual)
+       assert.EqualValues(t, `<relative-time  format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="invalid">invalid</relative-time>`, actual)
 
        actual = DateTime("short", refTimeStr)
-       assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</relative-time>`, actual)
+       assert.EqualValues(t, `<relative-time  format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</relative-time>`, actual)
 
        actual = DateTime("short", refTime)
-       assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01</relative-time>`, actual)
+       assert.EqualValues(t, `<relative-time  format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2018-01-01T00:00:00Z">2018-01-01</relative-time>`, actual)
 
        actual = DateTime("short", refTimeStamp)
-       assert.EqualValues(t, `<relative-time format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2017-12-31T19:00:00-05:00">2017-12-31</relative-time>`, actual)
+       assert.EqualValues(t, `<relative-time  format="datetime" year="numeric" month="short" day="numeric" weekday="" datetime="2017-12-31T19:00:00-05:00">2017-12-31</relative-time>`, actual)
 
        actual = DateTime("full", refTimeStamp)
-       assert.EqualValues(t, `<relative-time format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
+       assert.EqualValues(t, `<relative-time  format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
 }
index 04fcff54a33845d9a826b09bd1c1532df4540344..1cb3c4f288b2b54132cf7a4984587935fb9781c1 100644 (file)
@@ -9,6 +9,7 @@ import (
        "strings"
        "time"
 
+       "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/modules/translation"
 )
 
@@ -132,6 +133,9 @@ func timeSinceUnix(then, now time.Time, lang translation.Locale) template.HTML {
 
 // TimeSince renders relative time HTML given a time.Time
 func TimeSince(then time.Time, lang translation.Locale) template.HTML {
+       if setting.UI.PreferredTimestampTense == "absolute" {
+               return DateTime("full", then, `class="time-since"`)
+       }
        return timeSinceUnix(then, time.Now(), lang)
 }
 
index 439840c30639f91bc455478ee3f477df7b3ea199..54877a18c0fc5a60d9cca49a7505ef035d842500 100644 (file)
@@ -247,6 +247,7 @@ export default {
             <div class="gt-ellipsis text light-2">
               {{ commit.committer_or_author_name }}
               <span class="text right">
+                <!-- TODO: make this respect the PreferredTimestampTense setting -->
                 <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time>
               </span>
             </div>