-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from kabukky/development
Added: Support for custom pages. Added: Ghost migartion feature.
- Loading branch information
Showing
13 changed files
with
472 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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]. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
Oops, something went wrong.
d48e7f7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closing #24