Add config options to hide issue events (#17414)
* Add config option to hide issue events Adds a config option `HIDE_ISSUE_EVENTS` to hide most issue events (changed labels, milestones, projects...) on the issue detail page. If this is true, only the following events (comment types) are shown: * plain comments * closed/reopned/merged * reviews * Make configurable using a list * Add docs * Add missing newline * Fix merge issues * Allow changes per user settings * Fix lint * Rm old docs * Apply suggestions from code review * Use bitsets * Rm comment * fmt * Fix lint * Use variable/constant to provide key * fmt * fix lint * refactor * Add a prefix for user setting key * Add license comment * Add license comment * Update services/forms/user_form_hidden_comments.go Co-authored-by: Gusted <williamzijl7@hotmail.com> * check len == 0 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: Gusted <williamzijl7@hotmail.com> Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
108f1aab5c
commit
1f40933d38
16 changed files with 1125 additions and 808 deletions
|
@ -99,7 +99,7 @@ const (
|
||||||
// 28 merge pull request
|
// 28 merge pull request
|
||||||
CommentTypeMergePull
|
CommentTypeMergePull
|
||||||
// 29 push to PR head branch
|
// 29 push to PR head branch
|
||||||
CommentTypePullPush
|
CommentTypePullRequestPush
|
||||||
// 30 Project changed
|
// 30 Project changed
|
||||||
CommentTypeProject
|
CommentTypeProject
|
||||||
// 31 Project board changed
|
// 31 Project board changed
|
||||||
|
@ -725,7 +725,7 @@ func (c *Comment) CodeCommentURL() string {
|
||||||
|
|
||||||
// LoadPushCommits Load push commits
|
// LoadPushCommits Load push commits
|
||||||
func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
|
func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
|
||||||
if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullPush {
|
if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullRequestPush {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1325,7 +1325,7 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *Pul
|
||||||
}
|
}
|
||||||
|
|
||||||
ops := &CreateCommentOptions{
|
ops := &CreateCommentOptions{
|
||||||
Type: CommentTypePullPush,
|
Type: CommentTypePullRequestPush,
|
||||||
Doer: pusher,
|
Doer: pusher,
|
||||||
Repo: pr.BaseRepo,
|
Repo: pr.BaseRepo,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,8 @@ func init() {
|
||||||
db.RegisterModel(new(Setting))
|
db.RegisterModel(new(Setting))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSettings returns specific settings from user
|
// GetUserSettings returns specific settings from user
|
||||||
func GetSettings(uid int64, keys []string) (map[string]*Setting, error) {
|
func GetUserSettings(uid int64, keys []string) (map[string]*Setting, error) {
|
||||||
settings := make([]*Setting, 0, len(keys))
|
settings := make([]*Setting, 0, len(keys))
|
||||||
if err := db.GetEngine(db.DefaultContext).
|
if err := db.GetEngine(db.DefaultContext).
|
||||||
Where("user_id=?", uid).
|
Where("user_id=?", uid).
|
||||||
|
@ -62,21 +62,53 @@ func GetUserAllSettings(uid int64) (map[string]*Setting, error) {
|
||||||
return settingsMap, nil
|
return settingsMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSetting deletes a specific setting for a user
|
func validateUserSettingKey(key string) error {
|
||||||
func DeleteSetting(setting *Setting) error {
|
if len(key) == 0 {
|
||||||
_, err := db.GetEngine(db.DefaultContext).Delete(setting)
|
return fmt.Errorf("setting key must be set")
|
||||||
|
}
|
||||||
|
if strings.ToLower(key) != key {
|
||||||
|
return fmt.Errorf("setting key should be lowercase")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserSetting gets a specific setting for a user
|
||||||
|
func GetUserSetting(userID int64, key string, def ...string) (string, error) {
|
||||||
|
if err := validateUserSettingKey(key); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
setting := &Setting{UserID: userID, SettingKey: key}
|
||||||
|
has, err := db.GetEngine(db.DefaultContext).Get(setting)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
if len(def) == 1 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return setting.SettingValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserSetting deletes a specific setting for a user
|
||||||
|
func DeleteUserSetting(userID int64, key string) error {
|
||||||
|
if err := validateUserSettingKey(key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := db.GetEngine(db.DefaultContext).Delete(&Setting{UserID: userID, SettingKey: key})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSetting updates a users' setting for a specific key
|
// SetUserSetting updates a users' setting for a specific key
|
||||||
func SetSetting(setting *Setting) error {
|
func SetUserSetting(userID int64, key, value string) error {
|
||||||
if strings.ToLower(setting.SettingKey) != setting.SettingKey {
|
if err := validateUserSettingKey(key); err != nil {
|
||||||
return fmt.Errorf("setting key should be lowercase")
|
return err
|
||||||
}
|
}
|
||||||
return upsertSettingValue(setting.UserID, setting.SettingKey, setting.SettingValue)
|
return upsertUserSettingValue(userID, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func upsertSettingValue(userID int64, key, value string) error {
|
func upsertUserSettingValue(userID int64, key, value string) error {
|
||||||
return db.WithTx(func(ctx context.Context) error {
|
return db.WithTx(func(ctx context.Context) error {
|
||||||
e := db.GetEngine(ctx)
|
e := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
|
10
models/user/setting_keys.go
Normal file
10
models/user/setting_keys.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SettingsKeyHiddenCommentTypes is the settings key for hidden comment types
|
||||||
|
SettingsKeyHiddenCommentTypes = "issue.hidden_comment_types"
|
||||||
|
)
|
|
@ -19,21 +19,29 @@ func TestSettings(t *testing.T) {
|
||||||
newSetting := &Setting{UserID: 99, SettingKey: keyName, SettingValue: "Gitea User Setting Test"}
|
newSetting := &Setting{UserID: 99, SettingKey: keyName, SettingValue: "Gitea User Setting Test"}
|
||||||
|
|
||||||
// create setting
|
// create setting
|
||||||
err := SetSetting(newSetting)
|
err := SetUserSetting(newSetting.UserID, newSetting.SettingKey, newSetting.SettingValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// test about saving unchanged values
|
// test about saving unchanged values
|
||||||
err = SetSetting(newSetting)
|
err = SetUserSetting(newSetting.UserID, newSetting.SettingKey, newSetting.SettingValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get specific setting
|
// get specific setting
|
||||||
settings, err := GetSettings(99, []string{keyName})
|
settings, err := GetUserSettings(99, []string{keyName})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, settings, 1)
|
assert.Len(t, settings, 1)
|
||||||
assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue)
|
assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue)
|
||||||
|
|
||||||
|
settingValue, err := GetUserSetting(99, keyName)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, newSetting.SettingValue, settingValue)
|
||||||
|
|
||||||
|
settingValue, err = GetUserSetting(99, "no_such")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, "", settingValue)
|
||||||
|
|
||||||
// updated setting
|
// updated setting
|
||||||
updatedSetting := &Setting{UserID: 99, SettingKey: keyName, SettingValue: "Updated"}
|
updatedSetting := &Setting{UserID: 99, SettingKey: keyName, SettingValue: "Updated"}
|
||||||
err = SetSetting(updatedSetting)
|
err = SetUserSetting(updatedSetting.UserID, updatedSetting.SettingKey, updatedSetting.SettingValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get all settings
|
// get all settings
|
||||||
|
@ -43,7 +51,7 @@ func TestSettings(t *testing.T) {
|
||||||
assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
|
assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
|
||||||
|
|
||||||
// delete setting
|
// delete setting
|
||||||
err = DeleteSetting(&Setting{UserID: 99, SettingKey: keyName})
|
err = DeleteUserSetting(99, keyName)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
settings, err = GetUserAllSettings(99)
|
settings, err = GetUserAllSettings(99)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -46,9 +46,11 @@ func (ctx *Context) FormInt64(key string) int64 {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormBool returns true if the value for the provided key in the form is "1" or "true"
|
// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
|
||||||
func (ctx *Context) FormBool(key string) bool {
|
func (ctx *Context) FormBool(key string) bool {
|
||||||
v, _ := strconv.ParseBool(ctx.Req.FormValue(key))
|
s := ctx.Req.FormValue(key)
|
||||||
|
v, _ := strconv.ParseBool(s)
|
||||||
|
v = v || strings.EqualFold(s, "on")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +61,8 @@ func (ctx *Context) FormOptionalBool(key string) util.OptionalBool {
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
return util.OptionalBoolNone
|
return util.OptionalBoolNone
|
||||||
}
|
}
|
||||||
v, _ := strconv.ParseBool(ctx.Req.FormValue(key))
|
s := ctx.Req.FormValue(key)
|
||||||
|
v, _ := strconv.ParseBool(s)
|
||||||
|
v = v || strings.EqualFold(s, "on")
|
||||||
return util.OptionalBoolOf(v)
|
return util.OptionalBoolOf(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep
|
||||||
act = models.ActionCommentIssue
|
act = models.ActionCommentIssue
|
||||||
} else if comment.Type == models.CommentTypeCode {
|
} else if comment.Type == models.CommentTypeCode {
|
||||||
act = models.ActionCommentIssue
|
act = models.ActionCommentIssue
|
||||||
} else if comment.Type == models.CommentTypePullPush {
|
} else if comment.Type == models.CommentTypePullRequestPush {
|
||||||
act = 0
|
act = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -549,6 +549,22 @@ continue = Continue
|
||||||
cancel = Cancel
|
cancel = Cancel
|
||||||
language = Language
|
language = Language
|
||||||
ui = Theme
|
ui = Theme
|
||||||
|
hidden_comment_types = Hidden comment types
|
||||||
|
comment_type_group_reference = Reference
|
||||||
|
comment_type_group_label = Label
|
||||||
|
comment_type_group_milestone = Milestone
|
||||||
|
comment_type_group_assignee = Assignee
|
||||||
|
comment_type_group_title = Title
|
||||||
|
comment_type_group_branch = Branch
|
||||||
|
comment_type_group_time_tracking = Time Tracking
|
||||||
|
comment_type_group_deadline = Deadline
|
||||||
|
comment_type_group_dependency = Dependency
|
||||||
|
comment_type_group_lock = Lock Status
|
||||||
|
comment_type_group_review_request = Review request
|
||||||
|
comment_type_group_pull_request_push = Added commits
|
||||||
|
comment_type_group_project = Project
|
||||||
|
comment_type_group_issue_ref = Issue reference
|
||||||
|
saved_successfully = Your settings were saved successfully.
|
||||||
privacy = Privacy
|
privacy = Privacy
|
||||||
keep_activity_private = Hide the activity from the profile page
|
keep_activity_private = Hide the activity from the profile page
|
||||||
keep_activity_private_popup = Makes the activity visible only for you and the admins
|
keep_activity_private_popup = Makes the activity visible only for you and the admins
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
@ -1465,7 +1466,7 @@ func ViewIssue(ctx *context.Context) {
|
||||||
ctx.ServerError("LoadResolveDoer", err)
|
ctx.ServerError("LoadResolveDoer", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if comment.Type == models.CommentTypePullPush {
|
} else if comment.Type == models.CommentTypePullRequestPush {
|
||||||
participants = addParticipant(comment.Poster, participants)
|
participants = addParticipant(comment.Poster, participants)
|
||||||
if err = comment.LoadPushCommits(ctx); err != nil {
|
if err = comment.LoadPushCommits(ctx); err != nil {
|
||||||
ctx.ServerError("LoadPushCommits", err)
|
ctx.ServerError("LoadPushCommits", err)
|
||||||
|
@ -1650,6 +1651,20 @@ func ViewIssue(ctx *context.Context) {
|
||||||
ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin)
|
ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin)
|
||||||
ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons
|
ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons
|
||||||
ctx.Data["RefEndName"] = git.RefEndName(issue.Ref)
|
ctx.Data["RefEndName"] = git.RefEndName(issue.Ref)
|
||||||
|
|
||||||
|
var hiddenCommentTypes *big.Int
|
||||||
|
if ctx.IsSigned {
|
||||||
|
val, err := user_model.GetUserSetting(ctx.User.ID, user_model.SettingsKeyHiddenCommentTypes)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetUserSetting", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hiddenCommentTypes, _ = new(big.Int).SetString(val, 10) // we can safely ignore the failed conversion here
|
||||||
|
}
|
||||||
|
ctx.Data["ShouldShowCommentType"] = func(commentType models.CommentType) bool {
|
||||||
|
return hiddenCommentTypes == nil || hiddenCommentTypes.Bit(int(commentType)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplIssueView)
|
ctx.HTML(http.StatusOK, tplIssueView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -358,6 +359,18 @@ func Appearance(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings")
|
||||||
ctx.Data["PageIsSettingsAppearance"] = true
|
ctx.Data["PageIsSettingsAppearance"] = true
|
||||||
|
|
||||||
|
var hiddenCommentTypes *big.Int
|
||||||
|
val, err := user_model.GetUserSetting(ctx.User.ID, user_model.SettingsKeyHiddenCommentTypes)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetUserSetting", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hiddenCommentTypes, _ = new(big.Int).SetString(val, 10) // we can safely ignore the failed conversion here
|
||||||
|
|
||||||
|
ctx.Data["IsCommentTypeGroupChecked"] = func(commentTypeGroup string) bool {
|
||||||
|
return forms.IsUserHiddenCommentTypeGroupChecked(commentTypeGroup, hiddenCommentTypes)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplSettingsAppearance)
|
ctx.HTML(http.StatusOK, tplSettingsAppearance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,3 +429,16 @@ func UpdateUserLang(ctx *context.Context) {
|
||||||
ctx.Flash.Success(i18n.Tr(ctx.User.Language, "settings.update_language_success"))
|
ctx.Flash.Success(i18n.Tr(ctx.User.Language, "settings.update_language_success"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserHiddenComments update a user's shown comment types
|
||||||
|
func UpdateUserHiddenComments(ctx *context.Context) {
|
||||||
|
err := user_model.SetUserSetting(ctx.User.ID, user_model.SettingsKeyHiddenCommentTypes, forms.UserHiddenCommentTypesFromRequest(ctx).String())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("SetUserSetting", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("User settings updated: %s", ctx.User.Name)
|
||||||
|
ctx.Flash.Success(ctx.Tr("settings.saved_successfully"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
|
||||||
|
}
|
||||||
|
|
|
@ -323,6 +323,7 @@ func RegisterRoutes(m *web.Route) {
|
||||||
m.Group("/appearance", func() {
|
m.Group("/appearance", func() {
|
||||||
m.Get("", user_setting.Appearance)
|
m.Get("", user_setting.Appearance)
|
||||||
m.Post("/language", bindIgnErr(forms.UpdateLanguageForm{}), user_setting.UpdateUserLang)
|
m.Post("/language", bindIgnErr(forms.UpdateLanguageForm{}), user_setting.UpdateUserLang)
|
||||||
|
m.Post("/hidden_comments", user_setting.UpdateUserHiddenComments)
|
||||||
m.Post("/theme", bindIgnErr(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost)
|
m.Post("/theme", bindIgnErr(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost)
|
||||||
})
|
})
|
||||||
m.Group("/security", func() {
|
m.Group("/security", func() {
|
||||||
|
|
105
services/forms/user_form_hidden_comments.go
Normal file
105
services/forms/user_form_hidden_comments.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package forms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type hiddenCommentTypeGroupsType map[string][]models.CommentType
|
||||||
|
|
||||||
|
// hiddenCommentTypeGroups maps the group names to comment types, these group names comes from the Web UI (appearance.tmpl)
|
||||||
|
var hiddenCommentTypeGroups = hiddenCommentTypeGroupsType{
|
||||||
|
"reference": {
|
||||||
|
/*3*/ models.CommentTypeIssueRef,
|
||||||
|
/*4*/ models.CommentTypeCommitRef,
|
||||||
|
/*5*/ models.CommentTypeCommentRef,
|
||||||
|
/*6*/ models.CommentTypePullRef,
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
/*7*/ models.CommentTypeLabel,
|
||||||
|
},
|
||||||
|
"milestone": {
|
||||||
|
/*8*/ models.CommentTypeMilestone,
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
/*9*/ models.CommentTypeAssignees,
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
/*10*/ models.CommentTypeChangeTitle,
|
||||||
|
},
|
||||||
|
"branch": {
|
||||||
|
/*11*/ models.CommentTypeDeleteBranch,
|
||||||
|
/*25*/ models.CommentTypeChangeTargetBranch,
|
||||||
|
},
|
||||||
|
"time_tracking": {
|
||||||
|
/*12*/ models.CommentTypeStartTracking,
|
||||||
|
/*13*/ models.CommentTypeStopTracking,
|
||||||
|
/*14*/ models.CommentTypeAddTimeManual,
|
||||||
|
/*15*/ models.CommentTypeCancelTracking,
|
||||||
|
/*26*/ models.CommentTypeDeleteTimeManual,
|
||||||
|
},
|
||||||
|
"deadline": {
|
||||||
|
/*16*/ models.CommentTypeAddedDeadline,
|
||||||
|
/*17*/ models.CommentTypeModifiedDeadline,
|
||||||
|
/*18*/ models.CommentTypeRemovedDeadline,
|
||||||
|
},
|
||||||
|
"dependency": {
|
||||||
|
/*19*/ models.CommentTypeAddDependency,
|
||||||
|
/*20*/ models.CommentTypeRemoveDependency,
|
||||||
|
},
|
||||||
|
"lock": {
|
||||||
|
/*23*/ models.CommentTypeLock,
|
||||||
|
/*24*/ models.CommentTypeUnlock,
|
||||||
|
},
|
||||||
|
"review_request": {
|
||||||
|
/*27*/ models.CommentTypeReviewRequest,
|
||||||
|
},
|
||||||
|
"pull_request_push": {
|
||||||
|
/*29*/ models.CommentTypePullRequestPush,
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
/*30*/ models.CommentTypeProject,
|
||||||
|
/*31*/ models.CommentTypeProjectBoard,
|
||||||
|
},
|
||||||
|
"issue_ref": {
|
||||||
|
/*33*/ models.CommentTypeChangeIssueRef,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserHiddenCommentTypesFromRequest parse the form to hidden comment types bitset
|
||||||
|
func UserHiddenCommentTypesFromRequest(ctx *context.Context) *big.Int {
|
||||||
|
bitset := new(big.Int)
|
||||||
|
for group, commentTypes := range hiddenCommentTypeGroups {
|
||||||
|
if ctx.FormBool(group) {
|
||||||
|
for _, commentType := range commentTypes {
|
||||||
|
bitset = bitset.SetBit(bitset, int(commentType), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bitset
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserHiddenCommentTypeGroupChecked check whether a hidden comment type group is "enabled" (checked on UI)
|
||||||
|
func IsUserHiddenCommentTypeGroupChecked(group string, hiddenCommentTypes *big.Int) (ret bool) {
|
||||||
|
commentTypes, ok := hiddenCommentTypeGroups[group]
|
||||||
|
if !ok {
|
||||||
|
log.Critical("the group map for hidden comment types is out of sync, unknown group: %v", group)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hiddenCommentTypes == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, commentType := range commentTypes {
|
||||||
|
if hiddenCommentTypes.Bit(int(commentType)) == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -449,7 +449,7 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType,
|
||||||
name = "code"
|
name = "code"
|
||||||
case models.CommentTypeAssignees:
|
case models.CommentTypeAssignees:
|
||||||
name = "assigned"
|
name = "assigned"
|
||||||
case models.CommentTypePullPush:
|
case models.CommentTypePullRequestPush:
|
||||||
name = "push"
|
name = "push"
|
||||||
default:
|
default:
|
||||||
name = "default"
|
name = "default"
|
||||||
|
|
|
@ -21,7 +21,7 @@ func MailParticipantsComment(ctx context.Context, c *models.Comment, opType mode
|
||||||
}
|
}
|
||||||
|
|
||||||
content := c.Content
|
content := c.Content
|
||||||
if c.Type == models.CommentTypePullPush {
|
if c.Type == models.CommentTypePullRequestPush {
|
||||||
content = ""
|
content = ""
|
||||||
}
|
}
|
||||||
if err := mailIssueCommentToParticipants(
|
if err := mailIssueCommentToParticipants(
|
||||||
|
|
|
@ -108,7 +108,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *mode
|
||||||
}
|
}
|
||||||
|
|
||||||
ops := &models.CreateCommentOptions{
|
ops := &models.CreateCommentOptions{
|
||||||
Type: models.CommentTypePullPush,
|
Type: models.CommentTypePullRequestPush,
|
||||||
Doer: pull.Poster,
|
Doer: pull.Poster,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Issue: pr.Issue,
|
Issue: pr.Issue,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{{ template "base/alert" }}
|
{{ template "base/alert" }}
|
||||||
{{range .Issue.Comments}}
|
{{range .Issue.Comments}}
|
||||||
|
{{if call $.ShouldShowCommentType .Type}}
|
||||||
{{ $createdStr:= TimeSinceUnix .CreatedUnix $.Lang }}
|
{{ $createdStr:= TimeSinceUnix .CreatedUnix $.Lang }}
|
||||||
|
|
||||||
<!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF,
|
<!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF,
|
||||||
|
@ -833,3 +834,4 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
|
@ -68,6 +68,104 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Shown comment event types -->
|
||||||
|
<h4 class="ui top attached header">
|
||||||
|
{{.i18n.Tr "settings.hidden_comment_types"}}
|
||||||
|
</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
<form class="ui form" action="{{.Link}}/hidden_comments" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="reference" type="checkbox" {{if(call .IsCommentTypeGroupChecked "reference")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_reference"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="label" type="checkbox" {{if (call .IsCommentTypeGroupChecked "label")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_label"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="milestone" type="checkbox" {{if (call .IsCommentTypeGroupChecked "milestone")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_milestone"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="assignee" type="checkbox" {{if (call .IsCommentTypeGroupChecked "assignee")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_assignee"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="title" type="checkbox" {{if (call .IsCommentTypeGroupChecked "title")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_title"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="branch" type="checkbox" {{if (call .IsCommentTypeGroupChecked "branch")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_branch"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="time_tracking" type="checkbox" {{if (call .IsCommentTypeGroupChecked "time_tracking")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_time_tracking"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="deadline" type="checkbox" {{if (call .IsCommentTypeGroupChecked "deadline")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_deadline"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="dependency" type="checkbox" {{if (call .IsCommentTypeGroupChecked "dependency")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_dependency"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="lock" type="checkbox" {{if (call .IsCommentTypeGroupChecked "lock")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_lock"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="review_request" type="checkbox" {{if (call .IsCommentTypeGroupChecked "review_request")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_review_request"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="pull_request_push" type="checkbox" {{if (call .IsCommentTypeGroupChecked "pull_request_push")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_pull_request_push"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="project" type="checkbox" {{if (call .IsCommentTypeGroupChecked "project")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_project"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="issue_ref" type="checkbox" {{if (call .IsCommentTypeGroupChecked "issue_ref")}}checked{{end}}>
|
||||||
|
<label>{{.i18n.Tr "settings.comment_type_group_issue_ref"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="ui green button">{{$.i18n.Tr "save"}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue