Use AfterLoad instead of AfterSet on Structs (#2628)

* use AfterLoad instead of AfterSet on Structs

* fix the comments on AfterLoad

* fix the comments on action AfterLoad
tokarchuk/v1.17
Lunny Xiao 7 years ago committed by Lauris BH
parent 1ad902d529
commit a8717e5e3a
  1. 10
      models/action.go
  2. 10
      models/admin.go
  3. 14
      models/attachment.go
  4. 20
      models/gpg_key.go
  5. 15
      models/issue.go
  6. 37
      models/issue_comment.go
  7. 30
      models/issue_milestone.go
  8. 12
      models/issue_stopwatch.go
  9. 18
      models/issue_tracked_time.go
  10. 11
      models/lfs.go
  11. 12
      models/login_source.go
  12. 15
      models/pull.go
  13. 10
      models/release.go
  14. 31
      models/repo.go
  15. 20
      models/repo_mirror.go
  16. 9
      models/repo_unit.go
  17. 36
      models/ssh_key.go
  18. 12
      models/status.go
  19. 19
      models/token.go
  20. 15
      models/twofactor.go
  21. 15
      models/user.go
  22. 55
      models/webhook.go
  23. 17
      vendor/github.com/go-xorm/xorm/engine.go
  24. 2
      vendor/github.com/go-xorm/xorm/helpers.go
  25. 40
      vendor/github.com/go-xorm/xorm/processors.go
  26. 10
      vendor/github.com/go-xorm/xorm/rows.go
  27. 68
      vendor/github.com/go-xorm/xorm/session.go
  28. 4
      vendor/github.com/go-xorm/xorm/session_delete.go
  29. 7
      vendor/github.com/go-xorm/xorm/session_find.go
  30. 9
      vendor/github.com/go-xorm/xorm/session_get.go
  31. 4
      vendor/github.com/go-xorm/xorm/session_insert.go
  32. 50
      vendor/github.com/go-xorm/xorm/session_iterate.go
  33. 2
      vendor/github.com/go-xorm/xorm/session_update.go
  34. 2
      vendor/github.com/go-xorm/xorm/statement.go
  35. 6
      vendor/vendor.json

@ -16,7 +16,6 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
"code.gitea.io/git" "code.gitea.io/git"
api "code.gitea.io/sdk/gitea" api "code.gitea.io/sdk/gitea"
@ -91,12 +90,9 @@ type Action struct {
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64 `xorm:"INDEX created"`
} }
// AfterSet updates the webhook object upon setting a column. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (a *Action) AfterSet(colName string, _ xorm.Cell) { func (a *Action) AfterLoad() {
switch colName { a.Created = time.Unix(a.CreatedUnix, 0).Local()
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
} }
// GetOpType gets the ActionType of this action. // GetOpType gets the ActionType of this action.

