-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cdc15f0
commit 3459bf2
Showing
25 changed files
with
413 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ alanning:[email protected] | |
aldeed:[email protected] | ||
aldeed:[email protected] | ||
[email protected] | ||
[email protected] | ||
[email protected] | ||
[email protected] | ||
[email protected] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Meteor } from 'meteor/meteor'; | ||
import { check } from 'meteor/check'; | ||
import Documents from './Documents'; | ||
import rateLimit from '../../modules/rate-limit'; | ||
|
||
Meteor.methods({ | ||
'documents.insert': function documentsInsert(doc) { | ||
check(doc, { | ||
title: String, | ||
body: String, | ||
}); | ||
|
||
try { | ||
return Documents.insert({ owner: this.userId, ...doc }); | ||
} catch (exception) { | ||
throw new Meteor.Error('500', exception); | ||
} | ||
}, | ||
'documents.update': function documentsUpdate(doc) { | ||
check(doc, { | ||
_id: String, | ||
title: String, | ||
body: String, | ||
}); | ||
|
||
try { | ||
Documents.update(doc._id, { $set: doc }); | ||
return doc._id; // Return _id so we can redirect to document after update. | ||
} catch (exception) { | ||
throw new Meteor.Error('500', exception); | ||
} | ||
}, | ||
'documents.remove': function documentsRemove(documentId) { | ||
check(documentId, String); | ||
|
||
try { | ||
return Documents.remove(documentId); | ||
} catch (exception) { | ||
throw new Meteor.Error('500', exception); | ||
} | ||
}, | ||
}); | ||
|
||
rateLimit({ | ||
methods: [ | ||
'documents.insert', | ||
'documents.update', | ||
'documents.remove', | ||
], | ||
limit: 5, | ||
timeRange: 1000, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Meteor } from 'meteor/meteor'; | ||
import { check } from 'meteor/check'; | ||
import Documents from '../Documents'; | ||
|
||
Meteor.publish('documents', function documentsView() { | ||
return Documents.find({ owner: this.userId }); | ||
}); | ||
|
||
// Note: documents.view is also used when editing an existing document. | ||
Meteor.publish('documents.view', function documentsView(documentId) { | ||
check(documentId, String); | ||
|
||
const doc = Documents.find(documentId); | ||
const isOwner = doc.fetch()[0].owner === this.userId; | ||
|
||
return isOwner ? doc : this.ready(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Meteor } from 'meteor/meteor'; | ||
import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; | ||
import { _ } from 'meteor/underscore'; | ||
|
||
const fetchMethodNames = methods => _.pluck(methods, 'name'); | ||
|
||
const assignLimits = ({ methods, limit, timeRange }) => { | ||
const methodNames = fetchMethodNames(methods); | ||
|
||
if (Meteor.isServer) { | ||
DDPRateLimiter.addRule({ | ||
name(name) { return _.contains(methodNames, name); }, | ||
connectionId() { return true; }, | ||
}, limit, timeRange); | ||
} | ||
}; | ||
|
||
export default function rateLimit(options) { return assignLimits(options); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import '../../api/Documents/methods'; | ||
import '../../api/Documents/server/publications'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './api'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* eslint-disable max-len, no-return-assign */ | ||
|
||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { FormGroup, ControlLabel, Button } from 'react-bootstrap'; | ||
import { Meteor } from 'meteor/meteor'; | ||
import { Bert } from 'meteor/themeteorchef:bert'; | ||
import validate from '../../../modules/validate'; | ||
|
||
class DocumentEditor extends React.Component { | ||
componentDidMount() { | ||
const component = this; | ||
validate(component.form, { | ||
rules: { | ||
title: { | ||
required: true, | ||
}, | ||
body: { | ||
required: true, | ||
}, | ||
}, | ||
messages: { | ||
title: { | ||
required: 'Need a title in here, Seuss.', | ||
}, | ||
body: { | ||
required: 'This thneeds a body, please.', | ||
}, | ||
}, | ||
submitHandler() { component.handleSubmit(); }, | ||
}); | ||
} | ||
|
||
handleSubmit() { | ||
const { history } = this.props; | ||
const existingDocument = this.props.doc && this.props.doc._id; | ||
const methodToCall = existingDocument ? 'documents.update' : 'documents.insert'; | ||
const doc = { | ||
title: this.title.value.trim(), | ||
body: this.body.value.trim(), | ||
}; | ||
|
||
if (existingDocument) doc._id = existingDocument; | ||
|
||
Meteor.call(methodToCall, doc, (error, documentId) => { | ||
if (error) { | ||
Bert.alert(error.reason, 'danger'); | ||
} else { | ||
const confirmation = existingDocument ? 'Document updated!' : 'Document added!'; | ||
this.form.reset(); | ||
Bert.alert(confirmation, 'success'); | ||
history.push(`/documents/${documentId}`); | ||
} | ||
}); | ||
} | ||
|
||
render() { | ||
const { doc } = this.props; | ||
return (<form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}> | ||
<FormGroup> | ||
<ControlLabel>Title</ControlLabel> | ||
<input | ||
type="text" | ||
className="form-control" | ||
name="title" | ||
ref={title => (this.title = title)} | ||
defaultValue={doc && doc.title} | ||
placeholder="Oh, The Places You'll Go!" | ||
/> | ||
</FormGroup> | ||
<FormGroup> | ||
<ControlLabel>Body</ControlLabel> | ||
<textarea | ||
className="form-control" | ||
name="body" | ||
ref={body => (this.body = body)} | ||
defaultValue={doc && doc.body} | ||
placeholder="Congratulations! Today is your day. You're off to Great Places! You're off and away!" | ||
/> | ||
</FormGroup> | ||
<Button type="submit" bsStyle="success"> | ||
{doc && doc._id ? 'Save Changes' : 'Add Document'} | ||
</Button> | ||
</form>); | ||
} | ||
} | ||
|
||
DocumentEditor.defaultProps = { | ||
doc: PropTypes.object, | ||
}; | ||
|
||
DocumentEditor.propTypes = { | ||
doc: PropTypes.object, | ||
history: PropTypes.object.isRequired, | ||
}; | ||
|
||
export default DocumentEditor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Link } from 'react-router-dom'; | ||
import { Table, Alert, Button } from 'react-bootstrap'; | ||
import { timeago, monthDayYearAtTime } from '@cleverbeagle/dates'; | ||
import { Meteor } from 'meteor/meteor'; | ||
import { createContainer } from 'meteor/react-meteor-data'; | ||
import { Bert } from 'meteor/themeteorchef:bert'; | ||
import DocumentsCollection from '../../../api/Documents/Documents'; | ||
|
||
import './Documents.scss'; | ||
|
||
const handleRemove = (documentId) => { | ||
if (confirm('Are you sure? This is permanent!')) { | ||
Meteor.call('documents.remove', documentId, (error) => { | ||
if (error) { | ||
Bert.alert(error.reason, 'danger'); | ||
} else { | ||
Bert.alert('Document deleted!', 'success'); | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
const Documents = ({ documents, match, history }) => ( | ||
<div className="Documents"> | ||
<div className="page-header clearfix"> | ||
<h4 className="pull-left">Documents</h4> | ||
<Link className="btn btn-success pull-right" to={`${match.url}/new`}>Add Document</Link> | ||
</div> | ||
{documents.length ? <Table responsive> | ||
<thead> | ||
<tr> | ||
<th>Title</th> | ||
<th>Last Updated</th> | ||
<th>Created</th> | ||
<th /> | ||
<th /> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{documents.map(({ _id, title, createdAt, updatedAt }) => ( | ||
<tr key={_id}> | ||
<td>{title}</td> | ||
<td>{timeago(updatedAt)}</td> | ||
<td>{monthDayYearAtTime(createdAt)}</td> | ||
<td> | ||
<Button | ||
bsStyle="primary" | ||
onClick={() => history.push(`${match.url}/${_id}`)} | ||
block | ||
>View</Button> | ||
</td> | ||
<td> | ||
<Button | ||
bsStyle="danger" | ||
onClick={() => handleRemove(_id)} | ||
block | ||
>Delete</Button> | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</Table> : <Alert bsStyle="warning">No documents yet!</Alert>} | ||
</div> | ||
); | ||
|
||
Documents.propTypes = { | ||
documents: PropTypes.arrayOf(PropTypes.object).isRequired, | ||
match: PropTypes.object.isRequired, | ||
history: PropTypes.object.isRequired, | ||
}; | ||
|
||
export default createContainer(() => { | ||
const subscription = Meteor.subscribe('documents'); | ||
return { | ||
loading: !subscription.ready(), | ||
documents: DocumentsCollection.find().fetch(), | ||
}; | ||
}, Documents); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.Documents table tbody tr td { | ||
vertical-align: middle; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { createContainer } from 'meteor/react-meteor-data'; | ||
import { Meteor } from 'meteor/meteor'; | ||
import Documents from '../../../api/Documents/Documents'; | ||
import DocumentEditor from '../../components/DocumentEditor/DocumentEditor'; | ||
import NotFound from '../NotFound/NotFound'; | ||
|
||
const EditDocument = ({ doc, history }) => (doc ? ( | ||
<div className="EditDocument"> | ||
<h4 className="page-header">{`Editing "${doc.title}"`}</h4> | ||
<DocumentEditor doc={doc} history={history} /> | ||
</div> | ||
) : <NotFound />); | ||
|
||
EditDocument.propTypes = { | ||
doc: PropTypes.object.isRequired, | ||
history: PropTypes.object.isRequired, | ||
}; | ||
|
||
export default createContainer(({ match }) => { | ||
const documentId = match.params._id; | ||
const subscription = Meteor.subscribe('documents.view', documentId); | ||
|
||
return { | ||
loading: !subscription.ready(), | ||
doc: Documents.findOne(documentId), | ||
}; | ||
}, EditDocument); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.