mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 11:55:03 +03:00
merging with master
This commit is contained in:
commit
091790a525
@ -1,6 +1,6 @@
|
||||
# [Ghost v0.1.1](https://github.com/TryGhost/Ghost) [![Build Status](https://magnum.travis-ci.com/TryGhost/Ghost.png?token=hMRLUurj2P3wzBdscyQs&branch=master)](https://magnum.travis-ci.com/TryGhost/Ghost)
|
||||
|
||||
Ghost is a free, open, simple blogging platform that's available to anyone who wants to use it. Created and maintained by [John O'Nolan](http://twitter.com/JohnONolan) + [Hannah Wolfe](http://twitter.com/ErisDS) + an amazing group of [contributors](https://github.com/TryGhost/Ghost/pulse).
|
||||
Ghost is a free, open, simple blogging platform that's available to anyone who wants to use it. Lovingly created and maintained by [John O'Nolan](http://twitter.com/JohnONolan) + [Hannah Wolfe](http://twitter.com/ErisDS) + an amazing group of [contributors](https://github.com/TryGhost/Ghost/contributors).
|
||||
|
||||
Visit the project's home page at [http://tryghost.org](http://tryghost.org)!
|
||||
|
||||
|
114
app.js
114
app.js
@ -6,17 +6,23 @@
|
||||
|
||||
// Module dependencies.
|
||||
var express = require('express'),
|
||||
when = require('when'),
|
||||
_ = require('underscore'),
|
||||
errors = require('./core/shared/errorHandling'),
|
||||
admin = require('./core/admin/controllers'),
|
||||
frontend = require('./core/frontend/controllers'),
|
||||
api = require('./core/shared/api'),
|
||||
flash = require('connect-flash'),
|
||||
Ghost = require('./core/ghost'),
|
||||
I18n = require('./core/lang/i18n'),
|
||||
filters = require('./core/frontend/filters'),
|
||||
helpers = require('./core/frontend/helpers'),
|
||||
|
||||
// ## Variables
|
||||
auth,
|
||||
authAPI,
|
||||
ghostLocals,
|
||||
loading = when.defer(),
|
||||
|
||||
/**
|
||||
* Create new Ghost object
|
||||
@ -24,6 +30,7 @@
|
||||
*/
|
||||
ghost = new Ghost();
|
||||
|
||||
|
||||
ghost.app().configure('development', function () {
|
||||
ghost.app().use(express.favicon(__dirname + '/content/images/favicon.ico'));
|
||||
ghost.app().use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
@ -66,53 +73,76 @@
|
||||
next();
|
||||
};
|
||||
|
||||
helpers.loadCoreHelpers(ghost);
|
||||
|
||||
|
||||
/**
|
||||
* API routes..
|
||||
* @todo auth should be public auth not user auth
|
||||
* Expose the standard locals that every external page should have available;
|
||||
* path, navItems and ghostGlobals
|
||||
*/
|
||||
ghost.app().get('/api/v0.1/posts', authAPI, api.requestHandler(api.posts.browse));
|
||||
ghost.app().post('/api/v0.1/posts', authAPI, api.requestHandler(api.posts.add));
|
||||
ghost.app().get('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.read));
|
||||
ghost.app().put('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.edit));
|
||||
ghost.app().del('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.destroy));
|
||||
ghost.app().get('/api/v0.1/settings', authAPI, api.requestHandler(api.settings.browse));
|
||||
ghost.app().get('/api/v0.1/settings/:key', authAPI, api.requestHandler(api.settings.read));
|
||||
ghost.app().put('/api/v0.1/settings', authAPI, api.requestHandler(api.settings.edit));
|
||||
ghostLocals = function(req, res, next) {
|
||||
ghost.doFilter('ghostNavItems', {path: req.path, navItems: []}, function(navData) {
|
||||
// Make sure we have a locals value.
|
||||
res.locals = res.locals || {};
|
||||
|
||||
/**
|
||||
* Admin routes..
|
||||
* @todo put these somewhere in admin
|
||||
*/
|
||||
// Extend it with nav data and ghostGlobals
|
||||
_.extend(res.locals, navData, {
|
||||
ghostGlobals: ghost.globals()
|
||||
});
|
||||
|
||||
ghost.app().get(/^\/logout\/?$/, admin.logout);
|
||||
ghost.app().get('/ghost/login/', admin.login);
|
||||
ghost.app().get('/ghost/register/', admin.register);
|
||||
ghost.app().post('/ghost/login/', admin.auth);
|
||||
ghost.app().post('/ghost/register/', admin.doRegister);
|
||||
ghost.app().get('/ghost/editor/:id', auth, admin.editor);
|
||||
ghost.app().get('/ghost/editor', auth, admin.editor);
|
||||
ghost.app().get('/ghost/blog', auth, admin.blog);
|
||||
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$|(ghost-admin|admin|wp-admin|dashboard|login)\/?)/, auth, function (req, res) {
|
||||
res.redirect('/ghost/');
|
||||
});
|
||||
ghost.app().get('/ghost/', auth, admin.index);
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Frontend routes..
|
||||
* @todo dynamic routing, homepage generator, filters ETC ETC
|
||||
*/
|
||||
ghost.app().get('/:slug', frontend.single);
|
||||
ghost.app().get('/', frontend.homepage);
|
||||
// Expose the promise we will resolve after our pre-loading
|
||||
ghost.loaded = loading.promise;
|
||||
|
||||
when.all([filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(ghost)]).then(function () {
|
||||
|
||||
ghost.app().listen(3333, function () {
|
||||
console.log("Express server listening on port " + 3333);
|
||||
});
|
||||
/**
|
||||
* API routes..
|
||||
* @todo auth should be public auth not user auth
|
||||
*/
|
||||
ghost.app().get('/api/v0.1/posts', authAPI, api.requestHandler(api.posts.browse));
|
||||
ghost.app().post('/api/v0.1/posts', authAPI, api.requestHandler(api.posts.add));
|
||||
ghost.app().get('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.read));
|
||||
ghost.app().put('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.edit));
|
||||
ghost.app().del('/api/v0.1/posts/:id', authAPI, api.requestHandler(api.posts.destroy));
|
||||
ghost.app().get('/api/v0.1/settings', authAPI, api.requestHandler(api.settings.browse));
|
||||
ghost.app().get('/api/v0.1/settings/:key', authAPI, api.requestHandler(api.settings.read));
|
||||
ghost.app().put('/api/v0.1/settings', authAPI, api.requestHandler(api.settings.edit));
|
||||
|
||||
/**
|
||||
* Admin routes..
|
||||
* @todo put these somewhere in admin
|
||||
*/
|
||||
ghost.app().get(/^\/logout\/?$/, admin.logout);
|
||||
ghost.app().get('/ghost/login/', admin.login);
|
||||
ghost.app().get('/ghost/register/', admin.register);
|
||||
ghost.app().post('/ghost/login/', admin.auth);
|
||||
ghost.app().post('/ghost/register/', admin.doRegister);
|
||||
ghost.app().get('/ghost/editor/:id', auth, admin.editor);
|
||||
ghost.app().get('/ghost/editor', auth, admin.editor);
|
||||
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$|(ghost-admin|admin|wp-admin|dashboard|login)\/?)/, auth, function (req, res) {
|
||||
res.redirect('/ghost/');
|
||||
});
|
||||
ghost.app().get('/ghost/', auth, admin.index);
|
||||
|
||||
/**
|
||||
* Frontend routes..
|
||||
* @todo dynamic routing, homepage generator, filters ETC ETC
|
||||
*/
|
||||
ghost.app().get('/:slug', ghostLocals, frontend.single);
|
||||
ghost.app().get('/', ghostLocals, frontend.homepage);
|
||||
|
||||
ghost.app().listen(3333, function () {
|
||||
console.log("Express server listening on port " + 3333);
|
||||
|
||||
// Let everyone know we have finished loading
|
||||
loading.resolve();
|
||||
});
|
||||
|
||||
}, errors.logAndThrowError);
|
||||
}());
|
14
config.js
14
config.js
@ -68,7 +68,8 @@
|
||||
connection: {
|
||||
filename: './core/shared/data/testdb.db'
|
||||
},
|
||||
debug: true
|
||||
debug: false
|
||||
// debug: true
|
||||
},
|
||||
|
||||
staging: {},
|
||||
@ -86,6 +87,17 @@
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @property {Array} nav
|
||||
*/
|
||||
config.nav = [{
|
||||
title: 'Home',
|
||||
url: '/'
|
||||
}, {
|
||||
title: 'Admin',
|
||||
url: '/ghost'
|
||||
}];
|
||||
|
||||
/**
|
||||
* @property {Object} exports
|
||||
*/
|
||||
|
27
content/plugins/exampleFilters.js
Normal file
27
content/plugins/exampleFilters.js
Normal file
@ -0,0 +1,27 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Because I didn't want to write over FancyFirstChar
|
||||
*/
|
||||
var ExampleFilter;
|
||||
|
||||
var ExampleFilter = function(ghost){
|
||||
this.ghost = function() {
|
||||
return ghost;
|
||||
}
|
||||
}
|
||||
|
||||
ExampleFilter.prototype.init = function() {
|
||||
|
||||
this.ghost().registerFilter('messWithAdmin', function(adminNavbar){
|
||||
console.log('adminnavbar settings run');
|
||||
delete adminNavbar.add;
|
||||
return adminNavbar;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = ExampleFilter;
|
||||
|
||||
}());
|
@ -9,6 +9,7 @@
|
||||
return ghost;
|
||||
};
|
||||
};
|
||||
|
||||
FancyFirstChar.prototype.init = function () {
|
||||
this.ghost().registerFilter('prePostsRender', function (posts) {
|
||||
var post,
|
||||
@ -41,5 +42,7 @@
|
||||
FancyFirstChar.prototype.activate = function () {};
|
||||
FancyFirstChar.prototype.deactivate = function () {};
|
||||
|
||||
|
||||
|
||||
module.exports = FancyFirstChar;
|
||||
}());
|
||||
|
BIN
core/admin/assets/img/dash/Facebook_Images@2x.png
Normal file
BIN
core/admin/assets/img/dash/Facebook_Images@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
@ -149,10 +149,83 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ## Shortcuts
|
||||
// Zen writing mode
|
||||
shortcut.add("Alt+Shift+Z", function () {
|
||||
$('body').toggleClass('zen');
|
||||
});
|
||||
|
||||
var MarkdownShortcuts = [
|
||||
{
|
||||
'key': 'Ctrl+B',
|
||||
'style': 'bold'
|
||||
},
|
||||
{
|
||||
'key': 'Meta+B',
|
||||
'style': 'bold'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+I',
|
||||
'style': 'italic'
|
||||
},
|
||||
{
|
||||
'key': 'Meta+I',
|
||||
'style': 'italic'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Alt+U',
|
||||
'style': 'strike'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Shift+K',
|
||||
'style': 'code'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+1',
|
||||
'style': 'h1'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+2',
|
||||
'style': 'h2'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+3',
|
||||
'style': 'h3'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+4',
|
||||
'style': 'h4'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+5',
|
||||
'style': 'h5'
|
||||
},
|
||||
{
|
||||
'key': 'Alt+6',
|
||||
'style': 'h6'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Shift+L',
|
||||
'style': 'link'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Shift+I',
|
||||
'style': 'image'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Q',
|
||||
'style': 'blockquote'
|
||||
},
|
||||
{
|
||||
'key': 'Ctrl+Shift+1',
|
||||
'style': 'currentdate'
|
||||
}
|
||||
];
|
||||
|
||||
$.each(MarkdownShortcuts, function (index, short) {
|
||||
shortcut.add(short.key, function () {
|
||||
return editor.addMarkdown({style: short.style});
|
||||
});
|
||||
});
|
||||
});
|
||||
}(jQuery, Showdown, CodeMirror, shortcut));
|
53
core/admin/assets/js/markdown-actions.js
Normal file
53
core/admin/assets/js/markdown-actions.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*global console, jQuery, CodeMirror*/
|
||||
|
||||
// # Surrounds given text with Markdown syntax
|
||||
(function ($) {
|
||||
"use strict";
|
||||
var Markdown = {
|
||||
init : function (options, elem) {
|
||||
var self = this;
|
||||
self.elem = elem;
|
||||
|
||||
self.style = (typeof options === 'string') ? options : options.style;
|
||||
|
||||
self.options = $.extend({}, CodeMirror.prototype.addMarkdown.options, options);
|
||||
|
||||
self.replace();
|
||||
},
|
||||
replace: function () {
|
||||
var text = this.elem.getSelection(), md;
|
||||
if (this.options.syntax[this.style]) {
|
||||
md = this.options.syntax[this.style].replace('$1', text);
|
||||
this.elem.replaceSelection(md);
|
||||
} else {
|
||||
console.log("Invalid style.");
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
CodeMirror.prototype.addMarkdown = function (options) {
|
||||
var markdown = Object.create(Markdown);
|
||||
markdown.init(options, this);
|
||||
};
|
||||
|
||||
CodeMirror.prototype.addMarkdown.options = {
|
||||
style: null,
|
||||
syntax: {
|
||||
bold: "**$1**",
|
||||
italic: "_$1_",
|
||||
strike: "~~$1~~",
|
||||
code: "`$1`",
|
||||
h1: "\n# $1\n",
|
||||
h2: "\n## $1\n",
|
||||
h3: "\n### $1\n",
|
||||
h4: "\n#### $1\n",
|
||||
h5: "\n##### $1\n",
|
||||
h6: "\n###### $1\n",
|
||||
link: "[$1](http://)",
|
||||
image: "!image[$1](http://)",
|
||||
blockquote: "> $1",
|
||||
currentDate: new Date().toLocaleString()
|
||||
}
|
||||
};
|
||||
}(jQuery));
|
@ -4,141 +4,381 @@
|
||||
*
|
||||
*/
|
||||
|
||||
.widget {
|
||||
width:341px;
|
||||
height:300px;
|
||||
background:#fff;
|
||||
box-shadow: $shadow;
|
||||
float:left;
|
||||
margin:0 15px 15px 0;
|
||||
display:none;
|
||||
/* ==========================================================================
|
||||
Widget Base
|
||||
========================================================================== */
|
||||
|
||||
%widget, .widget {
|
||||
width: $widget-base-width;
|
||||
height: $widget-base-height;
|
||||
float:left;
|
||||
position:relative;
|
||||
margin:0 15px 15px 0;
|
||||
display: none;
|
||||
background-color:#fff;
|
||||
box-shadow: $shadow;
|
||||
|
||||
.widget-content {
|
||||
@include box-sizing(border-box);
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.widget-footer, .widget-header {
|
||||
@include box-sizing(border-box);
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 1.2em;
|
||||
color: #cecbc7;
|
||||
border-top: 1px solid #EDECE4;
|
||||
|
||||
.widget-title {
|
||||
display: inline-block;
|
||||
padding-top: 7px;
|
||||
padding-left: 15px;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-settings-toggle {
|
||||
@include box-sizing(border-box);
|
||||
display: block;
|
||||
height: 39px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
padding: 7px 14px;
|
||||
border-left: 1px solid #EDECE4;
|
||||
cursor: pointer;
|
||||
@include icon($i-settings, 1em);
|
||||
}
|
||||
|
||||
.widget-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.none {
|
||||
margin-right:0;
|
||||
/* ==========================================================================
|
||||
Widget Sizes
|
||||
========================================================================== */
|
||||
|
||||
.widget-1x2 {
|
||||
height: $widget-base-height * 2;
|
||||
}
|
||||
|
||||
.time {
|
||||
background-image: url(../img/dash/Time@2x.png);
|
||||
background-size: 341px 300px;
|
||||
.widget-2x2 {
|
||||
width: $widget-base-width * 2;
|
||||
height: $widget-base-height * 2;
|
||||
}
|
||||
|
||||
.image {
|
||||
max-width: 100%;
|
||||
width: 682px + 15px;
|
||||
background-image: url(../img/dash/Image@2x.png);
|
||||
background-size: 697px 300px;
|
||||
.widget-2x1 {
|
||||
width: $widget-base-width * 2;
|
||||
}
|
||||
|
||||
.stats {
|
||||
max-width: 100%;
|
||||
width: 682px + 15px;
|
||||
height: 615px;
|
||||
background-image: url(../img/dash/Stats@2x.png);
|
||||
background-size: 697px 615px;
|
||||
|
||||
/* ==========================================================================
|
||||
Widget Variations
|
||||
========================================================================== */
|
||||
|
||||
%widget-number, .widget-number {
|
||||
@extend %widget;
|
||||
|
||||
.widget-content {
|
||||
.info {
|
||||
margin-top: 30px;
|
||||
|
||||
.count {
|
||||
display: block;
|
||||
font-size: 5em;
|
||||
line-height: 1em;
|
||||
font-weight: 400;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 2em;
|
||||
color: #9b9b9b;
|
||||
|
||||
mark {
|
||||
background-color: transparent;
|
||||
|
||||
&.up {
|
||||
color: $green;
|
||||
}
|
||||
&.down {
|
||||
color: $red;
|
||||
}
|
||||
} // mark
|
||||
} // .sub
|
||||
} // .info
|
||||
} // .widget-content
|
||||
|
||||
&.widget-2x2 {
|
||||
.widget-content {
|
||||
.info {
|
||||
margin-top: 100px;
|
||||
|
||||
.count {
|
||||
font-size: 9em;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
} // .info
|
||||
} // .widget-content
|
||||
} // .widget-2x2
|
||||
} // %widget-number, .widget-number
|
||||
|
||||
//For the settings panel
|
||||
.widget-settings {
|
||||
@extend %widget;
|
||||
background-color: #2d3032;
|
||||
|
||||
.widget-header {
|
||||
height: 40px;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #4a4a4a;
|
||||
color: #7E878B;
|
||||
}
|
||||
|
||||
.widget-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
width:100%;
|
||||
height: 40px;
|
||||
display: block;
|
||||
border-bottom: 1px solid #4a4a4a;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include box-sizing(border-box);
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
color: #E3EDF2;
|
||||
text-transform: uppercase;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
@include box-sizing(border-box);
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
color: #E3EDF2;
|
||||
text-transform: none;
|
||||
background: none;
|
||||
border: none;
|
||||
border-left: 1px solid #4a4a4a;
|
||||
}
|
||||
|
||||
.widget-footer, .widget-header {
|
||||
border-color: #4a4a4a;
|
||||
}
|
||||
|
||||
.widget-settings-toggle {
|
||||
border-color: #4a4a4a;
|
||||
|
||||
&.close {
|
||||
@include icon($i-x, 1em);
|
||||
}
|
||||
&.done {
|
||||
background-color: #A0B95D;
|
||||
color: #ffffff;
|
||||
@include icon($i-check, 1em);
|
||||
}
|
||||
}
|
||||
} // .widget-settings
|
||||
|
||||
.none {
|
||||
margin-right:0;
|
||||
}
|
||||
|
||||
.facebook {
|
||||
background-image: url(../img/dash/Facebook@2x.png);
|
||||
background-size: 341px 300px;
|
||||
/* ==========================================================================
|
||||
Individual Widgets
|
||||
========================================================================== */
|
||||
|
||||
.widget-time {
|
||||
@extend %widget;
|
||||
|
||||
.summary {
|
||||
margin-bottom: 30px;
|
||||
font-size: 1.4em;
|
||||
color: #cecbc7;
|
||||
|
||||
.day {
|
||||
float: left;
|
||||
}
|
||||
.weather {
|
||||
float: right;
|
||||
// TODO: icon for weather
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
time {
|
||||
margin-top: 30px;
|
||||
|
||||
.clock {
|
||||
display: block;
|
||||
font-size: 5em;
|
||||
line-height: 1em;
|
||||
font-weight: 400;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 2em;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
}
|
||||
} // .widget-time
|
||||
|
||||
.widget-image {
|
||||
@extend %widget;
|
||||
|
||||
.widget-content {
|
||||
height: 100%;
|
||||
background-image: url(../img/dash/Image@2x.png);
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.widget-footer {
|
||||
margin-top: -40px;
|
||||
opacity: 0;
|
||||
background: #ffffff;
|
||||
@include transition(opacity 200ms linear);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.widget-footer {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // .widget-image
|
||||
|
||||
.widget-stats {
|
||||
@extend %widget-number;
|
||||
}
|
||||
|
||||
.gplus {
|
||||
background-image: url(../img/dash/GooglePlus@2x.png);
|
||||
background-size: 341px 300px;
|
||||
.widget-facebook {
|
||||
@extend %widget-number;
|
||||
|
||||
.info {
|
||||
.faces {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
margin-top: 25px;
|
||||
background-image: url("../img/dash/Facebook_Images@2x.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.twitter {
|
||||
background-image: url(../img/dash/Twitter@2x.png);
|
||||
background-size: 341px 300px;
|
||||
.widget-gplus {
|
||||
@extend %widget-number;
|
||||
}
|
||||
|
||||
.campaignmonitor {
|
||||
background-image: url(../img/dash/CampaignMonitor@2x.png);
|
||||
background-size: 341px 300px;
|
||||
.widget-twitter {
|
||||
@extend %widget;
|
||||
}
|
||||
|
||||
.posts {
|
||||
background-image: url(../img/dash/PostsStats@2x.png);
|
||||
background-size: 341px 300px;
|
||||
position: relative;
|
||||
.widget-campaignmonitor {
|
||||
@extend %widget-number;
|
||||
}
|
||||
|
||||
.chart {
|
||||
box-sizing: border-box;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
margin: 25px auto 0 auto;
|
||||
background: #242628;
|
||||
border: #efefef 54px solid;
|
||||
border-radius: 500px;
|
||||
}
|
||||
|
||||
#poststats {
|
||||
.widget-posts {
|
||||
@extend %widget;
|
||||
position: relative;
|
||||
top:-54px;
|
||||
left: -54px;
|
||||
|
||||
.chart {
|
||||
@include box-sizing(border-box);
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0 auto;
|
||||
background: #242628;
|
||||
border: #efefef 54px solid;
|
||||
border-radius: 250px;
|
||||
|
||||
#poststats {
|
||||
position: relative;
|
||||
top:-54px;
|
||||
left: -54px;
|
||||
}
|
||||
|
||||
.data {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
color: $midgrey;
|
||||
font-size: 13px;
|
||||
list-style: none;
|
||||
|
||||
.ready {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.pending {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: #f9e15d;
|
||||
}
|
||||
|
||||
.draft {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.data {
|
||||
position: absolute;
|
||||
top: 87px;
|
||||
color: $midgrey;
|
||||
font-size: 13px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.ready {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.pending {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: #f9e15d;
|
||||
}
|
||||
|
||||
.draft {
|
||||
font-size: 18px;
|
||||
vertical-align: -5%;
|
||||
margin-right: 5px;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
.dashboard-controls {
|
||||
.dashboard-controls {
|
||||
@extend %box;
|
||||
padding:0 15px;
|
||||
|
||||
.text {
|
||||
.text {
|
||||
padding:12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.controls-nav {
|
||||
.controls-nav {
|
||||
float:left;
|
||||
margin-left:20px;
|
||||
ul {
|
||||
ul {
|
||||
border-left: $lightgrey 1px solid;
|
||||
|
||||
li {
|
||||
li {
|
||||
margin: 0;
|
||||
border-right: 1px solid $lightgrey;
|
||||
|
||||
a {
|
||||
a {
|
||||
padding: 12px 15px;
|
||||
color: $grey;
|
||||
|
||||
span {
|
||||
span {
|
||||
color: $darkgrey;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover {
|
||||
color: $darkgrey;
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -147,18 +387,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.widget-stats {
|
||||
span {
|
||||
.widget-stats {
|
||||
span {
|
||||
display: block;
|
||||
font-size: 1.6em;
|
||||
line-height: 1.2em;
|
||||
color: $grey;
|
||||
margin-bottom: 15px;
|
||||
strong {
|
||||
strong {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
span:first-child {
|
||||
span:first-child {
|
||||
font-size: 5.4em;
|
||||
line-height: 1.4em;
|
||||
color: #000;
|
||||
@ -166,16 +406,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.span4 .vert2 {
|
||||
.widget-stats {
|
||||
span {
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.span4 .vert2 {
|
||||
.widget-stats {
|
||||
span {
|
||||
font-size: 2.6em;
|
||||
strong {
|
||||
strong {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
span:first-child {
|
||||
span:first-child {
|
||||
font-size: 12.4em;
|
||||
}
|
||||
}
|
||||
@ -183,30 +423,30 @@
|
||||
}
|
||||
|
||||
// Time & Date Box
|
||||
.time-date {
|
||||
.time {
|
||||
.time-date {
|
||||
.time {
|
||||
font-size: 7.4em;
|
||||
line-height: 0.7em;
|
||||
border-bottom: 1px solid $lightgrey;
|
||||
span {
|
||||
span {
|
||||
font-size: 0.2em;
|
||||
color: $grey;
|
||||
text-transform: uppercase;
|
||||
font-style: normal;
|
||||
}
|
||||
@media only screen and (min-width: 1400px) {
|
||||
span {
|
||||
@media only screen and (min-width: 1400px) {
|
||||
span {
|
||||
font-size: 0.3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.date {
|
||||
.date {
|
||||
font-size: 2.2em;
|
||||
line-height: 1em;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
padding: 15px 0;
|
||||
span {
|
||||
span {
|
||||
font-size: 0.7em;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
@ -217,23 +457,23 @@
|
||||
}
|
||||
|
||||
// Post Statuses Box
|
||||
.post-statuses {
|
||||
.status-levels {
|
||||
.post-statuses {
|
||||
.status-levels {
|
||||
width: 30%;
|
||||
div {
|
||||
div {
|
||||
text-indent: -9999px;
|
||||
}
|
||||
}
|
||||
.status-text {
|
||||
.status-text {
|
||||
width: 70%;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.2em;
|
||||
color: $grey;
|
||||
div {
|
||||
div {
|
||||
background: none;
|
||||
padding: 15px 0;
|
||||
}
|
||||
strong {
|
||||
strong {
|
||||
font-size: 1.6em;
|
||||
width: 60px;
|
||||
padding-right: 5px;
|
||||
@ -241,38 +481,38 @@
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.scheduled {
|
||||
.scheduled {
|
||||
background: $green;
|
||||
strong {
|
||||
strong {
|
||||
color: $green;
|
||||
}
|
||||
}
|
||||
.pending {
|
||||
.pending {
|
||||
background: #fcd039;
|
||||
strong {
|
||||
strong {
|
||||
color: #fcd039;
|
||||
}
|
||||
}
|
||||
.draft {
|
||||
.draft {
|
||||
background: $red;
|
||||
strong {
|
||||
strong {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.todays-traffic {
|
||||
ul {
|
||||
li {
|
||||
.todays-traffic {
|
||||
ul {
|
||||
li {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
margin-bottom: 1px;
|
||||
div {
|
||||
div {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
}
|
||||
}
|
||||
li:before {
|
||||
li:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 34px;
|
||||
@ -280,84 +520,84 @@
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
}
|
||||
li:nth-child(1):before {
|
||||
li:nth-child(1):before {
|
||||
background: $blue;
|
||||
width: 80%;
|
||||
}
|
||||
li:nth-child(2):before {
|
||||
li:nth-child(2):before {
|
||||
background: lighten($blue, 3%);
|
||||
width: 60%;
|
||||
}
|
||||
li:nth-child(3):before {
|
||||
li:nth-child(3):before {
|
||||
background: lighten($blue, 6%);
|
||||
width: 40%;
|
||||
}
|
||||
li:nth-child(4):before {
|
||||
li:nth-child(4):before {
|
||||
background: lighten($blue, 10%);
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
.table {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
thead, tbody, tr {
|
||||
thead, tbody, tr {
|
||||
display: block;
|
||||
}
|
||||
@media only screen and (min-width: 400px) {
|
||||
thead {
|
||||
@media only screen and (min-width: 400px) {
|
||||
thead {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
tr {
|
||||
tbody {
|
||||
tr {
|
||||
background: $lightbrown;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
position: relative;
|
||||
&:first-child {
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1200px) {
|
||||
tr {
|
||||
@media only screen and (min-width: 1200px) {
|
||||
tr {
|
||||
padding: 0 10px 0 40px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
td {
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: right;
|
||||
color: $grey;
|
||||
strong {
|
||||
strong {
|
||||
color: #000;
|
||||
}
|
||||
span {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
@media only screen and (min-width: 500px) {
|
||||
span {
|
||||
@media only screen and (min-width: 500px) {
|
||||
span {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.callout {
|
||||
.callout {
|
||||
color: $green;
|
||||
}
|
||||
&:first-child {
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-img {
|
||||
.user-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.user-img {
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.user-img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
@ -183,3 +183,10 @@ $green: #9FBB58;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
Widgets
|
||||
============================================================================= */
|
||||
|
||||
$widget-base-height: 300px;
|
||||
$widget-base-width: 340px;
|
@ -17,32 +17,36 @@
|
||||
name: 'Dashboard',
|
||||
navClass: 'dashboard',
|
||||
key: 'admin.navbar.dashboard',
|
||||
defaultString: 'dashboard',
|
||||
path: ''
|
||||
// defaultString: 'dashboard',
|
||||
path: '/'
|
||||
},
|
||||
blog: {
|
||||
content: {
|
||||
name: 'Content',
|
||||
navClass: 'content',
|
||||
key: 'admin.navbar.blog',
|
||||
defaultString: 'blog',
|
||||
path: '/blog'
|
||||
key: 'admin.navbar.content',
|
||||
// defaultString: 'content',
|
||||
path: '/content/'
|
||||
},
|
||||
add: {
|
||||
name: 'New Post',
|
||||
navClass: 'editor',
|
||||
key: 'admin.navbar.editor',
|
||||
defaultString: 'editor',
|
||||
path: '/editor'
|
||||
// defaultString: 'editor',
|
||||
path: '/editor/'
|
||||
},
|
||||
settings: {
|
||||
name: 'Settings',
|
||||
navClass: 'settings',
|
||||
key: 'admin.navbar.settings',
|
||||
defaultString: 'settings',
|
||||
path: '/settings'
|
||||
// defaultString: 'settings',
|
||||
path: '/settings/'
|
||||
}
|
||||
};
|
||||
|
||||
ghost.doFilter('messWithAdmin', adminNavbar, function() {
|
||||
console.log('the dofilter hook called in /core/admin/controllers/index.js');
|
||||
});
|
||||
|
||||
// TODO - make this a util or helper
|
||||
function setSelected(list, name) {
|
||||
_.each(list, function (item, key) {
|
||||
@ -64,9 +68,9 @@
|
||||
console.log('user found: ', user);
|
||||
req.session.user = "ghostadmin";
|
||||
res.redirect(req.query.redirect || '/ghost/');
|
||||
}, function (err) {
|
||||
}, function (error) {
|
||||
// Do something here to signal the reason for an error
|
||||
console.log(err.stack);
|
||||
req.flash('error', error.message);
|
||||
res.redirect('/ghost/login/');
|
||||
});
|
||||
},
|
||||
@ -78,16 +82,19 @@
|
||||
});
|
||||
},
|
||||
'doRegister': function (req, res) {
|
||||
// console.log(req.body);
|
||||
if (req.body.email_address !== '' && req.body.password.length > 5) {
|
||||
var email = req.body.email_address,
|
||||
password = req.body.password;
|
||||
|
||||
if (email !== '' && password.length > 5) {
|
||||
api.users.add({
|
||||
email_address: req.body.email_address,
|
||||
password: req.body.password
|
||||
email_address: email,
|
||||
password: password
|
||||
}).then(function (user) {
|
||||
console.log('user added', user);
|
||||
res.redirect('/ghost/login/');
|
||||
}, function (error) {
|
||||
console.log('there was an error', error);
|
||||
req.flash('error', error.message);
|
||||
res.redirect('/ghost/register/');
|
||||
});
|
||||
} else {
|
||||
req.flash('error', "The password is too short. Have at least 6 characters in there");
|
||||
@ -111,7 +118,7 @@
|
||||
.then(function (post) {
|
||||
res.render('editor', {
|
||||
bodyClass: 'editor',
|
||||
adminNav: setSelected(adminNavbar, 'blog'),
|
||||
adminNav: setSelected(adminNavbar, 'content'),
|
||||
title: post.get('title'),
|
||||
content: post.get('content')
|
||||
});
|
||||
@ -123,12 +130,12 @@
|
||||
});
|
||||
}
|
||||
},
|
||||
'blog': function (req, res) {
|
||||
'content': function (req, res) {
|
||||
api.posts.browse()
|
||||
.then(function (posts) {
|
||||
res.render('blog', {
|
||||
res.render('content', {
|
||||
bodyClass: 'manage',
|
||||
adminNav: setSelected(adminNavbar, 'blog'),
|
||||
adminNav: setSelected(adminNavbar, 'content'),
|
||||
posts: posts.toJSON()
|
||||
});
|
||||
});
|
||||
|
@ -4,9 +4,9 @@
|
||||
$(document).ready(function(){
|
||||
|
||||
//$('body').click(function(){
|
||||
$('.time').fadeIn(1000);
|
||||
$('.image').delay(300).fadeIn(1000);
|
||||
$('.posts').delay(600).fadeIn(900, function(){
|
||||
$('.widget-time').fadeIn(1000);
|
||||
$('.widget-image').delay(300).fadeIn(1000);
|
||||
$('.widget-posts').delay(600).fadeIn(900, function(){
|
||||
|
||||
var ctx = $("#poststats").get(0).getContext("2d");
|
||||
var data = [
|
||||
@ -32,32 +32,133 @@
|
||||
|
||||
});
|
||||
|
||||
$('.stats').delay(800).fadeIn(1000);
|
||||
$('.facebook').delay(1000).fadeIn(1000);
|
||||
$('.gplus').delay(1200).fadeIn(1000);
|
||||
$('.twitter').delay(1300).fadeIn(1000);
|
||||
$('.campaignmonitor').delay(1400).fadeIn(1000);
|
||||
$('.widget-stats').delay(800).fadeIn(1000);
|
||||
$('.widget-facebook').delay(1000).fadeIn(1000);
|
||||
$('.widget-gplus').delay(1200).fadeIn(1000);
|
||||
$('.widget-twitter').delay(1300).fadeIn(1000);
|
||||
$('.widget-campaignmonitor').delay(1400).fadeIn(1000);
|
||||
//});
|
||||
|
||||
});
|
||||
</script>
|
||||
{{/contentFor}}
|
||||
|
||||
{{!< default}}
|
||||
<div class="time widget"></div>
|
||||
<div class="image widget"></div>
|
||||
<div class="posts widget none">
|
||||
<div class="chart">
|
||||
<canvas id="poststats" width="250" height="250"></canvas>
|
||||
<ul class="data">
|
||||
<li><span class="ready">9</span> Ready</li>
|
||||
<li><span class="pending">4</span> Pending</li>
|
||||
<li><span class="draft">1</span> Draft</li>
|
||||
</ul>
|
||||
</div>
|
||||
<article class="widget-time">
|
||||
<section class="widget-content">
|
||||
<header class="summary clearfix">
|
||||
<span class="day">Today</span>
|
||||
<span class="weather">12°</span>
|
||||
</header>
|
||||
<time>
|
||||
<span class="clock">12:42pm</span>
|
||||
<span class="date">Monday / March 5 / 2013</span>
|
||||
</time>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Linz, Austria</span>
|
||||
<div class="widget-settings-toggle cog"></div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<div class="widget-image widget-2x1">
|
||||
<section class="widget-content">
|
||||
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Ghost</span>
|
||||
<div class="widget-settings-toggle cog"></div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="widget-posts none">
|
||||
<section class="widget-content">
|
||||
<div class="chart">
|
||||
<canvas id="poststats" width="250" height="250"></canvas>
|
||||
<ul class="data">
|
||||
<li><span class="ready">9</span> Ready</li>
|
||||
<li><span class="pending">4</span> Pending</li>
|
||||
<li><span class="draft">1</span> Draft</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Upcoming Posts</span>
|
||||
<div class="widget-settings-toggle"></div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="widget-stats widget-2x2">
|
||||
<section class="widget-content">
|
||||
<div class="info">
|
||||
<span class="count">293,042</span>
|
||||
<span class="sub"><mark class="up">+35%</mark> in the last 30 days</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Google Analytics Unique Visitors</span>
|
||||
<div class="widget-settings-toggle"></div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="widget-facebook">
|
||||
<section class="widget-content">
|
||||
<div class="info">
|
||||
<span class="count">9,392</span>
|
||||
<span class="sub"><mark class="down">-39</mark> likes today</span>
|
||||
<span class="faces"></span>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Facebook</span>
|
||||
<div class="widget-settings-toggle"></div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="widget-gplus none">
|
||||
<section class="widget-content">
|
||||
<div class="info">
|
||||
<span class="count">4,103</span>
|
||||
<span class="sub">have you in circles</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Google Plus</span>
|
||||
<div class="widget-settings-toggle"></div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="widget-twitter widget-settings">
|
||||
<header class="widget-header">
|
||||
<span class="widget-title">Twitter Settings</span>
|
||||
<div class="widget-settings-toggle close"></div>
|
||||
</header>
|
||||
<section class="widget-content">
|
||||
<label>
|
||||
<span class="title">Account</span> <input type="text" value="@JohnONolan"/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="title">Display</span> <input type="text" value="Latest Tweets"/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="title">Quantity</span> <input type="text" value="6"/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="title">Account</span> <input type="text" value="Account"/>
|
||||
</label>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<div class="widget-settings-toggle done"></div>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="widget-campaignmonitor none">
|
||||
<section class="widget-content">
|
||||
<div class="info">
|
||||
<span class="count">3,502</span>
|
||||
<span class="sub"><mark class="up">+35</mark> subscribers this week</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="widget-footer">
|
||||
<span class="widget-title">Campaign Monitor</span>
|
||||
<div class="widget-settings-toggle"></div>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="stats widget"></div>
|
||||
<div class="facebook widget"></div>
|
||||
<div class="gplus widget none"></div>
|
||||
<div class="twitter widget"></div>
|
||||
<div class="campaignmonitor widget none"></div>
|
@ -4,6 +4,7 @@
|
||||
<script src="/core/admin/assets/lib/showdown/showdown.js"></script>
|
||||
<script src="/core/admin/assets/lib/showdown/extensions/ghostdown.js"></script>
|
||||
<script src="/core/admin/assets/lib/shortcuts.js"></script>
|
||||
<script src="/core/admin/assets/js/markdown-actions.js"></script>
|
||||
<script src="/core/admin/assets/js/editor.js"></script>
|
||||
<script src="/core/admin/assets/js/tagui.js"></script>
|
||||
{{/contentFor}}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
<header id="global-header" class="navbar">
|
||||
<a id="ghost" href="#" data-off-canvas="left"><span class="hidden">Ghost</span></a>
|
||||
<nav id="global-nav" role="navigation">
|
||||
|
@ -16,14 +16,14 @@
|
||||
'homepage': function (req, res) {
|
||||
api.posts.browse().then(function (posts) {
|
||||
ghost.doFilter('prePostsRender', posts.toJSON(), function (posts) {
|
||||
res.render('index', {posts: posts, ghostGlobals: ghost.globalConfig});
|
||||
res.render('index', {posts: posts, ghostGlobals: ghost.globalConfig, navItems: res.locals.navItems});
|
||||
});
|
||||
});
|
||||
},
|
||||
'single': function (req, res) {
|
||||
api.posts.read({'slug': req.params.slug}).then(function (post) {
|
||||
ghost.doFilter('prePostsRender', post.toJSON(), function (post) {
|
||||
res.render('single', {post: post, ghostGlobals: ghost.globalConfig});
|
||||
res.render('single', {post: post, ghostGlobals: ghost.globalConfig, navItems: res.locals.navItems});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
30
core/frontend/filters/index.js
Normal file
30
core/frontend/filters/index.js
Normal file
@ -0,0 +1,30 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
coreFilters;
|
||||
|
||||
coreFilters = function (ghost) {
|
||||
ghost.registerFilter('ghostNavItems', function (args) {
|
||||
var selectedItem;
|
||||
|
||||
// Set the nav items based on the config
|
||||
args.navItems = ghost.config().nav;
|
||||
|
||||
// Mark the current selected Item
|
||||
selectedItem = _.find(args.navItems, function (item) {
|
||||
// TODO: Better selection determination?
|
||||
return item.url === args.path;
|
||||
});
|
||||
|
||||
if (selectedItem) {
|
||||
selectedItem.active = true;
|
||||
}
|
||||
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.loadCoreFilters = coreFilters;
|
||||
|
||||
}());
|
53
core/frontend/helpers/ghostNav.js
Normal file
53
core/frontend/helpers/ghostNav.js
Normal file
@ -0,0 +1,53 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('underscore'),
|
||||
handlebars = require('express-hbs').handlebars,
|
||||
nodefn = require('when/node/function'),
|
||||
GhostNavHelper;
|
||||
|
||||
GhostNavHelper = function (navTemplate) {
|
||||
// Bind the context for our methods.
|
||||
_.bindAll(this, 'compileTemplate', 'renderNavItems');
|
||||
|
||||
if (_.isFunction(navTemplate)) {
|
||||
this.navTemplateFunc = navTemplate;
|
||||
} else {
|
||||
this.navTemplatePath = navTemplate;
|
||||
}
|
||||
};
|
||||
|
||||
GhostNavHelper.prototype.compileTemplate = function (templatePath) {
|
||||
var self = this;
|
||||
|
||||
// Allow people to overwrite the navTemplatePath
|
||||
templatePath = templatePath || this.navTemplatePath;
|
||||
|
||||
return nodefn.call(fs.readFile, templatePath).then(function(navTemplateContents) {
|
||||
// TODO: Can handlebars compile async?
|
||||
self.navTemplateFunc = handlebars.compile(navTemplateContents.toString());
|
||||
});
|
||||
};
|
||||
|
||||
GhostNavHelper.prototype.renderNavItems = function (navItems) {
|
||||
var output;
|
||||
|
||||
output = this.navTemplateFunc({links: navItems});
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
// A static helper method for registering with ghost
|
||||
GhostNavHelper.registerWithGhost = function(ghost) {
|
||||
var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'),
|
||||
ghostNavHelper = new GhostNavHelper(templatePath);
|
||||
|
||||
return ghostNavHelper.compileTemplate().then(function() {
|
||||
ghost.registerThemeHelper("ghostNav", ghostNavHelper.renderNavItems);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = GhostNavHelper;
|
||||
}());
|
@ -3,10 +3,11 @@
|
||||
|
||||
var _ = require('underscore'),
|
||||
moment = require('moment'),
|
||||
when = require('when'),
|
||||
navHelper = require('./ghostNav'),
|
||||
coreHelpers;
|
||||
|
||||
coreHelpers = function (ghost) {
|
||||
|
||||
/**
|
||||
* [ description]
|
||||
* @todo ghost core helpers + a way for themes to register them
|
||||
@ -39,6 +40,10 @@
|
||||
return output;
|
||||
});
|
||||
|
||||
return when.all([
|
||||
// Just one async helper for now, but could be more in the future
|
||||
navHelper.registerWithGhost(ghost)
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
|
7
core/frontend/views/nav.hbs
Normal file
7
core/frontend/views/nav.hbs
Normal file
@ -0,0 +1,7 @@
|
||||
<nav id="site-navigation" role="navigation">
|
||||
<ul>
|
||||
{{#links}}
|
||||
<li class="{{#active}}active{{/active}}"><a title="{{title}}" href="{{url}}">{{title}}</a></li>
|
||||
{{/links}}
|
||||
</ul>
|
||||
</nav>
|
@ -13,10 +13,10 @@
|
||||
_ = require('underscore'),
|
||||
Polyglot = require('node-polyglot'),
|
||||
models = require('./shared/models'),
|
||||
ExampleFilter = require('../content/plugins/exampleFilters'),
|
||||
Ghost,
|
||||
instance,
|
||||
filterCallbacks = {},
|
||||
|
||||
instance,
|
||||
statuses;
|
||||
|
||||
// ## Article Statuses
|
||||
@ -40,10 +40,12 @@
|
||||
*/
|
||||
Ghost = function () {
|
||||
var app,
|
||||
plugin,
|
||||
polyglot;
|
||||
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
plugin = new ExampleFilter(instance).init();
|
||||
|
||||
app = express();
|
||||
|
||||
@ -60,19 +62,32 @@
|
||||
dataProvider: models,
|
||||
statuses: function () { return statuses; },
|
||||
polyglot: function () { return polyglot; },
|
||||
plugin: function() { return plugin; },
|
||||
paths: function () {
|
||||
return {
|
||||
'activeTheme': __dirname + '/../content/' + config.themeDir + '/' + config.activeTheme + '/',
|
||||
'adminViews': __dirname + '/admin/views/',
|
||||
'lang': __dirname + '/lang/'
|
||||
'activeTheme': __dirname + '/../content/' + config.themeDir + '/' + config.activeTheme + '/',
|
||||
'adminViews': __dirname + '/admin/views/',
|
||||
'frontendViews': __dirname + '/frontend/views/',
|
||||
'lang': __dirname + '/lang/'
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the filters
|
||||
* @type {Array}
|
||||
*/
|
||||
Ghost.prototype.filterCallbacks = [];
|
||||
|
||||
/**
|
||||
* Holds the filter hooks (that are built in to Ghost Core)
|
||||
* @type {Array}
|
||||
*/
|
||||
Ghost.prototype.filters = [];
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
@ -105,11 +120,11 @@
|
||||
* @param {Function} fn
|
||||
*/
|
||||
Ghost.prototype.registerFilter = function (name, fn) {
|
||||
if (!filterCallbacks.hasOwnProperty(name)) {
|
||||
filterCallbacks[name] = [];
|
||||
if (!this.filterCallbacks.hasOwnProperty(name)) {
|
||||
this.filterCallbacks[name] = [];
|
||||
}
|
||||
console.log('registering filter for ', name);
|
||||
filterCallbacks[name].push(fn);
|
||||
this.filterCallbacks[name].push(fn);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -121,14 +136,15 @@
|
||||
Ghost.prototype.doFilter = function (name, args, callback) {
|
||||
var fn;
|
||||
|
||||
if (filterCallbacks.hasOwnProperty(name)) {
|
||||
for (fn in filterCallbacks[name]) {
|
||||
if (filterCallbacks[name].hasOwnProperty(fn)) {
|
||||
if (this.filterCallbacks.hasOwnProperty(name)) {
|
||||
for (fn in this.filterCallbacks[name]) {
|
||||
if (this.filterCallbacks[name].hasOwnProperty(fn)) {
|
||||
console.log('doing filter for ', name);
|
||||
args = filterCallbacks[name][fn](args);
|
||||
args = this.filterCallbacks[name][fn](args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(args);
|
||||
};
|
||||
|
||||
|
@ -50,15 +50,16 @@ module.exports = {
|
||||
|
||||
users: [
|
||||
{
|
||||
"id": "1",
|
||||
"username": "johnonolan",
|
||||
"id": "1",
|
||||
"username": "johnonolan",
|
||||
"first_name": "John",
|
||||
"last_name": "O'Nolan",
|
||||
"password": "$2a$10$.pb3wOEhbEPvArvOBB.iyuKslBjC7lSXCUzp29civDTvCg3M1j0XO",
|
||||
"email_address": "john@onolan.org",
|
||||
"profile_picture": "logo.png",
|
||||
"cover_picture": "",
|
||||
"bio": "Interactive designer, public speaker, startup advisor and writer. Living in Austria, attempting world domination via keyboard.",
|
||||
"url": "john.onolan.org",
|
||||
"bio": "Interactive designer, public speaker, startup advisor and writer. Living in Austria, attempting world domination via keyboard.",
|
||||
"url": "john.onolan.org",
|
||||
"created_by": 1,
|
||||
"updated_by": 1
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
errors = {
|
||||
throwError: function (err) {
|
||||
if (!err) {
|
||||
return;
|
||||
err = new Error("An error occurred");
|
||||
}
|
||||
|
||||
if (_.isString(err)) {
|
||||
|
@ -31,6 +31,14 @@
|
||||
runThrowError.should['throw']("test2");
|
||||
});
|
||||
|
||||
it("throws error even if nothing passed", function () {
|
||||
var runThrowError = function () {
|
||||
errors.throwError();
|
||||
};
|
||||
|
||||
runThrowError.should['throw']("An error occurred");
|
||||
});
|
||||
|
||||
it("logs errors", function () {
|
||||
var err = new Error("test1"),
|
||||
logStub = sinon.stub(console, "log");
|
||||
|
69
core/test/ghost/frontend_helpers_ghostNav_spec.js
Normal file
69
core/test/ghost/frontend_helpers_ghostNav_spec.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*globals describe, beforeEach, it*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
_ = require('underscore'),
|
||||
path = require('path'),
|
||||
GhostNavHelper = require('../../frontend/helpers/ghostNav');
|
||||
|
||||
describe('ghostNav Helper', function () {
|
||||
var navTemplatePath = path.join(process.cwd(), 'core/frontend/views/nav.hbs');
|
||||
|
||||
should.exist(GhostNavHelper, "GhostNavHelper exists");
|
||||
|
||||
it('can compile the nav template', function (done) {
|
||||
var helper = new GhostNavHelper(navTemplatePath);
|
||||
|
||||
helper.compileTemplate().then(function () {
|
||||
should.exist(helper.navTemplateFunc);
|
||||
_.isFunction(helper.navTemplateFunc).should.equal(true);
|
||||
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('can render nav items', function () {
|
||||
var helper = new GhostNavHelper(function (data) { return "rendered " + data.links.length; }),
|
||||
templateSpy = sinon.spy(helper, 'navTemplateFunc'),
|
||||
fakeNavItems = [{
|
||||
title: 'test1',
|
||||
url: '/test1'
|
||||
}, {
|
||||
title: 'test2',
|
||||
url: '/test2'
|
||||
}],
|
||||
rendered;
|
||||
|
||||
rendered = helper.renderNavItems(fakeNavItems);
|
||||
|
||||
// Returns a string returned from navTemplateFunc
|
||||
should.exist(rendered);
|
||||
rendered.should.equal("rendered 2");
|
||||
|
||||
templateSpy.calledWith({ links: fakeNavItems }).should.equal(true);
|
||||
});
|
||||
|
||||
it('can register with ghost', function (done) {
|
||||
var fakeGhost = {
|
||||
paths: function () {
|
||||
return {
|
||||
frontendViews: path.join(process.cwd(), 'core/frontend/views/')
|
||||
};
|
||||
},
|
||||
|
||||
registerThemeHelper: function () {
|
||||
return;
|
||||
}
|
||||
},
|
||||
registerStub = sinon.stub(fakeGhost, 'registerThemeHelper');
|
||||
|
||||
GhostNavHelper.registerWithGhost(fakeGhost).then(function () {
|
||||
registerStub.called.should.equal(true);
|
||||
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
}());
|
@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "grunt validate"
|
||||
"test": "grunt validate --verbose"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "3.1.x",
|
||||
@ -23,6 +23,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "0.1.9",
|
||||
"grunt-jslint": "0.2.x",
|
||||
"should": "~1.2.2",
|
||||
"grunt-mocha-test": "~0.4.0",
|
||||
|
Loading…
Reference in New Issue
Block a user