@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm"
) )
//NoticeType describes the notice type //NoticeType describes the notice type
@ -32,12 +31,9 @@ type Notice struct {
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64 `xorm:"INDEX created"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (n *Notice) AfterSet(colName string, _ xorm.Cell) { func (n *Notice) AfterLoad() {
switch colName { n.Created = time.Unix(n.CreatedUnix, 0).Local()
case "created_unix":
n.Created = time.Unix(n.CreatedUnix, 0).Local()
}
} }
// TrStr returns a translation format string. // TrStr returns a translation format string.

@ -12,7 +12,6 @@ import (
"path" "path"
"time" "time"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid" gouuid "github.com/satori/go.uuid"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -31,13 +30,10 @@ type Attachment struct {
CreatedUnix int64 `xorm:"created"` CreatedUnix int64 `xorm:"created"`
} }
// AfterSet is invoked from XORM after setting the value of a field of // AfterLoad is invoked from XORM after setting the value of a field of
// this object. // this object.
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { func (a *Attachment) AfterLoad() {
switch colName { a.Created = time.Unix(a.CreatedUnix, 0).Local()
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
} }
// IncreaseDownloadCount is update download count + 1 // IncreaseDownloadCount is update download count + 1
@ -133,6 +129,10 @@ func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
// GetAttachmentsByCommentID returns all attachments if comment by given ID. // GetAttachmentsByCommentID returns all attachments if comment by given ID.
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID)
}
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10) attachments := make([]*Attachment, 0, 10)
return attachments, x.Where("comment_id=?", commentID).Find(&attachments) return attachments, x.Where("comment_id=?", commentID).Find(&attachments)
} }

@ -52,17 +52,15 @@ func (key *GPGKey) BeforeInsert() {
key.CreatedUnix = key.Created.Unix() key.CreatedUnix = key.Created.Unix()
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *GPGKey) AfterSet(colName string, _ xorm.Cell) { func (key *GPGKey) AfterLoad(session *xorm.Session) {
switch colName { key.Added = time.Unix(key.AddedUnix, 0).Local()
case "key_id": key.Expired = time.Unix(key.ExpiredUnix, 0).Local()
x.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey) key.Created = time.Unix(key.CreatedUnix, 0).Local()
case "added_unix":
key.Added = time.Unix(key.AddedUnix, 0).Local() err := session.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey)
case "expired_unix": if err != nil {
key.Expired = time.Unix(key.ExpiredUnix, 0).Local() log.Error(3, "Find Sub GPGkeys[%d]: %v", key.KeyID, err)
case "created_unix":
key.Created = time.Unix(key.CreatedUnix, 0).Local()
} }
} }

@ -67,17 +67,12 @@ func (issue *Issue) BeforeUpdate() {
issue.DeadlineUnix = issue.Deadline.Unix() issue.DeadlineUnix = issue.Deadline.Unix()
} }
// AfterSet is invoked from XORM after setting the value of a field of // AfterLoad is invoked from XORM after setting the value of a field of
// this object. // this object.
func (issue *Issue) AfterSet(colName string, _ xorm.Cell) { func (issue *Issue) AfterLoad() {
switch colName { issue.Deadline = time.Unix(issue.DeadlineUnix, 0).Local()
case "deadline_unix": issue.Created = time.Unix(issue.CreatedUnix, 0).Local()
issue.Deadline = time.Unix(issue.DeadlineUnix, 0).Local() issue.Updated = time.Unix(issue.UpdatedUnix, 0).Local()
case "created_unix":
issue.Created = time.Unix(issue.CreatedUnix, 0).Local()
case "updated_unix":
issue.Updated = time.Unix(issue.UpdatedUnix, 0).Local()
}
} }
func (issue *Issue) loadRepo(e Engine) (err error) { func (issue *Issue) loadRepo(e Engine) (err error) {

@ -112,30 +112,25 @@ type Comment struct {
ShowTag CommentTag `xorm:"-"` ShowTag CommentTag `xorm:"-"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (c *Comment) AfterSet(colName string, _ xorm.Cell) { func (c *Comment) AfterLoad(session *xorm.Session) {
c.Created = time.Unix(c.CreatedUnix, 0).Local()
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
var err error var err error
switch colName { c.Attachments, err = getAttachmentsByCommentID(session, c.ID)
case "id": if err != nil {
c.Attachments, err = GetAttachmentsByCommentID(c.ID) log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err)
if err != nil { }
log.Error(3, "GetAttachmentsByCommentID[%d]: %v", c.ID, err)
}
case "poster_id": c.Poster, err = getUserByID(session, c.PosterID)
c.Poster, err = GetUserByID(c.PosterID) if err != nil {
if err != nil { if IsErrUserNotExist(err) {
if IsErrUserNotExist(err) { c.PosterID = -1
c.PosterID = -1 c.Poster = NewGhostUser()
c.Poster = NewGhostUser() } else {
} else { log.Error(3, "getUserByID[%d]: %v", c.ID, err)
log.Error(3, "GetUserByID[%d]: %v", c.ID, err)
}
} }
case "created_unix":
c.Created = time.Unix(c.CreatedUnix, 0).Local()
case "updated_unix":
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
} }
} }

@ -51,27 +51,21 @@ func (m *Milestone) BeforeUpdate() {
m.ClosedDateUnix = m.ClosedDate.Unix() m.ClosedDateUnix = m.ClosedDate.Unix()
} }
// AfterSet is invoked from XORM after setting the value of a field of // AfterLoad is invoked from XORM after setting the value of a field of
// this object. // this object.
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { func (m *Milestone) AfterLoad() {
switch colName { m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
case "num_closed_issues": m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues if m.Deadline.Year() == 9999 {
return
case "deadline_unix": }
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 {
return
}
m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true
}
case "closed_date_unix": m.DeadlineString = m.Deadline.Format("2006-01-02")
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local() if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true
} }
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
} }
// State returns string representation of milestone status. // State returns string representation of milestone status.

