Skip to content

Commit

Permalink
Merge pull request #27 from kabukky/development
Browse files Browse the repository at this point in the history
Added: Support for custom pages. Added: Ghost migartion feature.
  • Loading branch information
kabukky committed Apr 28, 2015
2 parents 83eb990 + 9b61dc0 commit d48e7f7
Show file tree
Hide file tree
Showing 13 changed files with 472 additions and 21 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ content/https/*
content/plugins/*
!content/plugins/README.md

# Pages
content/pages/*
!content/pages/README.md

# Go
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Create or update your posts from any place and any device. Simply point your bro
Write plugins in Lua to implement custom behavior when generating pages. Learn how to do it on the [Wiki](https://github.com/kabukky/journey/wiki/Creating-a-Journey-Plugin)!

#### Good stuff available right away
Use Ghost themes to design your blog. There's already a great community of designers working on Ghost compatible themes. Check out the [Ghost Marketplace](http://marketplace.ghost.org) to get an idea.
Use Ghost themes to design your blog. There's already a great community of designers working on Ghost compatible themes. Check out the [Ghost Marketplace](http://marketplace.ghost.org) to get an idea. You can also migrate your existing Ghost installation to Journey with [a few simple steps](https://github.com/kabukky/journey/wiki/Migrating-from-Ghost-to-Journey).

#### Good stuff to come
Hopefully. Planning the future of Journey, high priority goals are support of MySQL, PostgreSQL, and Google App Engine.
Expand All @@ -37,6 +37,9 @@ To get started with Journey, go to the the [Releases Page](https://github.com/ka

After that, head over to [Setting up Journey](https://github.com/kabukky/journey/wiki/Setting-up-Journey) to configure your Journey blog on your local machine. If you'd like to set up Journey on a linux server, head over to [Installing Journey on Ubuntu Server](https://github.com/kabukky/journey/wiki/Installing-Journey-on-Ubuntu-Server) for a step-by-step tutorial.

## Plugins
Did you create a Journey plugin? Write me [@kabukky](https://twitter.com/kabukky) or [email protected] and I'll add a link to it here.

## Questions?
Please read the [FAQ](https://github.com/kabukky/journey/wiki/FAQ) Wiki page or write to [email protected].

Expand Down
4 changes: 4 additions & 0 deletions content/pages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# content/pages
Place your custom files here (e.g. in sub folders) to serve them with Journey.

Let's say you have a page (html, css) in content/pages/mypage/. That page will be reachable under yourblog.url/pages/mypage.
8 changes: 7 additions & 1 deletion database/initialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package database

import (
"database/sql"
"github.com/kabukky/journey/database/migration"
"github.com/kabukky/journey/filenames"
"github.com/kabukky/journey/helpers"
_ "github.com/mattn/go-sqlite3"
"github.com/twinj/uuid"
"time"
Expand Down Expand Up @@ -121,7 +123,11 @@ var stmtInitialization = `CREATE TABLE IF NOT EXISTS
`

func Initialize() error {
// TODO: If there is no journey.db, convert ghost database if available (time format needs to change to be compatible with journey)
// If journey.db does not exist, look for a Ghost database to convert
if !helpers.FileExists(filenames.DatabaseFilename) {
// Convert Ghost database if available (time format needs to change to be compatible with journey)
migration.Ghost()
}
// Open or create database file
var err error
readDB, err = sql.Open("sqlite3", filenames.DatabaseFilename)
Expand Down
325 changes: 325 additions & 0 deletions database/migration/ghost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
package migration

import (
"database/sql"
"errors"
"github.com/kabukky/journey/filenames"
"github.com/kabukky/journey/helpers"
"log"
"os"
"path/filepath"
"time"
)

const stmtRetrieveGhostPosts = "SELECT id, (created_at/1000), (updated_at/1000), (published_at/1000) FROM posts"
const stmtRetrieveGhostTags = "SELECT id, (created_at/1000), (updated_at/1000) FROM tags"
const stmtRetrieveGhostUsers = "SELECT id, name, email, (last_login/1000), (created_at/1000), (updated_at/1000) FROM users"
const stmtRetrieveGhostRoles = "SELECT id, (created_at/1000), (updated_at/1000) FROM roles"
const stmtRetrieveGhostSettings = "SELECT id, (created_at/1000), (updated_at/1000) FROM settings"
const stmtRetrieveGhostPermissions = "SELECT id, (created_at/1000), (updated_at/1000) FROM permissions"
const stmtRetrieveGhostClients = "SELECT id, (created_at/1000), (updated_at/1000) FROM clients"

const stmtUpdateGhostPost = "UPDATE posts SET created_at = ?, updated_at = ?, published_at = ? WHERE id = ?"
const stmtUpdateGhostTags = "UPDATE tags SET created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostUsers = "UPDATE users SET name = ?, email= ?, last_login = ?, created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostRoles = "UPDATE roles SET created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostSettings = "UPDATE settings SET created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostPermissions = "UPDATE permissions SET created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostClients = "UPDATE clients SET created_at = ?, updated_at = ? WHERE id = ?"
const stmtUpdateGhostTheme = "UPDATE settings SET value = ?, updated_at = ?, updated_by = ? WHERE key = 'activeTheme'"

type dateHolder struct {
id int64
name []byte
email []byte
createdAt *time.Time
updatedAt *time.Time
publishedAt *time.Time
lastLogin *time.Time
}

// Function to convert a Ghost database to use with Journey
func Ghost() {
// Check every file in data directory
err := filepath.Walk(filenames.DatabaseFilepath, inspectDatabaseFile)
if err != nil {
log.Println("Error while looking for a Ghost database to convert:", err)
return
}
}

func inspectDatabaseFile(filePath string, info os.FileInfo, err error) error {
if !info.IsDir() && filepath.Ext(filePath) == ".db" {
err := convertGhostDatabase(filePath)
if err != nil {
return err
}
}
return nil
}

// This function converts all fields in the Ghost db that are not compatible with Journey (only date fields for now. Ghost uses a javascript-specific unix timestamp).
func convertGhostDatabase(fileName string) error {
// If journey.db exists already, don't convert this file
if helpers.FileExists(filenames.DatabaseFilename) {
return errors.New(filenames.DatabaseFilename + " already exists.")
}
log.Println("Trying to convert " + fileName + "...")
readDB, err := sql.Open("sqlite3", fileName)
if err != nil {
log.Println("Error:", err)
return err
}
err = readDB.Ping()
if err != nil {
log.Println("Error:", err)
return err
}
// Convert posts
err = convertPosts(readDB)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert users
err = convertUsers(readDB)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert tags
err = convertDates(readDB, stmtRetrieveGhostTags, stmtUpdateGhostTags)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert roles
err = convertDates(readDB, stmtRetrieveGhostRoles, stmtUpdateGhostRoles)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert settings
err = convertDates(readDB, stmtRetrieveGhostSettings, stmtUpdateGhostSettings)
if err != nil {
log.Println("Error:", err)
return err
}
// Set default theme
err = setDefaultTheme(readDB)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert permissions (not used by Journey at the moment)
err = convertDates(readDB, stmtRetrieveGhostPermissions, stmtUpdateGhostPermissions)
if err != nil {
log.Println("Error:", err)
return err
}
// Convert clients (not used by Journey at the moment)
err = convertDates(readDB, stmtRetrieveGhostClients, stmtUpdateGhostClients)
if err != nil {
log.Println("Error:", err)
return err
}
// All went well. Close database connection.
err = readDB.Close()
if err != nil {
log.Println("Error:", err)
return err
}
// Rename file to Journey format
err = os.Rename(fileName, filenames.DatabaseFilename)
if err != nil {
log.Println("Error:", err)
return err
}
log.Println("Success!")
return nil
}

func convertPosts(readDB *sql.DB) error {
allRows := make([]dateHolder, 0)
// Retrieve posts
rows, err := readDB.Query(stmtRetrieveGhostPosts)
if err != nil {
return err
}
// Read all rows to structs
for rows.Next() {
row := dateHolder{}
var createdAt sql.NullInt64
var updatedAt sql.NullInt64
var publishedAt sql.NullInt64
err := rows.Scan(&row.id, &createdAt, &updatedAt, &publishedAt)
if err != nil {
return err
}
// Convert dates
if createdAt.Valid {
date := time.Unix(createdAt.Int64, 0)
row.createdAt = &date
}
if updatedAt.Valid {
date := time.Unix(updatedAt.Int64, 0)
row.updatedAt = &date
}
if publishedAt.Valid {
date := time.Unix(publishedAt.Int64, 0)
row.publishedAt = &date
}
allRows = append(allRows, row)
}
rows.Close()
// Write all new dates
for _, row := range allRows {
writeDB, err := readDB.Begin()
if err != nil {
writeDB.Rollback()
return err
}
// Update the database with new date formats
_, err = writeDB.Exec(stmtUpdateGhostPost, row.createdAt, row.updatedAt, row.publishedAt, row.id)
if err != nil {
writeDB.Rollback()
return err
}
err = writeDB.Commit()
if err != nil {
writeDB.Rollback()
return err
}
}
return nil
}

func convertUsers(readDB *sql.DB) error {
allRows := make([]dateHolder, 0)
// Retrieve posts
rows, err := readDB.Query(stmtRetrieveGhostUsers)
if err != nil {
return err
}
// Read all rows to structs
for rows.Next() {
row := dateHolder{}
var name sql.NullString
var email sql.NullString
var lastLogin sql.NullInt64
var createdAt sql.NullInt64
var updatedAt sql.NullInt64
err := rows.Scan(&row.id, &name, &email, &lastLogin, &createdAt, &updatedAt)
if err != nil {
return err
}
// Convert strings to byte array since that is how Journey saves the user name and email (login won't work without this).
if name.Valid {
row.name = []byte(name.String)
}
if email.Valid {
row.email = []byte(email.String)
}
// Convert dates
if lastLogin.Valid {
date := time.Unix(lastLogin.Int64, 0)
row.lastLogin = &date
}
if createdAt.Valid {
date := time.Unix(createdAt.Int64, 0)
row.createdAt = &date
}
if updatedAt.Valid {
date := time.Unix(updatedAt.Int64, 0)
row.updatedAt = &date
}
allRows = append(allRows, row)
}
rows.Close()
// Write all new dates
for _, row := range allRows {
writeDB, err := readDB.Begin()
if err != nil {
writeDB.Rollback()
return err
}
// Update the database with new date formats
_, err = writeDB.Exec(stmtUpdateGhostUsers, row.name, row.email, row.lastLogin, row.createdAt, row.updatedAt, row.id)
if err != nil {
writeDB.Rollback()
return err
}
err = writeDB.Commit()
if err != nil {
writeDB.Rollback()
return err
}
}
return nil
}

func convertDates(readDB *sql.DB, stmtRetrieve string, stmtUpdate string) error {
allRows := make([]dateHolder, 0)
// Retrieve posts
rows, err := readDB.Query(stmtRetrieve)
if err != nil {
return err
}
// Read all rows to structs
for rows.Next() {
row := dateHolder{}
var createdAt sql.NullInt64
var updatedAt sql.NullInt64
err := rows.Scan(&row.id, &createdAt, &updatedAt)
if err != nil {
return err
}
// Convert dates
if createdAt.Valid {
date := time.Unix(createdAt.Int64, 0)
row.createdAt = &date
}
if updatedAt.Valid {
date := time.Unix(updatedAt.Int64, 0)
row.updatedAt = &date
}
allRows = append(allRows, row)
}
rows.Close()
// Write all new dates
for _, row := range allRows {
writeDB, err := readDB.Begin()
if err != nil {
writeDB.Rollback()
return err
}
// Update the database with new date formats
_, err = writeDB.Exec(stmtUpdate, row.createdAt, row.updatedAt, row.id)
if err != nil {
writeDB.Rollback()
return err
}
err = writeDB.Commit()
if err != nil {
writeDB.Rollback()
return err
}
}
return nil
}

func setDefaultTheme(readDB *sql.DB) error {
writeDB, err := readDB.Begin()
if err != nil {
writeDB.Rollback()
return err
}
// Update the database with the default theme (promenade)
date := time.Now()
_, err = writeDB.Exec(stmtUpdateGhostTheme, "promenade", date, 1)
if err != nil {
writeDB.Rollback()
return err
}
return writeDB.Commit()
}
Loading

1 comment on commit d48e7f7

@kabukky
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closing #24

Please sign in to comment.