Skip to content

Commit

Permalink
Add support for shortcode-like feature in posts
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusgomes28 committed Oct 20, 2024
1 parent 8e125a9 commit 3a5fe07
Show file tree
Hide file tree
Showing 19 changed files with 249 additions and 63 deletions.
13 changes: 11 additions & 2 deletions admin-app/app.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package admin_app

import (
"log"

"github.com/gin-gonic/gin"
"github.com/matheusgomes28/urchin/common"
"github.com/matheusgomes28/urchin/database"
"github.com/matheusgomes28/urchin/plugins"
lua "github.com/yuin/gopher-lua"
)

func SetupRoutes(app_settings common.AppSettings, database database.Database) *gin.Engine {
func SetupRoutes(app_settings common.AppSettings, database database.Database, shortcode_handlers map[string]*lua.LState, hooks map[string]plugins.Hook) *gin.Engine {

r := gin.Default()
r.MaxMultipartMemory = 1

post_hook, ok := hooks["add_post"]
if !ok {
log.Fatalf("could not find add_post hook")
}

r.GET("/posts/:id", getPostHandler(database))
r.POST("/posts", postPostHandler(database))
r.POST("/posts", postPostHandler(database, shortcode_handlers, post_hook.(plugins.PostHook)))
r.PUT("/posts", putPostHandler(database))
r.DELETE("/posts/:id", deletePostHandler(database))

Expand Down
108 changes: 103 additions & 5 deletions admin-app/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"

"github.com/gin-gonic/gin"
"github.com/matheusgomes28/urchin/common"
"github.com/matheusgomes28/urchin/database"
"github.com/matheusgomes28/urchin/plugins"
"github.com/rs/zerolog/log"
lua "github.com/yuin/gopher-lua"
)

func getPostHandler(database database.Database) func(*gin.Context) {
Expand Down Expand Up @@ -37,7 +40,7 @@ func getPostHandler(database database.Database) func(*gin.Context) {
}
}

func postPostHandler(database database.Database) func(*gin.Context) {
func postPostHandler(database database.Database, shortcode_handlers map[string]*lua.LState, post_hook plugins.PostHook) func(*gin.Context) {
return func(c *gin.Context) {
var add_post_request AddPostRequest
if c.Request.Body == nil {
Expand All @@ -57,13 +60,25 @@ func postPostHandler(database database.Database) func(*gin.Context) {
if err != nil {
log.Error().Msgf("failed to add post required data is missing: %v", err)
c.JSON(http.StatusBadRequest, common.ErrorRes("missing required data", err))
return
}

// TODO : Move this into the plugin
altered_post := post_hook.UpdatePost(add_post_request.Title, add_post_request.Content, add_post_request.Excerpt, shortcode_handlers)

// transformed_content, err := transformContent(add_post_request.Content, shortcode_handlers)
// if err != nil {
// log.Warn().Msgf("could not transform post: %v", err)
// c.JSON(http.StatusBadRequest, gin.H{
// "error": "invalid request body",
// "msg": err.Error(),
// })
// return
// }

id, err := database.AddPost(
add_post_request.Title,
add_post_request.Excerpt,
add_post_request.Content,
altered_post.Title,
altered_post.Excerpt,
altered_post.Content,
)
if err != nil {
log.Error().Msgf("failed to add post: %v", err)
Expand Down Expand Up @@ -151,3 +166,86 @@ func checkRequiredData(addPostRequest AddPostRequest) error {

return nil
}

// partitionString will partition the strings by
// removing the given ranges
func partitionString(text string, indexes [][]int) []string {

if len(text) == 0 {
return []string{}
}

partitions := make([]string, 0)
start := 0
for _, window := range indexes {
partitions = append(partitions, text[start:window[0]])
start = window[1]
}

partitions = append(partitions, text[start:len(text)-1])
return partitions
}

func shortcodeToMarkdown(shortcode string, shortcode_handlers map[string]*lua.LState) (string, error) {
key_value := strings.Split(shortcode, ":")

key := key_value[0]
values := key_value[1:]

if handler, found := shortcode_handlers[key]; found {

// Need to quote all values for a valid lua syntax
quoted_values := make([]string, 0)
for _, value := range values {
quoted_values = append(quoted_values, fmt.Sprintf("%q", value))
}

err := handler.DoString(fmt.Sprintf(`result = HandleShortcode({%s})`, strings.Join(quoted_values, ",")))
if err != nil {
return "", fmt.Errorf("error running %s shortcode: %v", key, err)
}

value := handler.GetGlobal("result")
if ret_type := value.Type().String(); ret_type != "string" {
return "", fmt.Errorf("error running %s shortcode: invalid return type %s", key, ret_type)
} else if ret_type == "" {
return "", fmt.Errorf("error running %s shortcode: returned empty string", key)
}

return value.String(), nil
}

return "", fmt.Errorf("unsupported shortcode: %s", key)
}

func transformContent(content string, shortcode_handlers map[string]*lua.LState) (string, error) {
// Find all the occurences of {{ and }}
regex, _ := regexp.Compile(`{{[\w.-]+(:[\w.-]+)+}}`)

shortcodes := regex.FindAllStringIndex(content, -1)
if len(shortcodes) == 0 {
return content, nil
}

partitions := partitionString(content, shortcodes)

builder := strings.Builder{}
i := 0

for i, shortcode := range shortcodes {
builder.WriteString(partitions[i])

markdown, err := shortcodeToMarkdown(content[shortcode[0]+2:shortcode[1]-2], shortcode_handlers)
if err != nil {
log.Error().Msgf("%v", err)
markdown = ""
}
builder.WriteString(markdown)
}

// Guaranteed to have +1 than the number of
// shortcodes by algorithm
builder.WriteString(partitions[i+1])

return builder.String(), nil
}
1 change: 0 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func SetupRoutes(app_settings common.AppSettings, database database.Database) *g

// Where all the static files (css, js, etc) are served from
r.Static("/static", "./static")

return r
}

Expand Down
3 changes: 2 additions & 1 deletion app/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app

import (
"bytes"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -54,7 +55,7 @@ func postHandler(c *gin.Context, app_settings common.AppSettings, database datab
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"})
}

return nil, err
return nil, fmt.Errorf("invalid post id")
}

// Get the post with the ID
Expand Down
44 changes: 42 additions & 2 deletions cmd/urchin-admin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,28 @@ import (
admin_app "github.com/matheusgomes28/urchin/admin-app"
"github.com/matheusgomes28/urchin/common"
"github.com/matheusgomes28/urchin/database"
"github.com/matheusgomes28/urchin/plugins"
"github.com/rs/zerolog/log"
lua "github.com/yuin/gopher-lua"
)

func loadShortcodeHandlers(shortcodes []common.Shortcode) (map[string]*lua.LState, error) {
shortcode_handlers := make(map[string]*lua.LState, 0)
for _, shortcode := range shortcodes {
// Read the LUA state
state := lua.NewState()
err := state.DoFile(shortcode.Plugin)
// TODO : check that the function HandleShortcode(args)
// exists and returns the correct type
if err != nil {
return map[string]*lua.LState{}, fmt.Errorf("could not load shortcode %s: %v", shortcode.Name, err)
}
shortcode_handlers[shortcode.Name] = state
}

return shortcode_handlers, nil
}

func main() {
// sets zerolog as the main logger
// in this APP
Expand Down Expand Up @@ -46,8 +65,29 @@ func main() {
os.Exit(-1)
}

r := admin_app.SetupRoutes(app_settings, database)
err = r.Run(fmt.Sprintf(":%d", app_settings.AdminPort))
shortcode_handlers, err := loadShortcodeHandlers(app_settings.Shortcodes)
if err != nil {
log.Error().Msgf("%v", err)
os.Exit(-1)
}

// TODO : we probably want to refactor loadShortcodeHandler
// TODO : into loadPluginHandlers instead

post_hook := plugins.PostHook{}
image_plugin := plugins.Plugin{
ScriptName: "img",
Id: "img-plugin",
}
post_hook.Register(image_plugin)

// img, _ := shortcode_handlers["img"]
hooks_map := map[string]plugins.Hook{
"add_post": post_hook,
}

r := admin_app.SetupRoutes(app_settings, database, shortcode_handlers, hooks_map)
err = r.Run(fmt.Sprintf(":%d", app_settings.WebserverPort))
if err != nil {
log.Error().Msgf("could not run app: %v", err)
os.Exit(-1)
Expand Down
32 changes: 20 additions & 12 deletions common/app_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,27 @@ type Navbar struct {
Links []Link `toml:"links"`
}

type Shortcode struct {
// name for the shortcode {{name:...:...}}
Name string `toml:"name"`
// The lua plugin path
Plugin string `toml:"plugin"`
}

type AppSettings struct {
DatabaseAddress string `toml:"database_address"`
DatabasePort int `toml:"database_port"`
DatabaseUser string `toml:"database_user"`
DatabasePassword string `toml:"database_password"`
DatabaseName string `toml:"database_name"`
WebserverPort int `toml:"webserver_port"`
AdminPort int `toml:"admin_port"`
ImageDirectory string `toml:"image_dir"`
CacheEnabled bool `toml:"cache_enabled"`
RecaptchaSiteKey string `toml:"recaptcha_sitekey,omitempty"`
RecaptchaSecret string `toml:"recaptcha_secret,omitempty"`
AppNavbar Navbar `toml:"navbar"`
DatabaseAddress string `toml:"database_address"`
DatabasePort int `toml:"database_port"`
DatabaseUser string `toml:"database_user"`
DatabasePassword string `toml:"database_password"`
DatabaseName string `toml:"database_name"`
WebserverPort int `toml:"webserver_port"`
AdminPort int `toml:"admin_port"`
ImageDirectory string `toml:"image_dir"`
CacheEnabled bool `toml:"cache_enabled"`
RecaptchaSiteKey string `toml:"recaptcha_sitekey,omitempty"`
RecaptchaSecret string `toml:"recaptcha_secret,omitempty"`
AppNavbar Navbar `toml:"navbar"`
Shortcodes []Shortcode `toml:"shortcodes"`
}

func ReadConfigToml(filepath string) (AppSettings, error) {
Expand Down
2 changes: 1 addition & 1 deletion database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (db SqlDatabase) GetPage(link string) (common.Page, error) {
return common.Page{}, err
}
defer func() {
err = errors.Join(rows.Close())
err = errors.Join(err, rows.Close())
}()

page := common.Page{}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/pressly/goose/v3 v3.19.2
github.com/rs/zerolog v1.31.0
github.com/stretchr/testify v1.8.4
github.com/yuin/gopher-lua v1.1.1
github.com/zutto/shardedmap v0.0.0-20180201164343-415202d0910e
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf h1:ck
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-sdk/v3 v3.55.1 h1:Ebo6J5AMXgJ3A438ECYotA0aK7ETqjQx9WoZvVxzKBE=
github.com/ydb-platform/ydb-go-sdk/v3 v3.55.1/go.mod h1:udNPW8eupyH/EZocecFmaSNJacKKYjzQa7cVgX5U2nc=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zutto/shardedmap v0.0.0-20180201164343-415202d0910e h1:zqDzvgPMqbfHrSXOo2igl2H9q2RoY5l7z1YpaPGMuog=
github.com/zutto/shardedmap v0.0.0-20180201164343-415202d0910e/go.mod h1:NKD0ezOo5cN3cWU7CGigl5LQhm3gNi+jaOplrVspLjc=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
Expand Down
11 changes: 11 additions & 0 deletions plugins/image_shortcode.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function HandleShortcode(arguments)
if #arguments == 1 then
local image_src = string.format("/data/images/%s", arguments[1])
return string.format("![image](%s)", image_src)
elseif #arguments == 2 then
local image_src = string.format("/data/images/%s", arguments[1])
return string.format("![%s](%s)", arguments[2], image_src)
else
return ""
end
end
7 changes: 7 additions & 0 deletions plugins/table_shortcode.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function HandleShortcode(arguments)
if #arguments ~= 2 then
return ""
end

return "\n| Tables | Are | Cool |\n|----------|:-------------:|------:|\n| col 1 is | left-aligned | $1600 |\n| col 2 is | centered | $12 |\n| col 3 is | right-aligned | $1 |\n"
end
Loading

0 comments on commit 3a5fe07

Please sign in to comment.