From a8e008de0702f76733c79ae784357469b4bb496c Mon Sep 17 00:00:00 2001 From: sy-records <52o@qq52o.cn> Date: Tue, 15 Oct 2024 19:20:04 +0800 Subject: [PATCH 1/3] feat: Support related and hot questions template --- cmd/wire_gen.go | 2 +- internal/controller/template_controller.go | 68 ++++++++++++++++------ ui/template/hot-question.html | 42 +++++++++++++ ui/template/question-detail.html | 10 +++- ui/template/question.html | 4 +- ui/template/related-question.html | 42 +++++++++++++ 6 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 ui/template/hot-question.html create mode 100644 ui/template/related-question.html diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 978ab23b3..d8e6c744b 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -277,7 +277,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) shortIDMiddleware := middleware.NewShortIDMiddleware(siteInfoCommonService) templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService, siteInfoCommonService, questionRepo) - templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService, eventQueueService, userService) + templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService, eventQueueService, userService, questionService) templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController, authUserMiddleware) connectorController := controller.NewConnectorController(siteInfoCommonService, emailService, userExternalLoginService) userCenterLoginService := user_external_login2.NewUserCenterLoginService(userRepo, userCommon, userExternalLoginRepo, userActiveActivityRepo, siteInfoCommonService) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 56a38f006..5a63e6de1 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -22,6 +22,7 @@ package controller import ( "encoding/json" "fmt" + "github.com/apache/incubator-answer/internal/base/middleware" "github.com/apache/incubator-answer/internal/service/content" "github.com/apache/incubator-answer/internal/service/event_queue" "github.com/apache/incubator-answer/plugin" @@ -58,6 +59,7 @@ type TemplateController struct { siteInfoService siteinfo_common.SiteInfoCommonService eventQueueService event_queue.EventQueueService userService *content.UserService + questionService *content.QuestionService } // NewTemplateController new controller @@ -66,6 +68,7 @@ func NewTemplateController( siteInfoService siteinfo_common.SiteInfoCommonService, eventQueueService event_queue.EventQueueService, userService *content.UserService, + questionService *content.QuestionService, ) *TemplateController { script, css := GetStyle() return &TemplateController{ @@ -75,6 +78,7 @@ func NewTemplateController( siteInfoService: siteInfoService, eventQueueService: eventQueueService, userService: userService, + questionService: questionService, } } func GetStyle() (script []string, css string) { @@ -146,6 +150,14 @@ func (tc *TemplateController) Index(ctx *gin.Context) { return } + hotQuestionReq := &schema.QuestionPageReq{ + Page: 1, + PageSize: 6, + OrderCond: "hot", + InDays: 7, + } + hotQuestion, _, _ := tc.templateRenderController.Index(ctx, hotQuestionReq) + siteInfo := tc.SiteInfo(ctx) siteInfo.Canonical = siteInfo.General.SiteUrl @@ -156,10 +168,11 @@ func (tc *TemplateController) Index(ctx *gin.Context) { } siteInfo.Title = "" tc.html(ctx, http.StatusOK, "question.html", siteInfo, gin.H{ - "data": data, - "useTitle": UrlUseTitle, - "page": templaterender.Paginator(page, req.PageSize, count), - "path": "questions", + "data": data, + "useTitle": UrlUseTitle, + "page": templaterender.Paginator(page, req.PageSize, count), + "path": "questions", + "hotQuestion": hotQuestion, }) } @@ -177,6 +190,15 @@ func (tc *TemplateController) QuestionList(ctx *gin.Context) { tc.Page404(ctx) return } + + hotQuestionReq := &schema.QuestionPageReq{ + Page: 1, + PageSize: 6, + OrderCond: "hot", + InDays: 7, + } + hotQuestion, _, _ := tc.templateRenderController.Index(ctx, hotQuestionReq) + siteInfo := tc.SiteInfo(ctx) siteInfo.Canonical = fmt.Sprintf("%s/questions", siteInfo.General.SiteUrl) if page > 1 { @@ -190,13 +212,14 @@ func (tc *TemplateController) QuestionList(ctx *gin.Context) { } siteInfo.Title = fmt.Sprintf("%s - %s", translator.Tr(handler.GetLang(ctx), constant.QuestionsTitleTrKey), siteInfo.General.Name) tc.html(ctx, http.StatusOK, "question.html", siteInfo, gin.H{ - "data": data, - "useTitle": UrlUseTitle, - "page": templaterender.Paginator(page, req.PageSize, count), + "data": data, + "useTitle": UrlUseTitle, + "page": templaterender.Paginator(page, req.PageSize, count), + "hotQuestion": hotQuestion, }) } -func (tc *TemplateController) QuestionInfoeRdirect(ctx *gin.Context, siteInfo *schema.TemplateSiteInfoResp, correctTitle bool) (jump bool, url string) { +func (tc *TemplateController) QuestionInfoRedirect(ctx *gin.Context, siteInfo *schema.TemplateSiteInfoResp, correctTitle bool) (jump bool, url string) { questionID := ctx.Param("id") title := ctx.Param("title") answerID := uid.DeShortID(title) @@ -316,7 +339,7 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { } siteInfo := tc.SiteInfo(ctx) - jump, jumpurl := tc.QuestionInfoeRdirect(ctx, siteInfo, correctTitle) + jump, jumpurl := tc.QuestionInfoRedirect(ctx, siteInfo, correctTitle) if jump { ctx.Redirect(http.StatusFound, jumpurl) return @@ -337,7 +360,6 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { } // comments - objectIDs := []string{uid.DeShortID(id)} for _, answer := range answers { answerID := uid.DeShortID(answer.ID) @@ -348,6 +370,17 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { tc.Page404(ctx) return } + + UrlUseTitle := false + if siteInfo.SiteSeo.Permalink == constant.PermalinkQuestionIDAndTitle || + siteInfo.SiteSeo.Permalink == constant.PermalinkQuestionIDAndTitleByShortID { + UrlUseTitle = true + } + + //related question + userID := middleware.GetLoginUserIDFromContext(ctx) + relatedQuestion, _, _ := tc.questionService.SimilarQuestion(ctx, id, userID) + siteInfo.Canonical = fmt.Sprintf("%s/questions/%s/%s", siteInfo.General.SiteUrl, id, encodeTitle) if siteInfo.SiteSeo.Permalink == constant.PermalinkQuestionID || siteInfo.SiteSeo.Permalink == constant.PermalinkQuestionIDByShortID { siteInfo.Canonical = fmt.Sprintf("%s/questions/%s", siteInfo.General.SiteUrl, id) @@ -389,7 +422,6 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { item.Author.URL = fmt.Sprintf("%s/users/%s", siteInfo.General.SiteUrl, answer.UserInfo.Username) answerList = append(answerList, item) } - } jsonLD.MainEntity.SuggestedAnswer = answerList jsonLDStr, err := json.Marshal(jsonLD) @@ -405,12 +437,14 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { siteInfo.Keywords = strings.Replace(strings.Trim(fmt.Sprint(tags), "[]"), " ", ",", -1) siteInfo.Title = fmt.Sprintf("%s - %s", detail.Title, siteInfo.General.Name) tc.html(ctx, http.StatusOK, "question-detail.html", siteInfo, gin.H{ - "id": id, - "answerid": answerid, - "detail": detail, - "answers": answers, - "comments": comments, - "noindex": detail.Show == entity.QuestionHide, + "id": id, + "answerid": answerid, + "detail": detail, + "answers": answers, + "comments": comments, + "noindex": detail.Show == entity.QuestionHide, + "useTitle": UrlUseTitle, + "relatedQuestion": relatedQuestion, }) } diff --git a/ui/template/hot-question.html b/ui/template/hot-question.html new file mode 100644 index 000000000..239819267 --- /dev/null +++ b/ui/template/hot-question.html @@ -0,0 +1,42 @@ + +{{define "hot-question"}} +
+
{{translator $.language "ui.question.hot_questions"}}
+
+ {{ range .hotQuestion }} + {{if $.useTitle }} + + {{else}} + + {{end}} + + {{if ne 0 .AnswerCount}} + + {{end}} + + {{ end }} +
+
+{{end}} diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html index 2134a04f5..d0e6933d9 100644 --- a/ui/template/question-detail.html +++ b/ui/template/question-detail.html @@ -24,7 +24,11 @@

+ {{if $.useTitle }} + {{.detail.Title}} + {{else}} {{.detail.Title}} + {{end}}

@@ -116,7 +120,7 @@

{{.detail.AnswerCount}} Answers
{{range .answers}} -
+
{{formatLinkNofollow .HTML}}
@@ -192,8 +196,8 @@
{{.detail.AnswerCount}} Answers
{{end}}
-
- +
+ {{template "related-question" .}}
diff --git a/ui/template/question.html b/ui/template/question.html index 40c645edc..058645e5b 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -94,7 +94,9 @@
-
+
+ {{template "hot-question" .}} +
{{template "footer" .}} diff --git a/ui/template/related-question.html b/ui/template/related-question.html new file mode 100644 index 000000000..c93ccc8da --- /dev/null +++ b/ui/template/related-question.html @@ -0,0 +1,42 @@ + +{{define "related-question"}} +
+
{{translator $.language "ui.related_question.title"}}
+
+ {{ range .relatedQuestion }} + {{if $.useTitle }} + + {{else}} + + {{end}} + + {{if ne 0 .AnswerCount}} + + {{end}} + + {{ end }} +
+
+{{end}} From 916dba3f063b95ab8297c63630750b2d08988046 Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Fri, 18 Oct 2024 10:12:59 +0800 Subject: [PATCH 2/3] feat(pager): Show 404 when page is out of range (#1135) close #1133 --- internal/base/pager/pagination.go | 12 ++++++++++++ internal/controller/question_controller.go | 4 ++++ internal/controller/tag_controller.go | 5 +++++ internal/controller/template_controller.go | 10 +++++++--- internal/service/tag/tag_service.go | 1 - ui/src/services/client/question.ts | 5 ++--- ui/src/services/common.ts | 10 +++++----- 7 files changed, 35 insertions(+), 12 deletions(-) diff --git a/internal/base/pager/pagination.go b/internal/base/pager/pagination.go index 5411e6850..36849fed5 100644 --- a/internal/base/pager/pagination.go +++ b/internal/base/pager/pagination.go @@ -62,3 +62,15 @@ func ValPageAndPageSize(page, pageSize int) (int, int) { } return page, pageSize } + +// ValPageOutOfRange validate page out of range +func ValPageOutOfRange(total int64, page, pageSize int) bool { + if total <= 0 { + return false + } + if pageSize <= 0 { + return true + } + totalPages := (total + int64(pageSize) - 1) / int64(pageSize) + return page < 1 || page > int(totalPages) +} diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index 8f3498556..310a8201c 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -337,6 +337,10 @@ func (qc *QuestionController) QuestionPage(ctx *gin.Context) { handler.HandleResponse(ctx, err, nil) return } + if pager.ValPageOutOfRange(total, req.Page, req.PageSize) { + handler.HandleResponse(ctx, errors.NotFound(reason.RequestFormatError), nil) + return + } handler.HandleResponse(ctx, nil, pager.NewPageModel(total, questions)) } diff --git a/internal/controller/tag_controller.go b/internal/controller/tag_controller.go index 5f5cf5fbd..1e98b480c 100644 --- a/internal/controller/tag_controller.go +++ b/internal/controller/tag_controller.go @@ -22,6 +22,7 @@ package controller import ( "github.com/apache/incubator-answer/internal/base/handler" "github.com/apache/incubator-answer/internal/base/middleware" + "github.com/apache/incubator-answer/internal/base/pager" "github.com/apache/incubator-answer/internal/base/reason" "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/permission" @@ -269,6 +270,10 @@ func (tc *TagController) GetTagWithPage(ctx *gin.Context) { req.UserID = middleware.GetLoginUserIDFromContext(ctx) resp, err := tc.tagService.GetTagWithPage(ctx, req) + if pager.ValPageOutOfRange(resp.Count, req.Page, req.PageSize) { + handler.HandleResponse(ctx, errors.NotFound(reason.RequestFormatError), nil) + return + } handler.HandleResponse(ctx, err, resp) } diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 5a63e6de1..c3a2ff0af 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -23,6 +23,7 @@ import ( "encoding/json" "fmt" "github.com/apache/incubator-answer/internal/base/middleware" + "github.com/apache/incubator-answer/internal/base/pager" "github.com/apache/incubator-answer/internal/service/content" "github.com/apache/incubator-answer/internal/service/event_queue" "github.com/apache/incubator-answer/plugin" @@ -145,7 +146,7 @@ func (tc *TemplateController) Index(ctx *gin.Context) { var page = req.Page data, count, err := tc.templateRenderController.Index(ctx, req) - if err != nil { + if err != nil || (len(data) == 0 && pager.ValPageOutOfRange(count, page, req.PageSize)) { tc.Page404(ctx) return } @@ -186,7 +187,7 @@ func (tc *TemplateController) QuestionList(ctx *gin.Context) { } var page = req.Page data, count, err := tc.templateRenderController.Index(ctx, req) - if err != nil { + if err != nil || (len(data) == 0 && pager.ValPageOutOfRange(count, page, req.PageSize)) { tc.Page404(ctx) return } @@ -454,8 +455,11 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { if handler.BindAndCheck(ctx, req) { return } + if req.PageSize == 0 { + req.PageSize = constant.DefaultPageSize + } data, err := tc.templateRenderController.TagList(ctx, req) - if err != nil { + if err != nil || pager.ValPageOutOfRange(data.Count, req.Page, req.PageSize) { tc.Page404(ctx) return } diff --git a/internal/service/tag/tag_service.go b/internal/service/tag/tag_service.go index 018641634..e5fcf597a 100644 --- a/internal/service/tag/tag_service.go +++ b/internal/service/tag/tag_service.go @@ -432,7 +432,6 @@ func (ts *TagService) GetTagWithPage(ctx context.Context, req *schema.GetTagWith } item.GetExcerpt() resp = append(resp, item) - } return pager.NewPageModel(total, resp), nil } diff --git a/ui/src/services/client/question.ts b/ui/src/services/client/question.ts index b62fbf1a7..169ccf3d3 100644 --- a/ui/src/services/client/question.ts +++ b/ui/src/services/client/question.ts @@ -25,9 +25,8 @@ import type * as Type from '@/common/interface'; export const useQuestionList = (params: Type.QueryQuestionsReq) => { const apiUrl = `/answer/api/v1/question/page?${qs.stringify(params)}`; - const { data, error } = useSWR( - [apiUrl], - request.instance.get, + const { data, error } = useSWR(apiUrl, (url) => + request.get(url, { allow404: true }), ); return { data, diff --git a/ui/src/services/common.ts b/ui/src/services/common.ts index ad0d99e03..cfc1d9cd2 100644 --- a/ui/src/services/common.ts +++ b/ui/src/services/common.ts @@ -35,11 +35,11 @@ export const queryQuestionByTitle = (title: string) => { }; export const useQueryTags = (params) => { - const { data, error, mutate } = useSWR( - `/answer/api/v1/tags/page?${qs.stringify(params, { - skipNulls: true, - })}`, - request.instance.get, + const apiUrl = `/answer/api/v1/tags/page?${qs.stringify(params, { + skipNulls: true, + })}`; + const { data, error, mutate } = useSWR(apiUrl, (url) => + request.get(url, { allow404: true }), ); return { data, From 23cc755aa8ba7d536c8c5db36022264efc5b2201 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Fri, 18 Oct 2024 15:55:31 +0800 Subject: [PATCH 3/3] feat(question): add question linking feature for closing question --- cmd/wire_gen.go | 2 +- internal/service/content/question_service.go | 4 + internal/service/question_common/question.go | 76 ++++++++++++ plugin/importer.go | 124 +++++++++---------- 4 files changed, 143 insertions(+), 63 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index d8e6c744b..801c5a776 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -177,7 +177,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, answerCommon := answercommon.NewAnswerCommon(answerRepo) metaRepo := meta.NewMetaRepo(dataData) metaCommonService := metacommon.NewMetaCommonService(metaRepo) - questionCommon := questioncommon.NewQuestionCommon(questionRepo, answerRepo, voteRepo, followRepo, tagCommonService, userCommon, collectionCommon, answerCommon, metaCommonService, configService, activityQueueService, revisionRepo, dataData) + questionCommon := questioncommon.NewQuestionCommon(questionRepo, answerRepo, voteRepo, followRepo, tagCommonService, userCommon, collectionCommon, answerCommon, metaCommonService, configService, activityQueueService, revisionRepo, siteInfoCommonService, dataData) eventQueueService := event_queue.NewEventQueueService() userService := content.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo, userNotificationConfigService, questionCommon, eventQueueService) captchaRepo := captcha.NewCaptchaRepo(dataData) diff --git a/internal/service/content/question_service.go b/internal/service/content/question_service.go index 7cfd39a09..51f0da9b5 100644 --- a/internal/service/content/question_service.go +++ b/internal/service/content/question_service.go @@ -174,6 +174,9 @@ func (qs *QuestionService) CloseQuestion(ctx context.Context, req *schema.CloseQ if err != nil { return err } + if cf.Key == constant.ReasonADuplicate { + qs.questioncommon.AddQuestionLinkForCloseReason(ctx, questionInfo, req.CloseMsg) + } qs.activityQueueService.Send(ctx, &schema.ActivityMsg{ UserID: req.UserID, @@ -199,6 +202,7 @@ func (qs *QuestionService) ReopenQuestion(ctx context.Context, req *schema.Reope if err != nil { return err } + qs.questioncommon.RemoveQuestionLinkForReopen(ctx, questionInfo) qs.activityQueueService.Send(ctx, &schema.ActivityMsg{ UserID: req.UserID, ObjectID: questionInfo.ID, diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 63c8f5a2a..13df33896 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -23,6 +23,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/apache/incubator-answer/internal/service/siteinfo_common" "math" "strings" "time" @@ -98,6 +99,7 @@ type QuestionCommon struct { configService *config.ConfigService activityQueueService activity_queue.ActivityQueueService revisionRepo revision.RevisionRepo + siteInfoService siteinfo_common.SiteInfoCommonService data *data.Data } @@ -113,6 +115,7 @@ func NewQuestionCommon(questionRepo QuestionRepo, configService *config.ConfigService, activityQueueService activity_queue.ActivityQueueService, revisionRepo revision.RevisionRepo, + siteInfoService siteinfo_common.SiteInfoCommonService, data *data.Data, ) *QuestionCommon { return &QuestionCommon{ @@ -128,6 +131,7 @@ func NewQuestionCommon(questionRepo QuestionRepo, configService: configService, activityQueueService: activityQueueService, revisionRepo: revisionRepo, + siteInfoService: siteInfoService, data: data, } } @@ -795,3 +799,75 @@ func (qs *QuestionCommon) UpdateQuestionLink(ctx context.Context, questionID, an return parsedText, nil } + +// AddQuestionLinkForCloseReason When the reason about close question is a question link, add the link to the question +func (qs *QuestionCommon) AddQuestionLinkForCloseReason(ctx context.Context, + questionInfo *entity.Question, closeMsg string) { + questionID := qs.tryToGetQuestionIDFromMsg(ctx, closeMsg) + if len(questionID) == 0 { + return + } + + linkedQuestion, exist, err := qs.questionRepo.GetQuestion(ctx, questionID) + if err != nil { + log.Errorf("get question error %s", err) + return + } + if !exist { + return + } + err = qs.questionRepo.LinkQuestion(ctx, &entity.QuestionLink{ + FromQuestionID: questionInfo.ID, + ToQuestionID: linkedQuestion.ID, + Status: entity.QuestionLinkStatusAvailable, + }) + if err != nil { + log.Errorf("link question error %s", err) + } +} + +func (qs *QuestionCommon) RemoveQuestionLinkForReopen(ctx context.Context, questionInfo *entity.Question) { + questionInfo.ID = uid.DeShortID(questionInfo.ID) + metaInfo, err := qs.metaCommonService.GetMetaByObjectIdAndKey(ctx, questionInfo.ID, entity.QuestionCloseReasonKey) + if err != nil { + return + } + + closeMsgMeta := &schema.CloseQuestionMeta{} + _ = json.Unmarshal([]byte(metaInfo.Value), closeMsgMeta) + + linkedQuestionID := qs.tryToGetQuestionIDFromMsg(ctx, closeMsgMeta.CloseMsg) + if len(linkedQuestionID) == 0 { + return + } + err = qs.questionRepo.RemoveQuestionLink(ctx, &entity.QuestionLink{ + FromQuestionID: questionInfo.ID, + ToQuestionID: linkedQuestionID, + }) + if err != nil { + log.Errorf("remove question link error %s", err) + } +} + +func (qs *QuestionCommon) tryToGetQuestionIDFromMsg(ctx context.Context, closeMsg string) (questionID string) { + siteGeneral, err := qs.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Errorf("get site general error %s", err) + return + } + if !strings.HasPrefix(closeMsg, siteGeneral.SiteUrl) { + return + } + // get question id from url + // the url may like: https://xxx.com/questions/D1401/xxx + // the D1401 is question id + questionID = strings.TrimPrefix(closeMsg, siteGeneral.SiteUrl) + questionID = strings.TrimPrefix(questionID, "/questions/") + t := strings.Split(questionID, "/") + if len(t) < 1 { + return "" + } + questionID = t[0] + questionID = uid.DeShortID(questionID) + return questionID +} diff --git a/plugin/importer.go b/plugin/importer.go index 3893d54a1..bfd7d36a5 100644 --- a/plugin/importer.go +++ b/plugin/importer.go @@ -1,62 +1,62 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package plugin - -import ( - "context" -) - -type QuestionImporterInfo struct { - Title string `json:"title"` - Content string `json:"content"` - Tags []string `json:"tags"` - UserEmail string `json:"user_email"` -} - -type Importer interface { - Base - RegisterImporterFunc(ctx context.Context, importer ImporterFunc) -} - -type ImporterFunc interface { - AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) (err error) -} - -var ( - // CallImporter is a function that calls all registered parsers - CallImporter, - registerImporter = MakePlugin[Importer](false) -) - -func ImporterEnabled() (enabled bool) { - _ = CallImporter(func(fn Importer) error { - enabled = true - return nil - }) - return -} -func GetImporter() (ip Importer, ok bool) { - _ = CallImporter(func(fn Importer) error { - ip = fn - ok = true - return nil - }) - return -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package plugin + +import ( + "context" +) + +type QuestionImporterInfo struct { + Title string `json:"title"` + Content string `json:"content"` + Tags []string `json:"tags"` + UserEmail string `json:"user_email"` +} + +type Importer interface { + Base + RegisterImporterFunc(ctx context.Context, importer ImporterFunc) +} + +type ImporterFunc interface { + AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) (err error) +} + +var ( + // CallImporter is a function that calls all registered parsers + CallImporter, + registerImporter = MakePlugin[Importer](false) +) + +func ImporterEnabled() (enabled bool) { + _ = CallImporter(func(fn Importer) error { + enabled = true + return nil + }) + return +} +func GetImporter() (ip Importer, ok bool) { + _ = CallImporter(func(fn Importer) error { + ip = fn + ok = true + return nil + }) + return +}