diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..f9ced93c
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+packages
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 00000000..91df3274
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,62 @@
+{
+ "parser": "babel-eslint",
+ "parserOptions": {
+ "ecmaVersion": 6,
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true
+ },
+ "allowImportExportEverywhere": true
+ },
+ "plugins": [
+ "meteor"
+ ],
+ "extends": [
+ "airbnb",
+ "plugin:meteor/recommended"
+ ],
+ "rules": {
+ "import/extensions": "off",
+ "import/no-extraneous-dependencies": "off",
+ "import/prefer-default-export": "off",
+ "no-underscore-dangle": "off",
+ "class-methods-use-this": "off",
+ "meteor/no-session": "off",
+ "object-shorthand": [
+ "error",
+ "always",
+ {
+ "avoidQuotes": false
+ }
+ ],
+ "meteor/eventmap-params": [
+ "error",
+ {
+ "eventParamName": "event",
+ "templateInstanceParamName": "instance"
+ }
+ ],
+ "meteor/template-names": [
+ "off"
+ ],
+ "react/jsx-filename-extension": [
+ 1,
+ {
+ "extensions": [
+ ".js",
+ ".jsx"
+ ]
+ }
+ ],
+ "react/forbid-prop-types": "off",
+ "jsx-a11y/no-static-element-interactions": "off",
+ "prefer-arrow-callback": "off"
+ },
+ "settings": {
+ "import/resolver": "meteor"
+ },
+ "globals": {
+ "window": true,
+ "document": true
+ }
+}
diff --git a/.gitignore b/.gitignore
index e9964c3c..cde5f439 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,17 @@
-.DS_Store
settings-production.json
-npm-debug.log
+# OSX
+#
+.DS_Store
+.tmp/
+
+# IntelliJ
+#
+.idea
+
+# node.js
+#
node_modules
+npm-debug.log
+yarn-error.log
+
diff --git a/.meteor/packages b/.meteor/packages
index ba54c24f..d151087f 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -12,18 +12,17 @@ session@1.1.7
tracker@1.1.2 # Meteor's client-side reactive programming library
standard-minifier-css@1.3.4 # CSS minifier run for production mode
-standard-minifier-js@1.2.3 # JS minifier run for production mode
+standard-minifier-js@2.0.0 # JS minifier run for production mode
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
-ecmascript@0.6.3 # Enable ECMAScript2015+ syntax in app code
+ecmascript@0.7.2 # Enable ECMAScript2015+ syntax in app code
-accounts-password@1.3.4
-accounts-base@1.2.15
+accounts-password@1.3.5
+accounts-base@1.2.16
check@1.2.5
audit-argument-checks@1.0.7
browser-policy@1.1.0
fourseven:scss@4.5.0
-aldeed:collection2
alanning:roles
themeteorchef:bert
static-html@1.1.11
@@ -34,3 +33,4 @@ mdg:validated-method
dburles:factory@1.0.0
ddp-rate-limiter@1.0.7
shell-server@0.2.3
+aldeed:collection2-core@2.0.0
diff --git a/.meteor/release b/.meteor/release
index 31ce024f..605b4e1f 100644
--- a/.meteor/release
+++ b/.meteor/release
@@ -1 +1 @@
-METEOR@1.4.3.2
+METEOR@1.4.4.1
diff --git a/.meteor/versions b/.meteor/versions
index e2954800..e73eb94f 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -1,19 +1,15 @@
-accounts-base@1.2.15
-accounts-password@1.3.4
+accounts-base@1.2.16
+accounts-password@1.3.5
alanning:roles@1.2.16
-aldeed:collection2@2.10.0
-aldeed:collection2-core@1.2.0
-aldeed:schema-deny@1.1.0
-aldeed:schema-index@1.1.1
-aldeed:simple-schema@1.5.3
+aldeed:collection2-core@2.0.0
allow-deny@1.0.5
audit-argument-checks@1.0.7
autoupdate@1.3.12
-babel-compiler@6.14.1
+babel-compiler@6.18.2
babel-runtime@1.0.1
base64@1.0.10
binary-heap@1.0.10
-blaze@2.3.0
+blaze@2.3.2
blaze-tools@1.0.10
boilerplate-generator@1.0.11
browser-policy@1.1.0
@@ -21,22 +17,22 @@ browser-policy-common@1.0.11
browser-policy-content@1.1.0
browser-policy-framing@1.1.0
caching-compiler@1.1.9
-caching-html-compiler@1.1.0
+caching-html-compiler@1.1.2
callback-hook@1.0.10
check@1.2.5
coffeescript@1.12.3_1
dburles:factory@1.1.0
ddp@1.2.5
-ddp-client@1.3.3
+ddp-client@1.3.4
ddp-common@1.2.8
ddp-rate-limiter@1.0.7
-ddp-server@1.3.13
+ddp-server@1.3.14
deps@1.0.12
diff-sequence@1.0.7
-ecmascript@0.6.3
+ecmascript@0.7.3
ecmascript-runtime@0.3.15
ejson@1.0.13
-email@1.1.18
+email@1.2.1
es5-shim@4.6.15
fastclick@1.0.13
fortawesome:fontawesome@4.7.0
@@ -53,16 +49,15 @@ livedata@1.0.18
localstorage@1.0.12
logging@1.1.17
mdg:validated-method@1.1.0
-mdg:validation-error@0.5.1
meteor@1.6.1
meteor-base@1.0.4
minifier-css@1.2.16
-minifier-js@1.2.18
+minifier-js@2.0.0
minimongo@1.0.21
mobile-experience@1.0.4
mobile-status-bar@1.0.14
-modules@0.7.9
-modules-runtime@0.7.9
+modules@0.8.2
+modules-runtime@0.7.10
mongo@1.1.16
mongo-id@1.0.6
npm-bcrypt@0.9.2
@@ -77,7 +72,7 @@ practicalmeteor:sinon@1.14.1_2
promise@0.8.8
raix:eventemitter@0.1.3
random@1.0.10
-rate-limit@1.0.7
+rate-limit@1.0.8
reactive-dict@1.1.8
reactive-var@1.0.11
reload@1.1.11
@@ -87,23 +82,24 @@ service-configuration@1.0.11
session@1.1.7
sha@1.0.9
shell-server@0.2.3
-spacebars@1.0.13
-spacebars-compiler@1.1.0
+spacebars@1.0.15
+spacebars-compiler@1.1.2
srp@1.0.10
standard-minifier-css@1.3.4
-standard-minifier-js@1.2.3
-static-html@1.2.0
-templating@1.3.0
-templating-compiler@1.3.0
-templating-runtime@1.3.0
-templating-tools@1.1.0
+standard-minifier-js@2.0.0
+static-html@1.2.2
+templating@1.3.2
+templating-compiler@1.3.2
+templating-runtime@1.3.2
+templating-tools@1.1.2
themeteorchef:bert@2.1.2
+tmeasday:check-npm-versions@0.3.1
tmeasday:test-reporter-helpers@0.2.1
tracker@1.1.2
-ui@1.0.12
+ui@1.0.13
underscore@1.0.10
url@1.1.0
-webapp@1.3.14
+webapp@1.3.15
webapp-hashing@1.0.9
xolvio:backdoor@0.2.1
xolvio:cleaner@0.3.1
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000..a043f11c
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,11 @@
+MIT
+
+---
+
+Copyright (c) 2014-2017 The Meteor Chef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index b57f0053..f04512d6 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,11 @@ A starting point for Meteor apps.
| Base Version |
- v4.14.0 |
+ v4.15.0 |
| Meteor Version |
- v1.4.3.2 |
+ v1.4.4.1 |
diff --git a/imports/api/documents/documents.js b/imports/api/documents/documents.js
index 58c3ae4c..c280dc98 100644
--- a/imports/api/documents/documents.js
+++ b/imports/api/documents/documents.js
@@ -1,5 +1,5 @@
import { Mongo } from 'meteor/mongo';
-import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import SimpleSchema from 'simpl-schema';
import { Factory } from 'meteor/dburles:factory';
const Documents = new Mongo.Collection('Documents');
diff --git a/imports/api/documents/methods.js b/imports/api/documents/methods.js
index 0ded4d74..7468c6e4 100644
--- a/imports/api/documents/methods.js
+++ b/imports/api/documents/methods.js
@@ -1,4 +1,4 @@
-import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import Documents from './documents';
import rateLimit from '../../modules/rate-limit.js';
diff --git a/imports/api/users/server/publications.js b/imports/api/users/server/publications.js
new file mode 100644
index 00000000..7e247b18
--- /dev/null
+++ b/imports/api/users/server/publications.js
@@ -0,0 +1,13 @@
+import { Meteor } from 'meteor/meteor';
+
+Meteor.publish('users.info', function userInfoPublish() {
+ if (!this.userId) {
+ return this.ready();
+ }
+
+ return Meteor.users.find(this.userId, {
+ fields: {
+ name: 1,
+ },
+ });
+});
diff --git a/imports/ui/components/AppNavigation.js b/imports/client/ui/components/AppNavigation.js
similarity index 60%
rename from imports/ui/components/AppNavigation.js
rename to imports/client/ui/components/AppNavigation.js
index 319c9362..d2aa978d 100644
--- a/imports/ui/components/AppNavigation.js
+++ b/imports/client/ui/components/AppNavigation.js
@@ -1,8 +1,11 @@
+import { Meteor } from 'meteor/meteor';
import React from 'react';
import { Navbar } from 'react-bootstrap';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
+import { PropTypes } from 'prop-types';
import PublicNavigation from './PublicNavigation.js';
import AuthenticatedNavigation from './AuthenticatedNavigation.js';
+import container from '../../../modules/container';
const renderNavigation = hasUser => (hasUser ? : );
@@ -21,7 +24,12 @@ const AppNavigation = ({ hasUser }) => (
);
AppNavigation.propTypes = {
- hasUser: React.PropTypes.object,
+ hasUser: PropTypes.object,
};
-export default AppNavigation;
+export default container((props, onData) => {
+ const subscription = Meteor.subscribe('users.info');
+ if (subscription.ready()) {
+ onData(null, { hasUser: Meteor.user() });
+ }
+}, AppNavigation);
diff --git a/imports/client/ui/components/AuthenticatedNavigation.js b/imports/client/ui/components/AuthenticatedNavigation.js
new file mode 100644
index 00000000..866c69b9
--- /dev/null
+++ b/imports/client/ui/components/AuthenticatedNavigation.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { withRouter } from 'react-router-dom';
+import { PropTypes } from 'prop-types';
+import { LinkContainer } from 'react-router-bootstrap';
+import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap';
+import { Meteor } from 'meteor/meteor';
+
+const userName = () => {
+ const user = Meteor.user();
+ const name = user ? user.name : '';
+ return user ? `${name.first} ${name.last}` : '';
+};
+
+const AuthenticatedNavigation = ({ history }) => (
+
+
+
+
+);
+
+AuthenticatedNavigation.defaultProps = {
+ history: null,
+};
+
+AuthenticatedNavigation.propTypes = {
+ history: PropTypes.object,
+};
+
+export default withRouter(AuthenticatedNavigation);
diff --git a/imports/client/ui/components/DocumentEditor.js b/imports/client/ui/components/DocumentEditor.js
new file mode 100644
index 00000000..3cea5821
--- /dev/null
+++ b/imports/client/ui/components/DocumentEditor.js
@@ -0,0 +1,57 @@
+/* eslint-disable max-len, no-return-assign */
+
+import React, { Component } from 'react';
+import { FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
+import { withRouter } from 'react-router-dom';
+import { PropTypes } from 'prop-types';
+import documentEditor from '../../../modules/document-editor.js';
+
+class DocumentEditor extends Component {
+ componentDidMount() {
+ documentEditor({ component: this });
+ setTimeout(() => { document.querySelector('[name="title"]').focus(); }, 0);
+ }
+
+ render() {
+ const { doc } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+DocumentEditor.defaultProps = {
+ doc: null,
+};
+
+DocumentEditor.propTypes = {
+ doc: PropTypes.object,
+};
+
+export default withRouter(DocumentEditor);
diff --git a/imports/client/ui/components/DocumentsList.js b/imports/client/ui/components/DocumentsList.js
new file mode 100644
index 00000000..26f72ecd
--- /dev/null
+++ b/imports/client/ui/components/DocumentsList.js
@@ -0,0 +1,38 @@
+import { Meteor } from 'meteor/meteor';
+import React from 'react';
+import { ListGroup, ListGroupItem, Alert } from 'react-bootstrap';
+import { withRouter } from 'react-router-dom';
+import { PropTypes } from 'prop-types';
+import Documents from '../../../api/documents/documents';
+import container from '../../../modules/container';
+import Loading from '../components/Loading';
+
+const DocumentsList = ({ documents, history }) => (
+ documents.length > 0 ?
+ {documents.map(({ _id, title }) => (
+ history.push(`/documents/${_id}`)}>
+ { title }
+
+ ))}
+ :
+ No documents yet.
+);
+
+DocumentsList.defaultProps = {
+ documents: [],
+ history: null,
+};
+
+DocumentsList.propTypes = {
+ documents: PropTypes.array,
+ history: PropTypes.object,
+};
+
+export default withRouter(container((props, onData) => {
+ const subscription = Meteor.subscribe('documents.list');
+ if (subscription.ready()) {
+ const documents = Documents.find().fetch();
+ onData(null, { documents });
+ }
+}, DocumentsList, { loadingHandler: () => }));
+
diff --git a/imports/ui/components/Loading.js b/imports/client/ui/components/Loading.js
similarity index 100%
rename from imports/ui/components/Loading.js
rename to imports/client/ui/components/Loading.js
diff --git a/imports/ui/components/PublicNavigation.js b/imports/client/ui/components/PublicNavigation.js
similarity index 72%
rename from imports/ui/components/PublicNavigation.js
rename to imports/client/ui/components/PublicNavigation.js
index d04fc80f..a590f554 100644
--- a/imports/ui/components/PublicNavigation.js
+++ b/imports/client/ui/components/PublicNavigation.js
@@ -5,10 +5,10 @@ import { Nav, NavItem } from 'react-bootstrap';
const PublicNavigation = () => (
);
diff --git a/imports/client/ui/layouts/App.js b/imports/client/ui/layouts/App.js
new file mode 100644
index 00000000..b7ec52f7
--- /dev/null
+++ b/imports/client/ui/layouts/App.js
@@ -0,0 +1,41 @@
+/* eslint-disable max-len */
+
+import React from 'react';
+import { BrowserRouter, Route, Switch } from 'react-router-dom';
+import { Grid } from 'react-bootstrap';
+import PrivateRoute from '../../../modules/private-route';
+import AppNavigation from '../components/AppNavigation';
+import EditDocument from '../pages/EditDocument';
+import ViewDocument from '../pages/ViewDocument';
+import Documents from '../pages/Documents';
+import NewDocument from '../pages/NewDocument';
+import Index from '../pages/Index';
+import Login from '../pages/Login';
+import NotFound from '../pages/NotFound';
+import RecoverPassword from '../pages/RecoverPassword';
+import ResetPassword from '../pages/ResetPassword';
+import Signup from '../pages/Signup';
+
+const App = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export default App;
diff --git a/imports/ui/pages/Documents.js b/imports/client/ui/pages/Documents.js
similarity index 81%
rename from imports/ui/pages/Documents.js
rename to imports/client/ui/pages/Documents.js
index fbfc0001..43f496aa 100644
--- a/imports/ui/pages/Documents.js
+++ b/imports/client/ui/pages/Documents.js
@@ -1,12 +1,12 @@
import React from 'react';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
import { Row, Col, Button } from 'react-bootstrap';
-import DocumentsList from '../containers/DocumentsList.js';
+import DocumentsList from '../components/DocumentsList';
const Documents = () => (
-
+
Documents
diff --git a/imports/client/ui/pages/EditDocument.js b/imports/client/ui/pages/EditDocument.js
new file mode 100644
index 00000000..75a06c70
--- /dev/null
+++ b/imports/client/ui/pages/EditDocument.js
@@ -0,0 +1,31 @@
+import { Meteor } from 'meteor/meteor';
+import React from 'react';
+import { withRouter } from 'react-router-dom';
+import { PropTypes } from 'prop-types';
+import Documents from '../../../api/documents/documents';
+import DocumentEditor from '../components/DocumentEditor';
+import NotFound from './NotFound';
+import container from '../../../modules/container';
+import Loading from '../components/Loading';
+
+const EditDocument = ({ doc }) => (doc ? (
+
+
Editing "{doc.title}"
+
+
+) :
);
+
+EditDocument.propTypes = {
+ doc: PropTypes.object.isRequired,
+};
+
+export default withRouter(container((props, onData) => {
+ const documentId = props.match.params._id;
+ const subscription = Meteor.subscribe('documents.view', documentId);
+
+ if (subscription.ready()) {
+ const doc = Documents.findOne(documentId);
+ onData(null, { doc });
+ }
+}, EditDocument, { loadingHandler: () =>
}));
+
diff --git a/imports/ui/pages/Index.js b/imports/client/ui/pages/Index.js
similarity index 96%
rename from imports/ui/pages/Index.js
rename to imports/client/ui/pages/Index.js
index a5aa841e..86e1e1bd 100644
--- a/imports/ui/pages/Index.js
+++ b/imports/client/ui/pages/Index.js
@@ -7,7 +7,7 @@ const Index = () => (
Base
A starting point for Meteor applications.
Read the Documentation
-
Currently at v4.14.0
+
Currently at v4.15.0
);
diff --git a/imports/ui/pages/Login.js b/imports/client/ui/pages/Login.js
similarity index 79%
rename from imports/ui/pages/Login.js
rename to imports/client/ui/pages/Login.js
index dfaaefe1..14442e04 100644
--- a/imports/ui/pages/Login.js
+++ b/imports/client/ui/pages/Login.js
@@ -1,9 +1,9 @@
-import React from 'react';
-import { Link } from 'react-router';
+import React, { Component } from 'react';
+import { Link, withRouter } from 'react-router-dom';
import { Row, Col, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
-import handleLogin from '../../modules/login';
+import handleLogin from '../../../modules/login';
-export default class Login extends React.Component {
+class Login extends Component {
componentDidMount() {
handleLogin({ component: this });
}
@@ -16,12 +16,12 @@ export default class Login extends React.Component {
return (