@ -7,8 +7,6 @@ package models
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/go-xorm/xorm"
) )
// Stopwatch represents a stopwatch for time tracking. // Stopwatch represents a stopwatch for time tracking.
@ -26,13 +24,9 @@ func (s *Stopwatch) BeforeInsert() {
s.CreatedUnix = time.Now().Unix() s.CreatedUnix = time.Now().Unix()
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (s *Stopwatch) AfterSet(colName string, _ xorm.Cell) { func (s *Stopwatch) AfterLoad() {
switch colName { s.Created = time.Unix(s.CreatedUnix, 0).Local()
case "created_unix":
s.Created = time.Unix(s.CreatedUnix, 0).Local()
}
} }
func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) { func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {

@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
) )
// TrackedTime represents a time that was spent for a specific issue. // TrackedTime represents a time that was spent for a specific issue.
@ -17,22 +16,13 @@ type TrackedTime struct {
IssueID int64 `xorm:"INDEX" json:"issue_id"` IssueID int64 `xorm:"INDEX" json:"issue_id"`
UserID int64 `xorm:"INDEX" json:"user_id"` UserID int64 `xorm:"INDEX" json:"user_id"`
Created time.Time `xorm:"-" json:"created"` Created time.Time `xorm:"-" json:"created"`
CreatedUnix int64 `json:"-"` CreatedUnix int64 `xorm:"created" json:"-"`
Time int64 `json:"time"` Time int64 `json:"time"`
} }
// BeforeInsert will be invoked by XORM before inserting a record // AfterLoad is invoked from XORM after setting the values of all fields of this object.
// representing this object. func (t *TrackedTime) AfterLoad() {
func (t *TrackedTime) BeforeInsert() { t.Created = time.Unix(t.CreatedUnix, 0).Local()
t.CreatedUnix = time.Now().Unix()
}
// AfterSet is invoked from XORM after setting the value of a field of this object.
func (t *TrackedTime) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
t.Created = time.Unix(t.CreatedUnix, 0).Local()
}
} }
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored. // FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.

@ -3,8 +3,6 @@ package models
import ( import (
"errors" "errors"
"time" "time"
"github.com/go-xorm/xorm"
) )
// LFSMetaObject stores metadata for LFS tracked files. // LFSMetaObject stores metadata for LFS tracked files.
@ -109,10 +107,7 @@ func RemoveLFSMetaObjectByOid(oid string) error {
return sess.Commit() return sess.Commit()
} }
// AfterSet stores the LFSMetaObject creation time in the database as local time. // AfterLoad stores the LFSMetaObject creation time in the database as local time.
func (m *LFSMetaObject) AfterSet(colName string, _ xorm.Cell) { func (m *LFSMetaObject) AfterLoad() {
switch colName { m.Created = time.Unix(m.CreatedUnix, 0).Local()
case "created_unix":
m.Created = time.Unix(m.CreatedUnix, 0).Local()
}
} }

@ -183,14 +183,10 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
} }
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (source *LoginSource) AfterSet(colName string, _ xorm.Cell) { func (source *LoginSource) AfterLoad() {
switch colName { source.Created = time.Unix(source.CreatedUnix, 0).Local()
case "created_unix": source.Updated = time.Unix(source.UpdatedUnix, 0).Local()
source.Created = time.Unix(source.CreatedUnix, 0).Local()
case "updated_unix":
source.Updated = time.Unix(source.UpdatedUnix, 0).Local()
}
} }
// TypeName return name of this login source type. // TypeName return name of this login source type.

@ -80,17 +80,14 @@ func (pr *PullRequest) BeforeUpdate() {
pr.MergedUnix = pr.Merged.Unix() pr.MergedUnix = pr.Merged.Unix()
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
// Note: don't try to get Issue because will end up recursive querying. // Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) { func (pr *PullRequest) AfterLoad() {
switch colName { if !pr.HasMerged {
case "merged_unix": return
if !pr.HasMerged {
return
}
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
} }
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
} }
// Note: don't try to get Issue because will end up recursive querying. // Note: don't try to get Issue because will end up recursive querying.

