From 2b7d0f054d59cfb47735fe3845f00515d1a0f036 Mon Sep 17 00:00:00 2001 From: Jacob Gable Date: Sun, 23 Jun 2013 15:55:03 -0500 Subject: [PATCH] Import and Export UI Added a basic UI and implementation for importing and exporting data. Hooked up the routes and tested importing and exporting a version 001 file. Slipped in the TemplateView in base.js but didn't end up using it. I think it will encapsulate common logic for template views pretty well. Should close #175. --- .gitignore | 3 +- app.js | 7 +- core/admin/assets/js/router.js | 7 +- core/admin/assets/js/views/base.js | 36 +++++++- core/admin/assets/js/views/debug.js | 25 ++++++ core/admin/controllers/index.js | 131 ++++++++++++++++++++++------ core/admin/views/debug.hbs | 41 +++++---- core/admin/views/default.hbs | 1 + core/shared/api.js | 10 ++- 9 files changed, 209 insertions(+), 52 deletions(-) create mode 100644 core/admin/assets/js/views/debug.js diff --git a/.gitignore b/.gitignore index f920e25d65..5acaee8e90 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ projectFilesBackup /core/admin/assets/sass/layouts/config.rb /core/admin/assets/sass/modules/config.rb /core/admin/assets/sass/modules/bourbon -/ghost/.idea/ \ No newline at end of file +/ghost/.idea/ +/core/shared/data/export/exported* \ No newline at end of file diff --git a/app.js b/app.js index 14ef5d6edf..7c902deb25 100755 --- a/app.js +++ b/app.js @@ -38,7 +38,7 @@ ghost.app().use(express.errorHandler({ dumpExceptions: true, showStack: true })); ghost.app().use(express.logger('dev')); ghost.app().use(I18n.load(ghost)); - ghost.app().use(express.bodyParser()); + ghost.app().use(express.bodyParser({})); ghost.app().use(express.cookieParser('try-ghost')); ghost.app().use(express.cookieSession({ cookie: { maxAge: 60000000 }})); ghost.app().use(ghost.initTheme(ghost.app())); @@ -136,8 +136,9 @@ ghost.app().get('/ghost/content', auth, admin.content); ghost.app().get('/ghost/settings*', auth, admin.settings); ghost.app().get('/ghost/debug', auth, admin.debug.index); - ghost.app().get('/ghost/debug/db/delete/', auth, admin.debug.dbdelete); - ghost.app().get('/ghost/debug/db/populate/', auth, admin.debug.dbpopulate); + ghost.app().get('/ghost/debug/db/export/', auth, admin.debug['export']); + ghost.app().post('/ghost/debug/db/import/', auth, admin.debug.import); + ghost.app().get('/ghost/debug/db/reset/', auth, admin.debug.reset); ghost.app().get(/^\/(ghost$|(ghost-admin|admin|wp-admin|dashboard|login)\/?)/, auth, function (req, res) { res.redirect('/ghost/'); }); diff --git a/core/admin/assets/js/router.js b/core/admin/assets/js/router.js index 4916fb25c2..ef0d33dad1 100644 --- a/core/admin/assets/js/router.js +++ b/core/admin/assets/js/router.js @@ -11,7 +11,8 @@ 'settings/' : 'settings', 'settings(/:pane)' : 'settings', 'editor/' : 'editor', - 'editor(/:id)' : 'editor' + 'editor(/:id)' : 'editor', + 'debug/' : 'debug', }, blog: function () { @@ -41,6 +42,10 @@ } }, + debug: function () { + Ghost.currentView = new Ghost.Views.Debug({ el: "#main" }); + }, + dashboard: function () { var widgets = new Ghost.Collections.Widgets(); diff --git a/core/admin/assets/js/views/base.js b/core/admin/assets/js/views/base.js index 3c89bf5e35..77490d343d 100644 --- a/core/admin/assets/js/views/base.js +++ b/core/admin/assets/js/views/base.js @@ -1,5 +1,5 @@ -/*global window, document, Ghost, Backbone, $, _ */ -(function () { +/*global window, document, Ghost, Backbone, $, JST, _ */ +(function (_, undefined) { "use strict"; Ghost.View = Backbone.View.extend({ @@ -42,4 +42,34 @@ }); -}()); + Ghost.TemplateView = Ghost.View.extend({ + templateName: "widget", + + template: function (data) { + return JST[this.templateName](data); + }, + + templateData: function () { + if (this.model) { + return this.model.toJSON(); + } + + if (this.collection) { + return this.collection.toJSON(); + } + + return {}; + }, + + render: function () { + this.$el.html(this.template(this.templateData())); + + if (_.isFunction(this.afterRender)) { + this.afterRender(); + } + + return this; + } + }); + +}(_)); diff --git a/core/admin/assets/js/views/debug.js b/core/admin/assets/js/views/debug.js new file mode 100644 index 0000000000..ce1a69b079 --- /dev/null +++ b/core/admin/assets/js/views/debug.js @@ -0,0 +1,25 @@ +/*global jQuery, window, document, Ghost, Backbone, $, _, alert */ +(function ($, _, Backbone, Ghost, undefined) { + "use strict"; + + Ghost.Views.Debug = Ghost.View.extend({ + events: { + "click .settings-menu a": "handleMenuClick" + }, + + handleMenuClick: function (ev) { + ev.preventDefault(); + + var $target = $(ev.currentTarget); + + // Hide the current content + this.$(".settings-content").hide(); + + // Show the clicked content + this.$("#debug-" + $target.attr("class")).show(); + + return false; + } + }); + +}(jQuery, _, Backbone, window.Ghost)); \ No newline at end of file diff --git a/core/admin/controllers/index.js b/core/admin/controllers/index.js index db38ec9ef5..6f776b8a91 100755 --- a/core/admin/controllers/index.js +++ b/core/admin/controllers/index.js @@ -3,8 +3,13 @@ "use strict"; var Ghost = require('../../ghost'), + dataExport = require('../../shared/data/export'), + dataImport = require('../../shared/data/import'), _ = require('underscore'), fs = require('fs'), + path = require('path'), + when = require('when'), + nodefn = require('when/node/function'), api = require('../../shared/api'), ghost = new Ghost(), @@ -158,34 +163,108 @@ adminNav: setSelected(adminNavbar, 'settings') }); }, - 'dbdelete': function (req, res) { - fs.writeFile(__dirname + '/../ghost/data/datastore.db', '', function (error) { - if (error) { - req.flash('error', error); - } else { - req.flash('success', 'Everything got deleted'); - } - res.redirect('/ghost/debug'); - }); + 'export': function (req, res) { + // Get current version from settings + api.settings.read({ key: "currentVersion" }) + .then(function (setting) { + // Export the current versions data + return dataExport(setting.value); + }, function () { + // If no setting, assume 001 + return dataExport("001"); + }) + .then(function (exportedData) { + // Save the exported data to the file system for download + var fileName = path.resolve(__dirname + '/../../shared/data/export/exported-' + (new Date().getTime()) + '.json'); + + return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () { + return when(fileName); + }); + }) + .then(function (exportedFilePath) { + // Send the exported data file + res.download(exportedFilePath, 'GhostData.json'); + }) + .otherwise(function (error) { + // Notify of an error if it occurs + req.flash("error", error.message || error); + res.redirect("/ghost/debug"); + }); }, - 'dbpopulate': function (req, res) { - dataProvider.populateData(function (error) { - if (error) { - req.flash('error', error); - } else { - req.flash('success', 'Data populated'); - } - res.redirect('/ghost/debug'); - }); + 'import': function (req, res) { + if (!req.files.importfile) { + // Notify of an error if it occurs + req.flash("error", "Must select a file to import"); + return res.redirect("/ghost/debug"); + } + + // Get the current version for importing + api.settings.read({ key: "currentVersion" }) + .then(function (setting) { + return when(setting.value); + }, function () { + return when("001"); + }) + .then(function (currentVersion) { + // Read the file contents + return nodefn.call(fs.readFile, req.files.importfile.path) + .then(function (fileContents) { + var importData; + + // Parse the json data + try { + importData = JSON.parse(fileContents); + } catch (e) { + return when.reject(new Error("Failed to parse the import file")); + } + + if (!importData.meta || !importData.meta.version) { + return when.reject(new Error("Import data does not specify version")); + } + + // Import for the current version + return dataImport(currentVersion, importData); + }); + }) + .then(function () { + req.flash("success", "Data imported"); + }) + .otherwise(function (error) { + // Notify of an error if it occurs + req.flash("error", error.message || error); + }) + .then(function () { + res.redirect("/ghost/debug"); + }); }, - 'newUser': function (req, res) { - dataProvider.addNewUser(req, function (error) { - if (error) { - req.flash('error', error); - } else { - req.flash('success', 'User Added'); - } - }); + 'reset': function (req, res) { + // Grab the current version so we can get the migration + api.settings.read({ key: "currentVersion" }) + .then(function (setting) { + var migration = require("../../shared/data/migration/" + setting.value); + + // Run the downward migration + return migration.down(); + }, function () { + // If no version in the DB, assume 001 + var migration = require("../../shared/data/migration/001"); + + // Run the downward migration + return migration.down(); + }) + .then(function () { + // Re-initalize the providers (should run the migration up again) + return dataProvider.init(); + }) + .then(function () { + req.flash("success", "Database reset"); + }) + .otherwise(function (error) { + req.flash("error", error.message || error); + }) + .then(function () { + res.redirect('/ghost/debug'); + }); } } }; diff --git a/core/admin/views/debug.hbs b/core/admin/views/debug.hbs index 9369090278..12607190ee 100644 --- a/core/admin/views/debug.hbs +++ b/core/admin/views/debug.hbs @@ -1,37 +1,48 @@ {{!< default}} -
+
-
+

General

-
{{> flashes}} -
+
- +
+
+
+
+ +
+
+
+
+
-
diff --git a/core/admin/views/default.hbs b/core/admin/views/default.hbs index db6960f1ad..23563c31a9 100644 --- a/core/admin/views/default.hbs +++ b/core/admin/views/default.hbs @@ -64,6 +64,7 @@ + {{{block "bodyScripts"}}}