Merge remote-tracking branch 'origin/feat/0.6.0/seo' into test

This commit is contained in:
LinkinStar 2022-12-08 12:01:11 +08:00
commit e721ef2375
21 changed files with 125 additions and 81 deletions

View File

@ -45,6 +45,7 @@ func runApp() {
if err != nil {
panic(err)
}
conf.GetPathIgnoreList()
app, cleanup, err := initApplication(
c.Debug, c.Server, c.Data.Database, c.Data.Cache, c.I18n, c.Swaggerui, c.ServiceConfig, log.GetLogger())
if err != nil {

View File

@ -4,4 +4,6 @@ import _ "embed"
//go:embed config.yaml
var Config []byte
//go:embed path_ignore.yaml
var PathIgnore []byte

View File

@ -6400,11 +6400,7 @@ const docTemplate = `{
},
"user_info": {
"description": "user info",
"allOf": [
{
"$ref": "#/definitions/schema.UserBasicInfo"
}
]
"$ref": "#/definitions/schema.UserBasicInfo"
},
"vote_count": {
"type": "integer"
@ -6416,11 +6412,7 @@ const docTemplate = `{
"properties": {
"object": {
"description": "this object",
"allOf": [
{
"$ref": "#/definitions/schema.SearchObject"
}
]
"$ref": "#/definitions/schema.SearchObject"
},
"object_type": {
"description": "object_type",
@ -6818,11 +6810,7 @@ const docTemplate = `{
"properties": {
"avatar": {
"description": "avatar",
"allOf": [
{
"$ref": "#/definitions/schema.AvatarInfo"
}
]
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio",

View File

@ -6388,11 +6388,7 @@
},
"user_info": {
"description": "user info",
"allOf": [
{
"$ref": "#/definitions/schema.UserBasicInfo"
}
]
"$ref": "#/definitions/schema.UserBasicInfo"
},
"vote_count": {
"type": "integer"
@ -6404,11 +6400,7 @@
"properties": {
"object": {
"description": "this object",
"allOf": [
{
"$ref": "#/definitions/schema.SearchObject"
}
]
"$ref": "#/definitions/schema.SearchObject"
},
"object_type": {
"description": "object_type",
@ -6806,11 +6798,7 @@
"properties": {
"avatar": {
"description": "avatar",
"allOf": [
{
"$ref": "#/definitions/schema.AvatarInfo"
}
]
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio",

View File

@ -1139,8 +1139,7 @@ definitions:
title:
type: string
user_info:
allOf:
- $ref: '#/definitions/schema.UserBasicInfo'
$ref: '#/definitions/schema.UserBasicInfo'
description: user info
vote_count:
type: integer
@ -1148,8 +1147,7 @@ definitions:
schema.SearchResp:
properties:
object:
allOf:
- $ref: '#/definitions/schema.SearchObject'
$ref: '#/definitions/schema.SearchObject'
description: this object
object_type:
description: object_type
@ -1427,8 +1425,7 @@ definitions:
schema.UpdateInfoRequest:
properties:
avatar:
allOf:
- $ref: '#/definitions/schema.AvatarInfo'
$ref: '#/definitions/schema.AvatarInfo'
description: avatar
bio:
description: bio

View File

@ -109,6 +109,8 @@ backend:
other: "Should not contain synonym tags."
cannot_update:
other: "No permission to update."
cannot_set_synonym_as_itself:
other: "You cannot set the synonym of the current tag as itself."
theme:
not_found:
other: "Theme not found."

View File

@ -97,6 +97,8 @@ backend:
other: "不应包含同义词标签。"
cannot_update:
other: "没有更新标签权限。"
cannot_set_synonym_as_itself:
other: "你无法将当前标签的同义词设置为当前标签自己"
theme:
not_found:
other: "主题未找到"

View File

@ -4,6 +4,8 @@ import (
"bytes"
"path/filepath"
"github.com/answerdev/answer/configs"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/server"
"github.com/answerdev/answer/internal/base/translator"
@ -12,6 +14,7 @@ import (
"github.com/answerdev/answer/internal/service/service_config"
"github.com/answerdev/answer/pkg/writer"
"github.com/segmentfault/pacman/contrib/conf/viper"
"github.com/segmentfault/pacman/log"
"gopkg.in/yaml.v3"
)
@ -25,6 +28,10 @@ type AllConfig struct {
Swaggerui *router.SwaggerConfig `json:"swaggerui" mapstructure:"swaggerui" yaml:"swaggerui"`
}
type PathIgnore struct {
Users []string `yaml:"users"`
}
// Server server config
type Server struct {
HTTP *server.HTTP `json:"http" mapstructure:"http" yaml:"http"`
@ -62,3 +69,18 @@ func RewriteConfig(configFilePath string, allConfig *AllConfig) error {
}
return writer.ReplaceFile(configFilePath, buf.String())
}
func GetPathIgnoreList() map[string]bool {
list := make(map[string]bool, 0)
data := &PathIgnore{}
err := yaml.Unmarshal(configs.PathIgnore, data)
if err != nil {
log.Error(err)
return list
}
for _, item := range data.Users {
list[item] = true
}
constant.PathIgnoreMap = list
return list
}

View File

@ -30,6 +30,8 @@ const (
var (
Version string = ""
PathIgnoreMap map[string]bool
ObjectTypeStrMapping = map[string]int{
QuestionObjectType: 1,
AnswerObjectType: 2,
@ -59,3 +61,11 @@ const (
SiteTypeLegal = "legal"
SiteTypeSeo = "seo"
)
func ExistInPathIgnore(name string) bool {
_, ok := PathIgnoreMap[name]
if ok {
return true
}
return false
}

View File

@ -58,4 +58,5 @@ const (
RevisionReviewUnderway = "error.revision.review_underway"
RevisionNoPermission = "error.revision.no_permission"
UserCannotUpdateYourRole = "error.user.cannot_update_your_role"
TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself"
)

View File

@ -296,6 +296,7 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionEditWithoutReview,
permission.TagUseReservedTag,
}, req.ID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
@ -304,16 +305,18 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
req.CanEdit = canList[0]
req.CanDelete = canList[1]
req.NoNeedReview = canList[2]
req.CanClose = middleware.GetIsAdminFromContext(ctx)
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
req.CanUseReservedTag = canList[3]
if !req.CanEdit {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
_, err = qc.questionService.UpdateQuestion(ctx, req)
handler.HandleResponse(ctx, err, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
resp, err := qc.questionService.UpdateQuestion(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, resp)
return
}
handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
}
// CloseMsgList close question msg list

View File

@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/handler"
templaterender "github.com/answerdev/answer/internal/controller/template_render"
"github.com/answerdev/answer/internal/schema"
@ -265,15 +266,23 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) {
// UserInfo user info
func (tc *TemplateController) UserInfo(ctx *gin.Context) {
// urlPath := ctx.Request.URL.Path
// filePath := ""
// switch urlPath {
// case "/users/login":
// filePath = "build/index.html"
// case "/users/register":
// filePath = "build/index.html"
// default:
username := ctx.Param("username")
if username == "" {
tc.Page404(ctx)
return
}
exist := constant.ExistInPathIgnore(username)
if exist {
file, err := ui.Build.ReadFile("build/index.html")
if err != nil {
log.Error(err)
tc.Page404(ctx)
return
}
ctx.Header("content-type", "text/html;charset=utf-8")
ctx.String(http.StatusOK, string(file))
return
}
req := &schema.GetOtherUserInfoByUsernameReq{}
req.Username = username
userinfo, err := tc.templateRenderController.UserInfo(ctx, req)
@ -292,16 +301,6 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) {
"userinfo": userinfo,
"bio": template.HTML(userinfo.Info.BioHTML),
})
// }
// file, err := ui.Build.ReadFile(filePath)
// if err != nil {
// log.Error(err)
// ctx.Status(http.StatusNotFound)
// return
// }
// ctx.Header("content-type", "text/html;charset=utf-8")
// ctx.String(http.StatusOK, string(file))
}

View File

@ -121,6 +121,7 @@ func addRoleFeatures(x *xorm.Engine) error {
{RoleID: 2, PowerType: permission.AnswerAudit},
{RoleID: 2, PowerType: permission.QuestionAudit},
{RoleID: 2, PowerType: permission.TagAudit},
{RoleID: 2, PowerType: permission.TagUseReservedTag},
{RoleID: 3, PowerType: permission.QuestionAdd},
{RoleID: 3, PowerType: permission.QuestionEdit},
@ -154,6 +155,7 @@ func addRoleFeatures(x *xorm.Engine) error {
{RoleID: 3, PowerType: permission.AnswerAudit},
{RoleID: 3, PowerType: permission.QuestionAudit},
{RoleID: 3, PowerType: permission.TagAudit},
{RoleID: 3, PowerType: permission.TagUseReservedTag},
}
// insert default powers
@ -190,6 +192,7 @@ func addRoleFeatures(x *xorm.Engine) error {
defaultConfigTable := []*entity.Config{
{ID: 115, Key: "rank.question.close", Value: `-1`},
{ID: 116, Key: "rank.question.reopen", Value: `-1`},
{ID: 117, Key: "rank.tag.use_reserved_tag", Value: `-1`},
}
for _, c := range defaultConfigTable {
exist, err := x.Get(&entity.Config{ID: c.ID, Key: c.Key})

View File

@ -41,6 +41,6 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) {
r.GET("/tags", a.templateController.TagList)
r.GET("/tags/:tag", a.templateController.TagInfo)
// r.GET("/users/:username", a.templateController.UserInfo)
r.GET("/users/:username", a.templateController.UserInfo)
r.GET("/404", a.templateController.Page404)
}

View File

@ -51,6 +51,8 @@ type QuestionPermission struct {
CanClose bool `json:"-"`
// whether user can reopen it
CanReopen bool `json:"-"`
// whether user can use reserved it
CanUseReservedTag bool `json:"-"`
}
type CheckCanQuestionUpdate struct {
@ -76,7 +78,6 @@ type QuestionUpdate struct {
EditSummary string `validate:"omitempty" json:"edit_summary"`
// user id
UserID string `json:"-"`
IsAdmin bool `json:"-"`
NoNeedReview bool `json:"-"`
QuestionPermission
}

View File

@ -34,4 +34,5 @@ const (
AnswerAudit = "answer.audit"
QuestionAudit = "question.audit"
TagAudit = "tag.audit"
TagUseReservedTag = "tag.use_reserved_tag"
)

View File

@ -352,14 +352,23 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
return questionInfo, tagerr
}
// If it's not admin
if !req.IsAdmin {
//CheckChangeTag
CheckTag, CheckTaglist := qs.CheckChangeReservedTag(ctx, oldTags, Tags)
if !CheckTag {
// if user can not use reserved tag, old reserved tag can not be removed and new reserved tag can not be added.
if !req.CanUseReservedTag {
CheckOldTag, CheckNewTag, CheckOldTaglist, CheckNewTaglist := qs.CheckChangeReservedTag(ctx, oldTags, Tags)
if !CheckOldTag {
errMsg := fmt.Sprintf(`The reserved tag "%s" must be present.`,
strings.Join(CheckTaglist, ","))
strings.Join(CheckOldTaglist, ","))
errorlist := make([]*validator.FormErrorField, 0)
errorlist = append(errorlist, &validator.FormErrorField{
ErrorField: "tags",
ErrorMsg: errMsg,
})
err = errors.BadRequest(reason.RequestFormatError).WithMsg(errMsg)
return errorlist, err
}
if !CheckNewTag {
errMsg := fmt.Sprintf(`"%s" can only be used by moderators.`,
strings.Join(CheckNewTaglist, ","))
errorlist := make([]*validator.FormErrorField, 0)
errorlist = append(errorlist, &validator.FormErrorField{
ErrorField: "tags",
@ -393,7 +402,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
Log: req.EditSummary,
}
if req.NoNeedReview || req.IsAdmin || dbinfo.UserID == req.UserID {
if req.NoNeedReview {
canUpdate = true
}
@ -476,7 +485,7 @@ func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.
return qs.tagCommon.ObjectChangeTag(ctx, objectTagData)
}
func (qs *QuestionService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
func (qs *QuestionService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, bool, []string, []string) {
return qs.tagCommon.CheckChangeReservedTag(ctx, oldobjectTagData, objectTagData)
}

View File

@ -150,16 +150,10 @@ func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID
if !exist {
return can, nil
}
// TODO administrator have all permissions
//if userInfo.IsAdmin {
// return true, nil
//}
objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID)
if err != nil {
return can, err
}
action := ""
switch objectInfo.ObjectType {
case constant.QuestionObjectType:
@ -181,6 +175,11 @@ func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID
action = permission.CommentVoteDown
}
}
powerMapping := rs.getUserPowerMapping(ctx, userID)
if powerMapping[action] {
return true, nil
}
meetRank := rs.checkUserRank(ctx, userInfo.ID, userInfo.Rank, PermissionPrefix+action)
return meetRank, nil
}

View File

@ -216,6 +216,9 @@ func (ts *TagService) UpdateTagSynonym(ctx context.Context, req *schema.UpdateTa
// find all exist tag
for _, item := range req.SynonymTagList {
if item.SlugName == mainTagInfo.SlugName {
return errors.BadRequest(reason.TagCannotSetSynonymAsItself)
}
addSynonymTagList = append(addSynonymTagList, item.SlugName)
}
tagListInDB, err := ts.tagCommonService.GetTagListByNames(ctx, addSynonymTagList)

View File

@ -443,9 +443,10 @@ func (ts *TagCommonService) CheckTagsIsChange(ctx context.Context, tagNameList,
return false
}
func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, bool, []string, []string) {
reservedTagsMap := make(map[string]bool)
needTagsMap := make([]string, 0)
notNeedTagsMap := make([]string, 0)
for _, tag := range objectTagData {
if tag.Reserved {
reservedTagsMap[tag.SlugName] = true
@ -456,14 +457,27 @@ func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjec
_, ok := reservedTagsMap[tag.SlugName]
if !ok {
needTagsMap = append(needTagsMap, tag.SlugName)
} else {
reservedTagsMap[tag.SlugName] = false
}
}
}
if len(needTagsMap) > 0 {
return false, needTagsMap
for k, v := range reservedTagsMap {
if v {
notNeedTagsMap = append(notNeedTagsMap, k)
}
}
return true, []string{}
if len(needTagsMap) > 0 {
return false, true, needTagsMap, []string{}
}
if len(notNeedTagsMap) > 0 {
return true, false, []string{}, notNeedTagsMap
}
return true, true, []string{}, []string{}
}
// ObjectChangeTag change object tag list

View File

@ -1,5 +1,4 @@
{{template "header" . }}
{{translator $.language "error.object.old_password_verification_failed"}}
<div class="pt-4 mt-2 mb-5 container">
<div class="justify-content-center row">
<div class="col-xxl-7 col-lg-8 col-sm-12">