@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/sdk/gitea" api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
) )
// Release represents a release of repository. // Release represents a release of repository.
@ -50,12 +49,9 @@ func (r *Release) BeforeInsert() {
} }
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (r *Release) AfterSet(colName string, _ xorm.Cell) { func (r *Release) AfterLoad() {
switch colName { r.Created = time.Unix(r.CreatedUnix, 0).Local()
case "created_unix":
r.Created = time.Unix(r.CreatedUnix, 0).Local()
}
} }
func (r *Release) loadAttributes(e Engine) error { func (r *Release) loadAttributes(e Engine) error {

@ -216,25 +216,18 @@ type Repository struct {
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { func (repo *Repository) AfterLoad() {
switch colName { // FIXME: use models migration to solve all at once.
case "default_branch": if len(repo.DefaultBranch) == 0 {
// FIXME: use models migration to solve all at once. repo.DefaultBranch = "master"
if len(repo.DefaultBranch) == 0 { }
repo.DefaultBranch = "master"
} repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
case "num_closed_issues": repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls
repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
case "num_closed_pulls": repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls repo.Updated = time.Unix(repo.UpdatedUnix, 0)
case "num_closed_milestones":
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
case "created_unix":
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
case "updated_unix":
repo.Updated = time.Unix(repo.UpdatedUnix, 0)
}
} }
// MustOwner always returns a valid *User object to avoid // MustOwner always returns a valid *User object to avoid

@ -55,24 +55,20 @@ func (m *Mirror) BeforeUpdate() {
} }
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) { func (m *Mirror) AfterLoad(session *xorm.Session) {
if m == nil { if m == nil {
return return
} }
var err error var err error
switch colName { m.Repo, err = getRepositoryByID(session, m.RepoID)
case "repo_id": if err != nil {
m.Repo, err = GetRepositoryByID(m.RepoID) log.Error(3, "getRepositoryByID[%d]: %v", m.ID, err)
if err != nil {
log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err)
}
case "updated_unix":
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
case "next_update_unix":
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
} }
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
} }
// ScheduleNextUpdate calculates and sets next update time. // ScheduleNextUpdate calculates and sets next update time.

@ -106,12 +106,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
} }
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (r *RepoUnit) AfterSet(colName string, _ xorm.Cell) { func (r *RepoUnit) AfterLoad() {
switch colName { r.Created = time.Unix(r.CreatedUnix, 0).Local()
case "created_unix":
r.Created = time.Unix(r.CreatedUnix, 0).Local()
}
} }
// Unit returns Unit // Unit returns Unit

@ -56,22 +56,18 @@ type PublicKey struct {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"` CreatedUnix int64 `xorm:"created"`
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet. Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"` UpdatedUnix int64 `xorm:"updated"`
HasRecentActivity bool `xorm:"-"` HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"` HasUsed bool `xorm:"-"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *PublicKey) AfterSet(colName string, _ xorm.Cell) { func (key *PublicKey) AfterLoad() {
switch colName { key.Created = time.Unix(key.CreatedUnix, 0).Local()
case "created_unix": key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.Created = time.Unix(key.CreatedUnix, 0).Local() key.HasUsed = key.Updated.After(key.Created)
case "updated_unix": key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.HasUsed = key.Updated.After(key.Created)
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
} }
// OmitEmail returns content of public key without email address. // OmitEmail returns content of public key without email address.
@ -612,22 +608,18 @@ type DeployKey struct {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"` CreatedUnix int64 `xorm:"created"`
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet. Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"` UpdatedUnix int64 `xorm:"updated"`
HasRecentActivity bool `xorm:"-"` HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"` HasUsed bool `xorm:"-"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *DeployKey) AfterSet(colName string, _ xorm.Cell) { func (key *DeployKey) AfterLoad() {
switch colName { key.Created = time.Unix(key.CreatedUnix, 0).Local()
case "created_unix": key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.Created = time.Unix(key.CreatedUnix, 0).Local() key.HasUsed = key.Updated.After(key.Created)
case "updated_unix": key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.HasUsed = key.Updated.After(key.Created)
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
} }
// GetContent gets associated public key content. // GetContent gets associated public key content.

