From 2d8b5ea8c1675eb7255b8b682c2c741867cc158e Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Thu, 1 Aug 2013 23:33:06 +0100 Subject: [PATCH] Backbone template cleanup - Ghost.View now extends Ghost.TemplateView giving all views access to subviews and templates - Views which implemented templates no longer need to - Some views needed to re-override render which is a bit annoying - Settings screen now has sub-templates for each pane and for the sidebar - Additional Casper tests for settings screen --- core/client/tpl/settings/appearance.hbs | 11 + core/client/tpl/settings/content.hbs | 69 +++ core/client/tpl/settings/general.hbs | 60 +++ core/client/tpl/settings/plugins.hbs | 241 ++++++++++ core/client/tpl/settings/sidebar.hbs | 9 + core/client/tpl/settings/users.hbs | 57 +++ core/client/views/base.js | 66 +-- core/client/views/blog.js | 18 +- core/client/views/dashboard.js | 12 +- core/client/views/editor.js | 12 +- core/client/views/login.js | 16 +- core/client/views/settings.js | 33 +- core/server/views/settings.hbs | 440 +----------------- core/test/functional/admin/04_content_test.js | 8 +- .../test/functional/admin/05_settings_test.js | 24 +- 15 files changed, 543 insertions(+), 533 deletions(-) create mode 100644 core/client/tpl/settings/appearance.hbs create mode 100644 core/client/tpl/settings/content.hbs create mode 100644 core/client/tpl/settings/general.hbs create mode 100644 core/client/tpl/settings/plugins.hbs create mode 100644 core/client/tpl/settings/sidebar.hbs create mode 100644 core/client/tpl/settings/users.hbs diff --git a/core/client/tpl/settings/appearance.hbs b/core/client/tpl/settings/appearance.hbs new file mode 100644 index 0000000000..fadb08597d --- /dev/null +++ b/core/client/tpl/settings/appearance.hbs @@ -0,0 +1,11 @@ +
+
+

Appearance

+
+
+
Raw json be here
+

Active theme: {{json settings.activeTheme}}

+

Available themes: {{json availableThemes}}

+

Available plugins: {{json availablePlugins}}

+
+
\ No newline at end of file diff --git a/core/client/tpl/settings/content.hbs b/core/client/tpl/settings/content.hbs new file mode 100644 index 0000000000..ee6b163809 --- /dev/null +++ b/core/client/tpl/settings/content.hbs @@ -0,0 +1,69 @@ +
+

Content

+
+ +
+
+
+
+
+
+ + +

Sexy sans-serif font that will make your toes tickle.

+ +
+ +
+ + +

Post Author / Date / Views

+ + + +
+
+ +
+ +
+
+ + +

The pattern used to display your title tags

+ +
+ +
+ + +

The pattern used to display your meta descriptions

+
+ +
+ + + +
+ +
+ + +
+ +
+ +
+
\ No newline at end of file diff --git a/core/client/tpl/settings/general.hbs b/core/client/tpl/settings/general.hbs new file mode 100644 index 0000000000..0a89daf1bb --- /dev/null +++ b/core/client/tpl/settings/general.hbs @@ -0,0 +1,60 @@ +
+

General

+
+ +
+
+
+
+
+
+ + +

How your blog name appears on the site

+
+ +
+ + logo +

Display a logo on your site in place of blog title

+
+ +
+ + logo +

The icon for your blog, used in your browser tab and elsewhere

+
+ +
+ + +

Address to use for admin notifications

+ +
+ +
+ + +
+ +
+ +
+ +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/core/client/tpl/settings/plugins.hbs b/core/client/tpl/settings/plugins.hbs new file mode 100644 index 0000000000..fc4b3c7a23 --- /dev/null +++ b/core/client/tpl/settings/plugins.hbs @@ -0,0 +1,241 @@ + +
+

Plugins

+
+ +
+
+
+
+
+

Updates

