Skip to content

Commit

Permalink
Merge pull request #3 from kabukky/development
Browse files Browse the repository at this point in the history
Added Plugins.
  • Loading branch information
kabukky committed Apr 25, 2015
2 parents 87fd267 + 10dd344 commit fed5cee
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 160 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ content/themes/*
content/https/*
!content/https/README.md

# Plugins
content/plugins/*
!content/plugins/README.md

# Go
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
Expand Down
5 changes: 5 additions & 0 deletions content/plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# content/plugins

Place your plugins here.

Read https://github.com/kabukky/journey/wiki/Creating-a-Journey-Plugin for a tutorial on how to create your own Journey plugin.
3 changes: 2 additions & 1 deletion filenames/filenames.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ var (
// Initialization of the working directory - needed to load relative assets
_ = initializeWorkingDirectory()

// For assets that are created or changed while running journey
// For assets that are created, changed, our user-provided while running journey
ConfigFilename = filepath.Join(flags.CustomPath, "config.json")
LogFilename = filepath.Join(flags.CustomPath, "log.txt")
DatabaseFilename = filepath.Join(flags.CustomPath, "content", "data", "journey.db")
ThemesFilepath = filepath.Join(flags.CustomPath, "content", "themes")
ImagesFilepath = filepath.Join(flags.CustomPath, "content", "images")
ContentFilepath = filepath.Join(flags.CustomPath, "content")
PluginsFilepath = filepath.Join(flags.CustomPath, "content", "plugins")

// For https
HttpsCertFilename = filepath.Join(flags.CustomPath, "content", "https", "cert.pem")
Expand Down
18 changes: 18 additions & 0 deletions helpers/files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package helpers

import (
"os"
"path/filepath"
)

func GetFilenameWithoutExtension(path string) string {
return filepath.Base(path)[0 : len(filepath.Base(path))-len(filepath.Ext(path))]
}

func IsDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}
25 changes: 19 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/kabukky/journey/configuration"
"github.com/kabukky/journey/database"
"github.com/kabukky/journey/filenames"
"github.com/kabukky/journey/flags"
"github.com/kabukky/journey/plugins"
"github.com/kabukky/journey/server"
"github.com/kabukky/journey/templates"
"log"
Expand Down Expand Up @@ -34,17 +36,20 @@ func checkHttpsCertificates() {

func main() {
// Setup
var err error

// GOMAXPROCS - Maybe not needed
runtime.GOMAXPROCS(runtime.NumCPU())

// Write log to file
logFile, err := os.OpenFile(filenames.LogFilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatal("Error: Couldn't open log file: " + err.Error())
// Write log to file if Journey is not in dev mode
if !flags.IsInDevMode {
logFile, err := os.OpenFile(filenames.LogFilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatal("Error: Couldn't open log file: " + err.Error())
}
defer logFile.Close()
log.SetOutput(logFile)
}
defer logFile.Close()
log.SetOutput(logFile)

// Configuration is read from config.json by loading the configuration package

Expand All @@ -62,6 +67,14 @@ func main() {
return
}

// Plugins
err = plugins.Load()
if err == nil {
// Close LuaPool at the end
defer plugins.LuaPool.Shutdown()
log.Println("Plugins loaded.")
}

// HTTP(S) Server
// Determine the kind of https support (as set in the config.json)
switch configuration.Config.HttpsUsage {
Expand Down
71 changes: 71 additions & 0 deletions plugins/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package plugins

import (
"github.com/kabukky/journey/structure"
"github.com/yuin/gopher-lua"
)

func convertPost(vm *lua.LState, structurePost *structure.Post) *lua.LTable {
post := vm.NewTable()
post.RawSet(lua.LString("id"), lua.LNumber(structurePost.Id))
post.RawSet(lua.LString("uuid"), lua.LString(structurePost.Uuid))
post.RawSet(lua.LString("title"), lua.LString(structurePost.Title))
post.RawSet(lua.LString("slug"), lua.LString(structurePost.Slug))
post.RawSet(lua.LString("markdown"), lua.LString(structurePost.Markdown))
post.RawSet(lua.LString("html"), lua.LString(structurePost.Html))
post.RawSet(lua.LString("isfeatured"), lua.LBool(structurePost.IsFeatured))
post.RawSet(lua.LString("ispage"), lua.LBool(structurePost.IsPage))
post.RawSet(lua.LString("ispublished"), lua.LBool(structurePost.IsPublished))
post.RawSet(lua.LString("date"), lua.LNumber(structurePost.Date.Unix()))
post.RawSet(lua.LString("image"), lua.LString(structurePost.Image))
return post
}

func convertUser(vm *lua.LState, structureUser *structure.User) *lua.LTable {
user := vm.NewTable()
user.RawSet(lua.LString("id"), lua.LNumber(structureUser.Id))
user.RawSet(lua.LString("name"), lua.LString(structureUser.Name))
user.RawSet(lua.LString("slug"), lua.LString(structureUser.Slug))
user.RawSet(lua.LString("email"), lua.LString(structureUser.Email))
user.RawSet(lua.LString("image"), lua.LString(structureUser.Image))
user.RawSet(lua.LString("cover"), lua.LString(structureUser.Cover))
user.RawSet(lua.LString("bio"), lua.LString(structureUser.Bio))
user.RawSet(lua.LString("website"), lua.LString(structureUser.Website))
user.RawSet(lua.LString("location"), lua.LString(structureUser.Location))
user.RawSet(lua.LString("role"), lua.LNumber(structureUser.Role))
return user
}

func convertTags(vm *lua.LState, structureTags []structure.Tag) *lua.LTable {
table := make([]*lua.LTable, 0)
for index, _ := range structureTags {
tag := vm.NewTable()
tag.RawSet(lua.LString("id"), lua.LNumber(structureTags[index].Id))
tag.RawSet(lua.LString("name"), lua.LString(structureTags[index].Name))
tag.RawSet(lua.LString("slug"), lua.LString(structureTags[index].Slug))
table = append(table, tag)
}
return makeTable(vm, table)
}

func convertBlog(vm *lua.LState, structureBlog *structure.Blog) *lua.LTable {
blog := vm.NewTable()
blog.RawSet(lua.LString("url"), lua.LString(structureBlog.Url))
blog.RawSet(lua.LString("title"), lua.LString(structureBlog.Title))
blog.RawSet(lua.LString("description"), lua.LString(structureBlog.Description))
blog.RawSet(lua.LString("logo"), lua.LString(structureBlog.Logo))
blog.RawSet(lua.LString("cover"), lua.LString(structureBlog.Cover))
blog.RawSet(lua.LString("assetpath"), lua.LString(structureBlog.AssetPath))
blog.RawSet(lua.LString("postcount"), lua.LNumber(structureBlog.PostCount))
blog.RawSet(lua.LString("postsperpage"), lua.LNumber(structureBlog.PostsPerPage))
blog.RawSet(lua.LString("activetheme"), lua.LString(structureBlog.ActiveTheme))
return blog
}

func makeTable(vm *lua.LState, tables []*lua.LTable) *lua.LTable {
table := vm.NewTable()
for index, _ := range tables {
table.Append(tables[index])
}
return table
}
26 changes: 26 additions & 0 deletions plugins/execution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package plugins

import (
"github.com/kabukky/journey/structure"
"github.com/yuin/gopher-lua"
"log"
)

func Execute(name string, values *structure.RequestData) ([]byte, error) {
// Retrieve the lua state
vm := values.PluginVMs[name]
// Execute plugin
err := vm.CallByParam(lua.P{Fn: vm.GetGlobal(name), NRet: 1, Protect: true})
if err != nil {
log.Println("Error while executing plugin for helper "+name+":", err)
// Since the vm threw an error, close all vms and don't put the map back into the pool
for _, luavm := range values.PluginVMs {
luavm.Close()
}
values.PluginVMs = nil
return []byte{}, err
}
// Get return value from vm
ret := vm.ToString(-1)
return []byte(ret), nil
}
134 changes: 134 additions & 0 deletions plugins/loading.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package plugins

import (
"errors"
"github.com/kabukky/journey/filenames"
"github.com/kabukky/journey/structure"
"github.com/yuin/gopher-lua"
"log"
"os"
"path/filepath"
)

func Load() error {
// Reset LuaPool for a fresh start
LuaPool = nil
// Make map
nameMap := make(map[string]string, 0)
err := filepath.Walk(filenames.PluginsFilepath, func(filePath string, info os.FileInfo, err error) error {
if !info.IsDir() && filepath.Ext(filePath) == ".lua" {
// Check if the lua file is a plugin entry point by executing it
helperNames, err := getHelperNames(filePath)
if err != nil {
return err
}
// Add all file names of helpers to the name map
for _, helperName := range helperNames {
absPath, err := filepath.Abs(filePath)
if err != nil {
log.Println("Error while determining absolute path to lua file:", err)
return err
}
nameMap[helperName] = absPath
}
}
return nil
})
if err != nil {
return err
}
if len(nameMap) == 0 {
return errors.New("No plugins were loaded.")
}
// If plugins were loaded, create LuaPool and assign name map to LuaPool
LuaPool = newLuaPool()
LuaPool.m.Lock()
defer LuaPool.m.Unlock()
LuaPool.files = nameMap
return nil
}

func getHelperNames(fileName string) ([]string, error) {
// Make a slice to hold all helper names
helperList := make([]string, 0)
// Create a new lua state
vm := lua.NewState()
defer vm.Close()
// Set up vm functions
values := &structure.RequestData{}
absDir, err := filepath.Abs(fileName)
if err != nil {
log.Println("Error while determining absolute path to lua file:", err)
return helperList, err
}
setUpVm(vm, values, absDir)
// Execute plugin
// TODO: Is there a better way to just load the file? We only need to execute the register function (see below)
err = vm.DoFile(absDir)
if err != nil {
// TODO: We are not returning upon error here. Keep it like this?
log.Println("Error while loading plugin:", err)
}
err = vm.CallByParam(lua.P{Fn: vm.GetGlobal("register"), NRet: 1, Protect: true})
if err != nil {
// Fail silently since this is probably just a lua file without a register function
return helperList, nil
}
// Get return value
table := vm.ToTable(-1)
// Check if return value is a table
if table != nil {
// Iterate the table for every helper name to be registered
table.ForEach(func(key lua.LValue, value lua.LValue) {
if str, ok := value.(lua.LString); ok {
if string(str) != "" {
helperList = append(helperList, string(str))
}
}
})
}
return helperList, nil
}

// Creates all methods that can be used from Lua.
func setUpVm(vm *lua.LState, values *structure.RequestData, absPathToLuaFile string) {
luaPath := filepath.Dir(absPathToLuaFile)
// Function to get the directory of the current file (to add to LUA_PATH in Lua)
vm.SetGlobal("getCurrentDir", vm.NewFunction(func(vm *lua.LState) int {
vm.Push(lua.LString(luaPath))
return 1 // Number of results
}))
// Function to print to the log
vm.SetGlobal("print", vm.NewFunction(func(vm *lua.LState) int {
log.Println(vm.Get(-1).String())
return 0 // Number of results
}))
// Function to get number of posts in values
vm.SetGlobal("getNumberOfPosts", vm.NewFunction(func(vm *lua.LState) int {
vm.Push(lua.LNumber(len(values.Posts)))
return 1 // Number of results
}))
// Function to get a post by its index
vm.SetGlobal("getPost", vm.NewFunction(func(vm *lua.LState) int {
postIndex := vm.ToInt(-1)
vm.Push(convertPost(vm, &values.Posts[postIndex-1]))
return 1 // Number of results
}))
// Function to get a user by post
vm.SetGlobal("getAuthorForPost", vm.NewFunction(func(vm *lua.LState) int {
postIndex := vm.ToInt(-1)
vm.Push(convertUser(vm, values.Posts[postIndex-1].Author))
return 1 // Number of results
}))
// Function to get tags by post
vm.SetGlobal("getTagsForPost", vm.NewFunction(func(vm *lua.LState) int {
postIndex := vm.ToInt(-1)
vm.Push(convertTags(vm, values.Posts[postIndex-1].Tags))
return 1 // Number of results
}))
// Function to get blog
vm.SetGlobal("getBlog", vm.NewFunction(func(vm *lua.LState) int {
vm.Push(convertBlog(vm, values.Blog))
return 1 // Number of results
}))
}
Loading

0 comments on commit fed5cee

Please sign in to comment.