@ -71,15 +71,11 @@ type CommitStatus struct {
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`
} }
// AfterSet is invoked from XORM after setting the value of a field of // AfterLoad is invoked from XORM after setting the value of a field of
// this object. // this object.
func (status *CommitStatus) AfterSet(colName string, _ xorm.Cell) { func (status *CommitStatus) AfterLoad() {
switch colName { status.Created = time.Unix(status.CreatedUnix, 0).Local()
case "created_unix": status.Updated = time.Unix(status.UpdatedUnix, 0).Local()
status.Created = time.Unix(status.CreatedUnix, 0).Local()
case "updated_unix":
status.Updated = time.Unix(status.UpdatedUnix, 0).Local()
}
} }
func (status *CommitStatus) loadRepo(e Engine) (err error) { func (status *CommitStatus) loadRepo(e Engine) (err error) {

@ -7,7 +7,6 @@ package models
import ( import (
"time" "time"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid" gouuid "github.com/satori/go.uuid"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@ -22,22 +21,18 @@ type AccessToken struct {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet. Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`
HasRecentActivity bool `xorm:"-"` HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"` HasUsed bool `xorm:"-"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) { func (t *AccessToken) AfterLoad() {
switch colName { t.Created = time.Unix(t.CreatedUnix, 0).Local()
case "created_unix": t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.Created = time.Unix(t.CreatedUnix, 0).Local() t.HasUsed = t.Updated.After(t.Created)
case "updated_unix": t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.HasUsed = t.Updated.After(t.Created)
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
} }
// NewAccessToken creates new access token. // NewAccessToken creates new access token.

@ -11,7 +11,6 @@ import (
"time" "time"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@ -27,18 +26,14 @@ type TwoFactor struct {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet. Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (t *TwoFactor) AfterSet(colName string, _ xorm.Cell) { func (t *TwoFactor) AfterLoad() {
switch colName { t.Created = time.Unix(t.CreatedUnix, 0).Local()
case "created_unix": t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.Created = time.Unix(t.CreatedUnix, 0).Local()
case "updated_unix":
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
}
} }
// GenerateScratchToken recreates the scratch token the user is using. // GenerateScratchToken recreates the scratch token the user is using.

@ -153,16 +153,11 @@ func (u *User) UpdateDiffViewStyle(style string) error {
return UpdateUserCols(u, "diff_view_style") return UpdateUserCols(u, "diff_view_style")
} }
// AfterSet is invoked from XORM after setting the value of a field of this object. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (u *User) AfterSet(colName string, _ xorm.Cell) { func (u *User) AfterLoad() {
switch colName { u.Created = time.Unix(u.CreatedUnix, 0).Local()
case "created_unix": u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
u.Created = time.Unix(u.CreatedUnix, 0).Local() u.LastLogin = time.Unix(u.LastLoginUnix, 0).Local()
case "updated_unix":
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
case "last_login_unix":
u.LastLogin = time.Unix(u.LastLoginUnix, 0).Local()
}
} }
// getEmail returns an noreply email, if the user has set to keep his // getEmail returns an noreply email, if the user has set to keep his

