mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-15 03:01:37 -05:00
Merge pull request #7412 from TryGhost/playground
Refactored config, utf8mb4 support, and more alpha features
This commit is contained in:
commit
58c5b96b30
154 changed files with 1647 additions and 2871 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -100,7 +100,7 @@ Any other info e.g. Why do you consider this to be a bug? What did you expect to
|
|||
* Ghost Version: master (latest commit: a761de2079dca4df49567b1bddac492f25033985)
|
||||
* Node Version: 4.4.7
|
||||
* Browser: Chrome 48.0.2564.109 on Mac OS X 10.10.4
|
||||
* Database: SQLite / MySQL / postgres
|
||||
* Database: SQLite / MySQL
|
||||
```
|
||||
|
||||
<a name="features"></a>
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -60,7 +60,8 @@ CHANGELOG.md
|
|||
/core/test/functional/*.png
|
||||
/core/test/coverage
|
||||
|
||||
/config.js
|
||||
# ignore all custom json files for config
|
||||
config.*.json
|
||||
|
||||
# Built asset files
|
||||
/core/built
|
||||
|
|
|
@ -3,7 +3,6 @@ language: node_js
|
|||
node_js:
|
||||
- "4"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
- "6"
|
||||
sudo: false
|
||||
cache:
|
||||
|
@ -11,8 +10,6 @@ cache:
|
|||
- node_modules
|
||||
- core/client/node_modules
|
||||
- core/client/bower_components
|
||||
addons:
|
||||
postgresql: "9.3"
|
||||
env:
|
||||
global:
|
||||
- GITHUB_OAUTH_KEY=003a44d58f12089d0c0261338298af3813330949
|
||||
|
@ -21,20 +18,16 @@ env:
|
|||
matrix:
|
||||
- DB=sqlite3 NODE_ENV=testing
|
||||
- DB=mysql NODE_ENV=testing-mysql
|
||||
- DB=pg NODE_ENV=testing-pg
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "4"
|
||||
env: TEST_SUITE=lint
|
||||
allow_failures:
|
||||
- node_js: "6"
|
||||
fast_finish: true
|
||||
branches:
|
||||
except:
|
||||
- /^greenkeeper-.+$/
|
||||
before_install:
|
||||
- if [ $DB == "mysql" ]; then mysql -e 'create database ghost_testing'; fi
|
||||
- if [ $DB == "pg" ]; then psql -c 'create database ghost_testing;' -U postgres; fi
|
||||
after_success:
|
||||
- |
|
||||
if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
|
|
25
Gruntfile.js
25
Gruntfile.js
|
@ -407,25 +407,6 @@ var overrides = require('./core/server/overrides'),
|
|||
cfg.express.test.options.node_env = process.env.NODE_ENV;
|
||||
});
|
||||
|
||||
// #### Ensure Config *(Utility Task)*
|
||||
// Make sure that we have a `config.js` file when running tests
|
||||
// Ghost requires a `config.js` file to specify the database settings etc. Ghost comes with an example file:
|
||||
// `config.example.js` which is copied and renamed to `config.js` by the bootstrap process
|
||||
grunt.registerTask('ensureConfig', function () {
|
||||
var config = require('./core/server/config'),
|
||||
done = this.async();
|
||||
|
||||
if (!process.env.TEST_SUITE || process.env.TEST_SUITE !== 'client') {
|
||||
config.load().then(function () {
|
||||
done();
|
||||
}).catch(function (err) {
|
||||
grunt.fail.fatal(err.stack);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// #### Reset Database to "New" state *(Utility Task)*
|
||||
// Drops all database tables and then runs the migration process to put the database
|
||||
// in a "new" state.
|
||||
|
@ -538,7 +519,7 @@ var overrides = require('./core/server/overrides'),
|
|||
// ### test-setup *(utility)(
|
||||
// `grunt test-setup` will run all the setup tasks required for running tests
|
||||
grunt.registerTask('test-setup', 'Setup ready to run tests',
|
||||
['clean:test', 'setTestEnv', 'ensureConfig']
|
||||
['clean:test', 'setTestEnv']
|
||||
);
|
||||
|
||||
// ### Unit Tests *(sub task)*
|
||||
|
@ -580,8 +561,8 @@ var overrides = require('./core/server/overrides'),
|
|||
// `grunt test:integration/api/api_tags_spec.js`
|
||||
//
|
||||
// Their purpose is to test that both the api and models behave as expected when the database layer is involved.
|
||||
// These tests are run against sqlite3, mysql and pg on travis and ensure that differences between the databases
|
||||
// don't cause bugs. At present, pg often fails and is not officially supported.
|
||||
// These tests are run against sqlite3 and mysql on travis and ensure that differences between the databases
|
||||
// don't cause bugs.
|
||||
//
|
||||
// A coverage report can be generated for these tests using the `grunt test-coverage` task.
|
||||
grunt.registerTask('test-integration', 'Run integration tests (mocha + db access)',
|
||||
|
|
65
README.md
65
README.md
|
@ -1,9 +1,7 @@
|
|||
<a href="https://github.com/TryGhost/Ghost"><img src="https://cloud.githubusercontent.com/assets/120485/6622822/c4c639fe-c8e7-11e4-9e64-5bec06c8b4c3.png" alt="Ghost" /></a>
|
||||
<a href="https://github.com/TryGhost/Ghost"><img src="https://cloud.githubusercontent.com/assets/120485/18661790/cf942eda-7f17-11e6-9eb6-9c65bfc2abd8.png" alt="Ghost" /></a>
|
||||
<a href="https://travis-ci.org/TryGhost/Ghost"><img align="right" src="https://travis-ci.org/TryGhost/Ghost.svg?branch=master" alt="Build status" /></a>
|
||||
|
||||

|
||||
|
||||

|
||||
<a href="https://dev.ghost.org/lts"><img src="https://cloud.githubusercontent.com/assets/120485/18661856/0930282e-7f18-11e6-948a-00546393fd93.png" alt="Warning: Major release in progress. Expect things to be broken in master." /></a>
|
||||
|
||||
The project is maintained by a non-profit organisation called the **Ghost Foundation**, along with an amazing group of independent [contributors](https://github.com/TryGhost/Ghost/contributors). We're trying to make publishing software that changes the shape of online journalism.
|
||||
|
||||
|
@ -13,52 +11,45 @@ The project is maintained by a non-profit organisation called the **Ghost Founda
|
|||
- [Theme Docs](http://themes.ghost.org)
|
||||
- [Contributing Guide](https://github.com/TryGhost/Ghost/blob/master/.github/CONTRIBUTING.md)
|
||||
- [Feature Requests](http://ideas.ghost.org/)
|
||||
- [Dev Blog](http://dev.ghost.org)
|
||||
- [Developer Blog](http://dev.ghost.org)
|
||||
|
||||
**NOTE: If you’re stuck, can’t get something working or need some help, please head on over and join our [Slack community](https://ghost.org/slack/) rather than opening an issue.**
|
||||
|
||||
|
||||
|
||||
# Quick Start Install
|
||||
|
||||
Make sure you've installed Node.js - We recommend the latest **Node v4 LTS** release. For other versions [click here](http://support.ghost.org/supported-node-versions/). May contain nuts.
|
||||
First, you’ll need **Node.js v4 LTS** or a [supported version](http://support.ghost.org/supported-node-versions/).
|
||||
|
||||
1. Download the [latest release](https://ghost.org/developers/) of Ghost
|
||||
1. Unzip in the location you want to install
|
||||
1. Fire up a terminal
|
||||
1. Unzip, and fire up terminal
|
||||
1. `npm install --production`
|
||||
1. Start Ghost!
|
||||
- Local environment: `npm start`
|
||||
- On a server: `npm start --production`
|
||||
1. `http://localhost:2368/ghost` :tada:
|
||||
|
||||
More [install docs](http://support.ghost.org/installation/) here in case you got stuck.
|
||||
More [install docs](http://support.ghost.org/installation/) here in case you get stuck.
|
||||
|
||||
|
||||
|
||||
<a name="getting-started"></a>
|
||||
# Developer Install (from git)
|
||||
# Developer Install
|
||||
|
||||
Install Node.js. (See [Supported Node.js versions](http://support.ghost.org/supported-node-versions/))
|
||||
|
||||
```bash
|
||||
# Node v4.2+ LTS - recommended
|
||||
# Node v0.10.x and v0.12.x - supported
|
||||
#
|
||||
# Choose wisely
|
||||
```
|
||||
|
||||
Clone :ghost:
|
||||
This is for if you want to hack on Ghost core. First, you’ll need **Node.js v4 LTS** or a [supported version](http://support.ghost.org/supported-node-versions/). Then:
|
||||
|
||||
```bash
|
||||
git clone git://github.com/tryghost/ghost.git
|
||||
cd ghost
|
||||
```
|
||||
|
||||
Install grunt. No prizes here.
|
||||
Install grunt
|
||||
|
||||
```bash
|
||||
npm install -g grunt-cli
|
||||
```
|
||||
|
||||
Install Ghost. If you're running locally, use [master](https://github.com/TryGhost/Ghost/tree/master). For production, use [stable](https://github.com/TryGhost/Ghost/tree/stable). :no_entry_sign::rocket::microscope:
|
||||
Install Ghost
|
||||
|
||||
```bash
|
||||
npm install
|
||||
|
@ -70,34 +61,28 @@ Build the things!
|
|||
grunt init
|
||||
```
|
||||
|
||||
Minify that shit for production?
|
||||
Start your engines
|
||||
|
||||
```bash
|
||||
grunt prod
|
||||
grunt dev
|
||||
```
|
||||
|
||||
Start your engines.
|
||||
Congrats! You made it. BTW you can also just `npm install ghost` if you're into that sort of thing. NPM aficionados can also read up on using [Ghost as an NPM module](https://github.com/TryGhost/Ghost/wiki/Using-Ghost-as-an-npm-module). More general [install docs](http://support.ghost.org/installation/) here in case you got stuck.
|
||||
|
||||
```bash
|
||||
npm start
|
||||
|
||||
## running production? Add --production
|
||||
```
|
||||
|
||||
Congrats! You made it. BTW you can also just `npm install ghost` if you're into that sort of thing. NPM aficionados can also read up on using [Ghost as an NPM module](https://github.com/TryGhost/Ghost/wiki/Using-Ghost-as-an-npm-module).
|
||||
|
||||
More general [install docs](http://support.ghost.org/installation/) here in case you got stuck.
|
||||
|
||||
|
||||
|
||||
# Deploying Ghost
|
||||
|
||||

|
||||
<a href="https://ghost.org/pricing"><img src="https://cloud.githubusercontent.com/assets/120485/18662071/f30da886-7f18-11e6-90f2-42c0ade79fd1.png" alt="Ghost(Pro)" /></a>
|
||||
|
||||
Save yourself time and headaches with our fully managed **[Ghost(Pro)](https://ghost.org/pricing/)** service. Deploy a new instance of Ghost in a couple of clicks running on [DigitalOcean](https://digitalocean.com)’s rock solid infrastructure, with a worldwide CDN thrown in at no extra charge.
|
||||
The easiest way to deploy Ghost is with our official **[Ghost(Pro)](https://ghost.org/pricing/)** managed service. You can have a fresh instance up and running in a couple of clicks with a worldwide CDN, backups, security and maintenance all done for you.
|
||||
|
||||
All revenue from **Ghost(Pro)** goes to the Ghost Foundation, the non-profit org which funds the maintenance and further development of Ghost.
|
||||
Not only will it save you [many hours per month](https://ghost.org/ghost-pro-vs-self-hosting/), but all revenue goes to the Ghost Foundation, which funds the maintenance and further development of Ghost itself. So you’ll be supporting open source software *and* getting a great service **at the same time**! Talk about win/win. :trophy:
|
||||
|
||||
[Other options](http://support.ghost.org/deploying-ghost/) are also available if you prefer playing around with servers by yourself.
|
||||
[Other options](http://support.ghost.org/deploying-ghost/) are also available if you prefer playing around with servers by yourself, of course. The freedom of choice is in your hands.
|
||||
|
||||
|
||||
|
||||
|
||||
# Staying Up to Date
|
||||
|
@ -106,9 +91,9 @@ When a new version of Ghost comes out, you'll want to look over these [upgrade i
|
|||
|
||||
You can talk to other Ghost users and developers in our [public Slack team](https://ghost.org/slack/) (it's pretty awesome). We have a public meeting every Tuesday at 5:30pm UK time.
|
||||
|
||||
New releases are announced on the [dev blog](http://dev.ghost.org/tag/releases/). You can subscribe by email or follow [@TryGhost_Dev](https://twitter.com/tryghost_dev) on Twitter, if you prefer your updates bite-sized and facetious.
|
||||
New releases are announced on the [dev blog](http://dev.ghost.org/tag/releases/). You can subscribe by email or follow [@TryGhost_Dev](https://twitter.com/tryghost_dev) on Twitter, if you prefer your updates bite-sized and facetious. :saxophone::turtle:
|
||||
|
||||
:saxophone::turtle:
|
||||
|
||||
|
||||
|
||||
# Copyright & License
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
// # Ghost Configuration
|
||||
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).
|
||||
|
||||
// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/
|
||||
|
||||
var path = require('path'),
|
||||
config;
|
||||
|
||||
config = {
|
||||
// ### Production
|
||||
// When running Ghost in the wild, use the production environment.
|
||||
// Configure your URL and mail settings here
|
||||
production: {
|
||||
url: 'http://my-ghost-blog.com',
|
||||
mail: {},
|
||||
database: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: path.join(__dirname, '/content/data/ghost.db')
|
||||
},
|
||||
debug: false
|
||||
},
|
||||
|
||||
server: {
|
||||
host: '127.0.0.1',
|
||||
port: '2368'
|
||||
}
|
||||
},
|
||||
|
||||
// ### Development **(default)**
|
||||
development: {
|
||||
// The url to use when providing links to the site, E.g. in RSS and email.
|
||||
// Change this to your Ghost blog's published URL.
|
||||
url: 'http://localhost:2368',
|
||||
|
||||
// Example refferer policy
|
||||
// Visit https://www.w3.org/TR/referrer-policy/ for instructions
|
||||
// default 'origin-when-cross-origin',
|
||||
// referrerPolicy: 'origin-when-cross-origin',
|
||||
|
||||
// Example mail config
|
||||
// Visit http://support.ghost.org/mail for instructions
|
||||
// ```
|
||||
// mail: {
|
||||
// transport: 'SMTP',
|
||||
// options: {
|
||||
// service: 'Mailgun',
|
||||
// auth: {
|
||||
// user: '', // mailgun username
|
||||
// pass: '' // mailgun password
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// ```
|
||||
|
||||
// #### Database
|
||||
// Ghost supports sqlite3 (default), MySQL & PostgreSQL
|
||||
database: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: path.join(__dirname, '/content/data/ghost-dev.db')
|
||||
},
|
||||
debug: false
|
||||
},
|
||||
// #### Server
|
||||
// Can be host & port (default), or socket
|
||||
server: {
|
||||
// Host to be passed to node's `net.Server#listen()`
|
||||
host: '127.0.0.1',
|
||||
// Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
|
||||
port: '2368'
|
||||
},
|
||||
// #### Paths
|
||||
// Specify where your content directory lives
|
||||
paths: {
|
||||
contentPath: path.join(__dirname, '/content/')
|
||||
}
|
||||
},
|
||||
|
||||
// **Developers only need to edit below here**
|
||||
|
||||
// ### Testing
|
||||
// Used when developing Ghost to run tests and check the health of Ghost
|
||||
// Uses a different port number
|
||||
testing: {
|
||||
url: 'http://127.0.0.1:2369',
|
||||
database: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: path.join(__dirname, '/content/data/ghost-test.db')
|
||||
},
|
||||
pool: {
|
||||
afterCreate: function (conn, done) {
|
||||
conn.run('PRAGMA synchronous=OFF;' +
|
||||
'PRAGMA journal_mode=MEMORY;' +
|
||||
'PRAGMA locking_mode=EXCLUSIVE;' +
|
||||
'BEGIN EXCLUSIVE; COMMIT;', done);
|
||||
}
|
||||
},
|
||||
useNullAsDefault: true
|
||||
},
|
||||
server: {
|
||||
host: '127.0.0.1',
|
||||
port: '2369'
|
||||
},
|
||||
logging: false
|
||||
},
|
||||
|
||||
// ### Testing MySQL
|
||||
// Used by Travis - Automated testing run through GitHub
|
||||
'testing-mysql': {
|
||||
url: 'http://127.0.0.1:2369',
|
||||
database: {
|
||||
client: 'mysql',
|
||||
connection: {
|
||||
host : '127.0.0.1',
|
||||
user : 'root',
|
||||
password : '',
|
||||
database : 'ghost_testing',
|
||||
charset : 'utf8'
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '127.0.0.1',
|
||||
port: '2369'
|
||||
},
|
||||
logging: false
|
||||
},
|
||||
|
||||
// ### Testing pg
|
||||
// Used by Travis - Automated testing run through GitHub
|
||||
'testing-pg': {
|
||||
url: 'http://127.0.0.1:2369',
|
||||
database: {
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host : '127.0.0.1',
|
||||
user : 'postgres',
|
||||
password : '',
|
||||
database : 'ghost_testing',
|
||||
charset : 'utf8'
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '127.0.0.1',
|
||||
port: '2369'
|
||||
},
|
||||
logging: false
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -1 +1 @@
|
|||
Subproject commit e432af7b107c53f141f2fed7d3aec1dabbaadd58
|
||||
Subproject commit c4e548a85888708ab214a43fdc50e25a6d37beb9
|
|
@ -168,7 +168,7 @@ authentication = {
|
|||
}
|
||||
|
||||
function sendResetNotification(data) {
|
||||
var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url,
|
||||
var baseUrl = config.get('forceAdminSSL') ? (config.get('urlSSL') || config.get('url')) : config.get('url'),
|
||||
resetUrl = baseUrl.replace(/\/$/, '') +
|
||||
'/ghost/reset/' +
|
||||
globalUtils.encodeBase64URLsafe(data.resetToken) + '/';
|
||||
|
|
|
@ -20,10 +20,10 @@ function fetchAvailableTimezones() {
|
|||
|
||||
function getAboutConfig() {
|
||||
return {
|
||||
version: config.ghostVersion,
|
||||
version: config.get('ghostVersion'),
|
||||
environment: process.env.NODE_ENV,
|
||||
database: config.database.client,
|
||||
mail: _.isObject(config.mail) ? config.mail.transport : ''
|
||||
database: config.get('database').client,
|
||||
mail: _.isObject(config.get('mail')) ? config.get('mail').transport : ''
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,9 @@ function getBaseConfig() {
|
|||
useGravatar: {value: !config.isPrivacyDisabled('useGravatar'), type: 'bool'},
|
||||
publicAPI: labsFlag('publicAPI'),
|
||||
internalTags: labsFlag('internalTags'),
|
||||
blogUrl: {value: config.url.replace(/\/$/, ''), type: 'string'},
|
||||
blogTitle: {value: config.theme.title, type: 'string'},
|
||||
routeKeywords: {value: JSON.stringify(config.routeKeywords), type: 'json'}
|
||||
blogUrl: {value: config.get('url').replace(/\/$/, ''), type: 'string'},
|
||||
blogTitle: {value: config.get('theme').title, type: 'string'},
|
||||
routeKeywords: {value: JSON.stringify(config.get('routeKeywords')), type: 'json'}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
config = require('../config'),
|
||||
utils = require('../utils'),
|
||||
configuration = require('./configuration'),
|
||||
db = require('./db'),
|
||||
mail = require('./mail'),
|
||||
|
@ -99,7 +100,7 @@ cacheInvalidationHeader = function cacheInvalidationHeader(req, result) {
|
|||
if (hasStatusChanged || wasPublishedUpdated) {
|
||||
return INVALIDATE_ALL;
|
||||
} else {
|
||||
return config.urlFor({relativeUrl: '/' + config.routeKeywords.preview + '/' + post.uuid + '/'});
|
||||
return utils.url.urlFor({relativeUrl: '/' + config.get('routeKeywords').preview + '/' + post.uuid + '/'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ cacheInvalidationHeader = function cacheInvalidationHeader(req, result) {
|
|||
* @return {String} Resolves to header string
|
||||
*/
|
||||
locationHeader = function locationHeader(req, result) {
|
||||
var apiRoot = config.urlFor('api'),
|
||||
var apiRoot = utils.url.urlFor('api'),
|
||||
location,
|
||||
newObject;
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ var _ = require('lodash'),
|
|||
Promise = require('bluebird'),
|
||||
moment = require('moment'),
|
||||
config = require('../config'),
|
||||
pipeline = require(config.paths.corePath + '/server/utils/pipeline'),
|
||||
dataProvider = require(config.paths.corePath + '/server/models'),
|
||||
i18n = require(config.paths.corePath + '/server/i18n'),
|
||||
errors = require(config.paths.corePath + '/server/errors'),
|
||||
apiPosts = require(config.paths.corePath + '/server/api/posts'),
|
||||
pipeline = require(config.get('paths').corePath + '/server/utils/pipeline'),
|
||||
dataProvider = require(config.get('paths').corePath + '/server/models'),
|
||||
i18n = require(config.get('paths').corePath + '/server/i18n'),
|
||||
errors = require(config.get('paths').corePath + '/server/errors'),
|
||||
apiPosts = require(config.get('paths').corePath + '/server/api/posts'),
|
||||
utils = require('./utils');
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ exports.publishPost = function publishPost(object, options) {
|
|||
}
|
||||
|
||||
var post, publishedAtMoment,
|
||||
publishAPostBySchedulerToleranceInMinutes = config.times.publishAPostBySchedulerToleranceInMinutes;
|
||||
publishAPostBySchedulerToleranceInMinutes = config.get('times').publishAPostBySchedulerToleranceInMinutes;
|
||||
|
||||
// CASE: only the scheduler client is allowed to publish (hardcoded because of missing client permission system)
|
||||
if (!options.context || !options.context.client || options.context.client !== 'ghost-scheduler') {
|
||||
|
|
|
@ -50,20 +50,20 @@ updateConfigCache = function () {
|
|||
}
|
||||
}
|
||||
|
||||
config.set({
|
||||
theme: {
|
||||
title: (settingsCache.title && settingsCache.title.value) || '',
|
||||
description: (settingsCache.description && settingsCache.description.value) || '',
|
||||
logo: (settingsCache.logo && settingsCache.logo.value) || '',
|
||||
cover: (settingsCache.cover && settingsCache.cover.value) || '',
|
||||
navigation: (settingsCache.navigation && JSON.parse(settingsCache.navigation.value)) || [],
|
||||
postsPerPage: (settingsCache.postsPerPage && settingsCache.postsPerPage.value) || 5,
|
||||
permalinks: (settingsCache.permalinks && settingsCache.permalinks.value) || '/:slug/',
|
||||
twitter: (settingsCache.twitter && settingsCache.twitter.value) || '',
|
||||
facebook: (settingsCache.facebook && settingsCache.facebook.value) || '',
|
||||
timezone: (settingsCache.activeTimezone && settingsCache.activeTimezone.value) || config.theme.timezone
|
||||
},
|
||||
labs: labsValue
|
||||
config.set('theme:title', (settingsCache.title && settingsCache.title.value) || '');
|
||||
config.set('theme:description', (settingsCache.description && settingsCache.description.value) || '');
|
||||
config.set('theme:logo', (settingsCache.logo && settingsCache.logo.value) || '');
|
||||
config.set('theme:cover', (settingsCache.cover && settingsCache.cover.value) || '');
|
||||
config.set('theme:navigation', (settingsCache.navigation && JSON.parse(settingsCache.navigation.value)) || []);
|
||||
config.set('theme:postsPerPage', (settingsCache.postsPerPage && settingsCache.postsPerPage.value) || config.get('theme').postsPerPage);
|
||||
config.set('theme:permalinks', (settingsCache.permalinks && settingsCache.permalinks.value) || config.get('theme').permalinks);
|
||||
config.set('theme:twitter', (settingsCache.twitter && settingsCache.twitter.value) || '');
|
||||
config.set('theme:facebook', (settingsCache.facebook && settingsCache.facebook.value) || '');
|
||||
config.set('theme:timezone', (settingsCache.activeTimezone && settingsCache.activeTimezone.value) || config.get('theme').timezone);
|
||||
config.set('theme:url', config.get('url') ? config.get('url').replace(/\/$/, '') : '');
|
||||
|
||||
_.each(labsValue, function (value, key) {
|
||||
config.set('labs:' + key, value);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -177,8 +177,8 @@ readSettingsResult = function (settingsModels) {
|
|||
|
||||
return memo;
|
||||
}, {}),
|
||||
themes = config.paths.availableThemes,
|
||||
apps = config.paths.availableApps,
|
||||
themes = config.get('paths').availableThemes,
|
||||
apps = config.get('paths').availableApps,
|
||||
res;
|
||||
|
||||
if (settings.activeTheme && themes) {
|
||||
|
|
|
@ -9,7 +9,8 @@ var Promise = require('bluebird'),
|
|||
events = require('../events'),
|
||||
storage = require('../storage'),
|
||||
settings = require('./settings'),
|
||||
utils = require('./utils'),
|
||||
apiUtils = require('./utils'),
|
||||
utils = require('./../utils'),
|
||||
i18n = require('../i18n'),
|
||||
themes;
|
||||
|
||||
|
@ -19,6 +20,13 @@ var Promise = require('bluebird'),
|
|||
* **See:** [API Methods](index.js.html#api%20methods)
|
||||
*/
|
||||
themes = {
|
||||
loadThemes: function () {
|
||||
return utils.readThemes(config.getContentPath('themes'))
|
||||
.then(function (result) {
|
||||
config.set('paths:availableThemes', result);
|
||||
});
|
||||
},
|
||||
|
||||
upload: function upload(options) {
|
||||
options = options || {};
|
||||
|
||||
|
@ -37,7 +45,7 @@ themes = {
|
|||
throw new errors.ValidationError(i18n.t('errors.api.themes.overrideCasper'));
|
||||
}
|
||||
|
||||
return utils.handlePermissions('themes', 'add')(options)
|
||||
return apiUtils.handlePermissions('themes', 'add')(options)
|
||||
.then(function () {
|
||||
return gscan.checkZip(zip, {keepExtractedDir: true});
|
||||
})
|
||||
|
@ -55,12 +63,12 @@ themes = {
|
|||
);
|
||||
})
|
||||
.then(function () {
|
||||
return storageAdapter.exists(config.paths.themePath + '/' + zip.shortName);
|
||||
return storageAdapter.exists(config.getContentPath('themes') + '/' + zip.shortName);
|
||||
})
|
||||
.then(function (themeExists) {
|
||||
// delete existing theme
|
||||
if (themeExists) {
|
||||
return storageAdapter.delete(zip.shortName, config.paths.themePath);
|
||||
return storageAdapter.delete(zip.shortName, config.getContentPath('themes'));
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
|
@ -69,13 +77,13 @@ themes = {
|
|||
return storageAdapter.save({
|
||||
name: zip.shortName,
|
||||
path: theme.path
|
||||
}, config.paths.themePath);
|
||||
}, config.getContentPath('themes'));
|
||||
})
|
||||
.then(function () {
|
||||
// force reload of availableThemes
|
||||
// right now the logic is in the ConfigManager
|
||||
// if we create a theme collection, we don't have to read them from disk
|
||||
return config.loadThemes();
|
||||
return themes.loadThemes();
|
||||
})
|
||||
.then(function () {
|
||||
// the settings endpoint is used to fetch the availableThemes
|
||||
|
@ -111,14 +119,14 @@ themes = {
|
|||
|
||||
download: function download(options) {
|
||||
var themeName = options.name,
|
||||
theme = config.paths.availableThemes[themeName],
|
||||
theme = config.get('paths').availableThemes[themeName],
|
||||
storageAdapter = storage.getStorage('themes');
|
||||
|
||||
if (!theme) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.themes.invalidRequest')));
|
||||
}
|
||||
|
||||
return utils.handlePermissions('themes', 'read')(options)
|
||||
return apiUtils.handlePermissions('themes', 'read')(options)
|
||||
.then(function () {
|
||||
events.emit('theme.downloaded', themeName);
|
||||
return storageAdapter.serve({isTheme: true, name: themeName});
|
||||
|
@ -134,23 +142,23 @@ themes = {
|
|||
theme,
|
||||
storageAdapter = storage.getStorage('themes');
|
||||
|
||||
return utils.handlePermissions('themes', 'destroy')(options)
|
||||
return apiUtils.handlePermissions('themes', 'destroy')(options)
|
||||
.then(function () {
|
||||
if (name === 'casper') {
|
||||
throw new errors.ValidationError(i18n.t('errors.api.themes.destroyCasper'));
|
||||
}
|
||||
|
||||
theme = config.paths.availableThemes[name];
|
||||
theme = config.get('paths').availableThemes[name];
|
||||
|
||||
if (!theme) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.themes.themeDoesNotExist'));
|
||||
}
|
||||
|
||||
events.emit('theme.deleted', name);
|
||||
return storageAdapter.delete(name, config.paths.themePath);
|
||||
return storageAdapter.delete(name, config.getContentPath('themes'));
|
||||
})
|
||||
.then(function () {
|
||||
return config.loadThemes();
|
||||
return themes.loadThemes();
|
||||
})
|
||||
.then(function () {
|
||||
return settings.updateSettingsCache();
|
||||
|
|
|
@ -41,7 +41,7 @@ sendInviteEmail = function sendInviteEmail(user) {
|
|||
|
||||
return dataProvider.User.generateResetToken(user.email, expires, dbHash);
|
||||
}).then(function (resetToken) {
|
||||
var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url;
|
||||
var baseUrl = config.get('forceAdminSSL') ? (config.get('urlSSL') || config.get('url')) : config.get('url');
|
||||
|
||||
emailData.resetLink = baseUrl.replace(/\/$/, '') + '/ghost/signup/' + globalUtils.encodeBase64URLsafe(resetToken) + '/';
|
||||
|
||||
|
|
|
@ -10,6 +10,6 @@ module.exports = {
|
|||
},
|
||||
|
||||
setupRoutes: function setupRoutes(blogRouter) {
|
||||
blogRouter.use('*/' + config.routeKeywords.amp + '/', router);
|
||||
blogRouter.use('*/' + config.get('routeKeywords').amp + '/', router);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@ function getAmperizeHTML(html, post) {
|
|||
}
|
||||
|
||||
// make relative URLs abolute
|
||||
html = makeAbsoluteUrl(html, config.url, post.url).html();
|
||||
html = makeAbsoluteUrl(html, config.get('url'), post.url).html();
|
||||
|
||||
if (!amperizeCache[post.id] || moment(new Date(amperizeCache[post.id].updated_at)).diff(new Date(post.updated_at)) < 0) {
|
||||
return new Promise(function (resolve) {
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('AMP Controller', function () {
|
|||
body: {}
|
||||
};
|
||||
|
||||
defaultPath = path.join(configUtils.config.paths.appRoot, '/core/server/apps/amp/lib/views/amp.hbs');
|
||||
defaultPath = path.join(configUtils.config.get('paths').appRoot, '/core/server/apps/amp/lib/views/amp.hbs');
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
|
|
|
@ -21,13 +21,13 @@ function getInstalledApps() {
|
|||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
return installed.concat(config.internalApps);
|
||||
return installed.concat(config.get('internalApps'));
|
||||
});
|
||||
}
|
||||
|
||||
function saveInstalledApps(installedApps) {
|
||||
return getInstalledApps().then(function (currentInstalledApps) {
|
||||
var updatedAppsInstalled = _.difference(_.uniq(installedApps.concat(currentInstalledApps)), config.internalApps);
|
||||
var updatedAppsInstalled = _.difference(_.uniq(installedApps.concat(currentInstalledApps)), config.get('internalApps'));
|
||||
|
||||
return api.settings.edit({settings: [{key: 'installedApps', value: updatedAppsInstalled}]}, {context: {internal: true}});
|
||||
});
|
||||
|
@ -44,7 +44,7 @@ module.exports = {
|
|||
|
||||
appsToLoad = JSON.parse(aApps.value) || [];
|
||||
|
||||
appsToLoad = appsToLoad.concat(config.internalApps);
|
||||
appsToLoad = appsToLoad.concat(config.get('internalApps'));
|
||||
});
|
||||
} catch (e) {
|
||||
errors.logError(
|
||||
|
|
|
@ -11,16 +11,16 @@ var path = require('path'),
|
|||
loader;
|
||||
|
||||
function isInternalApp(name) {
|
||||
return _.includes(config.internalApps, name);
|
||||
return _.includes(config.get('internalApps'), name);
|
||||
}
|
||||
|
||||
// Get the full path to an app by name
|
||||
function getAppAbsolutePath(name) {
|
||||
if (isInternalApp(name)) {
|
||||
return path.join(config.paths.internalAppPath, name);
|
||||
return path.join(config.get('paths').internalAppPath, name);
|
||||
}
|
||||
|
||||
return path.join(config.paths.appPath, name);
|
||||
return path.join(config.getContentPath('apps'), name);
|
||||
}
|
||||
|
||||
// Get a relative path to the given apps root, defaults
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
i18n = require('../../i18n'),
|
||||
middleware = require('./lib/middleware'),
|
||||
|
@ -6,10 +7,10 @@ var config = require('../../config'),
|
|||
|
||||
module.exports = {
|
||||
activate: function activate() {
|
||||
if (config.paths.subdir) {
|
||||
var paths = config.paths.subdir.split('/');
|
||||
if (utils.url.getSubdir()) {
|
||||
var paths = utils.url.getSubdir().split('/');
|
||||
|
||||
if (paths.pop() === config.routeKeywords.private) {
|
||||
if (paths.pop() === config.get('routeKeywords').private) {
|
||||
errors.logErrorAndExit(
|
||||
new Error(i18n.t('errors.config.urlCannotContainPrivateSubdir.error')),
|
||||
i18n.t('errors.config.urlCannotContainPrivateSubdir.description'),
|
||||
|
@ -25,6 +26,6 @@ module.exports = {
|
|||
},
|
||||
|
||||
setupRoutes: function setupRoutes(blogRouter) {
|
||||
blogRouter.use('/' + config.routeKeywords.private + '/', router);
|
||||
blogRouter.use('/' + config.get('routeKeywords').private + '/', router);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ var _ = require('lodash'),
|
|||
session = require('cookie-session'),
|
||||
utils = require('../../../utils'),
|
||||
i18n = require('../../../i18n'),
|
||||
privateRoute = '/' + config.routeKeywords.private + '/',
|
||||
privateRoute = '/' + config.get('routeKeywords').private + '/',
|
||||
protectedSecurity = [],
|
||||
privateBlogging;
|
||||
|
||||
|
@ -82,7 +82,7 @@ privateBlogging = {
|
|||
if (isVerified) {
|
||||
return next();
|
||||
} else {
|
||||
url = config.urlFor({relativeUrl: privateRoute});
|
||||
url = utils.url.urlFor({relativeUrl: privateRoute});
|
||||
url += req.url === '/' ? '' : '?r=' + encodeURIComponent(req.url);
|
||||
return res.redirect(url);
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ privateBlogging = {
|
|||
// This is here so a call to /private/ after a session is verified will redirect to home;
|
||||
isPrivateSessionAuth: function isPrivateSessionAuth(req, res, next) {
|
||||
if (!res.isPrivateBlog) {
|
||||
return res.redirect(config.urlFor('home', true));
|
||||
return res.redirect(utils.url.urlFor('home', true));
|
||||
}
|
||||
|
||||
var hash = req.session.token || '',
|
||||
|
@ -101,7 +101,7 @@ privateBlogging = {
|
|||
return verifySessionHash(salt, hash).then(function then(isVerified) {
|
||||
if (isVerified) {
|
||||
// redirect to home if user is already authenticated
|
||||
return res.redirect(config.urlFor('home', true));
|
||||
return res.redirect(utils.url.urlFor('home', true));
|
||||
} else {
|
||||
return next();
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ privateBlogging = {
|
|||
req.session.token = hasher.digest('hex');
|
||||
req.session.salt = salt;
|
||||
|
||||
return res.redirect(config.urlFor({relativeUrl: decodeURIComponent(forward)}));
|
||||
return res.redirect(utils.url.urlFor({relativeUrl: decodeURIComponent(forward)}));
|
||||
} else {
|
||||
res.error = {
|
||||
message: i18n.t('errors.middleware.privateblogging.wrongPassword')
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('Private Controller', function () {
|
|||
params: {}
|
||||
};
|
||||
|
||||
defaultPath = path.join(configUtils.config.paths.appRoot, '/core/server/apps/private-blogging/lib/views/private.hbs');
|
||||
defaultPath = path.join(configUtils.config.get('paths').appRoot, '/core/server/apps/private-blogging/lib/views/private.hbs');
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
|
|
|
@ -91,7 +91,7 @@ AppSandbox.prototype.loadModule = function loadModuleSandboxed(modulePath) {
|
|||
};
|
||||
|
||||
AppSandbox.defaults = {
|
||||
blacklist: ['knex', 'fs', 'http', 'sqlite3', 'pg', 'mysql', 'ghost']
|
||||
blacklist: ['knex', 'fs', 'http', 'sqlite3', 'mysql', 'ghost']
|
||||
};
|
||||
|
||||
module.exports = AppSandbox;
|
||||
|
|
|
@ -10,6 +10,7 @@ var _ = require('lodash'),
|
|||
labs = require('../../utils/labs'),
|
||||
template = require('../../helpers/template'),
|
||||
utils = require('../../helpers/utils'),
|
||||
globalUtils = require('../../utils'),
|
||||
|
||||
params = ['error', 'success', 'email'],
|
||||
|
||||
|
@ -38,7 +39,7 @@ function makeHidden(name, extras) {
|
|||
function subscribeFormHelper(options) {
|
||||
var root = options.data.root,
|
||||
data = _.merge({}, options.hash, _.pick(root, params), {
|
||||
action: path.join('/', config.paths.subdir, config.routeKeywords.subscribe, '/'),
|
||||
action: path.join('/', globalUtils.url.getSubdir(), config.get('routeKeywords').subscribe, '/'),
|
||||
script: new hbs.handlebars.SafeString(subscribeScript),
|
||||
hidden: new hbs.handlebars.SafeString(
|
||||
makeHidden('confirm') +
|
||||
|
@ -70,7 +71,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
setupRoutes: function setupRoutes(blogRouter) {
|
||||
blogRouter.use('/' + config.routeKeywords.subscribe + '/', function labsEnabledRouter(req, res, next) {
|
||||
blogRouter.use('/' + config.get('routeKeywords').subscribe + '/', function labsEnabledRouter(req, res, next) {
|
||||
if (labs.isSet('subscribers') === true) {
|
||||
return router.apply(this, arguments);
|
||||
}
|
||||
|
|
28
core/server/config/defaults.json
Normal file
28
core/server/config/defaults.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"url": "http://localhost:2368",
|
||||
"urlSSL": false,
|
||||
"forceAdminSSL": false,
|
||||
"server": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 2368
|
||||
},
|
||||
"database": {
|
||||
"client": "sqlite3",
|
||||
"debug": false,
|
||||
"connection": {
|
||||
"filename": "content/data/ghost.db"
|
||||
}
|
||||
},
|
||||
"privacy": false,
|
||||
"paths": {
|
||||
"contentPath": "content/"
|
||||
},
|
||||
"storage": {
|
||||
"active": {
|
||||
"images": "local-file-store"
|
||||
}
|
||||
},
|
||||
"scheduling": {
|
||||
"active": "SchedulingDefault"
|
||||
}
|
||||
}
|
12
core/server/config/env/config.development.json
vendored
Normal file
12
core/server/config/env/config.development.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"url": "http://localhost:2368",
|
||||
"database": {
|
||||
"connection": {
|
||||
"filename": "content/data/ghost-dev.db"
|
||||
},
|
||||
"debug": false
|
||||
},
|
||||
"paths": {
|
||||
"contentPath": "content/"
|
||||
}
|
||||
}
|
12
core/server/config/env/config.production.json
vendored
Normal file
12
core/server/config/env/config.production.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"url": "http://my-ghost-blog.com",
|
||||
"database": {
|
||||
"connection": {
|
||||
"filename": "content/data/ghost.db"
|
||||
},
|
||||
"debug": false
|
||||
},
|
||||
"paths": {
|
||||
"contentPath": "content/"
|
||||
}
|
||||
}
|
16
core/server/config/env/config.testing-mysql.json
vendored
Normal file
16
core/server/config/env/config.testing-mysql.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"url": "http://127.0.0.1:2369",
|
||||
"server": {
|
||||
"port": 2369
|
||||
},
|
||||
"database": {
|
||||
"client": "mysql",
|
||||
"connection": {
|
||||
"host" : "127.0.0.1",
|
||||
"user" : "root",
|
||||
"password" : "",
|
||||
"database" : "ghost_testing"
|
||||
}
|
||||
},
|
||||
"logging": false
|
||||
}
|
13
core/server/config/env/config.testing.json
vendored
Normal file
13
core/server/config/env/config.testing.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"url": "http://127.0.0.1:2369",
|
||||
"database": {
|
||||
"connection": {
|
||||
"filename": "content/data/ghost-test.db"
|
||||
},
|
||||
"useNullAsDefault": true
|
||||
},
|
||||
"server": {
|
||||
"port": 2369
|
||||
},
|
||||
"logging": false
|
||||
}
|
|
@ -1,518 +1,40 @@
|
|||
// # Config
|
||||
// General entry point for all configuration data
|
||||
var path = require('path'),
|
||||
Promise = require('bluebird'),
|
||||
chalk = require('chalk'),
|
||||
crypto = require('crypto'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
_ = require('lodash'),
|
||||
var nconf = require('nconf'),
|
||||
path = require('path'),
|
||||
localUtils = require('./utils'),
|
||||
packageInfo = require('../../../package.json'),
|
||||
env = process.env.NODE_ENV || 'development';
|
||||
|
||||
validator = require('validator'),
|
||||
readDirectory = require('../utils/read-directory'),
|
||||
readThemes = require('../utils/read-themes'),
|
||||
errors = require('../errors'),
|
||||
configUrl = require('./url'),
|
||||
packageInfo = require('../../../package.json'),
|
||||
i18n = require('../i18n'),
|
||||
appRoot = path.resolve(__dirname, '../../../'),
|
||||
corePath = path.resolve(appRoot, 'core/'),
|
||||
testingEnvs = ['testing', 'testing-mysql', 'testing-pg'],
|
||||
defaultConfig = {};
|
||||
|
||||
function ConfigManager(config) {
|
||||
/**
|
||||
* Our internal true representation of our current config object.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._config = {};
|
||||
|
||||
// Allow other modules to be externally accessible.
|
||||
this.urlJoin = configUrl.urlJoin;
|
||||
this.urlFor = configUrl.urlFor;
|
||||
this.urlPathForPost = configUrl.urlPathForPost;
|
||||
this.apiUrl = configUrl.apiUrl;
|
||||
this.getBaseUrl = configUrl.getBaseUrl;
|
||||
|
||||
// If we're given an initial config object then we can set it.
|
||||
if (config && _.isObject(config)) {
|
||||
this.set(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Are we using sockets? Custom socket or the default?
|
||||
ConfigManager.prototype.getSocket = function () {
|
||||
var socketConfig,
|
||||
values = {
|
||||
path: path.join(this._config.paths.contentPath, process.env.NODE_ENV + '.socket'),
|
||||
permissions: '660'
|
||||
};
|
||||
|
||||
if (this._config.server.hasOwnProperty('socket')) {
|
||||
socketConfig = this._config.server.socket;
|
||||
|
||||
if (_.isString(socketConfig)) {
|
||||
values.path = socketConfig;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
if (_.isObject(socketConfig)) {
|
||||
values.path = socketConfig.path || values.path;
|
||||
values.permissions = socketConfig.permissions || values.permissions;
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
ConfigManager.prototype.init = function (rawConfig) {
|
||||
var self = this;
|
||||
|
||||
// Cache the config.js object's environment
|
||||
// object so we can later refer to it.
|
||||
// Note: this is not the entirety of config.js,
|
||||
// just the object appropriate for this NODE_ENV
|
||||
self.set(rawConfig);
|
||||
|
||||
return self.loadThemes()
|
||||
.then(function () {
|
||||
return self.loadApps();
|
||||
})
|
||||
.then(function () {
|
||||
return self._config;
|
||||
});
|
||||
};
|
||||
|
||||
ConfigManager.prototype.loadThemes = function () {
|
||||
var self = this;
|
||||
|
||||
return readThemes(self._config.paths.themePath)
|
||||
.then(function (result) {
|
||||
self._config.paths.availableThemes = result;
|
||||
});
|
||||
};
|
||||
|
||||
ConfigManager.prototype.loadApps = function () {
|
||||
var self = this;
|
||||
|
||||
return readDirectory(self._config.paths.appPath)
|
||||
.then(function (result) {
|
||||
self._config.paths.availableApps = result;
|
||||
});
|
||||
};
|
||||
nconf.set('NODE_ENV', env);
|
||||
|
||||
/**
|
||||
* Allows you to set the config object.
|
||||
* @param {Object} config Only accepts an object at the moment.
|
||||
* command line arguments
|
||||
*/
|
||||
ConfigManager.prototype.set = function (config) {
|
||||
var localPath = '',
|
||||
defaultStorageAdapter = 'local-file-store',
|
||||
defaultSchedulingAdapter = 'SchedulingDefault',
|
||||
activeStorageAdapter,
|
||||
activeSchedulingAdapter,
|
||||
contentPath,
|
||||
schedulingPath,
|
||||
subdir,
|
||||
assetHash;
|
||||
|
||||
// Merge passed in config object onto our existing config object.
|
||||
// We're using merge here as it doesn't assign `undefined` properties
|
||||
// onto our cached config object. This allows us to only update our
|
||||
// local copy with properties that have been explicitly set.
|
||||
_.merge(this._config, config);
|
||||
|
||||
// Special case for the database config, which should be overridden not merged
|
||||
|
||||
if (config && config.database) {
|
||||
this._config.database = config.database;
|
||||
}
|
||||
|
||||
// Special case for the them.navigation JSON object, which should be overridden not merged
|
||||
if (config && config.theme && config.theme.navigation) {
|
||||
this._config.theme.navigation = config.theme.navigation;
|
||||
}
|
||||
|
||||
// Special case for theme.timezone, which should be overridden not merged
|
||||
if (config && config.theme && config.theme.timezone) {
|
||||
this._config.theme.timezone = config.theme.timezone;
|
||||
} else {
|
||||
// until we have set the timezone from settings, we use the default
|
||||
this._config.theme = this._config.theme ? this._config.theme : {};
|
||||
this._config.theme.timezone = 'Etc/UTC';
|
||||
}
|
||||
|
||||
// Protect against accessing a non-existant object.
|
||||
// This ensures there's always at least a paths object
|
||||
// because it's referenced in multiple places.
|
||||
this._config.paths = this._config.paths || {};
|
||||
|
||||
// Parse local path location
|
||||
if (this._config.url) {
|
||||
localPath = url.parse(this._config.url).path;
|
||||
// Remove trailing slash
|
||||
if (localPath !== '/') {
|
||||
localPath = localPath.replace(/\/$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
subdir = localPath === '/' ? '' : localPath;
|
||||
|
||||
if (!_.isEmpty(subdir)) {
|
||||
this._config.slugs.protected.push(subdir.split('/').pop());
|
||||
}
|
||||
|
||||
// Allow contentPath to be over-written by passed in config object
|
||||
// Otherwise default to default content path location
|
||||
contentPath = this._config.paths.contentPath || path.resolve(appRoot, 'content');
|
||||
|
||||
assetHash = this._config.assetHash ||
|
||||
(crypto.createHash('md5').update(packageInfo.version + Date.now()).digest('hex')).substring(0, 10);
|
||||
|
||||
// read storage adapter from config file or attach default adapter
|
||||
this._config.storage = this._config.storage || {};
|
||||
activeStorageAdapter = this._config.storage.active || defaultStorageAdapter;
|
||||
|
||||
// read scheduling adapter(s) from config file or attach default adapter
|
||||
this._config.scheduling = this._config.scheduling || {};
|
||||
activeSchedulingAdapter = this._config.scheduling.active || defaultSchedulingAdapter;
|
||||
|
||||
// storage.active can be an object like {images: 'my-custom-image-storage-adapter', themes: 'local-file-storage'}
|
||||
// we ensure that passing a string to storage.active still works, but internal it's always an object
|
||||
if (_.isString(activeStorageAdapter)) {
|
||||
this._config.storage = _.merge(this._config.storage, {
|
||||
active: {
|
||||
images: activeStorageAdapter,
|
||||
themes: defaultStorageAdapter
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// ensure there is a default image storage adapter
|
||||
if (!this._config.storage.active.images) {
|
||||
this._config.storage.active.images = defaultStorageAdapter;
|
||||
}
|
||||
|
||||
// ensure there is a default theme storage adapter
|
||||
// @TODO: right now we only support theme uploads to local file storage
|
||||
// @TODO: we need to change reading themes from disk on bootstrap (see loadThemes)
|
||||
this._config.storage.active.themes = defaultStorageAdapter;
|
||||
}
|
||||
|
||||
if (activeSchedulingAdapter === defaultSchedulingAdapter) {
|
||||
schedulingPath = path.join(corePath, '/server/scheduling/');
|
||||
} else {
|
||||
schedulingPath = path.join(contentPath, '/scheduling/');
|
||||
}
|
||||
|
||||
_.merge(this._config, {
|
||||
ghostVersion: packageInfo.version,
|
||||
paths: {
|
||||
appRoot: appRoot,
|
||||
subdir: subdir,
|
||||
config: this._config.paths.config || path.join(appRoot, 'config.js'),
|
||||
configExample: path.join(appRoot, 'config.example.js'),
|
||||
corePath: corePath,
|
||||
|
||||
storagePath: {
|
||||
default: path.join(corePath, '/server/storage/'),
|
||||
custom: path.join(contentPath, 'storage/')
|
||||
},
|
||||
|
||||
contentPath: contentPath,
|
||||
themePath: path.resolve(contentPath, 'themes'),
|
||||
appPath: path.resolve(contentPath, 'apps'),
|
||||
imagesPath: path.resolve(contentPath, 'images'),
|
||||
internalAppPath: path.join(corePath, '/server/apps/'),
|
||||
imagesRelPath: 'content/images',
|
||||
|
||||
adminViews: path.join(corePath, '/server/views/'),
|
||||
helperTemplates: path.join(corePath, '/server/helpers/tpl/'),
|
||||
|
||||
availableThemes: this._config.paths.availableThemes || {},
|
||||
availableApps: this._config.paths.availableApps || {},
|
||||
clientAssets: path.join(corePath, '/built/assets/')
|
||||
},
|
||||
maintenance: {},
|
||||
scheduling: {
|
||||
active: activeSchedulingAdapter,
|
||||
path: schedulingPath
|
||||
},
|
||||
theme: {
|
||||
// normalise the URL by removing any trailing slash
|
||||
url: this._config.url ? this._config.url.replace(/\/$/, '') : ''
|
||||
},
|
||||
routeKeywords: {
|
||||
tag: 'tag',
|
||||
author: 'author',
|
||||
page: 'page',
|
||||
preview: 'p',
|
||||
private: 'private',
|
||||
subscribe: 'subscribe',
|
||||
amp: 'amp'
|
||||
},
|
||||
internalApps: ['private-blogging', 'subscribers', 'amp'],
|
||||
slugs: {
|
||||
// Used by generateSlug to generate slugs for posts, tags, users, ..
|
||||
// reserved slugs are reserved but can be extended/removed by apps
|
||||
// protected slugs cannot be changed or removed
|
||||
reserved: ['admin', 'app', 'apps', 'archive', 'archives', 'categories',
|
||||
'category', 'dashboard', 'feed', 'ghost-admin', 'login', 'logout',
|
||||
'page', 'pages', 'post', 'posts', 'public', 'register', 'setup',
|
||||
'signin', 'signout', 'signup', 'user', 'users', 'wp-admin', 'wp-login'],
|
||||
protected: ['ghost', 'rss', 'amp']
|
||||
},
|
||||
// used in middleware/validation/upload.js
|
||||
// if we finish the data/importer logic, each type selects an importer
|
||||
uploads: {
|
||||
subscribers: {
|
||||
extensions: ['.csv'],
|
||||
contentTypes: ['text/csv', 'application/csv', 'application/octet-stream']
|
||||
},
|
||||
images: {
|
||||
extensions: ['.jpg', '.jpeg', '.gif', '.png', '.svg', '.svgz'],
|
||||
contentTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']
|
||||
},
|
||||
db: {
|
||||
extensions: ['.json', '.zip'],
|
||||
contentTypes: ['application/octet-stream', 'application/json', 'application/zip', 'application/x-zip-compressed']
|
||||
},
|
||||
themes: {
|
||||
extensions: ['.zip'],
|
||||
contentTypes: ['application/zip', 'application/x-zip-compressed', 'application/octet-stream']
|
||||
}
|
||||
},
|
||||
deprecatedItems: ['updateCheck', 'mail.fromaddress'],
|
||||
// create a hash for cache busting assets
|
||||
assetHash: assetHash,
|
||||
preloadHeaders: this._config.preloadHeaders || false,
|
||||
times: {
|
||||
cannotScheduleAPostBeforeInMinutes: 2,
|
||||
publishAPostBySchedulerToleranceInMinutes: 2
|
||||
}
|
||||
});
|
||||
|
||||
// Also pass config object to
|
||||
// configUrl object to maintain
|
||||
// clean dependency tree
|
||||
configUrl.setConfig(this._config);
|
||||
|
||||
// For now we're going to copy the current state of this._config
|
||||
// so it's directly accessible on the instance.
|
||||
// @TODO: perhaps not do this? Put access of the config object behind
|
||||
// a function?
|
||||
_.extend(this, this._config);
|
||||
};
|
||||
nconf.argv();
|
||||
|
||||
/**
|
||||
* Allows you to read the config object.
|
||||
* @return {Object} The config object.
|
||||
* env arguments
|
||||
*/
|
||||
ConfigManager.prototype.get = function () {
|
||||
return this._config;
|
||||
};
|
||||
|
||||
ConfigManager.prototype.load = function (configFilePath) {
|
||||
var self = this;
|
||||
|
||||
self._config.paths.config = process.env.GHOST_CONFIG || configFilePath || self._config.paths.config;
|
||||
|
||||
/* Check for config file and copy from config.example.js
|
||||
if one doesn't exist. After that, start the server. */
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.stat(self._config.paths.config, function (err) {
|
||||
var exists = (err) ? false : true,
|
||||
pendingConfig;
|
||||
|
||||
if (!exists) {
|
||||
pendingConfig = self.writeFile();
|
||||
}
|
||||
|
||||
Promise.resolve(pendingConfig).then(function () {
|
||||
return self.validate();
|
||||
}).then(function (rawConfig) {
|
||||
return self.init(rawConfig);
|
||||
}).then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* Check for config file and copy from config.example.js
|
||||
if one doesn't exist. After that, start the server. */
|
||||
ConfigManager.prototype.writeFile = function () {
|
||||
var configPath = this._config.paths.config,
|
||||
configExamplePath = this._config.paths.configExample;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.stat(configExamplePath, function checkTemplate(err) {
|
||||
var templateExists = (err) ? false : true,
|
||||
read,
|
||||
write,
|
||||
error;
|
||||
|
||||
if (!templateExists) {
|
||||
error = new Error(i18n.t('errors.config.couldNotLocateConfigFile.error'));
|
||||
error.context = appRoot;
|
||||
error.help = i18n.t('errors.config.couldNotLocateConfigFile.help');
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// Copy config.example.js => config.js
|
||||
read = fs.createReadStream(configExamplePath);
|
||||
read.on('error', function (err) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.couldNotOpenForReading.error', {file: 'config.example.js'})),
|
||||
appRoot,
|
||||
i18n.t('errors.config.couldNotOpenForReading.help'));
|
||||
|
||||
reject(err);
|
||||
});
|
||||
|
||||
write = fs.createWriteStream(configPath);
|
||||
write.on('error', function (err) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.couldNotOpenForWriting.error', {file: 'config.js'})),
|
||||
appRoot,
|
||||
i18n.t('errors.config.couldNotOpenForWriting.help'));
|
||||
|
||||
reject(err);
|
||||
});
|
||||
|
||||
write.on('finish', resolve);
|
||||
|
||||
read.pipe(write);
|
||||
});
|
||||
});
|
||||
};
|
||||
nconf.env();
|
||||
|
||||
/**
|
||||
* Read config.js file from file system using node's require
|
||||
* @param {String} envVal Which environment we're in.
|
||||
* @return {Object} The config object.
|
||||
* load config files
|
||||
*/
|
||||
ConfigManager.prototype.readFile = function (envVal) {
|
||||
return require(this._config.paths.config)[envVal];
|
||||
};
|
||||
nconf.file('1', __dirname + '/overrides.json');
|
||||
nconf.file('2', path.join(process.cwd(), 'config.' + env + '.json'));
|
||||
nconf.file('3', __dirname + '/env/config.' + env + '.json');
|
||||
nconf.file('4', __dirname + '/defaults.json');
|
||||
|
||||
/**
|
||||
* Validates the config object has everything we want and in the form we want.
|
||||
* @return {Promise.<Object>} Returns a promise that resolves to the config object.
|
||||
* transform all relative paths to absolute paths
|
||||
*/
|
||||
ConfigManager.prototype.validate = function () {
|
||||
var envVal = process.env.NODE_ENV || undefined,
|
||||
hasHostAndPort,
|
||||
hasSocket,
|
||||
config,
|
||||
parsedUrl;
|
||||
|
||||
try {
|
||||
config = this.readFile(envVal);
|
||||
}
|
||||
catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
// Check that our url is valid
|
||||
if (!validator.isURL(config.url, {protocols: ['http', 'https'], require_protocol: true})) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.invalidUrlInConfig.description'),
|
||||
config.url,
|
||||
i18n.t('errors.config.invalidUrlInConfig.help')));
|
||||
|
||||
return Promise.reject(new Error(i18n.t('errors.config.invalidUrlInConfig.error')));
|
||||
}
|
||||
|
||||
parsedUrl = url.parse(config.url || 'invalid', false, true);
|
||||
|
||||
if (/\/ghost(\/|$)/.test(parsedUrl.pathname)) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.urlCannotContainGhostSubdir.description'),
|
||||
config.url,
|
||||
i18n.t('errors.config.urlCannotContainGhostSubdir.help')));
|
||||
|
||||
return Promise.reject(new Error(i18n.t('errors.config.urlCannotContainGhostSubdir.error')));
|
||||
}
|
||||
|
||||
// Check that we have database values
|
||||
if (!config.database || !config.database.client) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.dbConfigInvalid.description')),
|
||||
JSON.stringify(config.database),
|
||||
i18n.t('errors.config.dbConfigInvalid.help'));
|
||||
|
||||
return Promise.reject(new Error(i18n.t('errors.config.dbConfigInvalid.error')));
|
||||
}
|
||||
|
||||
hasHostAndPort = config.server && !!config.server.host && !!config.server.port;
|
||||
hasSocket = config.server && !!config.server.socket;
|
||||
|
||||
// Check for valid server host and port values
|
||||
if (!config.server || !(hasHostAndPort || hasSocket)) {
|
||||
errors.logError(
|
||||
new Error(i18n.t('errors.config.invalidServerValues.description')),
|
||||
JSON.stringify(config.server),
|
||||
i18n.t('errors.config.invalidServerValues.help'));
|
||||
|
||||
return Promise.reject(new Error(i18n.t('errors.config.invalidServerValues.error')));
|
||||
}
|
||||
|
||||
return Promise.resolve(config);
|
||||
};
|
||||
localUtils.makePathsAbsolute.bind(nconf)();
|
||||
|
||||
/**
|
||||
* Helper method for checking the state of a particular privacy flag
|
||||
* @param {String} privacyFlag The flag to check
|
||||
* @returns {boolean}
|
||||
* values we have to set manual
|
||||
* @TODO: ghost-cli?
|
||||
*/
|
||||
ConfigManager.prototype.isPrivacyDisabled = function (privacyFlag) {
|
||||
if (!this.privacy) {
|
||||
return false;
|
||||
}
|
||||
nconf.set('ghostVersion', packageInfo.version);
|
||||
|
||||
if (this.privacy.useTinfoil === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.privacy[privacyFlag] === false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if any of the currently set config items are deprecated, and issues a warning.
|
||||
*/
|
||||
ConfigManager.prototype.checkDeprecated = function () {
|
||||
var self = this;
|
||||
_.each(this.deprecatedItems, function (property) {
|
||||
self.displayDeprecated(self._config, property.split('.'), []);
|
||||
});
|
||||
};
|
||||
|
||||
ConfigManager.prototype.displayDeprecated = function (item, properties, address) {
|
||||
var self = this,
|
||||
property = properties.shift(),
|
||||
errorText,
|
||||
explanationText,
|
||||
helpText;
|
||||
|
||||
address.push(property);
|
||||
|
||||
if (item.hasOwnProperty(property)) {
|
||||
if (properties.length) {
|
||||
return self.displayDeprecated(item[property], properties, address);
|
||||
}
|
||||
errorText = i18n.t('errors.config.deprecatedProperty.error', {property: chalk.bold(address.join('.'))});
|
||||
explanationText = i18n.t('errors.config.deprecatedProperty.explanation');
|
||||
helpText = i18n.t('errors.config.deprecatedProperty.help', {url: 'http://support.ghost.org/config'});
|
||||
errors.logWarn(errorText, explanationText, helpText);
|
||||
}
|
||||
};
|
||||
|
||||
if (testingEnvs.indexOf(process.env.NODE_ENV) > -1) {
|
||||
defaultConfig = require('../../../config.example')[process.env.NODE_ENV];
|
||||
}
|
||||
|
||||
module.exports = new ConfigManager(defaultConfig);
|
||||
module.exports = nconf;
|
||||
module.exports.isPrivacyDisabled = localUtils.isPrivacyDisabled.bind(nconf);
|
||||
module.exports.getContentPath = localUtils.getContentPath.bind(nconf);
|
||||
|
|
67
core/server/config/overrides.json
Normal file
67
core/server/config/overrides.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"paths": {
|
||||
"appRoot": ".",
|
||||
"corePath": "core/",
|
||||
"clientAssets": "core/built/assets",
|
||||
"imagesRelPath": "content/images",
|
||||
"helperTemplates": "core/server/helpers/tpl/",
|
||||
"adminViews": "core/server/views/",
|
||||
"internalAppPath": "core/server/apps/",
|
||||
"internalStoragePath": "core/server/storage/",
|
||||
"internalSchedulingPath": "core/server/scheduling/"
|
||||
},
|
||||
"internalApps": [
|
||||
"private-blogging",
|
||||
"subscribers",
|
||||
"amp"
|
||||
],
|
||||
"routeKeywords": {
|
||||
"tag": "tag",
|
||||
"author": "author",
|
||||
"page": "page",
|
||||
"preview": "p",
|
||||
"private": "private",
|
||||
"subscribe": "subscribe",
|
||||
"amp": "amp"
|
||||
},
|
||||
"slugs": {
|
||||
"reserved": ["admin", "app", "apps", "archive", "archives", "categories",
|
||||
"category", "dashboard", "feed", "ghost-admin", "login", "logout",
|
||||
"page", "pages", "post", "posts", "public", "register", "setup",
|
||||
"signin", "signout", "signup", "user", "users", "wp-admin", "wp-login"],
|
||||
"protected": ["ghost", "rss", "amp"]
|
||||
},
|
||||
"uploads": {
|
||||
"subscribers": {
|
||||
"extensions": [".csv"],
|
||||
"contentTypes": ["text/csv", "application/csv", "application/octet-stream"]
|
||||
},
|
||||
"images": {
|
||||
"extensions": [".jpg", ".jpeg", ".gif", ".png", ".svg", ".svgz"],
|
||||
"contentTypes": ["image/jpeg", "image/png", "image/gif", "image/svg+xml"]
|
||||
},
|
||||
"db": {
|
||||
"extensions": [".json", ".zip"],
|
||||
"contentTypes": ["application/octet-stream", "application/json", "application/zip", "application/x-zip-compressed"]
|
||||
},
|
||||
"themes": {
|
||||
"extensions": [".zip"],
|
||||
"contentTypes": ["application/zip", "application/x-zip-compressed", "application/octet-stream"]
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"active": {
|
||||
"themes": "local-file-store"
|
||||
}
|
||||
},
|
||||
"times": {
|
||||
"cannotScheduleAPostBeforeInMinutes": 2,
|
||||
"publishAPostBySchedulerToleranceInMinutes": 2
|
||||
},
|
||||
"theme": {
|
||||
"timezone": "Etc/UTC"
|
||||
},
|
||||
"maintenance": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
57
core/server/config/utils.js
Normal file
57
core/server/config/utils.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
var path = require('path'),
|
||||
_ = require('lodash');
|
||||
|
||||
exports.isPrivacyDisabled = function isPrivacyDisabled(privacyFlag) {
|
||||
if (!this.get('privacy')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.get('privacy').useTinfoil === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.get('privacy')[privacyFlag] === false;
|
||||
};
|
||||
|
||||
/**
|
||||
* transform all relative paths to absolute paths
|
||||
* @TODO: imagesRelPath is a dirty little attribute (especially when looking at the usages)
|
||||
*/
|
||||
exports.makePathsAbsolute = function makePathsAbsolute(paths, parent) {
|
||||
var self = this;
|
||||
|
||||
if (!paths && !parent) {
|
||||
paths = this.get('paths');
|
||||
parent = 'paths';
|
||||
}
|
||||
|
||||
_.each(paths, function (configValue, pathsKey) {
|
||||
if (_.isObject(configValue)) {
|
||||
makePathsAbsolute.bind(self)(configValue, parent + ':' + pathsKey);
|
||||
} else {
|
||||
if (configValue[0] !== '/' && pathsKey !== 'imagesRelPath') {
|
||||
self.set(parent + ':' + pathsKey, path.join(__dirname + '/../../../', configValue));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* we can later support setting folder names via custom config values
|
||||
*/
|
||||
exports.getContentPath = function getContentPath(type) {
|
||||
switch (type) {
|
||||
case 'storage':
|
||||
return path.join(this.get('paths:contentPath'), 'storage/');
|
||||
case 'images':
|
||||
return path.join(this.get('paths:contentPath'), 'images/');
|
||||
case 'apps':
|
||||
return path.join(this.get('paths:contentPath'), 'apps/');
|
||||
case 'themes':
|
||||
return path.join(this.get('paths:contentPath'), 'themes/');
|
||||
case 'scheduling':
|
||||
return path.join(this.get('paths:contentPath'), 'scheduling/');
|
||||
default:
|
||||
throw new Error('getContentPath was called with: ' + type);
|
||||
}
|
||||
};
|
|
@ -11,7 +11,7 @@ channelConfig = function channelConfig() {
|
|||
},
|
||||
tag: {
|
||||
name: 'tag',
|
||||
route: '/' + config.routeKeywords.tag + '/:slug/',
|
||||
route: '/' + config.get('routeKeywords').tag + '/:slug/',
|
||||
postOptions: {
|
||||
filter: 'tags:\'%s\''
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ channelConfig = function channelConfig() {
|
|||
},
|
||||
author: {
|
||||
name: 'author',
|
||||
route: '/' + config.routeKeywords.author + '/:slug/',
|
||||
route: '/' + config.get('routeKeywords').author + '/:slug/',
|
||||
postOptions: {
|
||||
filter: 'author:\'%s\''
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ var express = require('express'),
|
|||
channelRouter;
|
||||
|
||||
function handlePageParam(req, res, next, page) {
|
||||
var pageRegex = new RegExp('/' + config.routeKeywords.page + '/(.*)?/'),
|
||||
var pageRegex = new RegExp('/' + config.get('routeKeywords').page + '/(.*)?/'),
|
||||
rssRegex = new RegExp('/rss/(.*)?/');
|
||||
|
||||
page = parseInt(page, 10);
|
||||
|
@ -48,10 +48,10 @@ rssRouter = function rssRouter(channelConfig) {
|
|||
router.get(baseRoute, stack);
|
||||
router.get(baseRoute + ':page/', stack);
|
||||
router.get('/feed/', function redirectToRSS(req, res) {
|
||||
return utils.redirect301(res, config.paths.subdir + req.baseUrl + baseRoute);
|
||||
return utils.redirect301(res, utils.url.getSubdir() + req.baseUrl + baseRoute);
|
||||
});
|
||||
router.param('page', handlePageParam);
|
||||
|
||||
router.param('page', handlePageParam);
|
||||
return router;
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ channelRouter = function router() {
|
|||
|
||||
var channelsRouter = express.Router({mergeParams: true}),
|
||||
baseRoute = '/',
|
||||
pageRoute = '/' + config.routeKeywords.page + '/:page/';
|
||||
pageRoute = '/' + config.get('routeKeywords').page + '/:page/';
|
||||
|
||||
_.each(channelConfig.list(), function (channel) {
|
||||
var channelRouter = express.Router({mergeParams: true}),
|
||||
|
@ -79,7 +79,7 @@ channelRouter = function router() {
|
|||
|
||||
if (channel.editRedirect) {
|
||||
channelRouter.get('/edit/', function redirect(req, res) {
|
||||
res.redirect(config.paths.subdir + channel.editRedirect.replace(':slug', req.params.slug));
|
||||
res.redirect(utils.url.getSubdir() + channel.editRedirect.replace(':slug', req.params.slug));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
var config = require('../../config'),
|
||||
|
||||
// Context patterns, should eventually come from Channel configuration
|
||||
privatePattern = new RegExp('^\\/' + config.routeKeywords.private + '\\/'),
|
||||
subscribePattern = new RegExp('^\\/' + config.routeKeywords.subscribe + '\\/'),
|
||||
ampPattern = new RegExp('\\/' + config.routeKeywords.amp + '\\/$'),
|
||||
privatePattern = new RegExp('^\\/' + config.get('routeKeywords').private + '\\/'),
|
||||
subscribePattern = new RegExp('^\\/' + config.get('routeKeywords').subscribe + '\\/'),
|
||||
ampPattern = new RegExp('\\/' + config.get('routeKeywords').amp + '\\/$'),
|
||||
rssPattern = new RegExp('^\\/rss\\/'),
|
||||
homePattern = new RegExp('^\\/$');
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ _.extend(defaultPostQuery, queryDefaults, {
|
|||
function fetchPostsPerPage(options) {
|
||||
options = options || {};
|
||||
|
||||
var postsPerPage = parseInt(config.theme.postsPerPage);
|
||||
var postsPerPage = parseInt(config.get('theme').postsPerPage);
|
||||
|
||||
// No negative posts per page, must be number
|
||||
if (!isNaN(postsPerPage) && postsPerPage > 0) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
/*global require, module */
|
||||
|
||||
var api = require('../../api'),
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
filters = require('../../filters'),
|
||||
templates = require('./templates'),
|
||||
handleError = require('./error'),
|
||||
|
@ -48,7 +48,7 @@ frontendControllers = {
|
|||
}
|
||||
|
||||
if (post.status === 'published') {
|
||||
return res.redirect(301, config.urlFor('post', {post: post}));
|
||||
return res.redirect(301, utils.url.urlFor('post', {post: post}));
|
||||
}
|
||||
|
||||
setRequestIsSecure(req, post);
|
||||
|
@ -68,7 +68,8 @@ frontendControllers = {
|
|||
|
||||
// CASE: last param is of url is /edit, redirect to admin
|
||||
if (lookup.isEditURL) {
|
||||
return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/');
|
||||
return res.redirect(utils.url.getSubdir()
|
||||
+ '/ghost/editor/' + post.id + '/');
|
||||
}
|
||||
|
||||
// CASE: permalink is not valid anymore, we redirect him permanently to the correct one
|
||||
|
|
|
@ -13,7 +13,7 @@ function getOptionsFormat(linkStructure) {
|
|||
|
||||
function postLookup(postUrl) {
|
||||
var postPath = url.parse(postUrl).path,
|
||||
postPermalink = config.theme.permalinks,
|
||||
postPermalink = config.get('theme').permalinks,
|
||||
pagePermalink = '/:slug/',
|
||||
isEditURL = false,
|
||||
isAmpURL = false,
|
||||
|
|
|
@ -6,7 +6,7 @@ var _ = require('lodash'),
|
|||
config = require('../../config');
|
||||
|
||||
function getActiveThemePaths(activeTheme) {
|
||||
return config.paths.availableThemes[activeTheme];
|
||||
return config.get('paths').availableThemes[activeTheme];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,37 +7,7 @@ var knex = require('knex'),
|
|||
// - then this file is cached and you have no chance to connect to the db anymore
|
||||
// - bring dynamic into this file (db.connect())
|
||||
function configure(dbConfig) {
|
||||
var client = dbConfig.client,
|
||||
pg;
|
||||
|
||||
dbConfig.isPostgreSQL = function () {
|
||||
return client === 'pg' || client === 'postgres' || client === 'postgresql';
|
||||
};
|
||||
|
||||
if (dbConfig.isPostgreSQL()) {
|
||||
try {
|
||||
pg = require('pg');
|
||||
} catch (e) {
|
||||
pg = require('pg.js');
|
||||
}
|
||||
|
||||
// By default PostgreSQL returns data as strings along with an OID that identifies
|
||||
// its type. We're setting the parser to convert OID 20 (int8) into a javascript
|
||||
// integer.
|
||||
pg.types.setTypeParser(20, function (val) {
|
||||
return val === null ? null : parseInt(val, 10);
|
||||
});
|
||||
|
||||
// https://github.com/tgriesser/knex/issues/97
|
||||
// this sets the timezone to UTC only for the connection!
|
||||
dbConfig.pool = {
|
||||
afterCreate: function (connection, callback) {
|
||||
connection.query('set timezone=\'UTC\'', function (err) {
|
||||
callback(err, connection);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
var client = dbConfig.client;
|
||||
|
||||
if (client === 'sqlite3') {
|
||||
dbConfig.useNullAsDefault = dbConfig.useNullAsDefault || false;
|
||||
|
@ -45,13 +15,14 @@ function configure(dbConfig) {
|
|||
|
||||
if (client === 'mysql') {
|
||||
dbConfig.connection.timezone = 'UTC';
|
||||
dbConfig.connection.charset = 'utf8mb4';
|
||||
}
|
||||
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
if (!knexInstance && config.database && config.database.client) {
|
||||
knexInstance = knex(configure(config.database));
|
||||
if (!knexInstance && config.get('database') && config.get('database').client) {
|
||||
knexInstance = knex(configure(config.get('database')));
|
||||
}
|
||||
|
||||
module.exports = knexInstance;
|
||||
|
|
|
@ -2,20 +2,21 @@ var _ = require('lodash'),
|
|||
Promise = require('bluebird'),
|
||||
path = require('path'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
storage = require('../../../storage'),
|
||||
|
||||
ImageHandler;
|
||||
|
||||
ImageHandler = {
|
||||
type: 'images',
|
||||
extensions: config.uploads.images.extensions,
|
||||
contentTypes: config.uploads.images.contentTypes,
|
||||
extensions: config.get('uploads').images.extensions,
|
||||
contentTypes: config.get('uploads').images.contentTypes,
|
||||
directories: ['images', 'content'],
|
||||
|
||||
loadFile: function (files, baseDir) {
|
||||
var store = storage.getStorage(),
|
||||
baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp(''),
|
||||
imageFolderRegexes = _.map(config.paths.imagesRelPath.split('/'), function (dir) {
|
||||
imageFolderRegexes = _.map(config.get('paths').imagesRelPath.split('/'), function (dir) {
|
||||
return new RegExp('^' + dir + '/');
|
||||
});
|
||||
|
||||
|
@ -30,15 +31,16 @@ ImageHandler = {
|
|||
|
||||
file.originalPath = noBaseDir;
|
||||
file.name = noGhostDirs;
|
||||
file.targetDir = path.join(config.paths.imagesPath, path.dirname(noGhostDirs));
|
||||
file.targetDir = path.join(config.getContentPath('images'), path.dirname(noGhostDirs));
|
||||
return file;
|
||||
});
|
||||
|
||||
return Promise.map(files, function (image) {
|
||||
return store.getUniqueFileName(store, image, image.targetDir).then(function (targetFilename) {
|
||||
image.newPath = (config.paths.subdir + '/' +
|
||||
config.paths.imagesRelPath + '/' + path.relative(config.paths.imagesPath, targetFilename))
|
||||
image.newPath = (utils.url.getSubdir() + '/' +
|
||||
config.get('paths').imagesRelPath + '/' + path.relative(config.getContentPath('images'), targetFilename))
|
||||
.replace(new RegExp('\\' + path.sep, 'g'), '/');
|
||||
|
||||
return image;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var config = require('../../config'),
|
||||
var utils = require('../../utils'),
|
||||
getUrl = require('./url'),
|
||||
_ = require('lodash');
|
||||
|
||||
|
@ -6,7 +6,7 @@ function getAmplUrl(data) {
|
|||
var context = data.context ? data.context : null;
|
||||
|
||||
if (_.includes(context, 'post') && !_.includes(context, 'amp')) {
|
||||
return config.urlJoin(config.getBaseUrl(false),
|
||||
return utils.url.urlJoin(utils.url.getBaseUrl(false),
|
||||
getUrl(data, false)) + 'amp/';
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
var config = require('../../config');
|
||||
var config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
crypto = require('crypto');
|
||||
|
||||
function getAssetUrl(path, isAdmin, minify) {
|
||||
var output = '';
|
||||
|
||||
output += config.paths.subdir + '/';
|
||||
output += utils.url.getSubdir() + '/';
|
||||
|
||||
if (!path.match(/^favicon\.ico$/) && !path.match(/^shared/) && !path.match(/^asset/)) {
|
||||
if (isAdmin) {
|
||||
|
@ -24,7 +26,11 @@ function getAssetUrl(path, isAdmin, minify) {
|
|||
output += path;
|
||||
|
||||
if (!path.match(/^favicon\.ico$/)) {
|
||||
output = output + '?v=' + config.assetHash;
|
||||
if (!config.get('assetHash')) {
|
||||
config.set('assetHash', (crypto.createHash('md5').update(config.get('ghostVersion') + Date.now()).digest('hex')).substring(0, 10));
|
||||
}
|
||||
|
||||
output = output + '?v=' + config.get('assetHash');
|
||||
}
|
||||
|
||||
return output;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var config = require('../../config'),
|
||||
var utils = require('../../utils'),
|
||||
getContextObject = require('./context_object.js'),
|
||||
_ = require('lodash');
|
||||
|
||||
|
@ -7,7 +7,7 @@ function getAuthorImage(data, absolute) {
|
|||
contextObject = getContextObject(data, context);
|
||||
|
||||
if ((_.includes(context, 'post') || _.includes(context, 'page')) && contextObject.author && contextObject.author.image) {
|
||||
return config.urlFor('image', {image: contextObject.author.image}, absolute);
|
||||
return utils.url.urlFor('image', {image: contextObject.author.image}, absolute);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var config = require('../../config');
|
||||
var utils = require('../../utils');
|
||||
|
||||
function getAuthorUrl(data, absolute) {
|
||||
var context = data.context ? data.context[0] : null;
|
||||
|
@ -6,10 +6,10 @@ function getAuthorUrl(data, absolute) {
|
|||
context = context === 'amp' ? 'post' : context;
|
||||
|
||||
if (data.author) {
|
||||
return config.urlFor('author', {author: data.author}, absolute);
|
||||
return utils.url.urlFor('author', {author: data.author}, absolute);
|
||||
}
|
||||
if (data[context] && data[context].author) {
|
||||
return config.urlFor('author', {author: data[context].author}, absolute);
|
||||
return utils.url.urlFor('author', {author: data[context].author}, absolute);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
var config = require('../../config'),
|
||||
var utils = require('../../utils'),
|
||||
getUrl = require('./url');
|
||||
|
||||
function getCanonicalUrl(data) {
|
||||
var url = config.urlJoin(config.getBaseUrl(false),
|
||||
var url = utils.url.urlJoin(utils.url.getBaseUrl(false),
|
||||
getUrl(data, false));
|
||||
|
||||
if (url.indexOf('/amp/')) {
|
||||
|
|
|
@ -2,7 +2,7 @@ var config = require('../../config'),
|
|||
_ = require('lodash');
|
||||
|
||||
function getContextObject(data, context) {
|
||||
var blog = config.theme,
|
||||
var blog = config.get('theme'),
|
||||
contextObject;
|
||||
|
||||
context = _.includes(context, 'page') || _.includes(context, 'amp') ? 'post' : context;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var config = require('../../config'),
|
||||
var utils = require('../../utils'),
|
||||
getContextObject = require('./context_object.js'),
|
||||
_ = require('lodash');
|
||||
|
||||
|
@ -8,11 +8,11 @@ function getCoverImage(data) {
|
|||
|
||||
if (_.includes(context, 'home') || _.includes(context, 'author')) {
|
||||
if (contextObject.cover) {
|
||||
return config.urlFor('image', {image: contextObject.cover}, true);
|
||||
return utils.url.urlFor('image', {image: contextObject.cover}, true);
|
||||
}
|
||||
} else {
|
||||
if (contextObject.image) {
|
||||
return config.urlFor('image', {image: contextObject.image}, true);
|
||||
return utils.url.urlFor('image', {image: contextObject.image}, true);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -10,7 +10,7 @@ function getDescription(data, root) {
|
|||
} else if (_.includes(context, 'paged')) {
|
||||
description = '';
|
||||
} else if (_.includes(context, 'home')) {
|
||||
description = config.theme.description;
|
||||
description = config.get('theme').description;
|
||||
} else if (_.includes(context, 'author') && data.author) {
|
||||
description = data.author.meta_description || data.author.bio;
|
||||
} else if (_.includes(context, 'tag') && data.tag) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
getUrl = require('./url'),
|
||||
getImageDimensions = require('./image-dimensions'),
|
||||
getCanonicalUrl = require('./canonical_url'),
|
||||
|
@ -45,12 +46,12 @@ function getMetaData(data, root) {
|
|||
publishedDate: getPublishedDate(data),
|
||||
modifiedDate: getModifiedDate(data),
|
||||
ogType: getOgType(data),
|
||||
blog: _.cloneDeep(config.theme)
|
||||
blog: _.cloneDeep(config.get('theme'))
|
||||
};
|
||||
|
||||
metaData.blog.logo = {};
|
||||
metaData.blog.logo.url = config.theme.logo ?
|
||||
config.urlFor('image', {image: config.theme.logo}, true) : config.urlFor({relativeUrl: '/ghost/img/ghosticon.jpg'}, {}, true);
|
||||
metaData.blog.logo.url = config.get('theme').logo ?
|
||||
utils.url.urlFor('image', {image: config.get('theme').logo}, true) : utils.url.urlFor({relativeUrl: '/ghost/img/ghosticon.jpg'}, {}, true);
|
||||
|
||||
// TODO: cleanup these if statements
|
||||
if (data.post && data.post.html) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var _ = require('lodash'),
|
||||
config = require('../../config');
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils');
|
||||
|
||||
function getPaginatedUrl(page, data, absolute) {
|
||||
// If we don't have enough information, return null right away
|
||||
|
@ -7,9 +8,9 @@ function getPaginatedUrl(page, data, absolute) {
|
|||
return null;
|
||||
}
|
||||
|
||||
var pagePath = '/' + config.routeKeywords.page + '/',
|
||||
var pagePath = '/' + config.get('routeKeywords').page + '/',
|
||||
// Try to match the base url, as whatever precedes the pagePath
|
||||
baseUrlPattern = new RegExp('(.+)?(/' + config.routeKeywords.page + '/\\d+/)'),
|
||||
baseUrlPattern = new RegExp('(.+)?(/' + config.get('routeKeywords').page + '/\\d+/)'),
|
||||
baseUrlMatch = data.relativeUrl.match(baseUrlPattern),
|
||||
// If there is no match for pagePath, use the original url, without the trailing slash
|
||||
baseUrl = baseUrlMatch ? baseUrlMatch[1] : data.relativeUrl.slice(0, -1),
|
||||
|
@ -29,7 +30,7 @@ function getPaginatedUrl(page, data, absolute) {
|
|||
// baseUrl can be undefined, if there was nothing preceding the pagePath (e.g. first page of the index channel)
|
||||
newRelativeUrl = baseUrl ? baseUrl + newRelativeUrl : newRelativeUrl;
|
||||
|
||||
return config.urlFor({relativeUrl: newRelativeUrl, secure: data.secure}, absolute);
|
||||
return utils.url.urlFor({relativeUrl: newRelativeUrl, secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
module.exports = getPaginatedUrl;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var config = require('../../config');
|
||||
var utils = require('../../utils');
|
||||
|
||||
function getRssUrl(data, absolute) {
|
||||
return config.urlFor('rss', {secure: data.secure}, absolute);
|
||||
return utils.url.urlFor('rss', {secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
module.exports = getRssUrl;
|
||||
|
|
|
@ -4,7 +4,7 @@ var _ = require('lodash'),
|
|||
function getTitle(data, root) {
|
||||
var title = '',
|
||||
context = root ? root.context : null,
|
||||
blog = config.theme,
|
||||
blog = config.get('theme'),
|
||||
pagination = root ? root.pagination : null,
|
||||
pageString = '';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
var schema = require('../schema').checks,
|
||||
config = require('../../config');
|
||||
utils = require('../../utils');
|
||||
|
||||
// This cleans the url from any `/amp` postfixes, so we'll never
|
||||
// output a url with `/amp` in the end, except for the needed `amphtml`
|
||||
|
@ -13,23 +13,23 @@ function sanitizeAmpUrl(url) {
|
|||
|
||||
function getUrl(data, absolute) {
|
||||
if (schema.isPost(data)) {
|
||||
return config.urlFor('post', {post: data, secure: data.secure}, absolute);
|
||||
return utils.url.urlFor('post', {post: data, secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
if (schema.isTag(data)) {
|
||||
return config.urlFor('tag', {tag: data, secure: data.secure}, absolute);
|
||||
return utils.url.urlFor('tag', {tag: data, secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
if (schema.isUser(data)) {
|
||||
return config.urlFor('author', {author: data, secure: data.secure}, absolute);
|
||||
return utils.url.urlFor('author', {author: data, secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
if (schema.isNav(data)) {
|
||||
return config.urlFor('nav', {nav: data, secure: data.secure}, absolute);
|
||||
return utils.url.urlFor('nav', {nav: data, secure: data.secure}, absolute);
|
||||
}
|
||||
|
||||
// sanitize any trailing `/amp` in the url
|
||||
return sanitizeAmpUrl(config.urlFor(data, {}, absolute));
|
||||
return sanitizeAmpUrl(utils.url.urlFor(data, {}, absolute));
|
||||
}
|
||||
|
||||
module.exports = getUrl;
|
||||
|
|
|
@ -11,7 +11,7 @@ var _ = require('lodash'),
|
|||
backup;
|
||||
|
||||
writeExportFile = function writeExportFile(exportResult) {
|
||||
var filename = path.resolve(config.paths.contentPath + '/data/' + exportResult.filename);
|
||||
var filename = path.resolve(config.get('paths').contentPath + '/data/' + exportResult.filename);
|
||||
|
||||
return Promise.promisify(fs.writeFile)(filename, JSON.stringify(exportResult.data)).return(filename);
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ module.exports = function moveJQuery(options, logger) {
|
|||
}
|
||||
})
|
||||
.then(function () {
|
||||
if (_.isEmpty(config.privacy)) {
|
||||
if (_.isEmpty(config.get('privacy'))) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var config = require('../../../../config'),
|
||||
models = require(config.paths.corePath + '/server/models'),
|
||||
api = require(config.paths.corePath + '/server/api'),
|
||||
sequence = require(config.paths.corePath + '/server/utils/sequence'),
|
||||
models = require(config.get('paths').corePath + '/server/models'),
|
||||
api = require(config.get('paths').corePath + '/server/api'),
|
||||
sequence = require(config.get('paths').corePath + '/server/utils/sequence'),
|
||||
moment = require('moment'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
|
@ -26,7 +26,6 @@ _private.addOffset = function addOffset(date) {
|
|||
};
|
||||
|
||||
/**
|
||||
* postgres: stores dates with offset, so it's enough to force timezone UTC in the db connection (see data/db/connection.js)
|
||||
* sqlite: stores UTC timestamps, but we will normalize the format to YYYY-MM-DD HH:mm:ss
|
||||
*/
|
||||
module.exports = function transformDatesIntoUTC(options, logger) {
|
||||
|
@ -39,15 +38,13 @@ module.exports = function transformDatesIntoUTC(options, logger) {
|
|||
return sequence([
|
||||
function databaseCheck() {
|
||||
// we have to change the sqlite format, because it stores dates as integer
|
||||
if (ServerTimezoneOffset === 0 && config.database.client === 'mysql') {
|
||||
if (ServerTimezoneOffset === 0 && config.get('database').client === 'mysql') {
|
||||
return Promise.reject(new Error('skip'));
|
||||
}
|
||||
|
||||
if (config.database.isPostgreSQL()) {
|
||||
_private.noOffset = true;
|
||||
} else if (config.database.client === 'mysql') {
|
||||
if (config.get('database').client === 'mysql') {
|
||||
_private.noOffset = false;
|
||||
} else if (config.database.client === 'sqlite3') {
|
||||
} else if (config.get('database').client === 'sqlite3') {
|
||||
_private.noOffset = true;
|
||||
}
|
||||
|
||||
|
@ -187,7 +184,7 @@ module.exports = function transformDatesIntoUTC(options, logger) {
|
|||
});
|
||||
},
|
||||
function setActiveTimezone() {
|
||||
var timezone = config.forceTimezoneOnMigration || moment.tz.guess();
|
||||
var timezone = config.get('forceTimezoneOnMigration') || moment.tz.guess();
|
||||
return models.Settings.edit({
|
||||
key: 'activeTimezone',
|
||||
value: timezone
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
var config = require('../../../../config'),
|
||||
_ = require('lodash'),
|
||||
models = require(config.paths.corePath + '/server/models'),
|
||||
transfomDatesIntoUTC = require(config.paths.corePath + '/server/data/migration/fixtures/006/01-transform-dates-into-utc'),
|
||||
models = require(config.get('paths').corePath + '/server/models'),
|
||||
transfomDatesIntoUTC = require(config.get('paths').corePath + '/server/data/migration/fixtures/006/01-transform-dates-into-utc'),
|
||||
Promise = require('bluebird'),
|
||||
messagePrefix = 'Fix sqlite/pg format: ',
|
||||
messagePrefix = 'Fix sqlite format: ',
|
||||
_private = {};
|
||||
|
||||
_private.rerunDateMigration = function rerunDateMigration(options, logger) {
|
||||
|
@ -33,22 +33,16 @@ _private.rerunDateMigration = function rerunDateMigration(options, logger) {
|
|||
};
|
||||
|
||||
/**
|
||||
* this migration script is a very special one for people who run their server in UTC and use sqlite3 or run their server in any TZ and use postgres
|
||||
* this migration script is a very special one for people who run their server in UTC and use sqlite3
|
||||
* 006/01-transform-dates-into-utc had a bug for this case, see what happen because of this bug https://github.com/TryGhost/Ghost/issues/7192
|
||||
*/
|
||||
module.exports = function fixSqliteFormat(options, logger) {
|
||||
// CASE: skip this script when using mysql
|
||||
if (config.database.client === 'mysql') {
|
||||
logger.warn(messagePrefix + 'This script only runs, when using sqlite/postgres as database.');
|
||||
if (config.get('database').client === 'mysql') {
|
||||
logger.warn(messagePrefix + 'This script only runs, when using sqlite as database.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// CASE: database is postgres, server is in ANY TZ, run 006/001 again
|
||||
// we can't check the format for PG somehow, so we just run the migration again
|
||||
if (config.database.isPostgreSQL()) {
|
||||
return _private.rerunDateMigration(options, logger);
|
||||
}
|
||||
|
||||
// CASE: sqlite3 and server is UTC, we check if the date migration was already running
|
||||
return options.transacting.raw('select created_at from users')
|
||||
.then(function (users) {
|
||||
|
|
27
core/server/data/schema/bootup.js
Normal file
27
core/server/data/schema/bootup.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
var Promise = require('bluebird'),
|
||||
versioning = require('./versioning'),
|
||||
migrations = require('../migration'),
|
||||
errors = require('./../../errors');
|
||||
|
||||
module.exports = function bootUp() {
|
||||
return versioning
|
||||
.getDatabaseVersion()
|
||||
.then(function successHandler(result) {
|
||||
if (!/^alpha/.test(result)) {
|
||||
// This database was not created with Ghost alpha, and is not compatible
|
||||
throw new errors.DatabaseVersion(
|
||||
'Your database version is not compatible with Ghost 1.0.0 Alpha (master branch)',
|
||||
'Want to keep your DB? Use Ghost < 1.0.0 or the "stable" branch. Otherwise please delete your DB and restart Ghost',
|
||||
'More information on the Ghost 1.0.0 Alpha at https://support.ghost.org/v1-0-alpha'
|
||||
);
|
||||
}
|
||||
},
|
||||
// We don't use .catch here, as it would catch the error from the successHandler
|
||||
function errorHandler(err) {
|
||||
if (err instanceof errors.DatabaseNotPopulated) {
|
||||
return migrations.populate();
|
||||
}
|
||||
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
|
@ -1,11 +1,7 @@
|
|||
var sqlite3 = require('./sqlite3'),
|
||||
mysql = require('./mysql'),
|
||||
pg = require('./pg');
|
||||
mysql = require('./mysql');
|
||||
|
||||
module.exports = {
|
||||
sqlite3: sqlite3,
|
||||
mysql: mysql,
|
||||
pg: pg,
|
||||
postgres: pg,
|
||||
postgresql: pg
|
||||
mysql: mysql
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"core": {
|
||||
"databaseVersion": {
|
||||
"defaultValue": "008"
|
||||
"defaultValue": "alpha.1"
|
||||
},
|
||||
"dbHash": {
|
||||
"defaultValue": null
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
var schema = require('./schema'),
|
||||
checks = require('./checks'),
|
||||
commands = require('./commands'),
|
||||
versioning = require('./versioning'),
|
||||
defaultSettings = require('./default-settings');
|
||||
|
||||
module.exports.tables = schema;
|
||||
module.exports.checks = checks;
|
||||
module.exports.commands = commands;
|
||||
module.exports.versioning = versioning;
|
||||
module.exports.defaultSettings = defaultSettings;
|
||||
module.exports.tables = require('./schema');
|
||||
module.exports.checks = require('./checks');
|
||||
module.exports.commands = require('./commands');
|
||||
module.exports.versioning = require('./versioning');
|
||||
module.exports.defaultSettings = require('./default-settings');
|
||||
module.exports.bootUp = require('./bootup');
|
||||
|
|
|
@ -30,7 +30,7 @@ module.exports = {
|
|||
name: {type: 'string', maxlength: 150, nullable: false},
|
||||
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
password: {type: 'string', maxlength: 60, nullable: false},
|
||||
email: {type: 'string', maxlength: 254, nullable: false, unique: true, validations: {isEmail: true}},
|
||||
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
|
||||
image: {type: 'text', maxlength: 2000, nullable: true},
|
||||
cover: {type: 'text', maxlength: 2000, nullable: true},
|
||||
bio: {type: 'string', maxlength: 200, nullable: true},
|
||||
|
@ -188,14 +188,14 @@ module.exports = {
|
|||
},
|
||||
accesstokens: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
token: {type: 'string', nullable: false, unique: true},
|
||||
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
||||
user_id: {type: 'integer', nullable: false, unsigned: true, references: 'users.id'},
|
||||
client_id: {type: 'integer', nullable: false, unsigned: true, references: 'clients.id'},
|
||||
expires: {type: 'bigInteger', nullable: false}
|
||||
},
|
||||
refreshtokens: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
token: {type: 'string', nullable: false, unique: true},
|
||||
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
||||
user_id: {type: 'integer', nullable: false, unsigned: true, references: 'users.id'},
|
||||
client_id: {type: 'integer', nullable: false, unsigned: true, references: 'clients.id'},
|
||||
expires: {type: 'bigInteger', nullable: false}
|
||||
|
@ -204,7 +204,7 @@ module.exports = {
|
|||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
|
||||
name: {type: 'string', maxlength: 150, nullable: true},
|
||||
email: {type: 'string', maxlength: 254, nullable: false, unique: true, validations: {isEmail: true}},
|
||||
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
|
||||
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['subscribed', 'pending', 'unsubscribed']]}},
|
||||
post_id: {type: 'integer', nullable: true, unsigned: true, references: 'posts.id'},
|
||||
subscribed_url: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
|
||||
|
|
|
@ -31,10 +31,6 @@ function getDatabaseVersion() {
|
|||
.where('key', 'databaseVersion')
|
||||
.first('value')
|
||||
.then(function (version) {
|
||||
if (!version || isNaN(version.value)) {
|
||||
return Promise.reject(new errors.DatabaseVersion(i18n.t('errors.data.versioning.index.dbVersionNotRecognized')));
|
||||
}
|
||||
|
||||
return version.value;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ var https = require('https'),
|
|||
errors = require('../../errors'),
|
||||
url = require('url'),
|
||||
Promise = require('bluebird'),
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
events = require('../../events'),
|
||||
api = require('../../api/settings'),
|
||||
i18n = require('../../i18n'),
|
||||
|
@ -47,7 +47,7 @@ function ping(post) {
|
|||
|
||||
// If this is a post, we want to send the link of the post
|
||||
if (schema.isPost(post)) {
|
||||
message = config.urlFor('post', {post: post}, true);
|
||||
message = utils.url.urlFor('post', {post: post}, true);
|
||||
} else {
|
||||
message = post.message;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ function ping(post) {
|
|||
slackData = {
|
||||
text: message,
|
||||
unfurl_links: true,
|
||||
icon_url: config.urlFor({relativeUrl: '/ghost/img/ghosticon.jpg'}, {}, true),
|
||||
icon_url: utils.url.urlFor({relativeUrl: '/ghost/img/ghosticon.jpg'}, {}, true),
|
||||
username: 'Ghost'
|
||||
};
|
||||
|
||||
|
|
|
@ -133,15 +133,15 @@ validateSettings = function validateSettings(defaultSettings, model) {
|
|||
validateActiveTheme = function validateActiveTheme(themeName) {
|
||||
// If Ghost is running and its availableThemes collection exists
|
||||
// give it priority.
|
||||
if (config.paths.availableThemes && Object.keys(config.paths.availableThemes).length > 0) {
|
||||
availableThemes = Promise.resolve(config.paths.availableThemes);
|
||||
if (config.get('paths').availableThemes && Object.keys(config.get('paths').availableThemes).length > 0) {
|
||||
availableThemes = Promise.resolve(config.get('paths').availableThemes);
|
||||
}
|
||||
|
||||
if (!availableThemes) {
|
||||
// A Promise that will resolve to an object with a property for each installed theme.
|
||||
// This is necessary because certain configuration data is only available while Ghost
|
||||
// is running and at times the validations are used when it's not (e.g. tests)
|
||||
availableThemes = readThemes(config.paths.themePath);
|
||||
availableThemes = readThemes(config.getContentPath('themes'));
|
||||
}
|
||||
|
||||
return availableThemes.then(function then(themes) {
|
||||
|
|
|
@ -2,6 +2,7 @@ var crypto = require('crypto'),
|
|||
downsize = require('downsize'),
|
||||
RSS = require('rss'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
errors = require('../../../errors'),
|
||||
filters = require('../../../filters'),
|
||||
processUrls = require('../../../utils/make-absolute-urls'),
|
||||
|
@ -17,11 +18,11 @@ var crypto = require('crypto'),
|
|||
feedCache = {};
|
||||
|
||||
function isTag(req) {
|
||||
return req.originalUrl.indexOf('/' + config.routeKeywords.tag + '/') !== -1;
|
||||
return req.originalUrl.indexOf('/' + config.get('routeKeywords').tag + '/') !== -1;
|
||||
}
|
||||
|
||||
function isAuthor(req) {
|
||||
return req.originalUrl.indexOf('/' + config.routeKeywords.author + '/') !== -1;
|
||||
return req.originalUrl.indexOf('/' + config.get('routeKeywords').author + '/') !== -1;
|
||||
}
|
||||
|
||||
function handleError(next) {
|
||||
|
@ -40,8 +41,8 @@ function getData(channelOpts, slugParam) {
|
|||
if (result.data && result.data.tag) { titleStart = result.data.tag[0].name + ' - ' || ''; }
|
||||
if (result.data && result.data.author) { titleStart = result.data.author[0].name + ' - ' || ''; }
|
||||
|
||||
response.title = titleStart + config.theme.title;
|
||||
response.description = config.theme.description;
|
||||
response.title = titleStart + config.get('theme').title;
|
||||
response.description = config.get('theme').description;
|
||||
response.results = {
|
||||
posts: result.posts,
|
||||
meta: result.meta
|
||||
|
@ -52,12 +53,12 @@ function getData(channelOpts, slugParam) {
|
|||
}
|
||||
|
||||
function getBaseUrl(req, slugParam) {
|
||||
var baseUrl = config.paths.subdir;
|
||||
var baseUrl = utils.url.getSubdir();
|
||||
|
||||
if (isTag(req)) {
|
||||
baseUrl += '/' + config.routeKeywords.tag + '/' + slugParam + '/rss/';
|
||||
baseUrl += '/' + config.get('routeKeywords').tag + '/' + slugParam + '/rss/';
|
||||
} else if (isAuthor(req)) {
|
||||
baseUrl += '/' + config.routeKeywords.author + '/' + slugParam + '/rss/';
|
||||
baseUrl += '/' + config.get('routeKeywords').author + '/' + slugParam + '/rss/';
|
||||
} else {
|
||||
baseUrl += '/rss/';
|
||||
}
|
||||
|
@ -106,7 +107,7 @@ generateFeed = function generateFeed(data) {
|
|||
});
|
||||
|
||||
data.results.posts.forEach(function forEach(post) {
|
||||
var itemUrl = config.urlFor('post', {post: post, secure: data.secure}, true),
|
||||
var itemUrl = utils.url.urlFor('post', {post: post, secure: data.secure}, true),
|
||||
htmlContent = processUrls(post.html, data.siteUrl, itemUrl),
|
||||
item = {
|
||||
title: post.title,
|
||||
|
@ -121,7 +122,7 @@ generateFeed = function generateFeed(data) {
|
|||
imageUrl;
|
||||
|
||||
if (post.image) {
|
||||
imageUrl = config.urlFor('image', {image: post.image, secure: data.secure}, true);
|
||||
imageUrl = utils.url.urlFor('image', {image: post.image, secure: data.secure}, true);
|
||||
|
||||
// Add a media content tag
|
||||
item.custom_elements.push({
|
||||
|
@ -176,8 +177,8 @@ generate = function generate(req, res, next) {
|
|||
}
|
||||
|
||||
data.version = res.locals.safeVersion;
|
||||
data.siteUrl = config.urlFor('home', {secure: req.secure}, true);
|
||||
data.feedUrl = config.urlFor({relativeUrl: baseUrl, secure: req.secure}, true);
|
||||
data.siteUrl = utils.url.urlFor('home', {secure: req.secure}, true);
|
||||
data.feedUrl = utils.url.urlFor({relativeUrl: baseUrl, secure: req.secure}, true);
|
||||
data.secure = req.secure;
|
||||
|
||||
return getFeedXml(req.originalUrl, data).then(function then(feedXml) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
var _ = require('lodash'),
|
||||
xml = require('xml'),
|
||||
moment = require('moment'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
events = require('../../../events'),
|
||||
utils = require('./utils'),
|
||||
localUtils = require('./utils'),
|
||||
Promise = require('bluebird'),
|
||||
path = require('path'),
|
||||
CHANGE_FREQ = 'weekly',
|
||||
|
@ -92,7 +92,7 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
};
|
||||
|
||||
// Return the xml
|
||||
return utils.getDeclarations() + xml(data);
|
||||
return localUtils.getDeclarations() + xml(data);
|
||||
},
|
||||
|
||||
updateXmlFromNodes: function (urlElements) {
|
||||
|
@ -133,11 +133,11 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
getUrlForDatum: function () {
|
||||
return config.urlFor('home', true);
|
||||
return utils.url.urlFor('home', true);
|
||||
},
|
||||
|
||||
getUrlForImage: function (image) {
|
||||
return config.urlFor('image', {image: image}, true);
|
||||
return utils.url.urlFor('image', {image: image}, true);
|
||||
},
|
||||
|
||||
getPriorityForDatum: function () {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
var _ = require('lodash'),
|
||||
xml = require('xml'),
|
||||
moment = require('moment'),
|
||||
config = require('../../../config'),
|
||||
utils = require('./utils'),
|
||||
utils = require('../../../utils'),
|
||||
localUtils = require('./utils'),
|
||||
RESOURCES,
|
||||
XMLNS_DECLS;
|
||||
|
||||
|
@ -28,14 +28,14 @@ _.extend(SiteMapIndexGenerator.prototype, {
|
|||
};
|
||||
|
||||
// Return the xml
|
||||
return utils.getDeclarations() + xml(data);
|
||||
return localUtils.getDeclarations() + xml(data);
|
||||
},
|
||||
|
||||
generateSiteMapUrlElements: function () {
|
||||
var self = this;
|
||||
|
||||
return _.map(RESOURCES, function (resourceType) {
|
||||
var url = config.urlFor({
|
||||
var url = utils.url.urlFor({
|
||||
relativeUrl: '/sitemap-' + resourceType + '.xml'
|
||||
}, true),
|
||||
lastModified = self[resourceType].lastModified;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var _ = require('lodash'),
|
||||
api = require('../../../api'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
BaseMapGenerator = require('./base-generator');
|
||||
|
||||
// A class responsible for generating a sitemap from posts and keeping it updated
|
||||
|
@ -46,10 +46,10 @@ _.extend(PageMapGenerator.prototype, {
|
|||
|
||||
getUrlForDatum: function (post) {
|
||||
if (post.id === 0 && !_.isEmpty(post.name)) {
|
||||
return config.urlFor(post.name, true);
|
||||
return utils.url.urlFor(post.name, true);
|
||||
}
|
||||
|
||||
return config.urlFor('post', {post: post}, true);
|
||||
return utils.url.urlFor('post', {post: post}, true);
|
||||
},
|
||||
|
||||
getPriorityForDatum: function (post) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var _ = require('lodash'),
|
||||
api = require('../../../api'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
BaseMapGenerator = require('./base-generator');
|
||||
|
||||
// A class responsible for generating a sitemap from posts and keeping it updated
|
||||
|
@ -41,7 +41,7 @@ _.extend(PostMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
getUrlForDatum: function (post) {
|
||||
return config.urlFor('post', {post: post}, true);
|
||||
return utils.url.urlFor('post', {post: post}, true);
|
||||
},
|
||||
|
||||
getPriorityForDatum: function (post) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var _ = require('lodash'),
|
||||
api = require('../../../api'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
BaseMapGenerator = require('./base-generator');
|
||||
|
||||
// A class responsible for generating a sitemap from posts and keeping it updated
|
||||
|
@ -38,7 +38,7 @@ _.extend(TagsMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
getUrlForDatum: function (tag) {
|
||||
return config.urlFor('tag', {tag: tag}, true);
|
||||
return utils.url.urlFor('tag', {tag: tag}, true);
|
||||
},
|
||||
|
||||
getPriorityForDatum: function () {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var _ = require('lodash'),
|
||||
api = require('../../../api'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
validator = require('validator'),
|
||||
BaseMapGenerator = require('./base-generator'),
|
||||
// @TODO: figure out a way to get rid of this
|
||||
|
@ -42,7 +42,7 @@ _.extend(UserMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
getUrlForDatum: function (user) {
|
||||
return config.urlFor('author', {author: user}, true);
|
||||
return utils.url.urlFor('author', {author: user}, true);
|
||||
},
|
||||
|
||||
getPriorityForDatum: function () {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
var config = require('../../../config'),
|
||||
utils;
|
||||
var utils = require('../../../utils'),
|
||||
sitemapsUtils;
|
||||
|
||||
utils = {
|
||||
sitemapsUtils = {
|
||||
getDeclarations: function () {
|
||||
var baseUrl = config.urlFor('sitemap_xsl', true);
|
||||
var baseUrl = utils.url.urlFor('sitemap_xsl', true);
|
||||
baseUrl = baseUrl.replace(/^(http:|https:)/, '');
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>' +
|
||||
'<?xml-stylesheet type="text/xsl" href="' + baseUrl + '"?>';
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
module.exports = sitemapsUtils;
|
||||
|
|
|
@ -2,6 +2,7 @@ var _ = require('lodash'),
|
|||
http = require('http'),
|
||||
xml = require('xml'),
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
events = require('../../events'),
|
||||
i18n = require('../../i18n'),
|
||||
|
@ -19,7 +20,7 @@ pingList = [{
|
|||
function ping(post) {
|
||||
var pingXML,
|
||||
title = post.title,
|
||||
url = config.urlFor('post', {post: post}, true);
|
||||
url = utils.url.urlFor('post', {post: post}, true);
|
||||
|
||||
// Only ping when in production and not a page
|
||||
if (process.env.NODE_ENV !== 'production' || post.page || config.isPrivacyDisabled('useRpcPing')) {
|
||||
|
|
|
@ -25,22 +25,12 @@ var _ = require('lodash'),
|
|||
DatabaseNotPopulated = require('./database-not-populated'),
|
||||
DatabaseVersion = require('./database-version'),
|
||||
i18n = require('../i18n'),
|
||||
config,
|
||||
config = require('../config'),
|
||||
errors,
|
||||
|
||||
// Paths for views
|
||||
userErrorTemplateExists = false;
|
||||
|
||||
// Shim right now to deal with circular dependencies.
|
||||
// @TODO(hswolff): remove circular dependency and lazy require.
|
||||
function getConfigModule() {
|
||||
if (!config) {
|
||||
config = require('../config');
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function isValidErrorStatus(status) {
|
||||
return _.isNumber(status) && status >= 400 && status < 600;
|
||||
}
|
||||
|
@ -69,7 +59,7 @@ function getStatusCode(error) {
|
|||
*/
|
||||
errors = {
|
||||
updateActiveTheme: function (activeTheme) {
|
||||
userErrorTemplateExists = getConfigModule().paths.availableThemes[activeTheme].hasOwnProperty('error.hbs');
|
||||
userErrorTemplateExists = config.get('paths').availableThemes[activeTheme].hasOwnProperty('error.hbs');
|
||||
},
|
||||
|
||||
throwError: function (err) {
|
||||
|
@ -135,7 +125,14 @@ errors = {
|
|||
var self = this,
|
||||
origArgs = _.toArray(arguments).slice(1),
|
||||
stack,
|
||||
msgs;
|
||||
msgs,
|
||||
hideStack = false;
|
||||
|
||||
// DatabaseVersion errors are usually fatal, we output a nice message
|
||||
// And the stack is not at all useful in this case
|
||||
if (err instanceof DatabaseVersion) {
|
||||
hideStack = true;
|
||||
}
|
||||
|
||||
if (_.isArray(err)) {
|
||||
_.each(err, function (e) {
|
||||
|
@ -182,7 +179,7 @@ errors = {
|
|||
// add a new line
|
||||
msgs.push('\n');
|
||||
|
||||
if (stack) {
|
||||
if (stack && !hideStack) {
|
||||
msgs.push(stack, '\n');
|
||||
}
|
||||
|
||||
|
@ -295,7 +292,7 @@ errors = {
|
|||
renderErrorPage: function (statusCode, err, req, res, next) {
|
||||
/*jshint unused:false*/
|
||||
var self = this,
|
||||
defaultErrorTemplatePath = path.resolve(getConfigModule().paths.adminViews, 'user-error.hbs');
|
||||
defaultErrorTemplatePath = path.resolve(config.get('paths').adminViews, 'user-error.hbs');
|
||||
|
||||
function parseStack(stack) {
|
||||
if (!_.isString(stack)) {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
var Promise = require('bluebird'),
|
||||
chalk = require('chalk'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
errors = require('./errors'),
|
||||
config = require('./config'),
|
||||
i18n = require('./i18n'),
|
||||
|
@ -35,26 +37,37 @@ function GhostServer(rootApp) {
|
|||
*/
|
||||
GhostServer.prototype.start = function (externalApp) {
|
||||
var self = this,
|
||||
rootApp = externalApp ? externalApp : self.rootApp;
|
||||
rootApp = externalApp ? externalApp : self.rootApp,
|
||||
socketConfig, socketValues = {
|
||||
path: path.join(config.get('paths').contentPath, process.env.NODE_ENV + '.socket'),
|
||||
permissions: '660'
|
||||
};
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
var socketConfig = config.getSocket();
|
||||
if (config.get('server').hasOwnProperty('socket')) {
|
||||
socketConfig = config.get('server').socket;
|
||||
|
||||
if (_.isString(socketConfig)) {
|
||||
socketValues.path = socketConfig;
|
||||
} else if (_.isObject(socketConfig)) {
|
||||
socketValues.path = socketConfig.path || socketValues.path;
|
||||
socketValues.permissions = socketConfig.permissions || socketValues.permissions;
|
||||
}
|
||||
|
||||
if (socketConfig) {
|
||||
// Make sure the socket is gone before trying to create another
|
||||
try {
|
||||
fs.unlinkSync(socketConfig.path);
|
||||
fs.unlinkSync(socketValues.path);
|
||||
} catch (e) {
|
||||
// We can ignore this.
|
||||
}
|
||||
|
||||
self.httpServer = rootApp.listen(socketConfig.path);
|
||||
|
||||
fs.chmod(socketConfig.path, socketConfig.permissions);
|
||||
self.httpServer = rootApp.listen(socketValues.path);
|
||||
fs.chmod(socketValues.path, socketValues.permissions);
|
||||
config.set('server:socket', socketValues);
|
||||
} else {
|
||||
self.httpServer = rootApp.listen(
|
||||
config.server.port,
|
||||
config.server.host
|
||||
config.get('server').port,
|
||||
config.get('server').host
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -62,7 +75,7 @@ GhostServer.prototype.start = function (externalApp) {
|
|||
if (error.errno === 'EADDRINUSE') {
|
||||
errors.logError(
|
||||
i18n.t('errors.httpServer.addressInUse.error'),
|
||||
i18n.t('errors.httpServer.addressInUse.context', {port: config.server.port}),
|
||||
i18n.t('errors.httpServer.addressInUse.context', {port: config.get('server').port}),
|
||||
i18n.t('errors.httpServer.addressInUse.help')
|
||||
);
|
||||
} else {
|
||||
|
@ -168,16 +181,20 @@ GhostServer.prototype.logStartMessages = function () {
|
|||
// Startup & Shutdown messages
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
console.log(
|
||||
chalk.green(i18n.t('notices.httpServer.ghostIsRunningIn', {env: process.env.NODE_ENV})),
|
||||
i18n.t('notices.httpServer.yourBlogIsAvailableOn', {url: config.url}),
|
||||
chalk.red('Currently running Ghost 1.0.0 Alpha, this is NOT suitable for production! \n'),
|
||||
chalk.white('Please switch to the stable branch. \n'),
|
||||
chalk.white('More information on the Ghost 1.0.0 Alpha at: ') + chalk.cyan('https://support.ghost.org/v1-0-alpha') + '\n',
|
||||
chalk.gray(i18n.t('notices.httpServer.ctrlCToShutDown'))
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
chalk.blue('Welcome to the Ghost 1.0.0 Alpha - this version of Ghost is for development only.')
|
||||
);
|
||||
console.log(
|
||||
chalk.green(i18n.t('notices.httpServer.ghostIsRunningIn', {env: process.env.NODE_ENV})),
|
||||
i18n.t('notices.httpServer.listeningOn'),
|
||||
config.getSocket() || config.server.host + ':' + config.server.port,
|
||||
i18n.t('notices.httpServer.urlConfiguredAs', {url: config.url}),
|
||||
config.get('server').socket || config.get('server').host + ':' + config.get('server').port,
|
||||
i18n.t('notices.httpServer.urlConfiguredAs', {url: config.get('url')}),
|
||||
chalk.gray(i18n.t('notices.httpServer.ctrlCToShutDown'))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
utils = require('./utils'),
|
||||
utils = require('../utils'),
|
||||
localUtils = require('./utils'),
|
||||
author;
|
||||
|
||||
author = function (options) {
|
||||
|
@ -26,8 +26,8 @@ author = function (options) {
|
|||
|
||||
if (this.author && this.author.name) {
|
||||
if (autolink) {
|
||||
output = utils.linkTemplate({
|
||||
url: config.urlFor('author', {author: this.author}),
|
||||
output = localUtils.linkTemplate({
|
||||
url: utils.url.urlFor('author', {author: this.author}),
|
||||
text: _.escape(this.author.name)
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -82,7 +82,7 @@ function ghost_head(options) {
|
|||
context = this.context ? this.context : null,
|
||||
useStructuredData = !config.isPrivacyDisabled('useStructuredData'),
|
||||
safeVersion = this.safeVersion,
|
||||
referrerPolicy = config.referrerPolicy ? config.referrerPolicy : 'no-referrer-when-downgrade',
|
||||
referrerPolicy = config.get('referrerPolicy') ? config.get('referrerPolicy') : 'no-referrer-when-downgrade',
|
||||
fetch = {
|
||||
metaData: getMetaData(this, options.data.root),
|
||||
client: getClient()
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
// Returns the URL for the current object scope i.e. If inside a post scope will return image permalink
|
||||
// `absolute` flag outputs absolute URL, else URL is relative.
|
||||
|
||||
var config = require('../config'),
|
||||
var utils = require('../utils'),
|
||||
image;
|
||||
|
||||
image = function (options) {
|
||||
var absolute = options && options.hash.absolute;
|
||||
|
||||
if (this.image) {
|
||||
return config.urlFor('image', {image: this.image}, absolute);
|
||||
return utils.url.urlFor('image', {image: this.image}, absolute);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
utils = require('../utils'),
|
||||
labs = require('../utils/labs'),
|
||||
utils = require('./utils'),
|
||||
localUtils = require('./utils'),
|
||||
tags;
|
||||
|
||||
tags = function (options) {
|
||||
|
@ -24,7 +24,7 @@ tags = function (options) {
|
|||
limit = options.hash.limit ? parseInt(options.hash.limit, 10) : undefined,
|
||||
from = options.hash.from ? parseInt(options.hash.from, 10) : 1,
|
||||
to = options.hash.to ? parseInt(options.hash.to, 10) : undefined,
|
||||
visibility = utils.parseVisibility(options),
|
||||
visibility = localUtils.parseVisibility(options),
|
||||
output = '';
|
||||
|
||||
function createTagList(tags) {
|
||||
|
@ -42,8 +42,8 @@ tags = function (options) {
|
|||
}
|
||||
}
|
||||
|
||||
var tagOutput = autolink ? utils.linkTemplate({
|
||||
url: config.urlFor('tag', {tag: tag}),
|
||||
var tagOutput = autolink ? localUtils.linkTemplate({
|
||||
url: utils.url.urlFor('tag', {tag: tag}),
|
||||
text: _.escape(tag.name)
|
||||
}) : _.escape(tag.name);
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ require('./overrides');
|
|||
|
||||
// Module dependencies
|
||||
var express = require('express'),
|
||||
_ = require('lodash'),
|
||||
uuid = require('node-uuid'),
|
||||
Promise = require('bluebird'),
|
||||
i18n = require('./i18n'),
|
||||
|
@ -22,8 +21,7 @@ var express = require('express'),
|
|||
config = require('./config'),
|
||||
errors = require('./errors'),
|
||||
middleware = require('./middleware'),
|
||||
migrations = require('./data/migration'),
|
||||
versioning = require('./data/schema/versioning'),
|
||||
db = require('./data/schema'),
|
||||
models = require('./models'),
|
||||
permissions = require('./permissions'),
|
||||
apps = require('./apps'),
|
||||
|
@ -32,6 +30,8 @@ var express = require('express'),
|
|||
GhostServer = require('./ghost-server'),
|
||||
scheduling = require('./scheduling'),
|
||||
validateThemes = require('./utils/validate-themes'),
|
||||
readDirectory = require('./utils/read-directory'),
|
||||
utils = require('./utils'),
|
||||
dbHash;
|
||||
|
||||
function initDbHashAndFirstRun() {
|
||||
|
@ -71,48 +71,13 @@ function init(options) {
|
|||
// Initialize Internationalization
|
||||
i18n.init();
|
||||
|
||||
// Load our config.js file from the local file system.
|
||||
return config.load(options.config).then(function () {
|
||||
return config.checkDeprecated();
|
||||
return readDirectory(config.getContentPath('apps')).then(function loadThemes(result) {
|
||||
config.set('paths:availableApps', result);
|
||||
return api.themes.loadThemes();
|
||||
}).then(function () {
|
||||
models.init();
|
||||
}).then(function () {
|
||||
return versioning.getDatabaseVersion()
|
||||
.then(function (currentVersion) {
|
||||
var response = migrations.update.isDatabaseOutOfDate({
|
||||
fromVersion: currentVersion,
|
||||
toVersion: versioning.getNewestDatabaseVersion(),
|
||||
forceMigration: process.env.FORCE_MIGRATION
|
||||
}), maintenanceState;
|
||||
|
||||
if (response.migrate === true) {
|
||||
maintenanceState = config.maintenance.enabled || false;
|
||||
config.maintenance.enabled = true;
|
||||
|
||||
migrations.update.execute({
|
||||
fromVersion: currentVersion,
|
||||
toVersion: versioning.getNewestDatabaseVersion(),
|
||||
forceMigration: process.env.FORCE_MIGRATION
|
||||
}).then(function () {
|
||||
config.maintenance.enabled = maintenanceState;
|
||||
}).catch(function (err) {
|
||||
if (!err) {
|
||||
return;
|
||||
}
|
||||
|
||||
errors.logErrorAndExit(err, err.context, err.help);
|
||||
});
|
||||
} else if (response.error) {
|
||||
return Promise.reject(response.error);
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err instanceof errors.DatabaseNotPopulated) {
|
||||
return migrations.populate();
|
||||
}
|
||||
|
||||
return Promise.reject(err);
|
||||
});
|
||||
// @TODO: this is temporary, replace migrations with a warning if a DB exists
|
||||
return db.bootUp();
|
||||
}).then(function () {
|
||||
// Populate any missing default settings
|
||||
return models.Settings.populateDefaults();
|
||||
|
@ -142,7 +107,7 @@ function init(options) {
|
|||
middleware(parentApp);
|
||||
|
||||
// Log all theme errors and warnings
|
||||
validateThemes(config.paths.themePath)
|
||||
validateThemes(config.getContentPath('themes'))
|
||||
.catch(function (result) {
|
||||
// TODO: change `result` to something better
|
||||
result.errors.forEach(function (err) {
|
||||
|
@ -160,7 +125,12 @@ function init(options) {
|
|||
|
||||
// scheduling can trigger api requests, that's why we initialize the module after the ghost server creation
|
||||
// scheduling module can create x schedulers with different adapters
|
||||
return scheduling.init(_.extend(config.scheduling, {apiUrl: config.apiUrl()}));
|
||||
return scheduling.init({
|
||||
active: config.get('scheduling').active,
|
||||
apiUrl: utils.url.apiUrl(),
|
||||
internalPath: config.get('paths').internalSchedulingPath,
|
||||
contentPath: config.getContentPath('scheduling')
|
||||
});
|
||||
}).then(function () {
|
||||
return ghostServer;
|
||||
});
|
||||
|
|
|
@ -8,8 +8,8 @@ var _ = require('lodash'),
|
|||
i18n = require('../i18n');
|
||||
|
||||
function GhostMailer() {
|
||||
var transport = config.mail && config.mail.transport || 'direct',
|
||||
options = config.mail && _.clone(config.mail.options) || {};
|
||||
var transport = config.get('mail') && config.get('mail').transport || 'direct',
|
||||
options = config.get('mail') && _.clone(config.get('mail').options) || {};
|
||||
|
||||
this.state = {};
|
||||
|
||||
|
@ -19,7 +19,7 @@ function GhostMailer() {
|
|||
}
|
||||
|
||||
GhostMailer.prototype.from = function () {
|
||||
var from = config.mail && (config.mail.from || config.mail.fromaddress),
|
||||
var from = config.get('mail') && (config.get('mail').from || config.get('mail').fromaddress),
|
||||
defaultBlogTitle;
|
||||
|
||||
// If we don't have a from address at all
|
||||
|
@ -30,7 +30,7 @@ GhostMailer.prototype.from = function () {
|
|||
|
||||
// If we do have a from address, and it's just an email
|
||||
if (validator.isEmail(from)) {
|
||||
defaultBlogTitle = config.theme.title ? config.theme.title : i18n.t('common.mail.title', {domain: this.getDomain()});
|
||||
defaultBlogTitle = config.get('theme').title ? config.get('theme').title : i18n.t('common.mail.title', {domain: this.getDomain()});
|
||||
|
||||
from = '"' + defaultBlogTitle + '" <' + from + '>';
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ GhostMailer.prototype.from = function () {
|
|||
|
||||
// Moved it to its own module
|
||||
GhostMailer.prototype.getDomain = function () {
|
||||
var domain = config.url.match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
|
||||
var domain = config.get('url').match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
|
||||
return domain && domain[1];
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ exports.generateContent = function generateContent(options) {
|
|||
data;
|
||||
|
||||
defaults = {
|
||||
siteUrl: config.forceAdminSSL ? (config.urlSSL || config.url) : config.url
|
||||
siteUrl: config.get('forceAdminSSL') ? (config.get('urlSSL') || config.get('url')) : config.get('url')
|
||||
};
|
||||
|
||||
data = _.defaults(defaults, options.data);
|
||||
|
|
|
@ -38,12 +38,12 @@ function sslForbiddenOrRedirect(opt) {
|
|||
// Check to see if we should use SSL
|
||||
// and redirect if needed
|
||||
checkSSL = function checkSSL(req, res, next) {
|
||||
if (isSSLrequired(res.isAdmin, config.url, config.forceAdminSSL)) {
|
||||
if (isSSLrequired(res.isAdmin, config.get('url'), config.get('forceAdminSSL'))) {
|
||||
if (!req.secure) {
|
||||
var response = sslForbiddenOrRedirect({
|
||||
forceAdminSSL: config.forceAdminSSL,
|
||||
configUrlSSL: config.urlSSL,
|
||||
configUrl: config.url,
|
||||
forceAdminSSL: config.get('forceAdminSSL'),
|
||||
configUrlSSL: config.get('urlSSL'),
|
||||
configUrl: config.get('url'),
|
||||
reqUrl: req.url
|
||||
});
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ function getIPs() {
|
|||
}
|
||||
|
||||
function getUrls() {
|
||||
var urls = [url.parse(config.url).hostname];
|
||||
var urls = [url.parse(config.get('url')).hostname];
|
||||
|
||||
if (config.urlSSL) {
|
||||
urls.push(url.parse(config.urlSSL).hostname);
|
||||
if (config.get('urlSSL')) {
|
||||
urls.push(url.parse(config.get('urlSSL')).hostname);
|
||||
}
|
||||
|
||||
return urls;
|
||||
|
|
|
@ -61,15 +61,15 @@ middleware = {
|
|||
};
|
||||
|
||||
setupMiddleware = function setupMiddleware(blogApp) {
|
||||
var logging = config.logging,
|
||||
corePath = config.paths.corePath,
|
||||
var logging = config.get('logging'),
|
||||
corePath = config.get('paths').corePath,
|
||||
adminApp = express(),
|
||||
adminHbs = hbs.create();
|
||||
|
||||
// ##Configuration
|
||||
|
||||
// enabled gzip compression by default
|
||||
if (config.server.compress !== false) {
|
||||
if (config.get('server').compress !== false) {
|
||||
blogApp.use(compress());
|
||||
}
|
||||
|
||||
|
@ -103,10 +103,10 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
|||
}
|
||||
|
||||
// Preload link headers
|
||||
if (config.preloadHeaders) {
|
||||
if (config.get('preloadHeaders')) {
|
||||
blogApp.use(netjet({
|
||||
cache: {
|
||||
max: config.preloadHeaders
|
||||
max: config.get('preloadHeaders')
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
|||
|
||||
// Admin only config
|
||||
blogApp.use('/ghost', serveStatic(
|
||||
config.paths.clientAssets,
|
||||
config.get('paths').clientAssets,
|
||||
{maxAge: utils.ONE_YEAR_MS}
|
||||
));
|
||||
|
||||
|
@ -145,15 +145,15 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
|||
// which do not need HTTPS. In fact, if HTTPS is forced on them, then 404 page might
|
||||
// not display properly when HTTPS is not available!
|
||||
blogApp.use(checkSSL);
|
||||
adminApp.set('views', config.paths.adminViews);
|
||||
adminApp.set('views', config.get('paths').adminViews);
|
||||
|
||||
// Theme only config
|
||||
blogApp.use(staticTheme());
|
||||
|
||||
// setup middleware for internal apps
|
||||
// @TODO: refactor this to be a proper app middleware hook for internal & external apps
|
||||
config.internalApps.forEach(function (appName) {
|
||||
var app = require(path.join(config.paths.internalAppPath, appName));
|
||||
config.get('internalApps').forEach(function (appName) {
|
||||
var app = require(path.join(config.get('paths').internalAppPath, appName));
|
||||
if (app.hasOwnProperty('setupMiddleware')) {
|
||||
app.setupMiddleware(blogApp);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ var config = require('../config'),
|
|||
errors = require('../errors');
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
if (config.maintenance.enabled) {
|
||||
if (config.get('maintenance').enabled) {
|
||||
return next(new errors.Maintenance(
|
||||
i18n.t('errors.general.maintenance')
|
||||
));
|
||||
|
|
|
@ -14,7 +14,7 @@ function exchangeRefreshToken(client, refreshToken, scope, done) {
|
|||
return done(new errors.NoPermissionError(i18n.t('errors.middleware.oauth.invalidRefreshToken')), false);
|
||||
} else {
|
||||
var token = model.toJSON(),
|
||||
accessToken = utils.uid(256),
|
||||
accessToken = utils.uid(191),
|
||||
accessExpires = Date.now() + utils.ONE_HOUR_MS,
|
||||
refreshExpires = Date.now() + utils.ONE_WEEK_MS;
|
||||
|
||||
|
@ -47,8 +47,8 @@ function exchangePassword(client, username, password, scope, done) {
|
|||
// Validate the user
|
||||
return models.User.check({email: username, password: password}).then(function then(user) {
|
||||
// Everything validated, return the access- and refreshtoken
|
||||
var accessToken = utils.uid(256),
|
||||
refreshToken = utils.uid(256),
|
||||
var accessToken = utils.uid(191),
|
||||
refreshToken = utils.uid(191),
|
||||
accessExpires = Date.now() + utils.ONE_HOUR_MS,
|
||||
refreshExpires = Date.now() + utils.ONE_WEEK_MS;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
var api = require('../api'),
|
||||
config = require('../config');
|
||||
utils = require('../utils');
|
||||
|
||||
// Redirect to setup if no user exists
|
||||
function redirectToSetup(req, res, next) {
|
||||
api.authentication.isSetup().then(function then(exists) {
|
||||
if (!exists.setup[0].status && !req.path.match(/\/setup\//)) {
|
||||
return res.redirect(config.paths.subdir + '/ghost/setup/');
|
||||
return res.redirect(utils.url.getSubdir() + '/ghost/setup/');
|
||||
}
|
||||
next();
|
||||
}).catch(function handleError(err) {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
var crypto = require('crypto'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
config = require('../config');
|
||||
config = require('../config'),
|
||||
utils = require('../utils');
|
||||
|
||||
// ### ServeSharedFile Middleware
|
||||
// Handles requests to robots.txt and favicon.ico (and caches them)
|
||||
function serveSharedFile(file, type, maxAge) {
|
||||
var content,
|
||||
corePath = config.paths.corePath,
|
||||
corePath = config.get('paths').corePath,
|
||||
filePath,
|
||||
blogRegex = /(\{\{blog-url\}\})/g,
|
||||
apiRegex = /(\{\{api-url\}\})/g;
|
||||
|
@ -26,8 +27,8 @@ function serveSharedFile(file, type, maxAge) {
|
|||
}
|
||||
|
||||
if (type === 'text/xsl' || type === 'text/plain' || type === 'application/javascript') {
|
||||
buf = buf.toString().replace(blogRegex, config.url.replace(/\/$/, ''));
|
||||
buf = buf.toString().replace(apiRegex, config.apiUrl({cors: true}));
|
||||
buf = buf.toString().replace(blogRegex, config.get('url').replace(/\/$/, ''));
|
||||
buf = buf.toString().replace(apiRegex, utils.url.apiUrl({cors: true}));
|
||||
}
|
||||
content = {
|
||||
headers: {
|
||||
|
|
|
@ -21,7 +21,7 @@ function forwardToExpressStatic(req, res, next) {
|
|||
next();
|
||||
} else {
|
||||
express.static(
|
||||
path.join(config.paths.themePath, req.app.get('activeTheme')),
|
||||
path.join(config.getContentPath('themes'), req.app.get('activeTheme')),
|
||||
{maxAge: utils.ONE_YEAR_MS}
|
||||
)(req, res, next);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ themeHandler = {
|
|||
ghostLocals: function ghostLocals(req, res, next) {
|
||||
// Make sure we have a locals value.
|
||||
res.locals = res.locals || {};
|
||||
res.locals.version = config.ghostVersion;
|
||||
res.locals.safeVersion = config.ghostVersion.match(/^(\d+\.)?(\d+)/)[0];
|
||||
res.locals.version = config.get('ghostVersion');
|
||||
res.locals.safeVersion = config.get('ghostVersion').match(/^(\d+\.)?(\d+)/)[0];
|
||||
// relative path from the URL
|
||||
res.locals.relativeUrl = req.path;
|
||||
|
||||
|
@ -26,13 +26,13 @@ themeHandler = {
|
|||
// ### configHbsForContext Middleware
|
||||
// Setup handlebars for the current context (admin or theme)
|
||||
configHbsForContext: function configHbsForContext(req, res, next) {
|
||||
var themeData = _.cloneDeep(config.theme),
|
||||
labsData = _.cloneDeep(config.labs),
|
||||
var themeData = _.cloneDeep(config.get('theme')),
|
||||
labsData = _.cloneDeep(config.get('labs')),
|
||||
blogApp = req.app;
|
||||
|
||||
if (req.secure && config.urlSSL) {
|
||||
if (req.secure && config.get('urlSSL')) {
|
||||
// For secure requests override .url property with the SSL version
|
||||
themeData.url = config.urlSSL.replace(/\/$/, '');
|
||||
themeData.url = config.get('urlSSL').replace(/\/$/, '');
|
||||
}
|
||||
|
||||
// Change camelCase to snake_case
|
||||
|
@ -41,8 +41,8 @@ themeHandler = {
|
|||
|
||||
hbs.updateTemplateOptions({data: {blog: themeData, labs: labsData}});
|
||||
|
||||
if (config.paths.themePath && blogApp.get('activeTheme')) {
|
||||
blogApp.set('views', path.join(config.paths.themePath, blogApp.get('activeTheme')));
|
||||
if (config.getContentPath('themes') && blogApp.get('activeTheme')) {
|
||||
blogApp.set('views', path.join(config.getContentPath('themes'), blogApp.get('activeTheme')));
|
||||
}
|
||||
|
||||
// Pass 'secure' flag to the view engine
|
||||
|
@ -56,14 +56,14 @@ themeHandler = {
|
|||
// Helper for updateActiveTheme
|
||||
activateTheme: function activateTheme(blogApp, activeTheme) {
|
||||
var hbsOptions,
|
||||
themePartials = path.join(config.paths.themePath, activeTheme, 'partials');
|
||||
themePartials = path.join(config.getContentPath('themes'), activeTheme, 'partials');
|
||||
|
||||
// clear the view cache
|
||||
blogApp.cache = {};
|
||||
|
||||
// set view engine
|
||||
hbsOptions = {
|
||||
partialsDir: [config.paths.helperTemplates],
|
||||
partialsDir: [config.get('paths').helperTemplates],
|
||||
onCompile: function onCompile(exhbs, source) {
|
||||
return exhbs.handlebars.compile(source, {preventIndent: true});
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ themeHandler = {
|
|||
// Check if the theme changed
|
||||
if (activeTheme.value !== blogApp.get('activeTheme')) {
|
||||
// Change theme
|
||||
if (!config.paths.availableThemes.hasOwnProperty(activeTheme.value)) {
|
||||
if (!config.get('paths').availableThemes.hasOwnProperty(activeTheme.value)) {
|
||||
if (!res.isAdmin) {
|
||||
// Throw an error if the theme is not available, but not on the admin UI
|
||||
return errors.throwError(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value}));
|
||||
|
|
|
@ -8,8 +8,8 @@ module.exports = function upload(options) {
|
|||
|
||||
// if we finish the data/importer logic, we forward the request to the specified importer
|
||||
return function (req, res, next) {
|
||||
var extensions = (config.uploads[type] && config.uploads[type].extensions) || [],
|
||||
contentTypes = (config.uploads[type] && config.uploads[type].contentTypes) || [];
|
||||
var extensions = (config.get('uploads')[type] && config.get('uploads')[type].extensions) || [],
|
||||
contentTypes = (config.get('uploads')[type] && config.get('uploads')[type].contentTypes) || [];
|
||||
|
||||
req.file = req.file || {};
|
||||
req.file.name = req.file.originalname;
|
||||
|
|
|
@ -525,9 +525,9 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
// Check the filtered slug doesn't match any of the reserved keywords
|
||||
return filters.doFilter('slug.reservedSlugs', config.slugs.reserved).then(function then(slugList) {
|
||||
return filters.doFilter('slug.reservedSlugs', config.get('slugs').reserved).then(function then(slugList) {
|
||||
// Some keywords cannot be changed
|
||||
slugList = _.union(slugList, config.slugs.protected);
|
||||
slugList = _.union(slugList, utils.url.getProtectedSlugs());
|
||||
|
||||
return _.includes(slugList, slug) ? slug + '-' + baseName : slug;
|
||||
}).then(function then(slug) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
var config = require('../../config'),
|
||||
events = require(config.paths.corePath + '/server/events'),
|
||||
models = require(config.paths.corePath + '/server/models'),
|
||||
errors = require(config.paths.corePath + '/server/errors'),
|
||||
sequence = require(config.paths.corePath + '/server/utils/sequence'),
|
||||
events = require(config.get('paths:corePath') + '/server/events'),
|
||||
models = require(config.get('paths:corePath') + '/server/models'),
|
||||
errors = require(config.get('paths:corePath') + '/server/errors'),
|
||||
sequence = require(config.get('paths:corePath') + '/server/utils/sequence'),
|
||||
moment = require('moment-timezone');
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ var _ = require('lodash'),
|
|||
ghostBookshelf = require('./base'),
|
||||
events = require('../events'),
|
||||
config = require('../config'),
|
||||
utils = require('../utils'),
|
||||
baseUtils = require('./base/utils'),
|
||||
i18n = require('../i18n'),
|
||||
Post,
|
||||
|
@ -176,10 +177,10 @@ Post = ghostBookshelf.Model.extend({
|
|||
i18n.t('errors.models.post.valueCannotBeBlank', {key: 'published_at'})
|
||||
));
|
||||
// CASE: to schedule/reschedule a post, a minimum diff of x minutes is needed (default configured is 2minutes)
|
||||
} else if (publishedAtHasChanged && moment(publishedAt).isBefore(moment().add(config.times.cannotScheduleAPostBeforeInMinutes, 'minutes'))) {
|
||||
} else if (publishedAtHasChanged && moment(publishedAt).isBefore(moment().add(config.get('times').cannotScheduleAPostBeforeInMinutes, 'minutes'))) {
|
||||
return Promise.reject(new errors.ValidationError(
|
||||
i18n.t('errors.models.post.expectedPublishedAtInFuture', {
|
||||
cannotScheduleAPostBeforeInMinutes: config.times.cannotScheduleAPostBeforeInMinutes
|
||||
cannotScheduleAPostBeforeInMinutes: config.get('times').cannotScheduleAPostBeforeInMinutes
|
||||
})
|
||||
));
|
||||
}
|
||||
|
@ -419,7 +420,7 @@ Post = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
if (!options.columns || (options.columns && options.columns.indexOf('url') > -1)) {
|
||||
attrs.url = config.urlPathForPost(attrs);
|
||||
attrs.url = utils.url.urlPathForPost(attrs);
|
||||
}
|
||||
|
||||
return attrs;
|
||||
|
|
|
@ -9,8 +9,8 @@ var express = require('express'),
|
|||
|
||||
frontendRoutes = function frontendRoutes() {
|
||||
var router = express.Router(),
|
||||
subdir = config.paths.subdir,
|
||||
routeKeywords = config.routeKeywords;
|
||||
subdir = utils.url.getSubdir(),
|
||||
routeKeywords = config.get('routeKeywords');
|
||||
|
||||
// ### Admin routes
|
||||
router.get(/^\/(logout|signout)\/$/, function redirectToSignout(req, res) {
|
||||
|
@ -33,8 +33,8 @@ frontendRoutes = function frontendRoutes() {
|
|||
|
||||
// setup routes for internal apps
|
||||
// @TODO: refactor this to be a proper app route hook for internal & external apps
|
||||
config.internalApps.forEach(function (appName) {
|
||||
var app = require(path.join(config.paths.internalAppPath, appName));
|
||||
config.get('internalApps').forEach(function (appName) {
|
||||
var app = require(path.join(config.get('paths').internalAppPath, appName));
|
||||
if (app.hasOwnProperty('setupRoutes')) {
|
||||
app.setupRoutes(router);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var Promise = require('bluebird'),
|
||||
moment = require('moment'),
|
||||
utils = require(__dirname + '/../utils'),
|
||||
localUtils = require(__dirname + '/../utils'),
|
||||
events = require(__dirname + '/../../events'),
|
||||
errors = require(__dirname + '/../../errors'),
|
||||
models = require(__dirname + '/../../models'),
|
||||
|
@ -53,7 +53,7 @@ exports.init = function init(options) {
|
|||
.then(function (_client) {
|
||||
client = _client;
|
||||
|
||||
return utils.createAdapter(config);
|
||||
return localUtils.createAdapter(config);
|
||||
})
|
||||
.then(function (_adapter) {
|
||||
adapter = _adapter;
|
||||
|
|
|
@ -8,7 +8,8 @@ exports.createAdapter = function (options) {
|
|||
|
||||
var adapter = null,
|
||||
activeAdapter = options.active,
|
||||
path = options.path;
|
||||
internalPath = options.internalPath,
|
||||
contentPath = options.contentPath;
|
||||
|
||||
if (!activeAdapter) {
|
||||
return Promise.reject(new errors.IncorrectUsage('Please provide an active adapter.'));
|
||||
|
@ -29,10 +30,27 @@ exports.createAdapter = function (options) {
|
|||
* CASE: active adapter is located in specific ghost path
|
||||
*/
|
||||
try {
|
||||
adapter = adapter || new (require(path + activeAdapter))(options);
|
||||
adapter = adapter || new (require(contentPath + activeAdapter))(options);
|
||||
} catch (err) {
|
||||
// CASE: only throw error if module does exist
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
}
|
||||
// CASE: if module not found it can be an error within the adapter (cannot find bluebird for example)
|
||||
else if (err.code === 'MODULE_NOT_FOUND' && err.message.indexOf(contentPath + activeAdapter) === -1) {
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CASE: active adapter is located in internal ghost path
|
||||
*/
|
||||
try {
|
||||
adapter = adapter || new (require(internalPath + activeAdapter))(options);
|
||||
} catch (err) {
|
||||
// CASE: only throw error if module does exist
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
return Promise.reject(new errors.IncorrectUsage('MODULE_NOT_FOUND', activeAdapter));
|
||||
return Promise.reject(new errors.IncorrectUsage('We cannot find your adapter in: ' + contentPath + ' or: ' + internalPath));
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue