This is an automated email from the ASF dual-hosted git repository. kumfo pushed a commit to branch feat/1.7.0/quicklinks in repository https://gitbox.apache.org/repos/asf/answer.git
commit a6d95ae8f94a1648927d150426a74c2e9cae479f Author: kumfo <ku...@sifou.com> AuthorDate: Wed Aug 13 14:44:25 2025 +0800 feat: add sidebar plugin --- cmd/wire_gen.go | 3 +- internal/controller/controller.go | 1 + ...{controller.go => plugin_sidebar_controller.go} | 60 ++++++++++------------ internal/router/plugin_api_router.go | 6 +++ plugin/config.go | 30 +++++++---- plugin/plugin.go | 4 ++ plugin/sidebar.go | 37 +++++++++++++ 7 files changed, 96 insertions(+), 45 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 947b6605..ce352051 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -289,7 +289,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, captchaController := controller.NewCaptchaController() embedController := controller.NewEmbedController() renderController := controller.NewRenderController() - pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController, captchaController, embedController, renderController) + sidebarController := controller.NewSidebarController() + pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController, captchaController, embedController, renderController, sidebarController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, shortIDMiddleware, templateRouter, pluginAPIRouter, uiConf) scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService, fileRecordService, userAdminService, serviceConf) application := newApplication(serverConf, ginEngine, scheduledTaskManager) diff --git a/internal/controller/controller.go b/internal/controller/controller.go index cbf80f7f..b5c3f91c 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -53,4 +53,5 @@ var ProviderSetController = wire.NewSet( NewEmbedController, NewBadgeController, NewRenderController, + NewSidebarController, ) diff --git a/internal/controller/controller.go b/internal/controller/plugin_sidebar_controller.go similarity index 50% copy from internal/controller/controller.go copy to internal/controller/plugin_sidebar_controller.go index cbf80f7f..87186179 100644 --- a/internal/controller/controller.go +++ b/internal/controller/plugin_sidebar_controller.go @@ -19,38 +19,30 @@ package controller -import "github.com/google/wire" - -// ProviderSetController is controller providers. -var ProviderSetController = wire.NewSet( - NewLangController, - NewCommentController, - NewReportController, - NewVoteController, - NewTagController, - NewFollowController, - NewCollectionController, - NewUserController, - NewQuestionController, - NewAnswerController, - NewSearchController, - NewRevisionController, - NewRankController, - NewReasonController, - NewNotificationController, - NewSiteInfoController, - NewDashboardController, - NewUploadController, - NewActivityController, - NewTemplateController, - NewConnectorController, - NewUserCenterController, - NewPermissionController, - NewUserPluginController, - NewReviewController, - NewCaptchaController, - NewMetaController, - NewEmbedController, - NewBadgeController, - NewRenderController, +import ( + "github.com/apache/answer/internal/base/handler" + "github.com/apache/answer/plugin" + "github.com/gin-gonic/gin" ) + +// SidebarController is the controller for the sidebar plugin. +type SidebarController struct{} + +// NewSidebarController creates a new instance of SidebarController. +func NewSidebarController() *SidebarController { + return &SidebarController{} +} + +// GetSidebarConfig retrieves the sidebar configuration from the registered sidebar plugins. +func (uc *SidebarController) GetSidebarConfig(ctx *gin.Context) { + resp := &plugin.SidebarConfig{} + _ = plugin.CallSidebar(func(fn plugin.Sidebar) error { + cfg, err := fn.GetSidebarConfig() + if err != nil { + return err + } + resp = cfg + return nil + }) + handler.HandleResponse(ctx, nil, resp) +} diff --git a/internal/router/plugin_api_router.go b/internal/router/plugin_api_router.go index ce5062ba..3b74b3dd 100644 --- a/internal/router/plugin_api_router.go +++ b/internal/router/plugin_api_router.go @@ -30,6 +30,7 @@ type PluginAPIRouter struct { captchaController *controller.CaptchaController embedController *controller.EmbedController renderController *controller.RenderController + sidebarController *controller.SidebarController } func NewPluginAPIRouter( @@ -38,6 +39,7 @@ func NewPluginAPIRouter( captchaController *controller.CaptchaController, embedController *controller.EmbedController, renderController *controller.RenderController, + sidebarController *controller.SidebarController, ) *PluginAPIRouter { return &PluginAPIRouter{ connectorController: connectorController, @@ -45,6 +47,7 @@ func NewPluginAPIRouter( captchaController: captchaController, embedController: embedController, renderController: renderController, + sidebarController: sidebarController, } } @@ -68,6 +71,9 @@ func (pr *PluginAPIRouter) RegisterUnAuthConnectorRouter(r *gin.RouterGroup) { r.GET("/captcha/config", pr.captchaController.GetCaptchaConfig) r.GET("/embed/config", pr.embedController.GetEmbedConfig) r.GET("/render/config", pr.renderController.GetRenderConfig) + + // sidebar plugin + r.GET("/sidebar/config", pr.sidebarController.GetSidebarConfig) } func (pr *PluginAPIRouter) RegisterAuthUserConnectorRouter(r *gin.RouterGroup) { diff --git a/plugin/config.go b/plugin/config.go index b03e794e..849ad617 100644 --- a/plugin/config.go +++ b/plugin/config.go @@ -23,16 +23,17 @@ type ConfigType string type InputType string const ( - ConfigTypeInput ConfigType = "input" - ConfigTypeTextarea ConfigType = "textarea" - ConfigTypeCheckbox ConfigType = "checkbox" - ConfigTypeRadio ConfigType = "radio" - ConfigTypeSelect ConfigType = "select" - ConfigTypeUpload ConfigType = "upload" - ConfigTypeTimezone ConfigType = "timezone" - ConfigTypeSwitch ConfigType = "switch" - ConfigTypeButton ConfigType = "button" - ConfigTypeLegend ConfigType = "legend" + ConfigTypeInput ConfigType = "input" + ConfigTypeTextarea ConfigType = "textarea" + ConfigTypeCheckbox ConfigType = "checkbox" + ConfigTypeRadio ConfigType = "radio" + ConfigTypeSelect ConfigType = "select" + ConfigTypeUpload ConfigType = "upload" + ConfigTypeTimezone ConfigType = "timezone" + ConfigTypeSwitch ConfigType = "switch" + ConfigTypeButton ConfigType = "button" + ConfigTypeLegend ConfigType = "legend" + ConfigTypeTagSelector ConfigType = "tag_selector" ) const ( @@ -105,6 +106,15 @@ type OnCompleteAction struct { RefreshFormConfig bool `json:"refresh_form_config"` } +// TagSelectorOption represents a tag option in the tag selector config value field +type TagSelectorOption struct { + TagID string `json:"tag_id"` + SlugName string `json:"slug_name"` + DisplayName string `json:"display_name"` + Recommend bool `json:"recommend"` + Reserved bool `json:"reserved"` +} + type Config interface { Base diff --git a/plugin/plugin.go b/plugin/plugin.go index a9e17310..26684835 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -126,6 +126,10 @@ func Register(p Base) { if _, ok := p.(KVStorage); ok { registerKVStorage(p.(KVStorage)) } + + if _, ok := p.(Sidebar); ok { + registerSidebar(p.(Sidebar)) + } } type Stack[T Base] struct { diff --git a/plugin/sidebar.go b/plugin/sidebar.go new file mode 100644 index 00000000..690a70d9 --- /dev/null +++ b/plugin/sidebar.go @@ -0,0 +1,37 @@ +/* + * 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 +package plugin + +type SidebarConfig struct { + Tags []*TagSelectorOption `json:"tags"` + LinksText string `json:"links_text"` +} + +type Sidebar interface { + Base + GetSidebarConfig() (sidebarConfig *SidebarConfig, err error) +} + +var ( + // CallRender is a function that calls all registered parsers + CallSidebar, + registerSidebar = MakePlugin[Sidebar](false) +)