@ -19,7 +19,6 @@ import (
"code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/sync"
api "code.gitea.io/sdk/gitea" api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid" gouuid "github.com/satori/go.uuid"
) )
@ -112,20 +111,15 @@ type Webhook struct {
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`
} }
// AfterSet updates the webhook object upon setting a column // AfterLoad updates the webhook object upon setting a column
func (w *Webhook) AfterSet(colName string, _ xorm.Cell) { func (w *Webhook) AfterLoad() {
var err error w.HookEvent = &HookEvent{}
switch colName { if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
case "events": log.Error(3, "Unmarshal[%d]: %v", w.ID, err)
w.HookEvent = &HookEvent{}
if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error(3, "Unmarshal[%d]: %v", w.ID, err)
}
case "created_unix":
w.Created = time.Unix(w.CreatedUnix, 0).Local()
case "updated_unix":
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
} }
w.Created = time.Unix(w.CreatedUnix, 0).Local()
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
} }
// GetSlackHook returns slack metadata // GetSlackHook returns slack metadata
@ -432,32 +426,17 @@ func (t *HookTask) BeforeUpdate() {
} }
} }
// AfterSet updates the webhook object upon setting a column // AfterLoad updates the webhook object upon setting a column
func (t *HookTask) AfterSet(colName string, _ xorm.Cell) { func (t *HookTask) AfterLoad() {
var err error t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
switch colName {
case "delivered":
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
case "request_content":
if len(t.RequestContent) == 0 {
return
}
t.RequestInfo = &HookRequest{}
if err = json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
log.Error(3, "Unmarshal[%d]: %v", t.ID, err)
}
case "response_content": if len(t.RequestContent) == 0 {
if len(t.ResponseContent) == 0 { return
return }
}
t.ResponseInfo = &HookResponse{} t.RequestInfo = &HookRequest{}
if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil { if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
log.Error(3, "Unmarshal [%d]: %v", t.ID, err) log.Error(3, "Unmarshal[%d]: %v", t.ID, err)
}
} }
} }

@ -1516,10 +1516,14 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
return results, lastError return results, lastError
} }
// NowTime2 return current time // nowTime return current time
func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) { func (engine *Engine) nowTime(col *core.Column) (interface{}, time.Time) {
t := time.Now() t := time.Now()
return engine.formatTime(sqlTypeName, t.In(engine.DatabaseTZ)), t.In(engine.TZLocation) var tz = engine.DatabaseTZ
if !col.DisableTimeZone && col.TimeZone != nil {
tz = col.TimeZone
}
return engine.formatTime(col.SQLType.Name, t.In(tz)), t.In(engine.TZLocation)
} }
func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) { func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
@ -1574,3 +1578,10 @@ func (engine *Engine) CondDeleted(colName string) builder.Cond {
} }
return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1}) return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
} }
// BufferSize sets buffer size for iterate
func (engine *Engine) BufferSize(size int) *Session {
session := engine.NewSession()
session.isAutoClose = true
return session.BufferSize(size)
}

@ -422,7 +422,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
// if time is non-empty, then set to auto time // if time is non-empty, then set to auto time
val, t := session.engine.NowTime2(col.SQLType.Name) val, t := session.engine.nowTime(col)
args = append(args, val) args = append(args, val)
var colName = col.Name var colName = col.Name

@ -29,13 +29,6 @@ type AfterSetProcessor interface {
AfterSet(string, Cell) AfterSet(string, Cell)
} }
// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
//// Executed before an object is validated
//type BeforeValidateProcessor interface {
// BeforeValidate()
//}
// --
// AfterInsertProcessor executed after an object is persisted to the database // AfterInsertProcessor executed after an object is persisted to the database
type AfterInsertProcessor interface { type AfterInsertProcessor interface {
AfterInsert() AfterInsert()
@ -50,3 +43,36 @@ type AfterUpdateProcessor interface {
type AfterDeleteProcessor interface { type AfterDeleteProcessor interface {
AfterDelete() AfterDelete()
} }
// AfterLoadProcessor executed after an ojbect has been loaded from database
type AfterLoadProcessor interface {
AfterLoad()
}
// AfterLoadSessionProcessor executed after an ojbect has been loaded from database with session parameter
type AfterLoadSessionProcessor interface {
AfterLoad(*Session)
}
type executedProcessorFunc func(*Session, interface{}) error
type executedProcessor struct {
fun executedProcessorFunc
session *Session
bean interface{}
}
func (executor *executedProcessor) execute() error {
return executor.fun(executor.session, executor.bean)
}
func (session *Session) executeProcessors() error {
processors := session.afterProcessors
session.afterProcessors = make([]executedProcessor, 0)
for _, processor := range processors {
if err := processor.execute(); err != nil {
return err
}
}
return nil
}

@ -99,13 +99,17 @@ func (rows *Rows) Scan(bean interface{}) error {
return err return err
} }
scanResults, err := rows.session.row2Slice(rows.rows, rows.fields, len(rows.fields), bean) scanResults, err := rows.session.row2Slice(rows.rows, rows.fields, bean)
if err != nil { if err != nil {
return err return err
} }
_, err = rows.session.slice2Bean(scanResults, rows.fields, len(rows.fields), bean, &dataStruct, rows.session.statement.RefTable) _, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable)
return err if err != nil {
return err
}
return rows.session.executeProcessors()
} }
// Close session if session.IsAutoClose is true, and claimed any opened resources // Close session if session.IsAutoClose is true, and claimed any opened resources

@ -41,6 +41,8 @@ type Session struct {
beforeClosures []func(interface{}) beforeClosures []func(interface{})
afterClosures []func(interface{}) afterClosures []func(interface{})
afterProcessors []executedProcessor
prepareStmt bool prepareStmt bool
stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
@ -75,6 +77,8 @@ func (session *Session) Init() {
session.beforeClosures = make([]func(interface{}), 0) session.beforeClosures = make([]func(interface{}), 0)
session.afterClosures = make([]func(interface{}), 0) session.afterClosures = make([]func(interface{}), 0)
session.afterProcessors = make([]executedProcessor, 0)
session.lastSQL = "" session.lastSQL = ""
session.lastSQLArgs = []interface{}{} session.lastSQLArgs = []interface{}{}
} }
@ -296,37 +300,40 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
// Cell cell is a result of one column field // Cell cell is a result of one column field
type Cell *interface{} type Cell *interface{}
func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int, func (session *Session) rows2Beans(rows *core.Rows, fields []string,
table *core.Table, newElemFunc func([]string) reflect.Value, table *core.Table, newElemFunc func([]string) reflect.Value,
sliceValueSetFunc func(*reflect.Value, core.PK) error) error { sliceValueSetFunc func(*reflect.Value, core.PK) error) error {
for rows.Next() { for rows.Next() {
var newValue = newElemFunc(fields) var newValue = newElemFunc(fields)
bean := newValue.Interface() bean := newValue.Interface()
dataStruct := rValue(bean) dataStruct := newValue.Elem()
// handle beforeClosures // handle beforeClosures
scanResults, err := session.row2Slice(rows, fields, fieldsCount, bean) scanResults, err := session.row2Slice(rows, fields, bean)
if err != nil { if err != nil {
return err return err
} }
pk, err := session.slice2Bean(scanResults, fields, fieldsCount, bean, &dataStruct, table) pk, err := session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
if err != nil {
return err
}
err = sliceValueSetFunc(&newValue, pk)
if err != nil { if err != nil {
return err return err
} }
session.afterProcessors = append(session.afterProcessors, executedProcessor{
fun: func(*Session, interface{}) error {
return sliceValueSetFunc(&newValue, pk)
},
session: session,
bean: bean,
})
} }
return nil return nil
} }
func (session *Session) row2Slice(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) ([]interface{}, error) { func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interface{}) ([]interface{}, error) {
for _, closure := range session.beforeClosures { for _, closure := range session.beforeClosures {
closure(bean) closure(bean)
} }
scanResults := make([]interface{}, fieldsCount) scanResults := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ { for i := 0; i < len(fields); i++ {
var cell interface{} var cell interface{}
scanResults[i] = &cell scanResults[i] = &cell
@ -343,20 +350,49 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, fieldsCount
return scanResults, nil return scanResults, nil
} }
func (session *Session) slice2Bean(scanResults []interface{}, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) { func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) {
defer func() { defer func() {
if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet { if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet {
for ii, key := range fields { for ii, key := range fields {
b.AfterSet(key, Cell(scanResults[ii].(*interface{}))) b.AfterSet(key, Cell(scanResults[ii].(*interface{})))
} }
} }
// handle afterClosures
for _, closure := range session.afterClosures {
closure(bean)
}
}() }()
// handle afterClosures
for _, closure := range session.afterClosures {
session.afterProcessors = append(session.afterProcessors, executedProcessor{
fun: func(sess *Session, bean interface{}) error {
closure(bean)
return nil
},
session: session,
bean: bean,
})
}
if a, has := bean.(AfterLoadProcessor); has {
session.afterProcessors = append(session.afterProcessors, executedProcessor{
fun: func(sess *Session, bean interface{}) error {
a.AfterLoad()
return nil
},
session: session,
bean: bean,
})
}
if a, has := bean.(AfterLoadSessionProcessor); has {
session.afterProcessors = append(session.afterProcessors, executedProcessor{
fun: func(sess *Session, bean interface{}) error {
a.AfterLoad(sess)
return nil
},
session: session,
bean: bean,
})
}
var tempMap = make(map[string]int) var tempMap = make(map[string]int)
var pk core.PK var pk core.PK
for ii, key := range fields { for ii, key := range fields {

@ -184,12 +184,12 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
} }
} }
// !oinume! Insert NowTime to the head of session.statement.Params // !oinume! Insert nowTime to the head of session.statement.Params
condArgs = append(condArgs, "") condArgs = append(condArgs, "")
paramsLen := len(condArgs) paramsLen := len(condArgs)
copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
val, t := session.engine.NowTime2(deletedColumn.SQLType.Name) val, t := session.engine.nowTime(deletedColumn)
condArgs[0] = val condArgs[0] = val
var colName = deletedColumn.Name var colName = deletedColumn.Name

@ -239,7 +239,12 @@ func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Va
if err != nil { if err != nil {
return err return err
} }
return session.rows2Beans(rows, fields, len(fields), tb, newElemFunc, containerValueSetFunc) err = session.rows2Beans(rows, fields, tb, newElemFunc, containerValueSetFunc)
rows.Close()
if err != nil {
return err
}
return session.executeProcessors()
} }
for rows.Next() { for rows.Next() {

@ -87,7 +87,7 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea
return true, err return true, err
} }
scanResults, err := session.row2Slice(rows, fields, len(fields), bean) scanResults, err := session.row2Slice(rows, fields, bean)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -95,7 +95,12 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea
rows.Close() rows.Close()
dataStruct := rValue(bean) dataStruct := rValue(bean)
_, err = session.slice2Bean(scanResults, fields, len(fields), bean, &dataStruct, table) _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
if err != nil {
return true, err
}
return true, session.executeProcessors()
case reflect.Slice: case reflect.Slice:
err = rows.ScanSlice(bean) err = rows.ScanSlice(bean)
case reflect.Map: case reflect.Map:

@ -126,7 +126,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
} }
} }
if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
val, t := session.engine.NowTime2(col.SQLType.Name) val, t := session.engine.nowTime(col)
args = append(args, val) args = append(args, val)
var colName = col.Name var colName = col.Name
@ -181,7 +181,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
} }
} }
if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
val, t := session.engine.NowTime2(col.SQLType.Name) val, t := session.engine.nowTime(col)
args = append(args, val) args = append(args, val)
var colName = col.Name var colName = col.Name

@ -23,6 +23,10 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
defer session.Close() defer session.Close()
} }
if session.statement.bufferSize > 0 {
return session.bufferIterate(bean, fun)
}
rows, err := session.Rows(bean) rows, err := session.Rows(bean)
if err != nil { if err != nil {
return err return err
@ -44,3 +48,49 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
} }
return err return err
} }
// BufferSize sets the buffersize for iterate
func (session *Session) BufferSize(size int) *Session {
session.statement.bufferSize = size
return session
}
func (session *Session) bufferIterate(bean interface{}, fun IterFunc) error {
if session.isAutoClose {
defer session.Close()
}
var bufferSize = session.statement.bufferSize
var limit = session.statement.LimitN
if limit > 0 && bufferSize > limit {
bufferSize = limit
}
var start = session.statement.Start
v := rValue(bean)
sliceType := reflect.SliceOf(v.Type())
var idx = 0
for {
slice := reflect.New(sliceType)
if err := session.Limit(bufferSize, start).find(slice.Interface(), bean); err != nil {
return err
}
for i := 0; i < slice.Elem().Len(); i++ {
if err := fun(idx, slice.Elem().Index(i).Addr().Interface()); err != nil {
return err
}
idx++
}
start = start + slice.Elem().Len()
if limit > 0 && idx+bufferSize > limit {
bufferSize = limit - idx
}
if bufferSize <= 0 || slice.Elem().Len() < bufferSize || idx == limit {
break
}
}
return nil
}

@ -205,7 +205,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
if _, ok := session.statement.columnMap[strings.ToLower(table.Updated)]; !ok { if _, ok := session.statement.columnMap[strings.ToLower(table.Updated)]; !ok {
colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?") colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?")
col := table.UpdatedColumn() col := table.UpdatedColumn()
val, t := session.engine.NowTime2(col.SQLType.Name) val, t := session.engine.nowTime(col)
args = append(args, val) args = append(args, val)
var colName = col.Name var colName = col.Name

@ -73,6 +73,7 @@ type Statement struct {
decrColumns map[string]decrParam decrColumns map[string]decrParam
exprColumns map[string]exprParam exprColumns map[string]exprParam
cond builder.Cond cond builder.Cond
bufferSize int
} }
// Init reset all the statement's fields // Init reset all the statement's fields
@ -111,6 +112,7 @@ func (statement *Statement) Init() {
statement.decrColumns = make(map[string]decrParam) statement.decrColumns = make(map[string]decrParam)
statement.exprColumns = make(map[string]exprParam) statement.exprColumns = make(map[string]exprParam)
statement.cond = builder.NewCond() statement.cond = builder.NewCond()
statement.bufferSize = 0
} }
// NoAutoCondition if you do not want convert bean's field as query condition, then use this function // NoAutoCondition if you do not want convert bean's field as query condition, then use this function

@ -474,10 +474,10 @@
"revisionTime": "2016-08-11T02:11:45Z" "revisionTime": "2016-08-11T02:11:45Z"
}, },
{ {
"checksumSHA1": "lAzHeuH461JyawhsGLi27JpWsgs=", "checksumSHA1": "+KmPfckyKvrUZPIHBYHylg/7V8o=",
"path": "github.com/go-xorm/xorm", "path": "github.com/go-xorm/xorm",
"revision": "3101e3bc440f16f151687d97bce94da063c486f5", "revision": "29d4a0330a00b9be468b70e3fb0f74109348c358",
"revisionTime": "2017-09-15T01:51:15Z" "revisionTime": "2017-09-30T01:26:13Z"
}, },
{ {
"checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=", "checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=",

Loading…
Cancel
Save