Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/222tags/v1.21.12.1
| @@ -80,6 +80,18 @@ func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) { | |||||
| return email, nil | return email, nil | ||||
| } | } | ||||
| // GetEmailAddressByIDAndEmail gets a user's email address by ID and email | |||||
| func GetEmailAddressByIDAndEmail(uid int64, emailAddr string) (*EmailAddress, error) { | |||||
| // User ID is required for security reasons | |||||
| email := &EmailAddress{UID: uid, Email: emailAddr} | |||||
| if has, err := x.Get(email); err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, nil | |||||
| } | |||||
| return email, nil | |||||
| } | |||||
| func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) { | func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) { | ||||
| if len(email) == 0 { | if len(email) == 0 { | ||||
| return true, nil | return true, nil | ||||
| @@ -335,6 +335,7 @@ func ToUser(user *models.User, signed, authed bool) *api.User { | |||||
| AvatarURL: user.AvatarLink(), | AvatarURL: user.AvatarLink(), | ||||
| FullName: markup.Sanitize(user.FullName), | FullName: markup.Sanitize(user.FullName), | ||||
| Created: user.CreatedUnix.AsTime(), | Created: user.CreatedUnix.AsTime(), | ||||
| IsActive: user.IsActive, | |||||
| } | } | ||||
| // hide primary email if API caller is anonymous or user keep email private | // hide primary email if API caller is anonymous or user keep email private | ||||
| if signed && (!user.KeepEmailPrivate || authed) { | if signed && (!user.KeepEmailPrivate || authed) { | ||||
| @@ -26,6 +26,8 @@ type User struct { | |||||
| Language string `json:"language"` | Language string `json:"language"` | ||||
| // Is the user an administrator | // Is the user an administrator | ||||
| IsAdmin bool `json:"is_admin"` | IsAdmin bool `json:"is_admin"` | ||||
| // Is the user active | |||||
| IsActive bool `json:"is_active"` | |||||
| // swagger:strfmt date-time | // swagger:strfmt date-time | ||||
| LastLogin time.Time `json:"last_login,omitempty"` | LastLogin time.Time `json:"last_login,omitempty"` | ||||
| // swagger:strfmt date-time | // swagger:strfmt date-time | ||||
| @@ -141,6 +141,35 @@ func DeleteAttachment(ctx *context.Context) { | |||||
| }) | }) | ||||
| } | } | ||||
| func DownloadUserIsOrgOrCollaboration(ctx *context.Context, attach *models.Attachment) bool { | |||||
| dataset, err := models.GetDatasetByID(attach.DatasetID) | |||||
| if err != nil { | |||||
| log.Info("query dataset error") | |||||
| } else { | |||||
| repo, err := models.GetRepositoryByID(dataset.RepoID) | |||||
| if err != nil { | |||||
| log.Info("query repo error.") | |||||
| } else { | |||||
| repo.GetOwner() | |||||
| if ctx.User != nil { | |||||
| if repo.Owner.IsOrganization() { | |||||
| if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | |||||
| log.Info("org user may visit the attach.") | |||||
| return true | |||||
| } | |||||
| } | |||||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | |||||
| if isCollaborator { | |||||
| log.Info("Collaborator user may visit the attach.") | |||||
| return true | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| // GetAttachment serve attachements | // GetAttachment serve attachements | ||||
| func GetAttachment(ctx *context.Context) { | func GetAttachment(ctx *context.Context) { | ||||
| typeCloudBrain := ctx.QueryInt("type") | typeCloudBrain := ctx.QueryInt("type") | ||||
| @@ -165,16 +194,29 @@ func GetAttachment(ctx *context.Context) { | |||||
| ctx.ServerError("LinkedRepository", err) | ctx.ServerError("LinkedRepository", err) | ||||
| return | return | ||||
| } | } | ||||
| dataSet, err := attach.LinkedDataSet() | |||||
| if err != nil { | |||||
| ctx.ServerError("LinkedDataSet", err) | |||||
| return | |||||
| } | |||||
| if repository == nil && dataSet != nil { | |||||
| repository, _ = models.GetRepositoryByID(dataSet.RepoID) | |||||
| unitType = models.UnitTypeDatasets | |||||
| } | |||||
| if repository == nil { //If not linked | if repository == nil { //If not linked | ||||
| if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader | |||||
| //if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader | |||||
| //log.Info("ctx.IsSigned =" + fmt.Sprintf("%v", ctx.IsSigned)) | |||||
| if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate && !DownloadUserIsOrgOrCollaboration(ctx, attach) { //We block if not the uploader | |||||
| ctx.Error(http.StatusNotFound) | ctx.Error(http.StatusNotFound) | ||||
| return | return | ||||
| } | } | ||||
| } else { //If we have the repository we check access | } else { //If we have the repository we check access | ||||
| perm, err := models.GetUserRepoPermission(repository, ctx.User) | |||||
| if err != nil { | |||||
| ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error()) | |||||
| perm, errPermission := models.GetUserRepoPermission(repository, ctx.User) | |||||
| if errPermission != nil { | |||||
| ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", errPermission.Error()) | |||||
| return | return | ||||
| } | } | ||||
| if !perm.CanRead(unitType) { | if !perm.CanRead(unitType) { | ||||
| @@ -183,12 +225,6 @@ func GetAttachment(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| dataSet, err := attach.LinkedDataSet() | |||||
| if err != nil { | |||||
| ctx.ServerError("LinkedDataSet", err) | |||||
| return | |||||
| } | |||||
| if dataSet != nil { | if dataSet != nil { | ||||
| isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) | isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -205,7 +241,7 @@ func GetAttachment(ctx *context.Context) { | |||||
| if setting.Attachment.StoreType == storage.MinioStorageType { | if setting.Attachment.StoreType == storage.MinioStorageType { | ||||
| url := "" | url := "" | ||||
| if typeCloudBrain == models.TypeCloudBrainOne { | if typeCloudBrain == models.TypeCloudBrainOne { | ||||
| url, err = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath + attach.RelativePath(), attach.Name) | |||||
| url, err = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath+attach.RelativePath(), attach.Name) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("PresignedGetURL", err) | ctx.ServerError("PresignedGetURL", err) | ||||
| return | return | ||||
| @@ -1,15 +1,13 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "sort" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "sort" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| @@ -24,19 +22,42 @@ func MustEnableDataset(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| func filterPrivateAttachments(ctx *context.Context, list []*models.Attachment) []*models.Attachment { | |||||
| func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment, repo *models.Repository) []*models.Attachment { | |||||
| if ctx.Repo.CanWrite(models.UnitTypeDatasets) { | if ctx.Repo.CanWrite(models.UnitTypeDatasets) { | ||||
| log.Info("can write.") | |||||
| return list | return list | ||||
| } else { | } else { | ||||
| if repo.Owner == nil { | |||||
| repo.GetOwner() | |||||
| } | |||||
| permission := false | |||||
| if repo.Owner.IsOrganization() && ctx.User != nil { | |||||
| if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | |||||
| log.Info("user is member of org.") | |||||
| permission = true | |||||
| } | |||||
| } | |||||
| if !permission && ctx.User != nil { | |||||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | |||||
| if isCollaborator { | |||||
| log.Info("Collaborator user may visit the attach.") | |||||
| permission = true | |||||
| } | |||||
| } | |||||
| var publicList []*models.Attachment | var publicList []*models.Attachment | ||||
| for _, attach := range list { | for _, attach := range list { | ||||
| if !attach.IsPrivate { | if !attach.IsPrivate { | ||||
| publicList = append(publicList, attach) | publicList = append(publicList, attach) | ||||
| } else { | |||||
| if permission { | |||||
| publicList = append(publicList, attach) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| return publicList | return publicList | ||||
| } | } | ||||
| } | } | ||||
| func DatasetIndex(ctx *context.Context) { | func DatasetIndex(ctx *context.Context) { | ||||
| @@ -60,7 +81,7 @@ func DatasetIndex(ctx *context.Context) { | |||||
| ctx.ServerError("GetDatasetAttachments", err) | ctx.ServerError("GetDatasetAttachments", err) | ||||
| return | return | ||||
| } | } | ||||
| attachments := filterPrivateAttachments(ctx, dataset.Attachments) | |||||
| attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo) | |||||
| ctx.Data["SortType"] = ctx.Query("sort") | ctx.Data["SortType"] = ctx.Query("sort") | ||||
| switch ctx.Query("sort") { | switch ctx.Query("sort") { | ||||
| @@ -569,10 +569,19 @@ func safeURL(address string) string { | |||||
| } | } | ||||
| type ContributorInfo struct { | type ContributorInfo struct { | ||||
| UserInfo *models.User | |||||
| Email string // for contributor who is not a registered user | |||||
| UserInfo *models.User // nil for contributor who is not a registered user | |||||
| Email string | |||||
| CommitCnt int | |||||
| } | } | ||||
| func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{ | |||||
| for _, c := range contributorInfos { | |||||
| if strings.Compare(c.Email,email) == 0 { | |||||
| return c | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // Home render repository home page | // Home render repository home page | ||||
| func Home(ctx *context.Context) { | func Home(ctx *context.Context) { | ||||
| if len(ctx.Repo.Units) > 0 { | if len(ctx.Repo.Units) > 0 { | ||||
| @@ -581,15 +590,31 @@ func Home(ctx *context.Context) { | |||||
| if err == nil && contributors != nil { | if err == nil && contributors != nil { | ||||
| var contributorInfos []*ContributorInfo | var contributorInfos []*ContributorInfo | ||||
| for _, c := range contributors { | for _, c := range contributors { | ||||
| // get user info from committer email | |||||
| user, err := models.GetUserByEmail(c.Email) | user, err := models.GetUserByEmail(c.Email) | ||||
| if err == nil { | if err == nil { | ||||
| contributorInfos = append(contributorInfos, &ContributorInfo{ | |||||
| user, c.Email, | |||||
| }) | |||||
| // committer is system user, get info through user's primary email | |||||
| existedContributorInfo := getContributorInfo(contributorInfos,user.Email) | |||||
| if existedContributorInfo != nil { | |||||
| // existed: same primary email, different committer name | |||||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||||
| }else{ | |||||
| // new committer info | |||||
| contributorInfos = append(contributorInfos, &ContributorInfo{ | |||||
| user, user.Email,c.CommitCnt, | |||||
| }) | |||||
| } | |||||
| } else { | } else { | ||||
| contributorInfos = append(contributorInfos, &ContributorInfo{ | |||||
| nil, c.Email, | |||||
| }) | |||||
| // committer is not system user | |||||
| existedContributorInfo := getContributorInfo(contributorInfos,c.Email) | |||||
| if existedContributorInfo != nil { | |||||
| // existed: same primary email, different committer name | |||||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||||
| }else{ | |||||
| contributorInfos = append(contributorInfos, &ContributorInfo{ | |||||
| nil, c.Email,c.CommitCnt, | |||||
| }) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| ctx.Data["ContributorInfo"] = contributorInfos | ctx.Data["ContributorInfo"] = contributorInfos | ||||
| @@ -1129,7 +1129,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| //secure api, | //secure api, | ||||
| m.Group("/secure", func() { | m.Group("/secure", func() { | ||||
| m.Post("/user", binding.Bind(structs.CreateUserOption{}), secure.CreateUser) | |||||
| m.Post("/user", binding.BindIgnErr(structs.CreateUserOption{}), secure.CreateUser) | |||||
| }, reqBasicAuth) | }, reqBasicAuth) | ||||
| m.Group("/api/internal", func() { | m.Group("/api/internal", func() { | ||||
| @@ -7,6 +7,7 @@ package secure | |||||
| import ( | import ( | ||||
| "net/http" | "net/http" | ||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| @@ -75,11 +76,19 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { | |||||
| u.MustChangePassword = *form.MustChangePassword | u.MustChangePassword = *form.MustChangePassword | ||||
| } | } | ||||
| if strings.Contains(form.Email, " ") { | |||||
| log.Error("CreateUser failed: email(%s) contains blank space", form.Email, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusBadRequest, map[string]string{ | |||||
| "error_msg": "Email contains blank space", | |||||
| }) | |||||
| return | |||||
| } | |||||
| parseLoginSource(ctx, u, form.SourceID, form.LoginName) | parseLoginSource(ctx, u, form.SourceID, form.LoginName) | ||||
| if ctx.Written() { | if ctx.Written() { | ||||
| return | return | ||||
| } | } | ||||
| if !password.IsComplexEnough(form.Password) { | |||||
| if !password.IsComplexEnough(form.Password) || len(form.Password) < setting.MinPasswordLength { | |||||
| log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) | log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) | ||||
| ctx.JSON(http.StatusBadRequest, map[string]string{ | ctx.JSON(http.StatusBadRequest, map[string]string{ | ||||
| "error_msg": "PasswordComplexity", | "error_msg": "PasswordComplexity", | ||||
| @@ -104,6 +113,29 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { | |||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| err := models.AddEmailAddress(&models.EmailAddress{ | |||||
| UID: u.ID, | |||||
| Email: form.Email, | |||||
| IsActivated: !setting.Service.RegisterEmailConfirm, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("AddEmailAddress failed:%v", err.Error(), ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusInternalServerError, map[string]string{ | |||||
| "error_msg": err.Error(), | |||||
| }) | |||||
| return | |||||
| } | |||||
| // Send confirmation email | |||||
| if setting.Service.RegisterEmailConfirm{ | |||||
| mailer.SendActivateAccountMail(ctx.Locale, u) | |||||
| if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { | |||||
| log.Error("Set cache(MailResendLimit) fail: %v", err) | |||||
| } | |||||
| } | |||||
| log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) | log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) | ||||
| // Send email notification. | // Send email notification. | ||||
| @@ -1165,7 +1165,19 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo | |||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| log.Trace("Account created: %s", u.Name) | |||||
| log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"]) | |||||
| err := models.AddEmailAddress(&models.EmailAddress{ | |||||
| UID: u.ID, | |||||
| Email: form.Email, | |||||
| IsActivated: !setting.Service.RegisterEmailConfirm, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("AddEmailAddress failed:%v", err.Error(), ctx.Data["MsgID"]) | |||||
| ctx.ServerError("AddEmailAddress", err) | |||||
| return | |||||
| } | |||||
| // Auto-set admin for the only user. | // Auto-set admin for the only user. | ||||
| if models.CountUsers() == 1 { | if models.CountUsers() == 1 { | ||||
| @@ -1254,6 +1266,15 @@ func Activate(ctx *context.Context) { | |||||
| log.Error("Error storing session: %v", err) | log.Error("Error storing session: %v", err) | ||||
| } | } | ||||
| email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) | |||||
| if err != nil || email == nil{ | |||||
| log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"]) | |||||
| } else { | |||||
| if err := email.Activate(); err != nil { | |||||
| log.Error("Activate failed: %v", err, ctx.Data["MsgID"]) | |||||
| } | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("auth.account_activated")) | ctx.Flash.Success(ctx.Tr("auth.account_activated")) | ||||
| ctx.Redirect(setting.AppSubURL + "/") | ctx.Redirect(setting.AppSubURL + "/") | ||||
| return | return | ||||
| @@ -96,6 +96,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { | |||||
| ctx.User.Location = form.Location | ctx.User.Location = form.Location | ||||
| ctx.User.Language = form.Language | ctx.User.Language = form.Language | ||||
| ctx.User.Description = form.Description | ctx.User.Description = form.Description | ||||
| isUsed, err := models.IsEmailUsed(form.Email) | |||||
| if err != nil { | |||||
| ctx.ServerError("IsEmailUsed", err) | |||||
| return | |||||
| } | |||||
| if isUsed { | |||||
| ctx.Flash.Error(ctx.Tr("form.email_been_used")) | |||||
| ctx.Redirect(setting.AppSubURL + "/user/settings") | |||||
| return | |||||
| } | |||||
| if err := models.UpdateUserSetting(ctx.User); err != nil { | if err := models.UpdateUserSetting(ctx.User); err != nil { | ||||
| if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | ||||
| ctx.Flash.Error(ctx.Tr("form.email_been_used")) | ctx.Flash.Error(ctx.Tr("form.email_been_used")) | ||||
| @@ -4,6 +4,48 @@ | |||||
| font-size: 1.0em; | font-size: 1.0em; | ||||
| margin-bottom: 1.0rem; | margin-bottom: 1.0rem; | ||||
| } | } | ||||
| #contributorInfo > a:nth-child(n+25){ | |||||
| display:none; | |||||
| } | |||||
| #contributorInfo > a{ | |||||
| width: 2.0em; | |||||
| float: left; | |||||
| margin: .25em; | |||||
| } | |||||
| #contributorInfo > a.circular{ | |||||
| height: 2.0em; | |||||
| padding: 0; | |||||
| overflow: hidden; | |||||
| letter-spacing:1.0em; | |||||
| text-indent: 0.6em; | |||||
| line-height: 2.0em; | |||||
| text-transform:capitalize; | |||||
| color: #FFF; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+1){ | |||||
| background-color: #4ccdec; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+2){ | |||||
| background-color: #e0b265; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+3){ | |||||
| background-color: #d884b7; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+4){ | |||||
| background-color: #8c6bdc; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+5){ | |||||
| background-color: #3cb99f; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+6){ | |||||
| background-color: #6995b9; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+7){ | |||||
| background-color: #ab91a7; | |||||
| } | |||||
| #contributorInfo > a.circular:nth-child(9n+8){ | |||||
| background-color: #bfd0aa; | |||||
| } | |||||
| </style> | </style> | ||||
| <div class="repository file list"> | <div class="repository file list"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| @@ -211,17 +253,15 @@ | |||||
| <h4 class="ui header"> | <h4 class="ui header"> | ||||
| <strong>贡献者 ({{len .ContributorInfo}})</strong> | <strong>贡献者 ({{len .ContributorInfo}})</strong> | ||||
| <div class="ui right"> | <div class="ui right"> | ||||
| <a class="text grey" href="">全部 {{svg "octicon-chevron-right" 16}}</a> | |||||
| <a class="membersmore text grey" href="javascript:;">全部 {{svg "octicon-chevron-right" 16}}</a> | |||||
| </div> | </div> | ||||
| </h4> | </h4> | ||||
| <div class="ui members"> | |||||
| <div class="ui members" id="contributorInfo"> | |||||
| {{range .ContributorInfo}} | {{range .ContributorInfo}} | ||||
| {{/*<img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}" alt=""/> <a href="{{AppSubUrl}}/{{.UserInfo.Name}}">{{.UserInfo.Name}}</a>*/}} | |||||
| {{if .UserInfo}} | {{if .UserInfo}} | ||||
| <a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}" alt=""/></a> | |||||
| <a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a> | |||||
| {{else if .Email}} | {{else if .Email}} | ||||
| <a href="mailto:{{.Email}}"><img class="ui avatar image" src="{{AvatarLink .Email}}" alt=""/></a> | |||||
| <a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a> | |||||
| {{end}} | {{end}} | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| @@ -233,4 +273,12 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <script type="text/javascript"> | |||||
| $(document).ready(function(){ | |||||
| $(".membersmore").click(function(){ | |||||
| $("#contributorInfo > a:nth-child(n+25)").show(); | |||||
| }); | |||||
| }); | |||||
| </script> | |||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -335,6 +335,7 @@ export default { | |||||
| async function uploadMinio(url, e) { | async function uploadMinio(url, e) { | ||||
| const res = await axios.put(url, e.target.result); | const res = await axios.put(url, e.target.result); | ||||
| delete e.target.result | |||||
| etags[currentChunk] = res.headers.etag; | etags[currentChunk] = res.headers.etag; | ||||
| } | } | ||||
| @@ -2685,7 +2685,7 @@ tbody.commit-list { | |||||
| width: 1127px; | width: 1127px; | ||||
| } | } | ||||
| th .message-wrapper { | th .message-wrapper { | ||||
| max-width: 680px; | |||||
| max-width: 510px; | |||||
| } | } | ||||
| } | } | ||||