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 Mar 9, 2024
1 parent 31d7cf6 commit 4937610
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 19 deletions.
5 changes: 3 additions & 2 deletions admin-app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/matheusgomes28/urchin/common"
"github.com/matheusgomes28/urchin/database"
lua "github.com/yuin/gopher-lua"
)

type PostBinding struct {
Expand All @@ -27,13 +28,13 @@ type DeletePostRequest struct {
Id int `json:"id"`
}

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) *gin.Engine {

Check warning on line 31 in admin-app/app.go

View check run for this annotation

Codecov / codecov/patch

admin-app/app.go#L31

Added line #L31 was not covered by tests

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

r.GET("/posts/:id", getPostHandler(database))
r.POST("/posts", postPostHandler(database))
r.POST("/posts", postPostHandler(database, shortcode_handlers))

Check warning on line 37 in admin-app/app.go

View check run for this annotation

Codecov / codecov/patch

admin-app/app.go#L37

Added line #L37 was not covered by tests
r.PUT("/posts", putPostHandler(database))
r.DELETE("/posts", deletePostHandler(database))

Expand Down
92 changes: 90 additions & 2 deletions admin-app/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package admin_app

import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"

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

func getPostHandler(database database.Database) func(*gin.Context) {
Expand Down Expand Up @@ -50,7 +54,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) func(*gin.Context) {

Check warning on line 57 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L57

Added line #L57 was not covered by tests
return func(c *gin.Context) {
var add_post_request AddPostRequest
decoder := json.NewDecoder(c.Request.Body)
Expand All @@ -65,10 +69,20 @@ func postPostHandler(database database.Database) func(*gin.Context) {
return
}

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
}

Check warning on line 80 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L72-L80

Added lines #L72 - L80 were not covered by tests

id, err := database.AddPost(
add_post_request.Title,
add_post_request.Excerpt,
add_post_request.Content,
transformed_content,

Check warning on line 85 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L85

Added line #L85 was not covered by tests
)
if err != nil {
log.Error().Msgf("failed to add post: %v", err)
Expand Down Expand Up @@ -153,3 +167,77 @@ func deletePostHandler(database database.Database) func(*gin.Context) {
})
}
}

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

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

Check warning on line 180 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L173-L180

Added lines #L173 - L180 were not covered by tests

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

Check warning on line 183 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L182-L183

Added lines #L182 - L183 were not covered by tests
}

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))
}

Check warning on line 198 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L186-L198

Added lines #L186 - L198 were not covered by tests

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)
}

Check warning on line 203 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L200-L203

Added lines #L200 - L203 were not covered by tests

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)
}

Check warning on line 210 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L205-L210

Added lines #L205 - L210 were not covered by tests

return value.String(), nil

Check warning on line 212 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L212

Added line #L212 was not covered by tests
}

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

Check warning on line 215 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L215

Added line #L215 was not covered by tests
}

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)
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)

Check warning on line 235 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L218-L235

Added lines #L218 - L235 were not covered by tests
}

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

return builder.String(), nil

Check warning on line 242 in admin-app/post.go

View check run for this annotation

Codecov / codecov/patch

admin-app/post.go#L240-L242

Added lines #L240 - L242 were not covered by tests
}
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func SetupRoutes(app_settings common.AppSettings, database database.Database) *g
r.POST("/contact-send", makeContactFormHandler())

r.Static("/static", "./static")
r.Static("/media", "./media")

Check warning on line 33 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L33

Added line #L33 was not covered by tests
return r
}

Expand Down
26 changes: 25 additions & 1 deletion cmd/urchin-admin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,26 @@ import (
"github.com/matheusgomes28/urchin/common"
"github.com/matheusgomes28/urchin/database"
"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

Check warning on line 27 in cmd/urchin-admin/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/urchin-admin/main.go#L16-L27

Added lines #L16 - L27 were not covered by tests
}

return shortcode_handlers, nil

Check warning on line 30 in cmd/urchin-admin/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/urchin-admin/main.go#L30

Added line #L30 was not covered by tests
}

func main() {
// sets zerolog as the main logger
// in this APP
Expand Down Expand Up @@ -51,7 +69,13 @@ func main() {
os.Exit(-1)
}

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

Check warning on line 76 in cmd/urchin-admin/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/urchin-admin/main.go#L72-L76

Added lines #L72 - L76 were not covered by tests

r := admin_app.SetupRoutes(app_settings, database, shortcode_handlers)

Check warning on line 78 in cmd/urchin-admin/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/urchin-admin/main.go#L78

Added line #L78 was not covered by tests
err = r.Run(fmt.Sprintf(":%d", app_settings.WebserverPort))
if err != nil {
log.Error().Msgf("could not run app: %v", err)
Expand Down
22 changes: 15 additions & 7 deletions common/app_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ import (
"github.com/BurntSushi/toml"
)

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"`
ImageDirectory string `toml:"image_dir"`
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"`
ImageDirectory string `toml:"image_dir"`
Shortcodes []Shortcode `toml:"shortcodes"`
}

func LoadSettings() (AppSettings, error) {
Expand Down
13 changes: 8 additions & 5 deletions common/app_settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestCorrectToml(t *testing.T) {
DatabaseName: "test_database_name",
WebserverPort: 99999,
DatabasePort: 666,
Shortcodes: []Shortcode{},
}
bytes, err := toml.Marshal(expected)
assert.Nil(t, err)
Expand All @@ -50,17 +51,19 @@ func TestCorrectToml(t *testing.T) {
func TestMissingDatabaseAddress(t *testing.T) {

missing_database_address := struct {
DatabaseUser string `toml:"database_user"`
DatabasePassword string `toml:"database_password"`
DatabaseName string `toml:"database_name"`
WebserverPort string `toml:"webserver_port"`
DatabasePort string `toml:"database_port"`
DatabaseUser string `toml:"database_user"`
DatabasePassword string `toml:"database_password"`
DatabaseName string `toml:"database_name"`
WebserverPort string `toml:"webserver_port"`
DatabasePort string `toml:"database_port"`
Shortcodes []Shortcode `toml:"shortcodes"`
}{
DatabaseUser: "test_database_user",
DatabasePassword: "test_database_password",
DatabaseName: "test_database_name",
WebserverPort: "99999",
DatabasePort: "666",
Shortcodes: []Shortcode{},
}

bytes, err := toml.Marshal(missing_database_address)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
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=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
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("/media/%s", arguments[1])
return string.format("![image](%s)", image_src)
elseif #arguments == 2 then
local image_src = string.format("/media/%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
14 changes: 12 additions & 2 deletions urchin_config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Address to the MariaDB database
database_address = "mariadb"
database_address = "localhost"

# User to access datbaase
database_user = "root"
Expand All @@ -12,7 +12,17 @@ database_port = 3306

# name of database where urchin's
# migrations was installed
database_name = "urchin"
database_name = "gocms"

# port to run the webserver on
webserver_port = 8080

[[shortcodes]]
name = "img"
# must have function "HandleShortcode(arguments []string) -> string"
plugin = "plugins/image_shortcode.lua"

[[shortcodes]]
name = "table"
# must have function "HandleShortcode(arguments []string) -> string"
plugin = "plugins/table_shortcode.lua"

0 comments on commit 4937610

Please sign in to comment.