From c41bc4f1279c9e1e6e11d7b5fcfe7ef089fc7577 Mon Sep 17 00:00:00 2001
From: JakobDev <jakobdev@gmx.de>
Date: Wed, 26 Apr 2023 16:46:26 +0200
Subject: [PATCH] Display when a repo was archived (#22664)

This adds the date a repo is archived to Gitea and shows it in the UI
and API. A feature, that GitHub has been [introduced
recently](https://github.blog/changelog/2022-11-23-repository-archive-date-now-shown-in-ui/).

I currently don't know how to correctly deal with the Date in the
template, as different languages have different ways of writing a date.


![grafik](https://user-images.githubusercontent.com/15185051/234315187-7db5763e-d96e-4080-b894-9be178bfb6e1.png)

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 models/migrations/migrations.go  |  2 ++
 models/migrations/v1_20/v255.go  | 23 +++++++++++++++++++++++
 models/repo/archiver.go          |  9 ++++++++-
 models/repo/repo.go              |  5 +++--
 modules/structs/repo.go          |  1 +
 options/locale/locale_en-US.ini  |  1 +
 services/convert/repository.go   |  1 +
 templates/repo/diff/compare.tmpl |  6 +++++-
 templates/repo/empty.tmpl        |  6 +++++-
 templates/repo/home.tmpl         |  6 +++++-
 templates/swagger/v1_json.tmpl   |  5 +++++
 11 files changed, 59 insertions(+), 6 deletions(-)
 create mode 100644 models/migrations/v1_20/v255.go

diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 9de5931d71..1f1f43796c 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -487,6 +487,8 @@ var migrations = []Migration{
 	NewMigration("Fix ExternalTracker and ExternalWiki accessMode in owner and admin team", v1_20.FixExternalTrackerAndExternalWikiAccessModeInOwnerAndAdminTeam),
 	// v254 -> v255
 	NewMigration("Add ActionTaskOutput table", v1_20.AddActionTaskOutputTable),
+	// v255 -> v256
+	NewMigration("Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go
new file mode 100644
index 0000000000..14b70f8f96
--- /dev/null
+++ b/models/migrations/v1_20/v255.go
@@ -0,0 +1,23 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_20 //nolint
+
+import (
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/xorm"
+)
+
+func AddArchivedUnixToRepository(x *xorm.Engine) error {
+	type Repository struct {
+		ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT 0"`
+	}
+
+	if err := x.Sync(new(Repository)); err != nil {
+		return err
+	}
+
+	_, err := x.Exec("UPDATE repository SET archived_unix = updated_unix WHERE is_archived = ? AND archived_unix = 0", true)
+	return err
+}
diff --git a/models/repo/archiver.go b/models/repo/archiver.go
index 11ecaff34c..70f53cfe15 100644
--- a/models/repo/archiver.go
+++ b/models/repo/archiver.go
@@ -146,6 +146,13 @@ func FindRepoArchives(opts FindRepoArchiversOption) ([]*RepoArchiver, error) {
 // SetArchiveRepoState sets if a repo is archived
 func SetArchiveRepoState(repo *Repository, isArchived bool) (err error) {
 	repo.IsArchived = isArchived
-	_, err = db.GetEngine(db.DefaultContext).Where("id = ?", repo.ID).Cols("is_archived").NoAutoTime().Update(repo)
+
+	if isArchived {
+		repo.ArchivedUnix = timeutil.TimeStampNow()
+	} else {
+		repo.ArchivedUnix = timeutil.TimeStamp(0)
+	}
+
+	_, err = db.GetEngine(db.DefaultContext).ID(repo.ID).Cols("is_archived", "archived_unix").NoAutoTime().Update(repo)
 	return err
 }
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 266cbc288c..f9de6d493d 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -174,8 +174,9 @@ type Repository struct {
 	// Avatar: ID(10-20)-md5(32) - must fit into 64 symbols
 	Avatar string `xorm:"VARCHAR(64)"`
 
-	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
-	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+	CreatedUnix  timeutil.TimeStamp `xorm:"INDEX created"`
+	UpdatedUnix  timeutil.TimeStamp `xorm:"INDEX updated"`
+	ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT 0"`
 }
 
 func init() {
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 6d3e2c2909..259c230571 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -80,6 +80,7 @@ type Repository struct {
 	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
 	Updated                       time.Time        `json:"updated_at"`
+	ArchivedAt                    time.Time        `json:"archived_at"`
 	Permissions                   *Permission      `json:"permissions,omitempty"`
 	HasIssues                     bool             `json:"has_issues"`
 	InternalTracker               *InternalTracker `json:"internal_tracker,omitempty"`
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index ef9990d265..1fb68ff775 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -992,6 +992,7 @@ template.one_item = Must select at least one template item
 template.invalid = Must select a template repository
 
 archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
+archive.title_date = This repository has been archived on %s. You can view files and clone it, but cannot push or open issues/pull-requests.
 archive.issue.nocomment = This repo is archived. You cannot comment on issues.
 archive.pull.nocomment = This repo is archived. You cannot comment on pull requests.
 
diff --git a/services/convert/repository.go b/services/convert/repository.go
index a2a8570cc9..f470fd1656 100644
--- a/services/convert/repository.go
+++ b/services/convert/repository.go
@@ -183,6 +183,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc
 		DefaultBranch:                 repo.DefaultBranch,
 		Created:                       repo.CreatedUnix.AsTime(),
 		Updated:                       repo.UpdatedUnix.AsTime(),
+		ArchivedAt:                    repo.ArchivedUnix.AsTime(),
 		Permissions:                   permission,
 		HasIssues:                     hasIssues,
 		ExternalTracker:               externalTracker,
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl
index c7538a7969..3ec08b00ac 100644
--- a/templates/repo/diff/compare.tmpl
+++ b/templates/repo/diff/compare.tmpl
@@ -219,7 +219,11 @@
 				</div>
 			{{else if .Repository.IsArchived}}
 				<div class="ui warning message">
-					{{.locale.Tr "repo.archive.title"}}
+					{{if .Repository.ArchivedUnix.IsZero}}
+						{{.locale.Tr "repo.archive.title"}}
+					{{else}}
+						{{.locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix) | Safe}}
+					{{end}}
 				</div>
 			{{end}}
 			{{if $.IsSigned}}
diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl
index fbe61afeaa..ed726db512 100644
--- a/templates/repo/empty.tmpl
+++ b/templates/repo/empty.tmpl
@@ -7,7 +7,11 @@
 				{{template "base/alert" .}}
 				{{if .Repository.IsArchived}}
 					<div class="ui warning message">
-						{{.locale.Tr "repo.archive.title"}}
+						{{if .Repository.ArchivedUnix.IsZero}}
+							{{.locale.Tr "repo.archive.title"}}
+						{{else}}
+							{{.locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix) | Safe}}
+						{{end}}
 					</div>
 				{{end}}
 				{{if .Repository.IsBroken}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 69f7f03880..c47fa9d9ca 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -53,7 +53,11 @@
 		{{end}}
 		{{if .Repository.IsArchived}}
 			<div class="ui warning message">
-				{{.locale.Tr "repo.archive.title"}}
+				{{if .Repository.ArchivedUnix.IsZero}}
+					{{.locale.Tr "repo.archive.title"}}
+				{{else}}
+					{{.locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix) | Safe}}
+				{{end}}
 			</div>
 		{{end}}
 		{{template "repo/sub_menu" .}}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index bc403be446..19b5179927 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -20221,6 +20221,11 @@
           "type": "boolean",
           "x-go-name": "Archived"
         },
+        "archived_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "ArchivedAt"
+        },
         "avatar_url": {
           "type": "string",
           "x-go-name": "AvatarURL"