+
+ + + + + + + + + + + + + + + + + +
+
+ +
+
+ Ghost SEO + + + +
+
+
+ Yoast + yoast.com +
+
+
+ v 0.1.13 + 3 weeks ago +
+
+
+ + + + + + + + 48,200 users +
+
+
+ +
+
+
+ +
+
+ Ghost SEO + The #1 content marketing plugin for Ghost. +
+
+
+ Yoast + yoast.com +
+
+
+ v 0.1.13 + 3 weeks ago +
+
+
+ + + + + + + + 48,200 users +
+
+
+ +
+
+
+ +
+
+
+
+

Active Plugins

+
+ + + + + + + + + + + + + + + + + +
+
+ +
+
+ Ghost SEO + The #1 content marketing plugin for Ghost. +
+
+
+ Yoast + yoast.com +
+
+
+ v 0.1.13 + 3 weeks ago +
+
+
+ + + + + + + + 48,200 users +
+
+
+ +
+
+
+ +
+
+ Ghost SEO + The #1 content marketing plugin for Ghost. +
+
+
+ Yoast + yoast.com +
+
+
+ v 0.1.13 + 3 weeks ago +
+
+
+ + + + + + + + 48,200 users +
+
+
+ +
+
+
+
+
+

Inactive Plugins

+
+ + + + + + + + + + +
+
+ +
+
+ Ghost SEO + The #1 content marketing plugin for Ghost. +
+
+
+ Yoast + yoast.com +
+
+
+ v 0.1.13 + 3 weeks ago +
+
+
+ + + + + + + + 48,200 users +
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/core/client/tpl/settings/sidebar.hbs b/core/client/tpl/settings/sidebar.hbs new file mode 100644 index 0000000000..35886d3bbb --- /dev/null +++ b/core/client/tpl/settings/sidebar.hbs @@ -0,0 +1,9 @@ +
+

Settings

+
+ \ No newline at end of file diff --git a/core/client/tpl/settings/users.hbs b/core/client/tpl/settings/users.hbs new file mode 100644 index 0000000000..ae3ef9629a --- /dev/null +++ b/core/client/tpl/settings/users.hbs @@ -0,0 +1,57 @@ +
+
+

Users

+
+ +
+
+
+
+

Invited Users

+
    +
  • +
    +
    +

    Some Name

    + Invitation Sent: 7 hours ago +
    +
  • +
+
+
+
+

Active Users

+ + +
+ +
    +
  • +
    + user +
    +
    +

    Some Name

    + Last Seen: 7 hours ago +
    + Admin +
  • +
  • +
    + user +
    +
    +

    Some Name

    + Last Seen: 2 days ago +
    + Editor +
  • +
