Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions i18n/en_US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ backend:
other: No permission to update.
content_cannot_empty:
other: Content cannot be empty.
content_less_than_minumum:
other: Not enough content entered.
rank:
fail_to_meet_the_condition:
other: Reputation rank fail to meet the condition.
Expand Down Expand Up @@ -1160,6 +1162,9 @@ ui:
label: Body
msg:
empty: Body cannot be empty.
hint:
optional_body: Share what the question is about.
minimum_characters: "Share what the question is about, at least {{min_content_length}} characters are required."
tags:
label: Tags
msg:
Expand All @@ -1181,9 +1186,9 @@ ui:
add_btn: Add tag
create_btn: Create new tag
search_tag: Search tag
hint: "Describe what your content is about, at least one tag is required."
hint_zero_tags: " Describe what your content is about."
hint_more_than_one_tag: "Describe what your content is about, at least {{ min_tags_number }} tags are required."
hint: Describe what your content is about, at least one tag is required.
hint_zero_tags: Describe what your content is about.
hint_more_than_one_tag: "Describe what your content is about, at least {{min_tags_number}} tags are required."
no_result: No tags matched
tag_required_text: Required tag (at least one)
header:
Expand Down Expand Up @@ -2122,13 +2127,16 @@ ui:
ask_before_display: Ask before displaying external content
write:
page_title: Write
min_content:
label: Minimum question body length
text: Minimum allowed question body length in characters.
restrict_answer:
title: Answer write
label: Each user can only write one answer for the same question
text: "Turn off to allow users to write multiple answers to the same question, which may cause answers to be unfocused."
min_tags:
label: "Minimum tags per question"
text: "Minimum number of tags required in a question"
text: "Minimum number of tags required in a question."
recommend_tags:
label: Recommend tags
text: "Recommend tags will show in the dropdown list by default."
Expand Down
1 change: 1 addition & 0 deletions internal/base/reason/reason.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
QuestionAlreadyDeleted = "error.question.already_deleted"
QuestionUnderReview = "error.question.under_review"
QuestionContentCannotEmpty = "error.question.content_cannot_empty"
QuestionContentLessThanMinimum = "error.question.content_less_than_minumum"
AnswerNotFound = "error.answer.not_found"
AnswerCannotDeleted = "error.answer.cannot_deleted"
AnswerCannotUpdate = "error.answer.cannot_update"
Expand Down
1 change: 1 addition & 0 deletions internal/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ func (m *Mentor) initSiteInfoPrivilegeRank() {

func (m *Mentor) initSiteInfoWrite() {
writeData := map[string]interface{}{
"min_content": 6,
"restrict_answer": true,
"min_tags": 1,
"required_tag": false,
Expand Down
2 changes: 2 additions & 0 deletions internal/migrations/v28.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func addOptionalTags(ctx context.Context, x *xorm.Engine) error {
}
if exist {
type OldSiteWriteReq struct {
MinimumContent int `json:"min_content"`
RestrictAnswer bool `json:"restrict_answer"`
MinimumTags int `json:"min_tags"`
RequiredTag bool `json:"required_tag"`
Expand All @@ -55,6 +56,7 @@ func addOptionalTags(ctx context.Context, x *xorm.Engine) error {
content := &OldSiteWriteReq{}
_ = json.Unmarshal([]byte(writeSiteInfo.Content), content)
content.MinimumTags = 1
content.MinimumContent = 6
data, _ := json.Marshal(content)
writeSiteInfo.Content = string(data)
_, err = x.Context(ctx).ID(writeSiteInfo.ID).Cols("content").Update(writeSiteInfo)
Expand Down
26 changes: 3 additions & 23 deletions internal/schema/question_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type QuestionAdd struct {
// question title
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
// content
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
Content string `validate:"gte=0,lte=65535" json:"content"`
// html
HTML string `json:"-"`
// tags
Expand All @@ -100,20 +100,14 @@ func (req *QuestionAdd) Check() (errFields []*validator.FormErrorField, err erro
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
}
}
if req.HTML == "" {
return append(errFields, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: reason.QuestionContentCannotEmpty,
}), errors.BadRequest(reason.QuestionContentCannotEmpty)
}
return nil, nil
}

type QuestionAddByAnswer struct {
// question title
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
// content
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
Content string `validate:"gte=0,lte=65535" json:"content"`
// html
HTML string `json:"-"`
AnswerContent string `validate:"required,notblank,gte=6,lte=65535" json:"answer_content"`
Expand All @@ -138,19 +132,11 @@ func (req *QuestionAddByAnswer) Check() (errFields []*validator.FormErrorField,
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
}
}
if req.HTML == "" {
errFields = append(errFields, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: reason.QuestionContentCannotEmpty,
})
}
if req.AnswerHTML == "" {
errFields = append(errFields, &validator.FormErrorField{
ErrorField: "answer_content",
ErrorMsg: reason.AnswerContentCannotEmpty,
})
}
if req.HTML == "" || req.AnswerHTML == "" {
return errFields, errors.BadRequest(reason.QuestionContentCannotEmpty)
}
return nil, nil
Expand Down Expand Up @@ -195,7 +181,7 @@ type QuestionUpdate struct {
// question title
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
// content
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
Content string `validate:"gte=0,lte=65535" json:"content"`
// html
HTML string `json:"-"`
InviteUser []string `validate:"omitempty" json:"invite_user"`
Expand Down Expand Up @@ -227,12 +213,6 @@ type QuestionUpdateInviteUser struct {

func (req *QuestionUpdate) Check() (errFields []*validator.FormErrorField, err error) {
req.HTML = converter.Markdown2HTML(req.Content)
if req.HTML == "" {
return append(errFields, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: reason.QuestionContentCannotEmpty,
}), errors.BadRequest(reason.QuestionContentCannotEmpty)
}
return nil, nil
}

Expand Down
1 change: 1 addition & 0 deletions internal/schema/siteinfo_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type SiteBrandingReq struct {

// SiteWriteReq site write request
type SiteWriteReq struct {
MinimumContent int `validate:"omitempty,min=0" json:"min_content"`
RestrictAnswer bool `validate:"omitempty" json:"restrict_answer"`
MinimumTags int `validate:"omitempty" json:"min_tags"`
RequiredTag bool `validate:"omitempty" json:"required_tag"`
Expand Down
40 changes: 40 additions & 0 deletions internal/service/content/question_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,19 @@ func (qs *QuestionService) CheckAddQuestion(ctx context.Context, req *schema.Que
err = errors.BadRequest(reason.TagMinCount)
return errorlist, err
}
minimumContentLength, err := qs.questioncommon.GetMinimumContentLength(ctx)
if err != nil {
return
}
if len(req.Content) < minimumContentLength {
errorlist := make([]*validator.FormErrorField, 0)
errorlist = append(errorlist, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.QuestionContentLessThanMinimum),
})
err = errors.BadRequest(reason.QuestionContentLessThanMinimum)
return errorlist, err
}
recommendExist, err := qs.tagCommon.ExistRecommend(ctx, req.Tags)
if err != nil {
return
Expand Down Expand Up @@ -301,6 +314,19 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
err = errors.BadRequest(reason.TagMinCount)
return errorlist, err
}
minimumContentLength, err := qs.questioncommon.GetMinimumContentLength(ctx)
if err != nil {
return
}
if len(req.Content) < minimumContentLength {
errorlist := make([]*validator.FormErrorField, 0)
errorlist = append(errorlist, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.QuestionContentLessThanMinimum),
})
err = errors.BadRequest(reason.QuestionContentLessThanMinimum)
return errorlist, err
}
recommendExist, err := qs.tagCommon.ExistRecommend(ctx, req.Tags)
if err != nil {
return
Expand Down Expand Up @@ -907,6 +933,20 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
question.UserID = dbinfo.UserID
question.LastEditUserID = req.UserID

minimumContentLength, err := qs.questioncommon.GetMinimumContentLength(ctx)
if err != nil {
return
}
if len(req.Content) < minimumContentLength {
errorlist := make([]*validator.FormErrorField, 0)
errorlist = append(errorlist, &validator.FormErrorField{
ErrorField: "content",
ErrorMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.QuestionContentLessThanMinimum),
})
err = errors.BadRequest(reason.QuestionContentLessThanMinimum)
return errorlist, err
}

oldTags, tagerr := qs.tagCommon.GetObjectEntityTag(ctx, question.ID)
if tagerr != nil {
return questionInfo, tagerr
Expand Down
8 changes: 8 additions & 0 deletions internal/service/question_common/question.go
Original file line number Diff line number Diff line change
Expand Up @@ -899,3 +899,11 @@ func (qs *QuestionCommon) tryToGetQuestionIDFromMsg(ctx context.Context, closeMs
questionID = uid.DeShortID(questionID)
return questionID
}

func (qs *QuestionCommon) GetMinimumContentLength(ctx context.Context) (int, error) {
siteInfo, err := qs.siteInfoService.GetSiteWrite(ctx)
if err != nil {
return 6, err
}
return siteInfo.MinimumContent, nil
}
1 change: 1 addition & 0 deletions ui/src/common/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ export interface AdminSettingsLegal {
export interface AdminSettingsWrite {
restrict_answer?: boolean;
min_tags?: number,
min_content?:number,
recommend_tags?: Tag[];
required_tag?: boolean;
reserved_tags?: Tag[];
Expand Down
6 changes: 3 additions & 3 deletions ui/src/components/TagSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,9 @@ const TagSelector: FC<IProps> = ({
return t(`hint`);
}

let str: string = t(`hint_more_than_one_tag`);
str = str.replace(`{{ min_tags_number }}`, writeInfo.min_tags.toString());
return str;
return t(`hint_more_than_one_tag`, {
min_tags_number: writeInfo.min_tags,
});
};

useEffect(() => {
Expand Down
33 changes: 28 additions & 5 deletions ui/src/pages/Admin/Write/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const initFormData = {
errorMsg: '',
isInvalid: false,
},
min_content: {
value: 0,
errorMsg: '',
isInvalid: false,
},
min_tags: {
value: 0,
errorMsg: '',
Expand Down Expand Up @@ -142,6 +147,7 @@ const Index: FC = () => {
reserved_tags: formData.reserved_tags.value,
required_tag: formData.required_tag.value,
restrict_answer: formData.restrict_answer.value,
min_content: Number(formData.min_content.value),
max_image_size: Number(formData.max_image_size.value),
max_attachment_size: Number(formData.max_attachment_size.value),
max_image_megapixel: Number(formData.max_image_megapixel.value),
Expand Down Expand Up @@ -183,6 +189,7 @@ const Index: FC = () => {
if (Array.isArray(res.recommend_tags)) {
formData.recommend_tags.value = res.recommend_tags;
}
formData.min_content.value = res.min_content;
formData.min_tags.value = res.min_tags;
formData.required_tag.value = res.required_tag;
formData.restrict_answer.value = res.restrict_answer;
Expand All @@ -204,10 +211,6 @@ const Index: FC = () => {
initData();
}, []);

// const handleOnChange = (data) => {
// setFormData(data);
// };

return (
<>
<h3 className="mb-4">{t('page_title')}</h3>
Expand Down Expand Up @@ -295,7 +298,27 @@ const Index: FC = () => {
{formData.required_tag.errorMsg}
</Form.Control.Feedback>
</Form.Group>

<Form.Group className="mb-3" controlId="min_content">
<Form.Label>{t('min_content.label')}</Form.Label>
<Form.Control
type="number"
value={formData.min_content.value}
isInvalid={formData.min_content.isInvalid}
onChange={(evt) => {
handleValueChange({
min_content: {
value: evt.target.value,
errorMsg: '',
isInvalid: false,
},
});
}}
/>
<Form.Text>{t('min_content.text')}</Form.Text>
<Form.Control.Feedback type="invalid">
{formData.min_content.errorMsg}
</Form.Control.Feedback>
</Form.Group>
<Form.Group className="mb-3" controlId="restrict_answer">
<Form.Label>{t('restrict_answer.title')}</Form.Label>
<Form.Switch
Expand Down
Loading