diff --git a/.editorconfig b/.editorconfig
index 636ceea4c0..06dd60bfd1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -33,4 +33,4 @@ indent_style = tab
# Standard at:
[Makefile]
-indent_style = tab
\ No newline at end of file
+indent_style = tab
diff --git a/.gitignore b/.gitignore
index 79f6cf2871..721705ea02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,5 @@
npm-debug.log
node_modules/
public/lib
-app/tests/coverage/
.bower-*/
.idea/
-config/sslcert/*.pem
diff --git a/.jshintrc b/.jshintrc
index 4cd07cdcab..b3a00dc361 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -1,5 +1,7 @@
{
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
+ "mocha": true, // Enable globals available when code is running inside of the Mocha tests.
+ "jasmine": true, // Enable globals available when code is running inside of the Jasmine tests.
"browser": true, // Standard browser globals e.g. `window`, `document`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
@@ -18,25 +20,17 @@
"trailing": true, // Prohibit trailing whitespaces.
"smarttabs": false, // Suppresses warnings about mixed tabs and spaces
"globals": { // Globals variables.
- "jasmine": true,
"angular": true,
+ "io": true,
"ApplicationConfiguration": true
},
"predef": [ // Extra globals.
- "define",
- "require",
- "exports",
- "module",
- "describe",
- "before",
- "beforeEach",
- "after",
- "afterEach",
- "it",
"inject",
- "expect"
+ "by",
+ "browser",
+ "element"
],
"indent": 4, // Specify indentation spacing
"devel": true, // Allow development statements e.g. `console.log();`.
"noempty": true // Prohibit use of empty blocks.
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index 6f7d5f4f34..dec285d123 100644
--- a/README.md
+++ b/README.md
@@ -2,21 +2,20 @@
[![Build Status](https://travis-ci.org/meanjs/mean.svg?branch=master)](https://travis-ci.org/meanjs/mean)
[![Dependencies Status](https://david-dm.org/meanjs/mean.svg)](https://david-dm.org/meanjs/mean)
-[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/meanjs/mean?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
MEAN.JS is a full-stack JavaScript open-source solution, which provides a solid starting point for [MongoDB](http://www.mongodb.org/), [Node.js](http://www.nodejs.org/), [Express](http://expressjs.com/), and [AngularJS](http://angularjs.org/) based applications. The idea is to solve the common issues with connecting those frameworks, build a robust framework to support daily development needs, and help developers use better practices while working with popular JavaScript components.
## Before You Begin
Before you begin we recommend you read about the basic building blocks that assemble a MEAN.JS application:
* MongoDB - Go through [MongoDB Official Website](http://mongodb.org/) and proceed to their [Official Manual](http://docs.mongodb.org/manual/), which should help you understand NoSQL and MongoDB better.
-* Express - The best way to understand express is through its [Official Website](http://expressjs.com/), which has a [Getting Started](http://expressjs.com/starter/installing.html) guide, as well as an [ExpressJS Guide](http://expressjs.com/guide/error-handling.html) guide for general express topics. You can also go through this [StackOverflow Thread](http://stackoverflow.com/questions/8144214/learning-express-for-node-js) for more resources.
+* Express - The best way to understand express is through its [Official Website](http://expressjs.com/), particularly [The Express Guide](http://expressjs.com/guide.html); you can also go through this [StackOverflow Thread](http://stackoverflow.com/questions/8144214/learning-express-for-node-js) for more resources.
* AngularJS - Angular's [Official Website](http://angularjs.org/) is a great starting point. You can also use [Thinkster Popular Guide](http://www.thinkster.io/), and the [Egghead Videos](https://egghead.io/).
* Node.js - Start by going through [Node.js Official Website](http://nodejs.org/) and this [StackOverflow Thread](http://stackoverflow.com/questions/2353818/how-do-i-get-started-with-node-js), which should get you going with the Node.js platform in no time.
## Prerequisites
Make sure you have installed all these prerequisites on your development machine.
-* Node.js - [Download & Install Node.js](http://www.nodejs.org/download/) and the npm package manager, if you encounter any problems, you can also use this [GitHub Gist](https://gist.github.com/isaacs/579814) to install Node.js.
+* Node.js - [Download & Install Node.js](http://www.nodejs.org/download/) and the npm package manager, if you encounter any problems, you can also use this [Github Gist](https://gist.github.com/isaacs/579814) to install Node.js.
* MongoDB - [Download & Install MongoDB](http://www.mongodb.org/downloads), and make sure it's running on the default port (27017).
* Bower - You're going to use the [Bower Package Manager](http://bower.io/) to manage your front-end packages, in order to install it make sure you've installed Node.js and npm, then install bower globally using npm:
@@ -44,7 +43,7 @@ $ git clone https://github.com/meanjs/mean.git meanjs
This will clone the latest version of the MEAN.JS repository to a **meanjs** folder.
### Downloading The Repository Zip File
-Another way to use the MEAN.JS boilerplate is to download a zip copy from the [master branch on GitHub](https://github.com/meanjs/mean/archive/master.zip). You can also do this using `wget` command:
+Another way to use the MEAN.JS boilerplate is to download a zip copy from the [master branch on github](https://github.com/meanjs/mean/archive/master.zip). You can also do this using `wget` command:
```
$ wget https://github.com/meanjs/mean/archive/master.zip -O meanjs.zip; unzip meanjs.zip; rm meanjs.zip
```
@@ -64,7 +63,7 @@ $ npm install
This command does a few things:
* First it will install the dependencies needed for the application to run.
* If you're running in a development environment, it will then also install development dependencies needed for testing and running your application.
-* Finally, when the install process is over, npm will initiate a bower install command to install all the front-end modules needed for the application
+* Finally, when the install process is over, npm will initiate a bower installcommand to install all the front-end modules needed for the application
## Running Your Application
After the install process is over, you'll be able to run your application using Grunt, just run grunt default task:
@@ -98,20 +97,12 @@ $
* To enable live reload forward 35729 port and mount /app and /public as volumes:
```bash
-$ docker run -p 3000:3000 -p 35729:35729 -v /Users/mdl/workspace/mean-stack/mean/public:/home/mean/public -v /Users/mdl/workspace/mean-stack/mean/app:/home/mean/app --link db:db_1 mean
+$ docker run -p 3000:3000 -p 35729:35729 -v /Users/mdl/workspace/mean-stack/mean/public:/home/mean/public -v /Users/mdl/workspa/mean-stack/mean/app:/home/mean/app --link db:db_1 mean
```
-## Running in a secure environment
-To run your application in a secure manner you'll need to use OpenSSL and generate a set of self-signed certificates. Unix-based users can use the following commnad:
-```
-$ sh generate-ssl-certs.sh
-```
-Windows users can follow instructions found [here](http://www.websense.com/support/article/kbarticle/How-to-use-OpenSSL-and-Microsoft-Certification-Authority)
-To generate the key and certificate and place them in the *config/sslcert* folder.
-
## Getting Started With MEAN.JS
-You have your application running but there are a lot of stuff to understand, we recommend you'll go over the [Official Documentation](http://meanjs.org/docs.html).
-In the docs we'll try to explain both general concepts of MEAN components and give you some guidelines to help you improve your development process. We tried covering as many aspects as possible, and will keep update it by your request, you can also help us develop the documentation better by checking out the *gh-pages* branch of this repository.
+You have your application running but there are a lot of stuff to understand, we recommend you'll go over the [Offical Documentation](http://meanjs.org/docs.html).
+In the docs we'll try to explain both general concepts of MEAN components and give you some guidelines to help you improve your development procees. We tried covering as many aspects as possible, and will keep update it by your request, you can also help us develop the documentation better by checking out the *gh-pages* branch of this repository.
## Community
* Use to [Offical Website](http://meanjs.org) to learn about changes and the roadmap.
diff --git a/app/controllers/core.server.controller.js b/app/controllers/core.server.controller.js
deleted file mode 100644
index f2af8e1f7a..0000000000
--- a/app/controllers/core.server.controller.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-exports.index = function(req, res) {
- res.render('index', {
- user: req.user || null,
- request: req
- });
-};
\ No newline at end of file
diff --git a/app/controllers/users/users.profile.server.controller.js b/app/controllers/users/users.profile.server.controller.js
deleted file mode 100644
index dd38936fce..0000000000
--- a/app/controllers/users/users.profile.server.controller.js
+++ /dev/null
@@ -1,56 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-var _ = require('lodash'),
- errorHandler = require('../errors.server.controller.js'),
- mongoose = require('mongoose'),
- passport = require('passport'),
- User = mongoose.model('User');
-
-/**
- * Update user details
- */
-exports.update = function(req, res) {
- // Init Variables
- var user = req.user;
- var message = null;
-
- // For security measurement we remove the roles from the req.body object
- delete req.body.roles;
-
- if (user) {
- // Merge existing user
- user = _.extend(user, req.body);
- user.updated = Date.now();
- user.displayName = user.firstName + ' ' + user.lastName;
-
- user.save(function(err) {
- if (err) {
- return res.status(400).send({
- message: errorHandler.getErrorMessage(err)
- });
- } else {
- req.login(user, function(err) {
- if (err) {
- res.status(400).send(err);
- } else {
- res.json(user);
- }
- });
- }
- });
- } else {
- res.status(400).send({
- message: 'User is not signed in'
- });
- }
-};
-
-/**
- * Send User
- */
-exports.me = function(req, res) {
- res.json(req.user || null);
-};
\ No newline at end of file
diff --git a/app/routes/articles.server.routes.js b/app/routes/articles.server.routes.js
deleted file mode 100644
index 7d840e6fc2..0000000000
--- a/app/routes/articles.server.routes.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-var users = require('../../app/controllers/users.server.controller'),
- articles = require('../../app/controllers/articles.server.controller');
-
-module.exports = function(app) {
- // Article Routes
- app.route('/articles')
- .get(articles.list)
- .post(users.requiresLogin, articles.create);
-
- app.route('/articles/:articleId')
- .get(articles.read)
- .put(users.requiresLogin, articles.hasAuthorization, articles.update)
- .delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
-
- // Finish by binding the article middleware
- app.param('articleId', articles.articleByID);
-};
\ No newline at end of file
diff --git a/app/routes/core.server.routes.js b/app/routes/core.server.routes.js
deleted file mode 100644
index 1db9d40074..0000000000
--- a/app/routes/core.server.routes.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict';
-
-module.exports = function(app) {
- // Root routing
- var core = require('../../app/controllers/core.server.controller');
- app.route('/').get(core.index);
-};
\ No newline at end of file
diff --git a/app/routes/users.server.routes.js b/app/routes/users.server.routes.js
deleted file mode 100644
index 3120e9abb6..0000000000
--- a/app/routes/users.server.routes.js
+++ /dev/null
@@ -1,57 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-var passport = require('passport');
-
-module.exports = function(app) {
- // User Routes
- var users = require('../../app/controllers/users.server.controller');
-
- // Setting up the users profile api
- app.route('/users/me').get(users.me);
- app.route('/users').put(users.update);
- app.route('/users/accounts').delete(users.removeOAuthProvider);
-
- // Setting up the users password api
- app.route('/users/password').post(users.changePassword);
- app.route('/auth/forgot').post(users.forgot);
- app.route('/auth/reset/:token').get(users.validateResetToken);
- app.route('/auth/reset/:token').post(users.reset);
-
- // Setting up the users authentication api
- app.route('/auth/signup').post(users.signup);
- app.route('/auth/signin').post(users.signin);
- app.route('/auth/signout').get(users.signout);
-
- // Setting the facebook oauth routes
- app.route('/auth/facebook').get(passport.authenticate('facebook', {
- scope: ['email']
- }));
- app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));
-
- // Setting the twitter oauth routes
- app.route('/auth/twitter').get(passport.authenticate('twitter'));
- app.route('/auth/twitter/callback').get(users.oauthCallback('twitter'));
-
- // Setting the google oauth routes
- app.route('/auth/google').get(passport.authenticate('google', {
- scope: [
- 'https://www.googleapis.com/auth/userinfo.profile',
- 'https://www.googleapis.com/auth/userinfo.email'
- ]
- }));
- app.route('/auth/google/callback').get(users.oauthCallback('google'));
-
- // Setting the linkedin oauth routes
- app.route('/auth/linkedin').get(passport.authenticate('linkedin'));
- app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin'));
-
- // Setting the github oauth routes
- app.route('/auth/github').get(passport.authenticate('github'));
- app.route('/auth/github/callback').get(users.oauthCallback('github'));
-
- // Finish by binding the user middleware
- app.param('userId', users.userByID);
-};
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 3d7a7451dc..951e48aaac 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "meanjs",
- "version": "0.3.2",
+ "version": "0.4.0",
"description": "Fullstack JavaScript with MongoDB, Express, AngularJS, and Node.js.",
"dependencies": {
"bootstrap": "~3",
@@ -8,8 +8,9 @@
"angular-resource": "~1.2",
"angular-animate": "~1.2",
"angular-mocks": "~1.2",
- "angular-bootstrap": "~0.11.2",
+ "angular-bootstrap": "~0.11.0",
"angular-ui-utils": "~0.1.1",
- "angular-ui-router": "~0.2.11"
+ "angular-ui-router": "~0.2.10",
+ "angular-file-upload": "~1.1.5"
}
-}
\ No newline at end of file
+}
diff --git a/config/assets/default.js b/config/assets/default.js
new file mode 100644
index 0000000000..8d614fd4d3
--- /dev/null
+++ b/config/assets/default.js
@@ -0,0 +1,47 @@
+'use strict';
+
+module.exports = {
+ client: {
+ lib: {
+ css: [
+ 'public/lib/bootstrap/dist/css/bootstrap.css',
+ 'public/lib/bootstrap/dist/css/bootstrap-theme.css'
+ ],
+ js: [
+ 'public/lib/angular/angular.js',
+ 'public/lib/angular-resource/angular-resource.js',
+ 'public/lib/angular-animate/angular-animate.js',
+ 'public/lib/angular-ui-router/release/angular-ui-router.js',
+ 'public/lib/angular-ui-utils/ui-utils.js',
+ 'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
+ 'public/lib/angular-file-upload/angular-file-upload.js'
+ ],
+ tests: ['public/lib/angular-mocks/angular-mocks.js']
+ },
+ css: [
+ 'modules/*/client/css/*.css'
+ ],
+ less: [
+ 'modules/*/client/less/*.less'
+ ],
+ sass: [
+ 'modules/*/client/scss/*.scss'
+ ],
+ js: [
+ 'modules/core/client/app/config.js',
+ 'modules/core/client/app/init.js',
+ 'modules/*/client/*.js',
+ 'modules/*/client/**/*.js'
+ ],
+ views: ['modules/*/client/views/**/*.html']
+ },
+ server: {
+ allJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'modules/*/server/**/*.js'],
+ models: 'modules/*/server/models/**/*.js',
+ routes: ['modules/*[!core]/server/routes/**/*.js', 'modules/core/server/routes/**/*.js'],
+ sockets: 'modules/*/server/sockets/**/*.js',
+ config: 'modules/*/server/config/*.js',
+ policies: 'modules/*/server/policies/*.js',
+ views: 'modules/*/server/views/*.html'
+ }
+};
diff --git a/config/assets/development.js b/config/assets/development.js
new file mode 100644
index 0000000000..47a2c6d20a
--- /dev/null
+++ b/config/assets/development.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = {
+ // Development assets
+};
\ No newline at end of file
diff --git a/config/assets/production.js b/config/assets/production.js
new file mode 100644
index 0000000000..9a664c455b
--- /dev/null
+++ b/config/assets/production.js
@@ -0,0 +1,23 @@
+'use strict';
+
+module.exports = {
+ client: {
+ lib: {
+ css: [
+ 'public/lib/bootstrap/dist/css/bootstrap.min.css',
+ 'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
+ ],
+ js: [
+ 'public/lib/angular/angular.min.js',
+ 'public/lib/angular-resource/angular-resource.min.js',
+ 'public/lib/angular-animate/angular-animate.min.js',
+ 'public/lib/angular-ui-router/release/angular-ui-router.min.js',
+ 'public/lib/angular-ui-utils/ui-utils.min.js',
+ 'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',
+ 'public/lib/angular-file-upload/angular-file-upload.min.js'
+ ]
+ },
+ css: 'public/dist/application.min.css',
+ js: 'public/dist/application.min.js'
+ }
+};
diff --git a/config/assets/test.js b/config/assets/test.js
new file mode 100644
index 0000000000..ecc4cac91d
--- /dev/null
+++ b/config/assets/test.js
@@ -0,0 +1,9 @@
+'use strict';
+
+module.exports = {
+ tests: {
+ client: ['modules/*/tests/client/**/*.js'],
+ server: ['modules/*/tests/server/**/*.js'],
+ e2e: ['modules/*/tests/e2e/**/*.js']
+ }
+};
\ No newline at end of file
diff --git a/config/config.js b/config/config.js
index 3baa02e552..a966dde2f1 100644
--- a/config/config.js
+++ b/config/config.js
@@ -4,73 +4,169 @@
* Module dependencies.
*/
var _ = require('lodash'),
- glob = require('glob');
+ chalk = require('chalk'),
+ glob = require('glob'),
+ path = require('path');
/**
- * Load app configurations
+ * Get files by glob patterns
*/
-module.exports = _.extend(
- require('./env/all'),
- require('./env/' + process.env.NODE_ENV) || {}
-);
+var getGlobbedPaths = function(globPatterns, excludes) {
+ // URL paths regex
+ var urlRegex = new RegExp('^(?:[a-z]+:)?\/\/', 'i');
+
+ // The output array
+ var output = [];
+
+ // If glob pattern is array so we use each pattern in a recursive way, otherwise we use glob
+ if (_.isArray(globPatterns)) {
+ globPatterns.forEach(function(globPattern) {
+ output = _.union(output, getGlobbedPaths(globPattern, excludes));
+ });
+ } else if (_.isString(globPatterns)) {
+ if (urlRegex.test(globPatterns)) {
+ output.push(globPatterns);
+ } else {
+ glob(globPatterns, {
+ sync: true
+ }, function(err, files) {
+ if (excludes) {
+ files = files.map(function(file) {
+ if (_.isArray(excludes)) {
+ for (var i in excludes) {
+ file = file.replace(excludes[i], '');
+ }
+ } else {
+ file = file.replace(excludes, '');
+ }
+
+ return file;
+ });
+ }
+
+ output = _.union(output, files);
+ });
+ }
+ }
+
+ return output;
+};
/**
- * Get files by glob patterns
+ * Validate NODE_ENV existance
+ */
+var validateEnvironmentVariable = function() {
+ glob('./config/env/' + process.env.NODE_ENV + '.js', {
+ sync: true
+ }, function(err, environmentFiles) {
+ console.log();
+
+ if (!environmentFiles.length) {
+ if (process.env.NODE_ENV) {
+ console.error(chalk.red('No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead'));
+ } else {
+ console.error(chalk.red('NODE_ENV is not defined! Using default development environment'));
+ }
+
+ process.env.NODE_ENV = 'development';
+ } else {
+ console.log(chalk.bold('Application loaded using the "' + process.env.NODE_ENV + '" environment configuration'));
+ }
+
+ // Reset console color
+ console.log(chalk.white(''));
+ });
+};
+
+/**
+ * Initialize global configuration files
+ */
+var initGlobalConfigFolders = function(config, assets) {
+ // Appending files
+ config.folders = {
+ server: {},
+ client: {}
+ };
+
+ // Setting globbed client paths
+ config.folders.client = getGlobbedPaths(path.join(process.cwd(), 'modules/*/client/'), process.cwd());
+};
+
+/**
+ * Initialize global configuration files
*/
-module.exports.getGlobbedFiles = function(globPatterns, removeRoot) {
- // For context switching
- var _this = this;
-
- // URL paths regex
- var urlRegex = new RegExp('^(?:[a-z]+:)?\/\/', 'i');
-
- // The output array
- var output = [];
-
- // If glob pattern is array so we use each pattern in a recursive way, otherwise we use glob
- if (_.isArray(globPatterns)) {
- globPatterns.forEach(function(globPattern) {
- output = _.union(output, _this.getGlobbedFiles(globPattern, removeRoot));
- });
- } else if (_.isString(globPatterns)) {
- if (urlRegex.test(globPatterns)) {
- output.push(globPatterns);
- } else {
- glob(globPatterns, {
- sync: true
- }, function(err, files) {
- if (removeRoot) {
- files = files.map(function(file) {
- return file.replace(removeRoot, '');
- });
- }
-
- output = _.union(output, files);
- });
- }
- }
-
- return output;
+var initGlobalConfigFiles = function(config, assets) {
+ // Appending files
+ config.files = {
+ server: {},
+ client: {}
+ };
+
+ // Setting Globbed model files
+ config.files.server.models = getGlobbedPaths(assets.server.models);
+
+ // Setting Globbed route files
+ config.files.server.routes = getGlobbedPaths(assets.server.routes);
+
+ // Setting Globbed config files
+ config.files.server.configs = getGlobbedPaths(assets.server.config);
+
+ // Setting Globbed socket files
+ config.files.server.sockets = getGlobbedPaths(assets.server.sockets);
+
+ // Setting Globbed policies files
+ config.files.server.policies = getGlobbedPaths(assets.server.policies);
+
+ // Setting Globbed js files
+ config.files.client.js = getGlobbedPaths(assets.client.lib.js, 'public/').concat(getGlobbedPaths(assets.client.js, ['client/', 'public/']));
+
+ // Setting Globbed css files
+ config.files.client.css = getGlobbedPaths(assets.client.lib.css, 'public/').concat(getGlobbedPaths(assets.client.css, ['client/', 'public/']));
+
+ // Setting Globbed test files
+ config.files.client.tests = getGlobbedPaths(assets.client.tests);
};
/**
- * Get the modules JavaScript files
+ * Initialize global configuration
*/
-module.exports.getJavaScriptAssets = function(includeTests) {
- var output = this.getGlobbedFiles(this.assets.lib.js.concat(this.assets.js), 'public/');
+var initGlobalConfig = function() {
+ // Validate NDOE_ENV existance
+ validateEnvironmentVariable();
+
+ // Get the default assets
+ var defaultAssets = require(path.join(process.cwd(), 'config/assets/default'));
+
+ // Get the current assets
+ var environmentAssets = require(path.join(process.cwd(), 'config/assets/', process.env.NODE_ENV)) || {};
+
+ // Merge assets
+ var assets = _.extend(defaultAssets, environmentAssets);
+
+ // Get the default config
+ var defaultConfig = require(path.join(process.cwd(), 'config/env/default'));
+
+ // Get the current config
+ var environmentConfig = require(path.join(process.cwd(), 'config/env/', process.env.NODE_ENV)) || {};
+
+ // Merge config files
+ var config = _.extend(defaultConfig, environmentConfig);
+
+ // Initialize global globbed files
+ initGlobalConfigFiles(config, assets);
+
+ // Initialize global globbed folders
+ initGlobalConfigFolders(config, assets);
- // To include tests
- if (includeTests) {
- output = _.union(output, this.getGlobbedFiles(this.assets.tests));
- }
+ // Expose configuration utilities
+ config.utils = {
+ getGlobbedPaths: getGlobbedPaths
+ };
- return output;
+ return config;
};
/**
- * Get the modules CSS files
+ * Set configuration object
*/
-module.exports.getCSSAssets = function() {
- var output = this.getGlobbedFiles(this.assets.lib.css.concat(this.assets.css), 'public/');
- return output;
-};
\ No newline at end of file
+module.exports = initGlobalConfig();
diff --git a/config/env/all.js b/config/env/all.js
deleted file mode 100644
index b4e1749e75..0000000000
--- a/config/env/all.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict';
-
-module.exports = {
- app: {
- title: 'MEAN.JS',
- description: 'Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js',
- keywords: 'mongodb, express, angularjs, node.js, mongoose, passport'
- },
- port: process.env.PORT || 3000,
- templateEngine: 'swig',
- sessionSecret: 'MEAN',
- sessionCollection: 'sessions',
- log: {
- // Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
- format: 'combined',
- // Stream defaults to process.stdout
- // Uncomment to enable logging to a log on the file system
- options: {
- stream: 'access.log'
- }
- },
- assets: {
- lib: {
- css: [
- 'public/lib/bootstrap/dist/css/bootstrap.css',
- 'public/lib/bootstrap/dist/css/bootstrap-theme.css',
- ],
- js: [
- 'public/lib/angular/angular.js',
- 'public/lib/angular-resource/angular-resource.js',
- 'public/lib/angular-animate/angular-animate.js',
- 'public/lib/angular-ui-router/release/angular-ui-router.js',
- 'public/lib/angular-ui-utils/ui-utils.js',
- 'public/lib/angular-bootstrap/ui-bootstrap-tpls.js'
- ]
- },
- css: [
- 'public/modules/**/css/*.css'
- ],
- js: [
- 'public/config.js',
- 'public/application.js',
- 'public/modules/*/*.js',
- 'public/modules/*/*[!tests]*/*.js'
- ],
- tests: [
- 'public/lib/angular-mocks/angular-mocks.js',
- 'public/modules/*/tests/*.js'
- ]
- }
-};
diff --git a/config/env/default.js b/config/env/default.js
new file mode 100644
index 0000000000..8acd131a0b
--- /dev/null
+++ b/config/env/default.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = {
+ app: {
+ title: 'MEAN.JS',
+ description: 'Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js',
+ keywords: 'mongodb, express, angularjs, node.js, mongoose, passport',
+ googleAnalyticsTrackingID: process.env.GOOGLE_ANALYTICS_TRACKING_ID || 'GOOGLE_ANALYTICS_TRACKING_ID'
+ },
+ port: process.env.PORT || 3000,
+ templateEngine: 'swig',
+ sessionSecret: 'MEAN',
+ sessionCollection: 'sessions'
+};
diff --git a/config/env/development.js b/config/env/development.js
index 2b895b8bbc..310189454b 100644
--- a/config/env/development.js
+++ b/config/env/development.js
@@ -2,43 +2,34 @@
module.exports = {
db: 'mongodb://localhost/mean-dev',
- log: {
- // Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
- format: 'dev',
- // Stream defaults to process.stdout
- // Uncomment to enable logging to a log on the file system
- options: {
- //stream: 'access.log'
- }
- },
app: {
title: 'MEAN.JS - Development Environment'
},
facebook: {
- clientID: process.env.FACEBOOK_ID || 'APP_ID',
- clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
- callbackURL: '/auth/facebook/callback'
- },
- twitter: {
- clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
- clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
- callbackURL: '/auth/twitter/callback'
- },
- google: {
- clientID: process.env.GOOGLE_ID || 'APP_ID',
- clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
- callbackURL: '/auth/google/callback'
- },
- linkedin: {
- clientID: process.env.LINKEDIN_ID || 'APP_ID',
- clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
- callbackURL: '/auth/linkedin/callback'
- },
- github: {
- clientID: process.env.GITHUB_ID || 'APP_ID',
- clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
- callbackURL: '/auth/github/callback'
- },
+ clientID: process.env.FACEBOOK_ID || 'APP_ID',
+ clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/facebook/callback'
+ },
+ twitter: {
+ clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
+ clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
+ callbackURL: '/api/auth/twitter/callback'
+ },
+ google: {
+ clientID: process.env.GOOGLE_ID || 'APP_ID',
+ clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/google/callback'
+ },
+ linkedin: {
+ clientID: process.env.LINKEDIN_ID || 'APP_ID',
+ clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/linkedin/callback'
+ },
+ github: {
+ clientID: process.env.GITHUB_ID || 'APP_ID',
+ clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/github/callback'
+ },
mailer: {
from: process.env.MAILER_FROM || 'MAILER_FROM',
options: {
diff --git a/config/env/production.js b/config/env/production.js
index c9f2817382..3e87554779 100644
--- a/config/env/production.js
+++ b/config/env/production.js
@@ -1,67 +1,40 @@
'use strict';
module.exports = {
- db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean',
- log: {
- // Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
- format: 'combined',
- // Stream defaults to process.stdout
- // Uncomment to enable logging to a log on the file system
- options: {
- stream: 'access.log'
- }
- },
- assets: {
- lib: {
- css: [
- 'public/lib/bootstrap/dist/css/bootstrap.min.css',
- 'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
- ],
- js: [
- 'public/lib/angular/angular.min.js',
- 'public/lib/angular-resource/angular-resource.min.js',
- 'public/lib/angular-animate/angular-animate.min.js',
- 'public/lib/angular-ui-router/release/angular-ui-router.min.js',
- 'public/lib/angular-ui-utils/ui-utils.min.js',
- 'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js'
- ]
- },
- css: 'public/dist/application.min.css',
- js: 'public/dist/application.min.js'
- },
- facebook: {
- clientID: process.env.FACEBOOK_ID || 'APP_ID',
- clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
- callbackURL: '/auth/facebook/callback'
- },
- twitter: {
- clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
- clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
- callbackURL: '/auth/twitter/callback'
- },
- google: {
- clientID: process.env.GOOGLE_ID || 'APP_ID',
- clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
- callbackURL: '/auth/google/callback'
- },
- linkedin: {
- clientID: process.env.LINKEDIN_ID || 'APP_ID',
- clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
- callbackURL: '/auth/linkedin/callback'
- },
- github: {
- clientID: process.env.GITHUB_ID || 'APP_ID',
- clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
- callbackURL: '/auth/github/callback'
- },
- mailer: {
- from: process.env.MAILER_FROM || 'MAILER_FROM',
- options: {
- service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
- auth: {
- user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
- pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
- }
- }
- }
-};
+ db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean',
+ facebook: {
+ clientID: process.env.FACEBOOK_ID || 'APP_ID',
+ clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/facebook/callback'
+ },
+ twitter: {
+ clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
+ clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
+ callbackURL: '/api/auth/twitter/callback'
+ },
+ google: {
+ clientID: process.env.GOOGLE_ID || 'APP_ID',
+ clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/google/callback'
+ },
+ linkedin: {
+ clientID: process.env.LINKEDIN_ID || 'APP_ID',
+ clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/linkedin/callback'
+ },
+ github: {
+ clientID: process.env.GITHUB_ID || 'APP_ID',
+ clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
+ callbackURL: '/api/auth/github/callback'
+ },
+ mailer: {
+ from: process.env.MAILER_FROM || 'MAILER_FROM',
+ options: {
+ service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
+ auth: {
+ user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
+ pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
+ }
+ }
+ }
+};
\ No newline at end of file
diff --git a/config/env/secure.js b/config/env/secure.js
deleted file mode 100644
index ee2b270ad1..0000000000
--- a/config/env/secure.js
+++ /dev/null
@@ -1,68 +0,0 @@
-'use strict';
-
-module.exports = {
- port: 443,
- db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://localhost/mean',
- log: {
- // Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
- format: 'combined',
- // Stream defaults to process.stdout
- // Uncomment to enable logging to a log on the file system
- options: {
- stream: 'access.log'
- }
- },
- assets: {
- lib: {
- css: [
- 'public/lib/bootstrap/dist/css/bootstrap.min.css',
- 'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
- ],
- js: [
- 'public/lib/angular/angular.min.js',
- 'public/lib/angular-resource/angular-resource.min.js',
- 'public/lib/angular-animate/angular-animate.min.js',
- 'public/lib/angular-ui-router/release/angular-ui-router.min.js',
- 'public/lib/angular-ui-utils/ui-utils.min.js',
- 'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js'
- ]
- },
- css: 'public/dist/application.min.css',
- js: 'public/dist/application.min.js'
- },
- facebook: {
- clientID: process.env.FACEBOOK_ID || 'APP_ID',
- clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
- callbackURL: 'https://localhost:443/auth/facebook/callback'
- },
- twitter: {
- clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
- clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
- callbackURL: 'https://localhost:443/auth/twitter/callback'
- },
- google: {
- clientID: process.env.GOOGLE_ID || 'APP_ID',
- clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
- callbackURL: 'https://localhost:443/auth/google/callback'
- },
- linkedin: {
- clientID: process.env.LINKEDIN_ID || 'APP_ID',
- clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
- callbackURL: 'https://localhost:443/auth/linkedin/callback'
- },
- github: {
- clientID: process.env.GITHUB_ID || 'APP_ID',
- clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
- callbackURL: 'https://localhost:443/auth/github/callback'
- },
- mailer: {
- from: process.env.MAILER_FROM || 'MAILER_FROM',
- options: {
- service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
- auth: {
- user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
- pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
- }
- }
- }
-};
\ No newline at end of file
diff --git a/config/env/test.js b/config/env/test.js
index 22c47c9fb2..6f95fdd3f5 100644
--- a/config/env/test.js
+++ b/config/env/test.js
@@ -3,42 +3,33 @@
module.exports = {
db: 'mongodb://localhost/mean-test',
port: 3001,
- log: {
- // Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
- format: 'dev',
- // Stream defaults to process.stdout
- // Uncomment to enable logging to a log on the file system
- options: {
- //stream: 'access.log'
- }
- },
app: {
title: 'MEAN.JS - Test Environment'
},
facebook: {
clientID: process.env.FACEBOOK_ID || 'APP_ID',
clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
- callbackURL: '/auth/facebook/callback'
+ callbackURL: '/api/auth/facebook/callback'
},
twitter: {
clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
- callbackURL: '/auth/twitter/callback'
+ callbackURL: '/api/auth/twitter/callback'
},
google: {
clientID: process.env.GOOGLE_ID || 'APP_ID',
clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
- callbackURL: '/auth/google/callback'
+ callbackURL: '/api/auth/google/callback'
},
linkedin: {
clientID: process.env.LINKEDIN_ID || 'APP_ID',
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
- callbackURL: '/auth/linkedin/callback'
+ callbackURL: '/api/auth/linkedin/callback'
},
github: {
clientID: process.env.GITHUB_ID || 'APP_ID',
clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
- callbackURL: '/auth/github/callback'
+ callbackURL: '/api/auth/github/callback'
},
mailer: {
from: process.env.MAILER_FROM || 'MAILER_FROM',
diff --git a/config/express.js b/config/express.js
deleted file mode 100755
index f19ba586bb..0000000000
--- a/config/express.js
+++ /dev/null
@@ -1,164 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-var fs = require('fs'),
- http = require('http'),
- https = require('https'),
- express = require('express'),
- morgan = require('morgan'),
- logger = require('./logger'),
- bodyParser = require('body-parser'),
- session = require('express-session'),
- compress = require('compression'),
- methodOverride = require('method-override'),
- cookieParser = require('cookie-parser'),
- helmet = require('helmet'),
- passport = require('passport'),
- mongoStore = require('connect-mongo')({
- session: session
- }),
- flash = require('connect-flash'),
- config = require('./config'),
- consolidate = require('consolidate'),
- path = require('path');
-
-module.exports = function(db) {
- // Initialize express app
- var app = express();
-
- // Globbing model files
- config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
- require(path.resolve(modelPath));
- });
-
- // Setting application local variables
- app.locals.title = config.app.title;
- app.locals.description = config.app.description;
- app.locals.keywords = config.app.keywords;
- app.locals.facebookAppId = config.facebook.clientID;
- app.locals.jsFiles = config.getJavaScriptAssets();
- app.locals.cssFiles = config.getCSSAssets();
-
- // Passing the request url to environment locals
- app.use(function(req, res, next) {
- res.locals.url = req.protocol + '://' + req.headers.host + req.url;
- next();
- });
-
- // Should be placed before express.static
- app.use(compress({
- filter: function(req, res) {
- return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
- },
- level: 9
- }));
-
- // Showing stack errors
- app.set('showStackError', true);
-
- // Set swig as the template engine
- app.engine('server.view.html', consolidate[config.templateEngine]);
-
- // Set views path and view engine
- app.set('view engine', 'server.view.html');
- app.set('views', './app/views');
-
- // Enable logger (morgan)
- app.use(morgan(logger.getLogFormat(), logger.getLogOptions()));
-
- // Environment dependent middleware
- if (process.env.NODE_ENV === 'development') {
- // Disable views cache
- app.set('view cache', false);
- } else if (process.env.NODE_ENV === 'production') {
- app.locals.cache = 'memory';
- }
-
- // Request body parsing middleware should be above methodOverride
- app.use(bodyParser.urlencoded({
- extended: true
- }));
- app.use(bodyParser.json());
- app.use(methodOverride());
-
- // CookieParser should be above session
- app.use(cookieParser());
-
- // Express MongoDB session storage
- app.use(session({
- saveUninitialized: true,
- resave: true,
- secret: config.sessionSecret,
- store: new mongoStore({
- db: db.connection.db,
- collection: config.sessionCollection
- })
- }));
-
- // use passport session
- app.use(passport.initialize());
- app.use(passport.session());
-
- // connect flash for flash messages
- app.use(flash());
-
- // Use helmet to secure Express headers
- app.use(helmet.xframe());
- app.use(helmet.xssFilter());
- app.use(helmet.nosniff());
- app.use(helmet.ienoopen());
- app.disable('x-powered-by');
-
- // Setting the app router and static folder
- app.use(express.static(path.resolve('./public')));
-
- // Globbing routing files
- config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
- require(path.resolve(routePath))(app);
- });
-
- // Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
- app.use(function(err, req, res, next) {
- // If the error object doesn't exists
- if (!err) return next();
-
- // Log it
- console.error(err.stack);
-
- // Error page
- res.status(500).render('500', {
- error: err.stack
- });
- });
-
- // Assume 404 since no middleware responded
- app.use(function(req, res) {
- res.status(404).render('404', {
- url: req.originalUrl,
- error: 'Not Found'
- });
- });
-
- if (process.env.NODE_ENV === 'secure') {
- // Log SSL usage
- console.log('Securely using https protocol');
-
- // Load SSL key and certificate
- var privateKey = fs.readFileSync('./config/sslcerts/key.pem', 'utf8');
- var certificate = fs.readFileSync('./config/sslcerts/cert.pem', 'utf8');
-
- // Create HTTPS Server
- var httpsServer = https.createServer({
- key: privateKey,
- cert: certificate
- }, app);
-
- // Return HTTPS server instance
- return httpsServer;
- }
-
- // Return Express server instance
- return app;
-};
\ No newline at end of file
diff --git a/config/init.js b/config/init.js
deleted file mode 100644
index 6facd9d0f8..0000000000
--- a/config/init.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-var glob = require('glob'),
- chalk = require('chalk');
-
-/**
- * Module init function.
- */
-module.exports = function() {
- /**
- * Before we begin, lets set the environment variable
- * We'll Look for a valid NODE_ENV variable and if one cannot be found load the development NODE_ENV
- */
- glob('./config/env/' + process.env.NODE_ENV + '.js', {
- sync: true
- }, function(err, environmentFiles) {
- if (!environmentFiles.length) {
- if (process.env.NODE_ENV) {
- console.error(chalk.red('No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead'));
- } else {
- console.error(chalk.red('NODE_ENV is not defined! Using default development environment'));
- }
-
- process.env.NODE_ENV = 'development';
- } else {
- console.log(chalk.black.bgWhite('Application loaded using the "' + process.env.NODE_ENV + '" environment configuration'));
- }
- });
-
-};
\ No newline at end of file
diff --git a/config/lib/express.js b/config/lib/express.js
new file mode 100755
index 0000000000..2ad68145b2
--- /dev/null
+++ b/config/lib/express.js
@@ -0,0 +1,251 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var config = require('../config'),
+ express = require('express'),
+ morgan = require('morgan'),
+ bodyParser = require('body-parser'),
+ session = require('express-session'),
+ MongoStore = require('connect-mongo')(session),
+ multer = require('multer'),
+ favicon = require('serve-favicon'),
+ compress = require('compression'),
+ methodOverride = require('method-override'),
+ cookieParser = require('cookie-parser'),
+ helmet = require('helmet'),
+ passport = require('passport'),
+ flash = require('connect-flash'),
+ consolidate = require('consolidate'),
+ path = require('path');
+
+/**
+ * Initialize local variables
+ */
+module.exports.initLocalVariables = function (app) {
+ // Setting application local variables
+ app.locals.title = config.app.title;
+ app.locals.description = config.app.description;
+ app.locals.keywords = config.app.keywords;
+ app.locals.googleAnalyticsTrackingID = config.app.googleAnalyticsTrackingID;
+ app.locals.facebookAppId = config.facebook.clientID;
+ app.locals.jsFiles = config.files.client.js;
+ app.locals.cssFiles = config.files.client.css;
+
+ // Passing the request url to environment locals
+ app.use(function (req, res, next) {
+ res.locals.host = req.protocol + '://' + req.hostname;
+ res.locals.url = req.protocol + '://' + req.headers.host + req.originalUrl;
+ next();
+ });
+};
+
+/**
+ * Initialize application middleware
+ */
+module.exports.initMiddleware = function (app) {
+ // Showing stack errors
+ app.set('showStackError', true);
+
+ // Enable jsonp
+ app.enable('jsonp callback');
+
+ // Should be placed before express.static
+ app.use(compress({
+ filter: function (req, res) {
+ return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
+ },
+ level: 9
+ }));
+
+ // Initialize favicon middleware
+ app.use(favicon('./modules/core/client/img/brand/favicon.ico'));
+
+ // Environment dependent middleware
+ if (process.env.NODE_ENV === 'development') {
+ // Enable logger (morgan)
+ app.use(morgan('dev'));
+
+ // Disable views cache
+ app.set('view cache', false);
+ } else if (process.env.NODE_ENV === 'production') {
+ app.locals.cache = 'memory';
+ }
+
+ // Request body parsing middleware should be above methodOverride
+ app.use(bodyParser.urlencoded({
+ extended: true
+ }));
+ app.use(bodyParser.json());
+ app.use(methodOverride());
+
+ // Add the cookie parser and flash middleware
+ app.use(cookieParser());
+ app.use(flash());
+
+ // Add multipart handling middleware
+ app.use(multer({
+ dest: './uploads/',
+ inMemory: true
+ }));
+};
+
+/**
+ * Configure view engine
+ */
+module.exports.initViewEngine = function (app) {
+ // Set swig as the template engine
+ app.engine('server.view.html', consolidate[config.templateEngine]);
+
+ // Set views path and view engine
+ app.set('view engine', 'server.view.html');
+ app.set('views', './');
+};
+
+/**
+ * Configure Express session
+ */
+module.exports.initSession = function (app, db) {
+ // Express MongoDB session storage
+ app.use(session({
+ saveUninitialized: true,
+ resave: true,
+ secret: config.sessionSecret,
+ store: new MongoStore({
+ db: db.connection.db,
+ collection: config.sessionCollection
+ })
+ }));
+};
+
+/**
+ * Invoke modules server configuration
+ */
+module.exports.initModulesConfiguration = function (app, db) {
+ config.files.server.configs.forEach(function (configPath) {
+ require(path.resolve(configPath))(app, db);
+ });
+};
+
+/**
+ * Configure Helmet headers configuration
+ */
+module.exports.initHelmetHeaders = function (app) {
+ // Use helmet to secure Express headers
+ app.use(helmet.xframe());
+ app.use(helmet.xssFilter());
+ app.use(helmet.nosniff());
+ app.use(helmet.ienoopen());
+ app.disable('x-powered-by');
+};
+
+/**
+ * Configure the modules static routes
+ */
+module.exports.initModulesClientRoutes = function (app) {
+ // Setting the app router and static folder
+ app.use('/', express.static(path.resolve('./public')));
+
+ // Globbing static routing
+ config.folders.client.forEach(function (staticPath) {
+ app.use(staticPath.replace('/client', ''), express.static(path.resolve('./' + staticPath)));
+ });
+};
+
+/**
+ * Configure the modules ACL policies
+ */
+module.exports.initModulesServerPolicies = function (app) {
+ // Globbing policy files
+ config.files.server.policies.forEach(function (policyPath) {
+ require(path.resolve(policyPath)).invokeRolesPolicies();
+ });
+};
+
+/**
+ * Configure the modules server routes
+ */
+module.exports.initModulesServerRoutes = function (app) {
+ // Globbing routing files
+ config.files.server.routes.forEach(function (routePath) {
+ require(path.resolve(routePath))(app);
+ });
+};
+
+/**
+ * Configure error handling
+ */
+module.exports.initErrorRoutes = function (app) {
+ // Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
+ app.use(function (err, req, res, next) {
+ // If the error object doesn't exists
+ if (!err) return next();
+
+ // Log it
+ console.error(err.stack);
+
+ // Redirect to error page
+ res.redirect('/server-error');
+ });
+
+ // Assume 404 since no middleware responded
+ app.use(function (req, res) {
+ // Redirect to not found page
+ res.redirect('/not-found');
+ });
+};
+
+/**
+ * Configure Socket.io
+ */
+module.exports.configureSocketIO = function (app, db) {
+ // Load the Socket.io configuration
+ var server = require('./socket.io')(app, db);
+
+ // Return server object
+ return server;
+};
+
+/**
+ * Initialize the Express application
+ */
+module.exports.init = function (db) {
+ // Initialize express app
+ var app = express();
+
+ // Initialize local variables
+ this.initLocalVariables(app);
+
+ // Initialize Express middleware
+ this.initMiddleware(app);
+
+ // Initialize Express view engine
+ this.initViewEngine(app);
+
+ // Initialize Express session
+ this.initSession(app, db);
+
+ // Initialize Modules configuration
+ this.initModulesConfiguration(app);
+
+ // Initialize Helmet security headers
+ this.initHelmetHeaders(app);
+
+ // Initialize modules static client routes
+ this.initModulesClientRoutes(app);
+
+ // Initialize modules server authorization policies
+ this.initModulesServerPolicies(app);
+
+ // Initialize modules server routes
+ this.initModulesServerRoutes(app);
+
+ // Initialize error routes
+ this.initErrorRoutes(app);
+
+ // Configure Socket.io
+ app = this.configureSocketIO(app, db);
+
+ return app;
+};
diff --git a/config/lib/mongoose.js b/config/lib/mongoose.js
new file mode 100644
index 0000000000..9bb0b74a0e
--- /dev/null
+++ b/config/lib/mongoose.js
@@ -0,0 +1,36 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var config = require('../config'),
+ chalk = require('chalk'),
+ path = require('path'),
+ mongoose = require('mongoose');
+
+// Load the mongoose models
+module.exports.loadModels = function() {
+ // Globbing model files
+ config.files.server.models.forEach(function(modelPath) {
+ require(path.resolve(modelPath));
+ });
+};
+
+// Initialize Mongoose
+module.exports.connect = function(cb) {
+ var _this = this;
+
+ var db = mongoose.connect(config.db, function (err) {
+ // Log Error
+ if (err) {
+ console.error(chalk.red('Could not connect to MongoDB!'));
+ console.log(err);
+ } else {
+ // Load modules
+ _this.loadModels();
+
+ // Call callback FN
+ if (cb) cb(db);
+ }
+ });
+};
diff --git a/config/lib/socket.io.js b/config/lib/socket.io.js
new file mode 100644
index 0000000000..4e89bdee4b
--- /dev/null
+++ b/config/lib/socket.io.js
@@ -0,0 +1,61 @@
+'use strict';
+
+// Load the module dependencies
+var config = require('../config'),
+ path = require('path'),
+ cookieParser = require('cookie-parser'),
+ passport = require('passport'),
+ socketio = require('socket.io'),
+ session = require('express-session'),
+ MongoStore = require('connect-mongo')(session),
+ http = require('http');
+
+// Define the Socket.io configuration method
+module.exports = function(app, db) {
+ // Create a new HTTP server
+ var server = http.createServer(app);
+
+ // Create a new Socket.io server
+ var io = socketio.listen(server);
+
+ // Create a MongoDB storage object
+ var mongoStore = new MongoStore({
+ db: db.connection.db,
+ collection: config.sessionCollection
+ });
+
+ // Intercept Socket.io's handshake request
+ io.use(function(socket, next) {
+ // Use the 'cookie-parser' module to parse the request cookies
+ cookieParser(config.sessionSecret)(socket.request, {}, function(err) {
+ // Get the session id from the request cookies
+ var sessionId = socket.request.signedCookies['connect.sid'];
+
+ // Use the mongoStorage instance to get the Express session information
+ mongoStore.get(sessionId, function(err, session) {
+ // Set the Socket.io session information
+ socket.request.session = session;
+
+ // Use Passport to populate the user details
+ passport.initialize()(socket.request, {}, function() {
+ passport.session()(socket.request, {}, function() {
+ if (socket.request.user) {
+ next(null, true);
+ } else {
+ next(new Error('User is not authenticated'), false);
+ }
+ });
+ });
+ });
+ });
+ });
+
+ // Add an event listener to the 'connection' event
+ io.on('connection', function(socket) {
+ config.files.server.sockets.forEach(function(socketConfiguration) {
+ require(path.resolve(socketConfiguration))(io, socket);
+ });
+ });
+
+ return server;
+};
\ No newline at end of file
diff --git a/config/logger.js b/config/logger.js
deleted file mode 100644
index 98b3a4c5b9..0000000000
--- a/config/logger.js
+++ /dev/null
@@ -1,36 +0,0 @@
-'use strict';
-
-/**
- * Module dependencies.
- */
-
-var morgan = require('morgan');
-var config = require('./config');
-var fs = require('fs');
-
-/**
- * Module init function.
- */
-module.exports = {
-
- getLogFormat: function() {
- return config.log.format;
- },
-
- getLogOptions: function() {
- var options = {};
-
- try {
- if ('stream' in config.log.options) {
- options = {
- stream: fs.createWriteStream(process.cwd() + '/' + config.log.options.stream, {flags: 'a'})
- };
- }
- } catch (e) {
- options = {};
- }
-
- return options;
- }
-
-};
\ No newline at end of file
diff --git a/gruntfile.js b/gruntfile.js
index 4555485743..ea4eb0d88e 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -1,68 +1,117 @@
'use strict';
-module.exports = function(grunt) {
- // Unified Watch Object
- var watchFiles = {
- serverViews: ['app/views/**/*.*'],
- serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js'],
- clientViews: ['public/modules/**/views/**/*.html'],
- clientJS: ['public/js/*.js', 'public/modules/**/*.js'],
- clientCSS: ['public/modules/**/*.css'],
- mochaTests: ['app/tests/**/*.js']
- };
+/**
+ * Module dependencies.
+ */
+var _ = require('lodash'),
+ defaultAssets = require('./config/assets/default'),
+ testAssets = require('./config/assets/test');
+module.exports = function (grunt) {
// Project Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
+ env: {
+ test: {
+ NODE_ENV: 'test'
+ },
+ dev: {
+ NODE_ENV: 'development'
+ },
+ prod: {
+ NODE_ENV: 'production'
+ }
+ },
watch: {
serverViews: {
- files: watchFiles.serverViews,
+ files: defaultAssets.server.views,
options: {
livereload: true
}
},
serverJS: {
- files: watchFiles.serverJS,
+ files: defaultAssets.server.allJS,
tasks: ['jshint'],
options: {
livereload: true
}
},
clientViews: {
- files: watchFiles.clientViews,
+ files: defaultAssets.client.views,
options: {
- livereload: true,
+ livereload: true
}
},
clientJS: {
- files: watchFiles.clientJS,
+ files: defaultAssets.client.js,
tasks: ['jshint'],
options: {
livereload: true
}
},
clientCSS: {
- files: watchFiles.clientCSS,
+ files: defaultAssets.client.css,
tasks: ['csslint'],
options: {
livereload: true
}
+ },
+ clientSCSS: {
+ files: defaultAssets.client.sass,
+ tasks: ['sass', 'csslint'],
+ options: {
+ livereload: true
+ }
+ },
+ clientLESS: {
+ files: defaultAssets.client.less,
+ tasks: ['less', 'csslint'],
+ options: {
+ livereload: true
+ }
+ }
+ },
+ nodemon: {
+ dev: {
+ script: 'server.js',
+ options: {
+ nodeArgs: ['--debug'],
+ ext: 'js,html',
+ watch: _.union(defaultAssets.server.views, defaultAssets.server.allJS, defaultAssets.server.config)
+ }
+ }
+ },
+ concurrent: {
+ default: ['nodemon', 'watch'],
+ debug: ['nodemon', 'watch', 'node-inspector'],
+ options: {
+ logConcurrentOutput: true
}
},
jshint: {
all: {
- src: watchFiles.clientJS.concat(watchFiles.serverJS),
+ src: _.union(defaultAssets.server.allJS, defaultAssets.client.js, testAssets.tests.server, testAssets.tests.client, testAssets.tests.e2e),
options: {
- jshintrc: true
+ jshintrc: true,
+ node: true,
+ mocha: true,
+ jasmine: true
}
}
},
csslint: {
options: {
- csslintrc: '.csslintrc',
+ csslintrc: '.csslintrc'
},
all: {
- src: watchFiles.clientCSS
+ src: defaultAssets.client.css
+ }
+ },
+ ngAnnotate: {
+ production: {
+ files: {
+ 'public/dist/application.js': defaultAssets.client.js
+ }
}
},
uglify: {
@@ -78,18 +127,32 @@ module.exports = function(grunt) {
cssmin: {
combine: {
files: {
- 'public/dist/application.min.css': '<%= applicationCSSFiles %>'
+ 'public/dist/application.min.css': defaultAssets.client.css
}
}
},
- nodemon: {
- dev: {
- script: 'server.js',
- options: {
- nodeArgs: ['--debug'],
- ext: 'js,html',
- watch: watchFiles.serverViews.concat(watchFiles.serverJS)
- }
+ sass: {
+ dist: {
+ files: [{
+ expand: true,
+ src: defaultAssets.client.sass,
+ ext: '.css',
+ rename: function(base, src) {
+ return src.replace('/scss/', '/css/');
+ }
+ }]
+ }
+ },
+ less: {
+ dist: {
+ files: [{
+ expand: true,
+ src: defaultAssets.client.less,
+ ext: '.css',
+ rename: function(base, src) {
+ return src.replace('/less/', '/css/');
+ }
+ }]
}
},
'node-inspector': {
@@ -105,73 +168,66 @@ module.exports = function(grunt) {
}
}
},
- ngAnnotate: {
- production: {
- files: {
- 'public/dist/application.js': '<%= applicationJavaScriptFiles %>'
- }
- }
- },
- concurrent: {
- default: ['nodemon', 'watch'],
- debug: ['nodemon', 'watch', 'node-inspector'],
- options: {
- logConcurrentOutput: true,
- limit: 10
- }
- },
- env: {
- test: {
- NODE_ENV: 'test'
- },
- secure: {
- NODE_ENV: 'secure'
- }
- },
mochaTest: {
- src: watchFiles.mochaTests,
+ src: testAssets.tests.server,
options: {
- reporter: 'spec',
- require: 'server.js'
+ reporter: 'spec'
}
},
karma: {
unit: {
configFile: 'karma.conf.js'
}
+ },
+ protractor: {
+ options: {
+ configFile: 'protractor.conf.js',
+ keepAlive: true,
+ noColor: false
+ },
+ e2e: {
+ options: {
+ args: {} // Target-specific arguments
+ }
+ }
}
});
- // Load NPM tasks
+ // Load NPM tasks
require('load-grunt-tasks')(grunt);
// Making grunt default to force in order not to break the project.
grunt.option('force', true);
- // A Task for loading the configuration object
- grunt.task.registerTask('loadConfig', 'Task that loads the config into a grunt option.', function() {
- var init = require('./config/init')();
- var config = require('./config/config');
+ // Connect to the MongoDB instance and load the models
+ grunt.task.registerTask('mongoose', 'Task that connects to the MongoDB instance and loads the application models.', function() {
+ // Get the callback
+ var done = this.async();
+
+ // Use mongoose configuration
+ var mongoose = require('./config/lib/mongoose.js');
- grunt.config.set('applicationJavaScriptFiles', config.assets.js);
- grunt.config.set('applicationCSSFiles', config.assets.css);
+ // Connect to database
+ mongoose.connect(function(db) {
+ done();
+ });
});
- // Default task(s).
- grunt.registerTask('default', ['lint', 'concurrent:default']);
+ // Lint CSS and JavaScript files.
+ grunt.registerTask('lint', ['sass', 'less', 'jshint', 'csslint']);
- // Debug task.
- grunt.registerTask('debug', ['lint', 'concurrent:debug']);
+ // Lint project files and minify them into two production files.
+ grunt.registerTask('build', ['env:dev', 'lint', 'ngAnnotate', 'uglify', 'cssmin']);
- // Secure task(s).
- grunt.registerTask('secure', ['env:secure', 'lint', 'concurrent:default']);
+ // Run the project tests
+ grunt.registerTask('test', ['env:test', 'mongoose', 'mochaTest', 'karma:unit']);
- // Lint task(s).
- grunt.registerTask('lint', ['jshint', 'csslint']);
+ // Run the project in development mode
+ grunt.registerTask('default', ['env:dev', 'lint', 'concurrent:default']);
- // Build task(s).
- grunt.registerTask('build', ['lint', 'loadConfig', 'ngAnnotate', 'uglify', 'cssmin']);
+ // Run the project in debug mode
+ grunt.registerTask('debug', ['env:dev', 'lint', 'concurrent:debug']);
- // Test task.
- grunt.registerTask('test', ['env:test', 'mochaTest', 'karma:unit']);
-};
\ No newline at end of file
+ // Run the project in production mode
+ grunt.registerTask('prod', ['build', 'env:prod', 'concurrent:default']);
+};
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000000..b0a7ceda7f
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,187 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var _ = require('lodash'),
+ defaultAssets = require('./config/assets/default'),
+ testAssets = require('./config/assets/test'),
+ gulp = require('gulp'),
+ gulpLoadPlugins = require('gulp-load-plugins'),
+ runSequence = require('run-sequence'),
+ plugins = gulpLoadPlugins();
+
+// Set NODE_ENV to 'test'
+gulp.task('env:test', function () {
+ process.env.NODE_ENV = 'test';
+});
+
+// Set NODE_ENV to 'development'
+gulp.task('env:dev', function () {
+ process.env.NODE_ENV = 'development';
+});
+
+// Set NODE_ENV to 'production'
+gulp.task('env:prod', function () {
+ process.env.NODE_ENV = 'production';
+});
+
+// Nodemon task
+gulp.task('nodemon', function () {
+ return plugins.nodemon({
+ script: 'server.js',
+ nodeArgs: ['--debug'],
+ ext: 'js,html',
+ watch: _.union(defaultAssets.server.views, defaultAssets.server.allJS, defaultAssets.server.config)
+ });
+});
+
+// Watch Files For Changes
+gulp.task('watch', function() {
+ // Start livereload
+ plugins.livereload.listen();
+
+ // Add watch rules
+ gulp.watch(defaultAssets.server.views).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.server.allJS, ['jshint']).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.client.views).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.client.js, ['jshint']).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.client.css, ['csslint']).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.client.sass, ['sass', 'csslint']).on('change', plugins.livereload.changed);
+ gulp.watch(defaultAssets.client.less, ['less', 'csslint']).on('change', plugins.livereload.changed);
+});
+
+// CSS linting task
+gulp.task('csslint', function (done) {
+ return gulp.src(defaultAssets.client.css)
+ .pipe(plugins.csslint('.csslintrc'))
+ .pipe(plugins.csslint.reporter())
+ .pipe(plugins.csslint.reporter(function (file) {
+ if (!file.csslint.errorCount) {
+ done();
+ }
+ }));
+});
+
+// JS linting task
+gulp.task('jshint', function () {
+ return gulp.src(_.union(defaultAssets.server.allJS, defaultAssets.client.js, testAssets.tests.server, testAssets.tests.client, testAssets.tests.e2e))
+ .pipe(plugins.jshint())
+ .pipe(plugins.jshint.reporter('default'))
+ .pipe(plugins.jshint.reporter('fail'));
+});
+
+
+// JS minifying task
+gulp.task('uglify', function () {
+ return gulp.src(defaultAssets.client.js)
+ .pipe(plugins.ngAnnotate())
+ .pipe(plugins.uglify({
+ mangle: false
+ }))
+ .pipe(plugins.concat('application.min.js'))
+ .pipe(gulp.dest('public/dist'));
+});
+
+// CSS minifying task
+gulp.task('cssmin', function () {
+ return gulp.src(defaultAssets.client.css)
+ .pipe(plugins.cssmin())
+ .pipe(plugins.concat('application.min.css'))
+ .pipe(gulp.dest('public/dist'));
+});
+
+// Sass task
+gulp.task('sass', function () {
+ return gulp.src(defaultAssets.client.sass)
+ .pipe(plugins.sass())
+ .pipe(plugins.rename(function (path) {
+ path.dirname = path.dirname.replace('/scss', '/css');
+ }))
+ .pipe(gulp.dest('./modules/'));
+});
+
+// Less task
+gulp.task('less', function () {
+ return gulp.src(defaultAssets.client.less)
+ .pipe(plugins.less())
+ .pipe(plugins.rename(function (path) {
+ path.dirname = path.dirname.replace('/less', '/css');
+ }))
+ .pipe(gulp.dest('./modules/'));
+});
+
+// Connect to MongoDB using the mongoose module
+gulp.task('mongoose', function (done) {
+ var mongoose = require('./config/lib/mongoose.js');
+
+ mongoose.connect(function(db) {
+ done();
+ });
+});
+
+// Mocha tests task
+gulp.task('mocha', function () {
+ return gulp.src(testAssets.tests.server)
+ .pipe(plugins.mocha({
+ reporter: 'spec'
+ }));
+});
+
+// Karma test runner task
+gulp.task('karma', function (done) {
+ return gulp.src([])
+ .pipe(plugins.karma({
+ configFile: 'karma.conf.js',
+ action: 'run',
+ singleRun: true
+ }))
+ .on('error', function (err) {
+ // Make sure failed tests cause gulp to exit non-zero
+ throw err;
+ });
+});
+
+// Selenium standalone WebDriver update task
+gulp.task('webdriver-update', plugins.protractor.webdriver_update);
+
+// Protractor test runner task
+gulp.task('protractor', function () {
+ gulp.src([])
+ .pipe(plugins.protractor.protractor({
+ configFile: "protractor.conf.js"
+ }))
+ .on('error', function (e) {
+ throw e
+ })
+});
+
+// Lint CSS and JavaScript files.
+gulp.task('lint', function(done) {
+ runSequence('less', 'sass', ['csslint', 'jshint'], done);
+});
+
+// Lint project files and minify them into two production files.
+gulp.task('build', function(done) {
+ runSequence('env:dev' ,'lint', ['uglify', 'cssmin'], done);
+});
+
+// Run the project tests
+gulp.task('test', function(done) {
+ runSequence('env:test', 'mongoose', ['karma', 'mocha'], done);
+});
+
+// Run the project in development mode
+gulp.task('default', function(done) {
+ runSequence('env:dev', 'lint', ['nodemon', 'watch'], done);
+});
+
+// Run the project in debug mode
+gulp.task('debug', function(done) {
+ runSequence('env:dev', 'lint', ['nodemon', 'watch'], done);
+});
+
+// Run the project in production mode
+gulp.task('prod', function(done) {
+ runSequence('build', 'lint', ['nodemon', 'watch'], done);
+});
diff --git a/karma.conf.js b/karma.conf.js
index e85f39b7ca..0b6ead26de 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -3,16 +3,18 @@
/**
* Module dependencies.
*/
-var applicationConfiguration = require('./config/config');
+var _ = require('lodash'),
+ defaultAssets = require('./config/assets/default'),
+ testAssets = require('./config/assets/test');
// Karma configuration
-module.exports = function(config) {
- config.set({
+module.exports = function(karmaConfig) {
+ karmaConfig.set({
// Frameworks to use
frameworks: ['jasmine'],
// List of files / patterns to load in the browser
- files: applicationConfiguration.assets.lib.js.concat(applicationConfiguration.assets.js, applicationConfiguration.assets.tests),
+ files: _.union(defaultAssets.client.lib.js, defaultAssets.client.lib.tests, defaultAssets.client.js, testAssets.tests.client),
// Test results reporter to use
// Possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
@@ -26,8 +28,8 @@ module.exports = function(config) {
colors: true,
// Level of logging
- // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
- logLevel: config.LOG_INFO,
+ // Possible values: karmaConfig.LOG_DISABLE || karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN || karmaConfig.LOG_INFO || karmaConfig.LOG_DEBUG
+ logLevel: karmaConfig.LOG_INFO,
// Enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
@@ -49,4 +51,4 @@ module.exports = function(config) {
// If true, it capture browsers, run tests and exit
singleRun: true
});
-};
\ No newline at end of file
+};
diff --git a/public/modules/articles/articles.client.module.js b/modules/articles/client/articles.client.module.js
similarity index 100%
rename from public/modules/articles/articles.client.module.js
rename to modules/articles/client/articles.client.module.js
diff --git a/modules/articles/client/config/articles.client.config.js b/modules/articles/client/config/articles.client.config.js
new file mode 100644
index 0000000000..d43e0893f4
--- /dev/null
+++ b/modules/articles/client/config/articles.client.config.js
@@ -0,0 +1,25 @@
+'use strict';
+
+// Configuring the Articles module
+angular.module('articles').run(['Menus',
+ function(Menus) {
+ // Add the articles dropdown item
+ Menus.addMenuItem('topbar', {
+ title: 'Articles',
+ state: 'articles',
+ type: 'dropdown'
+ });
+
+ // Add the dropdown list item
+ Menus.addSubMenuItem('topbar', 'articles', {
+ title: 'List Articles',
+ state: 'articles.list'
+ });
+
+ // Add the dropdown create item
+ Menus.addSubMenuItem('topbar', 'articles', {
+ title: 'Create Articles',
+ state: 'articles.create'
+ });
+ }
+]);
diff --git a/public/modules/articles/config/articles.client.routes.js b/modules/articles/client/config/articles.client.routes.js
similarity index 64%
rename from public/modules/articles/config/articles.client.routes.js
rename to modules/articles/client/config/articles.client.routes.js
index 1531a9a57c..c6890be3ef 100755
--- a/public/modules/articles/config/articles.client.routes.js
+++ b/modules/articles/client/config/articles.client.routes.js
@@ -5,21 +5,26 @@ angular.module('articles').config(['$stateProvider',
function($stateProvider) {
// Articles state routing
$stateProvider.
- state('listArticles', {
+ state('articles', {
+ abstract: true,
url: '/articles',
+ template: '
MEAN.JS is a web application boilerplate, which means you should start changing everything :-)
This sample application tracks users and articles.
Express is an app server. Check out The ExpressJS guide for general topics or StackOverflow for more info.
+Express is an app server. Check out The Express Guide or StackOverflow for more info.