+
+
+
\ No newline at end of file diff --git a/core/client/views/base.js b/core/client/views/base.js index 2613e69dd8..19dbaa9610 100644 --- a/core/client/views/base.js +++ b/core/client/views/base.js @@ -2,7 +2,41 @@ (function () { "use strict"; - Ghost.View = Backbone.View.extend({ + Ghost.TemplateView = Backbone.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 () { + if (_.isFunction(this.beforeRender)) { + this.beforeRender(); + } + + this.$el.html(this.template(this.templateData())); + + if (_.isFunction(this.afterRender)) { + this.afterRender(); + } + + return this; + } + }); + + Ghost.View = Ghost.TemplateView.extend({ // Adds a subview to the current view, which will // ensure its removal when this view is removed, @@ -42,36 +76,6 @@ }); - 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; - } - }); - /** * This is the view to generate the markup for the individual * notification. Will be included into #flashbar. diff --git a/core/client/views/blog.js b/core/client/views/blog.js index 93b93e445a..9c9d4d8789 100644 --- a/core/client/views/blog.js +++ b/core/client/views/blog.js @@ -111,15 +111,9 @@ templateName: "list-item", - template: function (data) { - return JST[this.templateName](data); - }, - - render: function () { - this.$el.html(this.template(_.extend({active: this.active}, this.model.toJSON()))); - return this; + templateData: function () { + return _.extend({active: this.active}, this.model.toJSON()); } - }); // Content preview @@ -182,14 +176,10 @@ templateName: "preview", - template: function (data) { - return JST[this.templateName](data); - }, - render: function () { if (this.activeId) { this.model = this.collection.get(this.activeId); - this.$el.html(this.template(this.model.toJSON())); + this.$el.html(this.template(this.templateData())); } this.$('.wrapper').on('click', 'a', function (e) { $(e.currentTarget).attr('target', '_blank'); @@ -200,4 +190,4 @@ }); -}()); +}()); \ No newline at end of file diff --git a/core/client/views/dashboard.js b/core/client/views/dashboard.js index ce3bc8e36d..f284ce87e3 100644 --- a/core/client/views/dashboard.js +++ b/core/client/views/dashboard.js @@ -137,12 +137,7 @@ templateName: "widget", - template: function (data) { - return JST[this.templateName](data); - }, - - render: function () { - this.$el.html(this.template(this.model.toJSON())); + afterRender: function () { if (!this.model.attributes.settings.enabled) { this.$(".widget-content").html(this.addSubview(new WidgetContent({model: this.model})).render().el); } else { @@ -160,11 +155,6 @@ template: function (data) { return JST['widgets/' + this.model.attributes.content.template](data); - }, - - render: function () { - this.$el.html(this.template(this.model.toJSON())); - return this; } }); diff --git a/core/client/views/editor.js b/core/client/views/editor.js index efa8918305..3aa6f64594 100644 --- a/core/client/views/editor.js +++ b/core/client/views/editor.js @@ -41,14 +41,16 @@ initialize: function () { this.addSubview(new TagWidget({el: this.$('#entry-categories'), model: this.model})).render(); this.addSubview(new ActionsWidget({el: this.$('#entry-actions'), model: this.model})).render(); - } + }, + + render: function () { return this; } }); // The Tag UI area associated with a post // ---------------------------------------- TagWidget = Ghost.View.extend({ - + render: function () { return this; } }); // The Publish, Queue, Publish Now buttons @@ -372,9 +374,9 @@ } } })); - } + }, + + render: function () { return this; } }); - - }()); diff --git a/core/client/views/login.js b/core/client/views/login.js index dfa369549a..fc11162346 100644 --- a/core/client/views/login.js +++ b/core/client/views/login.js @@ -15,13 +15,8 @@ $(window).trigger('resize'); }, - template: function (data) { - return JST[this.templateName](data); - }, - - render: function () { + afterRender: function () { var self = this; - this.$el.html(this.template()); $(window).on('resize', _.debounce(function (e) { $(".js-login-container").center(); @@ -78,15 +73,6 @@ this.render(); }, - template: function (data) { - return JST[this.templateName](data); - }, - - render: function () { - this.$el.html(this.template()); - return this; - }, - submitHandler: function (event) { event.preventDefault(); var email = this.$el.find('.email').val(), diff --git a/core/client/views/settings.js b/core/client/views/settings.js index 1e5d5ec1a1..b0ad123c05 100644 --- a/core/client/views/settings.js +++ b/core/client/views/settings.js @@ -25,6 +25,7 @@ // --------------- Settings.Sidebar = Ghost.View.extend({ initialize: function (options) { + this.render(); this.menu = this.$('.settings-menu'); this.showContent(options.pane || 'general'); }, @@ -42,19 +43,21 @@ showContent: function (id) { Backbone.history.navigate('/settings/' + id); - if (this.pane && '#' + id === this.pane.el) { + if (this.pane && id === this.pane.el.id) { return; } _.result(this.pane, 'destroy'); this.setActive(id); - this.pane = new Settings[id]({ model: this.model }); + this.pane = new Settings[id]({ el: '.settings-content', model: this.model }); this.pane.render(); }, setActive: function (id) { this.menu.find('li').removeClass('active'); this.menu.find('a[href=#' + id + ']').parent().addClass('active'); - } + }, + + templateName: 'settings/sidebar' }); // Content panes @@ -65,17 +68,18 @@ this.undelegateEvents(); }, - render: function () { + afterRender: function () { + this.$el.attr('id', this.id); this.$el.addClass('active'); } }); - // TODO: render templates on the client // TODO: use some kind of data-binding for forms // ### General settings Settings.general = Settings.Pane.extend({ - el: '#general', + id: "general", + events: { 'click .button-save': 'saveSettings' }, @@ -107,26 +111,28 @@ }); }, - render: function () { + templateName: 'settings/general', + + beforeRender: function () { var settings = this.model.toJSON(); this.$('#blog-title').val(settings.title); this.$('#email-address').val(settings.email); - Settings.Pane.prototype.render.call(this); } }); // ### Content settings Settings.content = Settings.Pane.extend({ - el: '#content', + id: 'content', events: { 'click .button-save': 'saveSettings' }, saveSettings: function () { + var self = this; this.model.save({ description: this.$('#blog-description').val() }, { success: function () { - this.addSubview(new Ghost.Views.NotificationCollection({ + self.addSubview(new Ghost.Views.NotificationCollection({ model: [{ type: 'success', message: 'Saved', @@ -136,7 +142,7 @@ }, error: function () { - this.addSubview(new Ghost.Views.NotificationCollection({ + self.addSubview(new Ghost.Views.NotificationCollection({ model: [{ type: 'error', message: 'Something went wrong, not saved :(', @@ -147,10 +153,11 @@ }); }, - render: function () { + templateName: 'settings/content', + + beforeRender: function () { var settings = this.model.toJSON(); this.$('#blog-description').val(settings.description); - Settings.Pane.prototype.render.call(this); } }); diff --git a/core/server/views/settings.hbs b/core/server/views/settings.hbs index da395afedb..245bdbf553 100644 --- a/core/server/views/settings.hbs +++ b/core/server/views/settings.hbs @@ -1,444 +1,8 @@ {{!< default}}
-
-
-

General

-
- -
-
-
- {{#with settings}} -
-
-
- - -

How your blog name appears on the site

-
-
- - logo -

Display a logo on your site in place of blog title

-
- -
- - logo -

The icon for your blog, used in your browser tab and elsewhere

-
- -
- - -

Address to use for admin notifications

- -
- -
- - -
- -
- -
- -
-
- - -
-
- - {{/with}} -
-
-
- -
-
-

Content

-
- -
-
-
-
-
-
- - -

Sexy sans-serif font that will make your toes tickle.

- -
- -
- - -

Post Author / Date / Views

- - - -
-
- -
- -
-
- - -

The pattern used to display your title tags

- -
- -
- - -

The pattern used to display your meta descriptions

-
- -
- - - -
- -
- - -
- -
- -
-
-
- -
-
-

Users

-
- -
-
-
-
-

Invited Users

-
    -
  • -
    -
    -

    Some Name

    - Invitation Sent: 7 hours ago -
    -
  • -
-
-
-
-

Active Users

- - -
- -
    -
  • -
    - user -
    -
    -

    Some Name

    - Last Seen: 7 hours ago -
    - Admin -
  • -
  • -
    - user -
    -
    -

    Some Name

    - Last Seen: 2 days ago -
    - Editor -
  • -
-
-
-
- -
-
-

Appearance

-
-
-
Raw json be here
-

Active theme: {{json settings.activeTheme}}

-

Available themes: {{json availableThemes}}

-

Available plugins: {{json availablePlugins}}

-
-
- -
-
-

Plugins

-
- -
-
-
-
-
-

Updates

-
- - - - - - - - - - - - - - - - - -
-
- -
-
- Ghost SEO - - - -
-
-
- Yoast - yoast.com -
-
-
- v 0.1.13 - 3 weeks ago -
-
-
- - - - 48,200 users -
-
-
- -
-
-
- -
-
- Ghost SEO - The #1 content marketing plugin for Ghost. -
-
-
- Yoast - yoast.com -
-
-
- v 0.1.13 - 3 weeks ago -
-
-
- - - - 48,200 users -
-
-
- -
-
-
- -
-
-
-
-

Active Plugins

-
- - - - - - - - - - - - - - - - - -
-
- -
-
- Ghost SEO - The #1 content marketing plugin for Ghost. -
-
-
- Yoast - yoast.com -
-
-
- v 0.1.13 - 3 weeks ago -
-
-
- - - - 48,200 users -
-
-
- -
-
-
- -
-
- Ghost SEO - The #1 content marketing plugin for Ghost. -
-
-
- Yoast - yoast.com -
-
-
- v 0.1.13 - 3 weeks ago -
-
-
- - - - 48,200 users -
-
-
- -
-
-
-
-
-

Inactive Plugins

-
- - - - - - - - - - -
-
- -
-
- Ghost SEO - The #1 content marketing plugin for Ghost. -
-
-
- Yoast - yoast.com -
-
-
- v 0.1.13 - 3 weeks ago -
-
-
- - - - 48,200 users -
-
-
- -
-
-
-
-
+
\ No newline at end of file diff --git a/core/test/functional/admin/04_content_test.js b/core/test/functional/admin/04_content_test.js index bbc60a5770..281b18dc40 100644 --- a/core/test/functional/admin/04_content_test.js +++ b/core/test/functional/admin/04_content_test.js @@ -18,14 +18,14 @@ casper.test.begin("Content screen is correct", 9, function suite(test) { }); casper.then(function testActiveItem() { - casper.test.assertEquals(casper.evaluate(function () { + casper.test.assertEvalEquals(function () { return document.querySelector('.content-list-content li').className; - }), "active", "first item is active"); + }, "active", "first item is active"); }).thenClick(".content-list-content li:nth-child(2) a", function then() { - casper.test.assertEquals(casper.evaluate(function () { + casper.test.assertEvalEquals(function () { return document.querySelectorAll('.content-list-content li')[1].className; - }), "active", "second item is active"); + }, "active", "second item is active"); }); // TODO: finish testing delete diff --git a/core/test/functional/admin/05_settings_test.js b/core/test/functional/admin/05_settings_test.js index 8f41461806..1993be94b2 100644 --- a/core/test/functional/admin/05_settings_test.js +++ b/core/test/functional/admin/05_settings_test.js @@ -1,6 +1,6 @@ /*globals casper, __utils__, url */ -casper.test.begin("Settings screen is correct", 3, function suite(test) { +casper.test.begin("Settings screen is correct", 12, function suite(test) { casper.test.filename = "settings_test.png"; @@ -11,8 +11,28 @@ casper.test.begin("Settings screen is correct", 3, function suite(test) { casper.then(function testViews() { test.assertExists(".wrapper", "Settings main view is present"); + test.assertExists(".settings-sidebar", "Settings sidebar view is present"); + test.assertExists(".settings-menu", "Settings menu is present"); + test.assertExists(".wrapper", "Settings main view is present"); + test.assertExists(".settings-content", "Settings content view is present"); + test.assertEval(function testGeneralIsActive() { + return document.querySelector('.settings-menu .general').classList.contains('active'); + }, "general tab is marked active"); + test.assertEval(function testContentIsGeneral() { + return document.querySelector('.settings-content').id === 'general'; + }, "loaded content is general screen"); + }); - // TODO: real settings tests + casper.thenClick('.settings-menu .publishing', function then() { + test.assertEval(function testGeneralIsNotActive() { + return !document.querySelector('.settings-menu .general').classList.contains('active'); + }, "general tab is not marked active"); + test.assertEval(function testContentIsActive() { + return document.querySelector('.settings-menu .publishing').classList.contains('active'); + }, "content tab is marked active"); + test.assertEval(function testContentIsContent() { + return document.querySelector('.settings-content').id === 'content'; + }, "loaded content is contentß screen"); }); casper.run(function () {