Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fuzzybabybunny/microscope-orionjs
https://github.com/fuzzybabybunny/microscope-orionjs
Last synced: 5 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/fuzzybabybunny/microscope-orionjs
- Owner: fuzzybabybunny
- License: mit
- Created: 2015-07-24T04:10:27.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2017-03-26T13:36:28.000Z (over 7 years ago)
- Last Synced: 2024-08-02T18:43:17.180Z (3 months ago)
- Language: JavaScript
- Size: 302 KB
- Stars: 19
- Watchers: 4
- Forks: 8
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
**Table of Contents**
- [Meteor OrionJS with Microscope Tutorial](#meteor-orionjs-with-microscope-tutorial)
- [Purpose](#purpose)
- [Cloning Microscope](#cloning-microscope)
- [Download OrionJS](#download-orionjs)
- [Initial Impressions](#initial-impressions)
- [Creating Users](#creating-users)
- [Adding and Removing Roles from Users](#adding-and-removing-roles-from-users)
- [Getting Roles](#getting-roles)
- [Setting Roles](#setting-roles)
- [Adding Collections to OrionJS](#adding-collections-to-orionjs)
- [Updating Collection Documents](#updating-collection-documents)
- [Schemas](#schemas)
- [Adding Comments Collection](#adding-comments-collection)
- [Custom Input Types (Widgets)](#custom-input-types-widgets)
- [Adding Summernote](#adding-summernote)
- [Orion Attributes](#orion-attributes)
- [Adding Images to Amazon S3 (updated 07/28/2015)](#adding-images-to-amazon-s3-updated-07282015)
- [Setting up S3](#setting-up-s3)
- [Configuring OrionJS](#configuring-orionjs)
- [Changing Tabular Templates (updated 7/29/2015)](#changing-tabular-templates-updated-7292015)
- [orion.attributeColumn()](#orionattributecolumn)
- [Custom Tabular Templates](#custom-tabular-templates)
- [Template-Level Subscriptions](#template-level-subscriptions)
- [Meteor Tabular Render](#meteor-tabular-render)
- [Meteor Tabular with Actual Templates](#meteor-tabular-with-actual-templates)
- [Dictionary (updated 7/28/2015)](#dictionary-updated-7282015)
- [Relationships](#relationships)
- [hasOne](#hasone)
- [Chicken and the Egg](#chicken-and-the-egg)
- [Correcting File Load Order](#correcting-file-load-order)
- [hasMany](#hasmany)
- [Multiple Relationships (updated 7/31/2015)](#multiple-relationships-updated-7312015)
- [Limitations of Defining Relationships](#limitations-of-defining-relationships)
- [Setting Roles and Permissions (updated 8/3/2015)](#setting-roles-and-permissions-updated-832015)# Meteor OrionJS with Microscope Tutorial #
## Purpose ##
I haven't been able to find a good tutorial that goes through the end-to-end setup for OrionJS, so I decided to create this tutorial both as a learning resource for others but also as a way for me to keep track of my own progress as I poke around OrionJS and figure out how to do stuff with it.
There will be cursing in this tutorial.
## Cloning Microscope ##
Most Meteor developers should be familiar with Microscope, the social news app which the user codes along with when following the [Discover Meteor](https://www.discovermeteor.com/) tutorial book. If you haven't read and coded along with this book, do it. Now.
As of this writing, Microscope does not have a backend admin system in place to manage its data, so I thought that it would be the ideal candidate for creating a tutorial on how to get OrionJS working.
First, go to https://github.com/DiscoverMeteor/Microscope and clone the repo.
```
git clone [email protected]:DiscoverMeteor/Microscope.git
cd Microscope
meteor update
```## Download OrionJS ##
OrionJS is designed to work nicely with Bootstrap, which is perfect because Microscope does as well.
```
meteor add orionjs:core fortawesome:fontawesome orionjs:bootstrap
```When you do `meteor list` you should see (versions may be different):
```
accounts-password 1.1.1 Password support for accounts
audit-argument-checks 1.0.3 Try to detect inadequate input sanitization
fortawesome:fontawesome 4.3.0 Font Awesome (official): 500+ scalable vector icons, customizable via CSS, Retina friendly
ian:accounts-ui-bootstrap-3 1.2.76 Bootstrap-styled accounts-ui with multi-language support.
iron:router 1.0.9 Routing specifically designed for Meteor
orionjs:bootstrap 1.2.1 A simple theme for orion
orionjs:core 1.2.0 Orion
sacha:spin 2.3.1 Simple spinner package for Meteor
standard-app-packages 1.0.5 Moved to meteor-platform
twbs:bootstrap 3.3.5 The most popular front-end framework for developing responsive, mobile first projects on the web.
underscore 1.0.3 Collection of small helpers: _.map, _.each, ...```
## Initial Impressions ##Great! Now that we've added all these packages, let's start up Microscope and see how screwed up we made everything.
```
meteor
```
Then point your browser to `http://localhost:3000/`Everything looks like it should:
![enter image description here](https://lh3.googleusercontent.com/ADsFjG6v1AlTMaULUSVMG9qEBKrCQ902sUXuQI5N1dU=s0 "Screenshot from 2015-07-23 22:54:16.png")
Now let's go to `http://localhost:3000/admin`
![enter image description here](https://lh3.googleusercontent.com/yjUHPNxc1wclW3M-pPFIk1J_F8IglciV8NU85nIwNE0=s0 "Screenshot from 2015-07-23 22:56:17.png")
Looks like crap.
The reason this is here is because there's a hidden red alert box that is floating right. It's a style that was defined in Microscope so let's go remove it. Comment out that line of code.
```css
/client/stylesheets/style.css.alert {
animation: fadeOut 2700ms ease-in 0s 1 forwards;
-webkit-animation: fadeOut 2700ms ease-in 0s 1 forwards;
-moz-animation: fadeOut 2700ms ease-in 0s 1 forwards;
width: 250px;
/* float: right;*/
clear: both;
margin-bottom: 5px;
pointer-events: auto;
}
```
This is going to make Microscope look crappier but I'm crap at CSS so screwit.![enter image description here](https://lh3.googleusercontent.com/UwazTdhYJp0Rx_aYIbwJp52UOQbhLPfv5VvqrZJH8jo=s0 "Screenshot from 2015-07-23 23:10:09.png")
Better. The invisible alert box is still taking up space but I can't be screwed to do anything about it.
## Creating Users ##
Looks like we need to create users and log in first before we can see the OrionJS backend.
Go here and replace the original `// create two users` code with this:
(Don't just copy this code and replace everything in `fixtures.js` with this since it's just partial code.)
```javascript
/server/fixtures.js// Fixture data
if (Posts.find().count() === 0) {
var now = new Date().getTime();
// create two usersvar sachaId = Accounts.createUser({
profile: {
name: 'Sacha Greif'
},
username: "sacha",
email: "[email protected]",
password: "123456",
});var tomId = Accounts.createUser({
profile: {
name: 'Tom Coleman'
},
username: "tom",
email: "[email protected]",
password: "123456",
});var sacha = Meteor.users.findOne(sachaId);
var tom = Meteor.users.findOne(tomId);
var telescopeId = Posts.insert({
title: 'Introducing Telescope',
userId: sacha._id,
author: sacha.profile.name,
url: 'http://sachagreif.com/introducing-telescope/',
submitted: new Date(now - 7 * 3600 * 1000),
commentsCount: 2,
upvoters: [], votes: 0
});
Comments.insert({
postId: telescopeId,
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 5 * 3600 * 1000),
body: 'Interesting project Sacha, can I get involved?'
});```
Shut down Meteor with `Ctrl+C` and do a `meteor reset` and start it back up again with `meteor`.
Now let's log in **as Sacha** and see what happens. Once logged in, click on the `Accounts` link on the left.
![enter image description here](https://lh3.googleusercontent.com/J5K9jjcJU_0KKQVfBcqfXiUNrqcto6slnUyOZ6O1Lks=s0 "Screenshot from 2015-07-23 23:41:12.png")
- Nice. Absolutely nothing shows up except the `Accounts` collection. Microscope has a `Posts` collection as well as a `Comments` collection.
- ... and WTF. Tom is an `admin` but Sacha is not. We didn't even tell Meteor to make Tom an `admin` in my fixtures code, so what the hell happened?
It turns out that by default, OrionJS will create a user `Role` called `admin` and if there is no `admin`, will assign the **first** user created with `Accounts.createUser` as an `admin`. Remember this so you're not creating accidental admin users in your fixtures code.
I can't spell Sasha's name for crap so let's remove him as admin and have Tom as admin. Because ignoring your spelling weaknesses make them go away.
## Adding and Removing Roles from Users ##
Unlike other user roles packages, the `role` of the user in `OrionJS` isn't stored on the `user` object itself. You won't find something like:
```javascript
{
username: 'cletus',
email: '[email protected]',
roles: [
'admin',
'parent',
'varmit_hunter'
]
}
```
Instead, each separate `role` is stored in a `Roles` collection and the `userId` of the user is referenced along with an array containing their `roles`.### Getting Roles###
Let's screw around in the Chrome console before we do anything. While in the `Accounts` admin page, do:```console
var id = Meteor.users.findOne({username: "sacha"})._id;
Roles.userHasRole(id, "admin")true
```
It should return `true`. We got Sesha's userId and then used that ID to check if he has the role of "admin."Likewise, each user has a `roles()` and `hasRole()` method on its object:
```console
// gets the currently logged in user, which should be Sassha
var user = Meteor.user();
user.roles();["admin"]
user.hasRole("admin")
true
```
So that's how you check if a user has a role you want.Currently there doesn't seem to be a method to check *which* users have a certain role.
###Setting Roles###
We need to give Tom the role of `admin`.
In Chrome console:
```
var user = Meteor.users.findOne({username: "tom"});
Roles.addUserToRoles( user._id , ["admin"] );VM11445:2 Uncaught TypeError: Roles.addUserToRoles is not a function(anonymous function)
```
DUH. You can't define roles on the client. Because that's dumb.Let's make a new file called `/server/admin.js`
```javascript
/server/admin.jsvar tom = Meteor.users.findOne({username: 'tom'});
Roles.addUserToRoles( tom._id , ["admin"] );
```If we go back to the OrionJS admin console we should see that now Tom and Sache are both admins.
Now we want to remove Sachet as an admin.
```javascript
/server/admin.jsvar tom = Meteor.users.findOne({username: 'tom'});
Roles.addUserToRoles( tom._id , ["admin"] );var nameIcantSpel = Meteor.users.findOne({username: 'sacha'});
Roles.removeUserFromRoles( nameIcantSpel._id, ["admin"] );
```
You'll notice that you can log into `OrionJS` as Sacsh, but you won't see `Accounts` on the sidebar since he's no longer an `admin`. So log in as Tom instead.
And now Tom is the only admin!![enter image description here](https://lh3.googleusercontent.com/2cXRDE9AILHEWVhLumU2Fei-Tzf67Uwl8rOCZGfcsOk=s0 "Screenshot from 2015-07-24 00:26:13.png")
Spelling weakness successfully ignored!
##Adding Collections to OrionJS##
Here's where this tutorial gets less horrible.
Microscope has a `Posts` and `Comments` collection, but both aren't visible yet in the admin thingy. That's because they're not `Orion` collections yet.
Let's make them appear.
```javascript
// This is what it used to be:
// Posts = new Mongo.Collection('posts');// Instead let's do:
Posts = new orion.collection('posts', {
singularName: 'post', // The name of one of these items
pluralName: 'posts', // The name of more than one of these items
link: {
// *
// * The text that you want to show in the sidebar.
// * The default value is the name of the collection, so
// * in this case it is not necessary.
title: 'Posts'
},
/**
* Tabular settings for this collection
*/
tabular: {
// here we set which data columns we want to appear on the data table
// in the CMS panel
columns: [
{
data: "title",
title: "Title"
},{
data: "author",
title: "Author"
},{
data: "submitted",
title: "Submitted"
},
]
}
});Posts.allow({
update: function(userId, post) { return ownsDocument(userId, post); },
remove: function(userId, post) { return ownsDocument(userId, post); },
});Posts.deny({
update: function(userId, post, fieldNames) {
// may only edit the following two fields:
```
If you go back to the Microscope home page you'll see that everything appears to have remained normal. Orion collections are just extended Mongo collections.Now it looks like we got `Posts` appearing in `OrionJS`.
![enter image description here](https://lh3.googleusercontent.com/6T99yNvDLm2qxDp-ZLF8W0V9L64Op8W8-UAvuOQlEqo=s0 "Screenshot from 2015-07-24 00:37:51.png")
Reactive search works. Sorting columns is working. Pagination is working.
But when we click on a table item we get:
![enter image description here](https://lh3.googleusercontent.com/UPlVwdKLDXf2UBsX3JqLdTpoG989DelOau61HHB84MA=s0 "Screenshot from 2015-07-24 00:39:06.png")
Errors and crap.
Luckily the error is pretty descriptive. This form needs either a schema or a collection.
##Updating Collection Documents##
When we click on one of the table items we expect to go to an update form for that particular item. `OrionJS` uses the vastly powerful `aldeed:autoform` package to generate its forms. `aldeed:autoform` in turn uses the `aldeed:simple-schema` package to know *how* to generate its forms.
###Schemas###
Schemas are these little (tee-hee) things that define how the data in your database should be. If you've got a `User` document with a `first_name` property, you'd expect the value to be a `type: String`. If having a first name is critical, you'd want it to be `optional: false`.
We use schemas to keep our data consistent. MongoDB is inherently a schema-less database. It would happily allow you to screw yourself over by storing an array of booleans inside the `first_name` property of your `user` document, for instance. And then you go to access it and your wife leaves you (probably not your husband because he's clueless).
So this is why people decided to make a schema package for Meteor. They love you and want happy families.
Let's start by defining a schema for our `Posts` collection all the way at the very bottom of `/lib/collections/posts.js`. I'm too lazy to type so read the comments.
```javascript
/lib/collections/posts.js// Rest of the code above
$addToSet: {upvoters: this.userId},
$inc: {votes: 1}
});
if (! affected)
throw new Meteor.Error('invalid', "You weren't able to upvote that post");
}
});/**
* Now we will define and attach the schema for this collection.
* Orion will automatically create the corresponding form.
*/
Posts.attachSchema(new SimpleSchema({
// We use `label` to put a custom label for this form field
// Otherwise it would default to `Title`
// 'optional: false' means that this field is required
// If it's blank, the form won't submit and you'll get a red error message
// 'type' is where you can set the expected data type for the 'title' key's value
title: {
type: String,
optional: false,
label: 'Post Title'
},
// regEx will validate this form field according to a RegEx for a URL
url: {
type: String,
optional: false,
label: 'URL',
regEx: SimpleSchema.RegEx.Url
},
// autoform determines other aspects for how the form is generated
// In this case we're making this field hidden from view
userId: {
type: String,
optional: false,
autoform: {
type: "hidden",
label: false
},
},
author: {
type: String,
optional: false,
},
// 'type: Date' means that this field is expecting a data as an entry
submitted: {
type: Date,
optional: false,
},
commentsCount: {
type: Number,
optional: false
},
// 'type: [String]' means this key's value is an array of strings'
upvoters: {
type: [String],
optional: true,
autoform: {
disabled: true,
label: false
},
},
votes: {
type: Number,
optional: true
},
}));
```I told you schemas are little, right?
Save and try clicking on a post item again.
![enter image description here](https://lh3.googleusercontent.com/xUKRDnSsT3THV3fi-HocLdl3uAwkTyt_YRC_nswc8eA=s0 "Screenshot from 2015-07-24 03:57:39.png")
Ohhhhh... crap! It's almost like I... planned... things.
Play around with this form and look at how the `schema` we defined directly correlates to how this form was generated.
- make the `Post Title` blank and save the form
- remove `http` in `URL` and save the form
- click on the `Submitted` field to see how `type: Date` works##Adding Comments Collection##
How about we do the same thing to the `Comments` collection as we did to the `Posts` collection?
I'll race you. Ready? Go.
Done I WIN.```javascript
/lib/collections/comments.jsComments = new orion.collection('comments', {
singularName: 'comment', // The name of one of these items
pluralName: 'comments', // The name of more than one of these items
link: {
// *
// * The text that you want to show in the sidebar.
// * The default value is the name of the collection, so
// * in this case it is not necessary.
title: 'Comments'
},
/**
* Tabular settings for this collection
*/
tabular: {
// here we set which data columns we want to appear on the data table
// in the CMS panel
columns: [
{
data: "author",
title: "Author"
},{
data: "postId",
title: "Post ID"
},{
data: "submitted",
title: "Submitted"
},
]
}
});Meteor.methods({
commentInsert: function(commentAttributes) {
check(this.userId, String);
check(commentAttributes, {
postId: String,
body: String
});
var user = Meteor.user();
var post = Posts.findOne(commentAttributes.postId);if (!post)
throw new Meteor.Error('invalid-comment', 'You must comment on a post');
comment = _.extend(commentAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
// update the post with the number of comments
Posts.update(comment.postId, {$inc: {commentsCount: 1}});
// create the comment, save the id
comment._id = Comments.insert(comment);
// now create a notification, informing the user that there's been a comment
createCommentNotification(comment);
return comment._id;
}
});/**
* Now we will define and attach the schema for that collection.
* Orion will automatically create the corresponding form.
*/
Comments.attachSchema(new SimpleSchema({
postId: {
type: String,
optional: false,
label: 'Post ID'
},
userId: {
type: String,
optional: false,
label: 'User ID',
},
author: {
type: String,
optional: false,
},
submitted: {
type: Date,
optional: false,
},
body: {
type: String,
optional: false,
}
}));
```![enter image description here](https://lh3.googleusercontent.com/LROiQg6mMP5rpG844QNv-njdxyB5ltffUASPpbx606M=s0 "Screenshot from 2015-07-24 04:33:38.png")
![enter image description here](https://lh3.googleusercontent.com/89A5cA83MF5WZn-69hoq2yqPvh5hvllTXyTpuQMuJJU=s0 "Screenshot from 2015-07-24 04:34:43.png")
How about we do something even more CRAZY and add in a text editor for the `Body` field?
##Custom Input Types (Widgets)##
First, some background.
Forms have standard input types. Checkboxes. Radio buttons. Text. These are all supported out of the box by `aldeed:autoform`. But things like text editors (with buttons for selecting font type, size, color, etc) are custom, and `autoform` gives us a way to define our own widgets in the `schema` so that autoform can generate that form for us.
###Adding Summernote###
First do `meteor add orionjs:summernote`
Now do this to the `body` property:
```javascript
/lib/collections/comments.js// now create a notification, informing the user that there's been a comment
createCommentNotification(comment);
return comment._id;
}
});/**
* Now we will attach the schema for that collection.
* Orion will automatically create the corresponding form.
*/
Comments.attachSchema(new SimpleSchema({
postId: {
type: String,
optional: false,
label: 'Post ID'
},
userId: {
type: String,
optional: false,
label: 'User ID',
},
author: {
type: String,
optional: false,
},
submitted: {
type: Date,
optional: false,
},
body: orion.attribute('summernote', {
label: 'Body'
}),
}));
```
Click on the comment by Sashi in the OrionJS admin panel to see what's up.![enter image description here](https://lh3.googleusercontent.com/unDs4gjPq22hcncc11LY_Cp9eTD5RBh9G6IChwiVMYo=s0 "Screenshot from 2015-07-24 18:30:25.png")
Niiiice.
###Orion Attributes###
So what the hell is the `orion.attribute` we adding as the value for the `body` key in our schema? How about we just use our Chrome Console?
```
orion.attribute('summernote', {
label: 'Body'
})Object {label: "Body", type: function, orionAttribute: "summernote", ...}
autoform: Object
label: "Body"
orionAttribute: "summernote"
type: function String() { [native code] }
__proto__: Object
```
Oh. looks like by adding the `orionjs:summernote` package we got access to this method that returns a pre-made object for us that we can conveniently use in our schema. Remember that `aldeed:autoform` uses the schema to figure out *how* to generate form items, so this attribute did all the defining-the-input-widget stuff for us.`orion.attribute('nameOfAnAttribute', optionalObjectToExtendThisAttributeWith)`
I'm going to make this comment fabulous. ROYGBIV and Comic Sans the crap out of that comment, Sechie.
![enter image description here](https://lh3.googleusercontent.com/eVMz0FVebRmy05ZPPU9p3b_91o56QjbK7GfgK0mIvaA=s0 "Screenshot from 2015-07-24 18:37:15.png")
Save it.
Remember that this comment is now in HTML, so we have to add triple spacebars `{{{ TRIPLEX #vindiesel }}}` in our `comment_item.html` to make sure Spacebars will render the HTML properly.
```html
/client/templates/comments/comment_item.html
on {{submittedText}}
{{{ body }}}
```
Let's survey the improvements by going to the main page and clicking on the comments for the `Introducing Telescope` post.
![enter image description here](https://lh3.googleusercontent.com/8ja1upLimMWalVzUCAMWNmtULEb-xpvPzOiih5l99wg=s0 "Screenshot from 2015-07-24 18:46:03.png")
Beauty.
###Adding Images to Amazon S3 (updated 07/28/2015)###
No comment will be complete without image spamming.
Some notes:
- currently, uploading images directly from Summernote doesn't work
- images will be hosted through Amazon S3. I don't go over how to do it with `GridFS` or other filesystem packages.
`meteor add orionjs:image-attribute orionjs:filesystem orionjs:s3`
####Setting up S3####
You're going to want to follow this tutorial FIRST to set up your Amazon S3:
https://github.com/Lepozepo/S3/#amazon-s3-uploader
Make sure that you've got your S3 credentials in your `server` folder. It wouldn't hurt to add this file to your `.gitignore` file as well. On second thought, scratch what I just said and send me links to your repos instead... because... porn.
```javascript
/server/s3_credentials.js
// something like this
S3.config = {
key: 'BVT&(Y*(H&TG*&H',
secret: 'B^&Y*UGUFGO*(PU(/7sdfgwTVwS/',
bucket: 'meteor.microscopelolololololol',
region: 'us-west-1'
};
```
####Configuring OrionJS####
Create a new file:
```javascript
/lib/orion_filesystem.js
/**
* Official S3 Upload Provider
*
* Please replace this function with the
* provider you prefer.
*
* If success, call success(publicUrl);
* you can pass data and it will be saved in file.meta
* Ej: success(publicUrl, {local_path: '/user/path/to/file'})
*
* If it fails, call failure(error).
*
* When the progress change, call progress(newProgress)
*/
orion.filesystem.providerUpload = function(options, success, failure, progress) {
S3.upload({
files: options.fileList,
path: 'orionjs',
}, function(error, result) {
debugger
if (error) {
failure(error);
} else {
success(result.secure_url, { s3Path: result.relative_url });
result;
debugger
}
S3.collection.remove({})
});
Tracker.autorun(function () {
var file = S3.collection.findOne();
if (file) {
progress(file.percent_uploaded);
}
});
};
/**
* Official S3 Remove Provider
*
* Please replace this function with the
* provider you prefer.
*
* If success, call success();
* If it fails, call failure(error).
*/
orion.filesystem.providerRemove = function(file, success, failure) {
S3.delete(file.meta.s3Path, function(error, result) {
if (error) {
failure(error);
} else {
success();
}
})
};
```
What this bit of code does is it defines two methods - one for uploading a file to S3 and another for removing a file from S3. It also adds a progress bar during the upload process.
Now it's schema time again since we want to add a file upload section to our Comment Update form!
```javascript
/lib/collections/comments.js
author: {
type: String,
optional: false,
},
submitted: {
type: Date,
optional: false,
},
body: orion.attribute('summernote', {
label: 'Body'
}),
image: orion.attribute('image', {
optional: true,
label: 'Comment Image'
}),
}));
```
Go into a comment in the admin panel and upload something!
![enter image description here](https://lh3.googleusercontent.com/ZY2zJWFCdv25VDgbAE3yjPxD-ByUIngSW9-SEuzloaI=s0 "Screenshot from 2015-07-29 00:04:39.png")
Make sure to click on the `Save` button after you're done. Also note that the moment you select an image the Amazon uploader will start. You'll see a progress bar.
Finally, we should go see what it looks like on the main page, but remember to modify the `comment_item` template with the new `image` key that a `comment` document now has.
```
/client/templates/comments/comment_item.html
on {{submittedText}}
{{{body}}}
{{#if image }}
{{/if}}
```
Voila!
![enter image description here](https://lh3.googleusercontent.com/bKct-GlqtHXJYrfQkoiaZrq1XH0D1Q08UyH_PUgPZYE=s0 "Screenshot from 2015-07-29 00:05:07.png")
Sassha and Tom are going to KILL me.
##Changing Tabular Templates (updated 7/29/2015)##
If we go back to our admin panel and look at comments, we see that the table is pretty dumb:
`![enter image description here](https://lh3.googleusercontent.com/LROiQg6mMP5rpG844QNv-njdxyB5ltffUASPpbx606M=s0 "Screenshot from 2015-07-24 04:33:38.png")
1. The `Submitted` column contains WAY too much information. Something like Month-Day-Year-Time would look nicer. I'm going to completely ignore you people who do it the more logical way of Time-Day-Month-Year because, uh, freedom.
2. We also have the issue of the `Post ID` column being essentially stupid. I'd prefer if that column contained the title of the Post instead.
3. I also want a column that shows a short blurb of the comment's `body`, something like `Interesting project Sacha, can I...`
Let's tackle #1 first. If you guessed that we need to go back into our `Schema` to change this, you just WON the JACKPOT of zero money.
###orion.attributeColumn()###
We are interested in:
orion.attributeColumn('createdAt', 'submitted', 'FREEDOM!!!'),
```javascript
/lib/collections/comments.js
Comments = new orion.collection('comments', {
singularName: 'comment', // The name of one of these items
pluralName: 'comments', // The name of more than one of these items
link: {
// *
// * The text that you want to show in the sidebar.
// * The default value is the name of the collection, so
// * in this case it is not necessary.
title: 'Comments'
},
/**
* Tabular settings for this collection
*/
tabular: {
// here we set which data columns we want to appear on the data table
// in the CMS panel
columns: [
{
data: "author",
title: "Author"
},{
data: "postId",
title: "Post ID"
},
orion.attributeColumn('createdAt', 'submitted', 'FREEDOM!!!'),
]
}
});
```
Let's check it out!
![enter image description here](https://lh3.googleusercontent.com/YVicjxMpRLn3A5J1xNI-PRRQGWjCdtt_u6E77QHEp9A=s0 "Screenshot from 2015-07-24 20:10:05.png")
This handy-dandy method goes like this:
`orion.attributeColumn('nameOfTemplate', 'keyNameOnYourObject', 'columnLabel')`
Think about it. Meteor uses templates to display stuff. We had a crazy long date and we wanted to change the *look* of it, so using a template makes sense.
Luckily, `OrionJS` comes with some pre-made templates. One of them happens to be called `createdAt`.
`createdAt` wants a `Date` object, which happens to reside in the `submitted` key of each of your documents in the `Comments` collection. Lastly, we tell it what we want our column label to be.
Now, some freedom-hating people probably want a custom template for Time-Day-Month-Year. Let's get to that next.
###Custom Tabular Templates###
Now onto issue #2 - the `Post ID` column should be the title of the Post instead.
Open up Chrome Console and type in `Comments.findOne()`. It found a comment, right?
Now do `Posts.findOne()`. Hmmm... no post found. That's because this route isn't subscribed to anything in the `Posts` collection. If you've done Meteor before, chances are you've been using `Iron Router` to manage your data subscriptions for your routes. Unfortunately, since OrionJS is a package, it's difficult to tap into and modify the generated routes that OrionJS has already made for us. So what do we do?
####Template-Level Subscriptions####
Meteor can subscribe to data in normal template callbacks (`onRendered, onCreated`). And as it turns out, OrionJS has a very standardized template naming scheme*.
`collections.myCollection.index` - the main page that lists all the items in the collection
`collections.myCollection.create` - the form for creating a new item in the collection
`collections.myCollection.update` - the form for updating an existing item in the collection
`collections.myCollection.delete` - the form/page for deleting an existing item in the collection
These, by the way, are your standard pages for CRUD actions.
*These aren't necessarily the actual names of the templates, but the `identifier` that OrionJS uses to find the actual template.
BTW, if you want to see a list of all the route names that are registered with Iron Router, open up Chrome Console and do:
```javascript
_.each(Router.routes, function(route){
console.log(route.getName());
});
```
Here are some more identifiers: http://orionjs.org/docs/customization#overridetemplates
So let's use this to subscribe to the `Posts` collection on the `comments.index` template.
```javascript
/client/templates/orion/comments_index.js
ReactiveTemplates.onCreated('collections.comments.index', function() {
this.subscribe('posts', {sort: {submitted: -1, _id: -1}, limit: 0});
});
```
What this is saying is that when the template with an identifier of 'collections.comments.index' (the `Comments` index page) is created, subscribe the template to the data you specify.
Now go back to the `Comments` index page and do `Posts.find().count()` to see that you've got `Posts` now!
####Meteor Tabular Render####
So now that this page has the data we need, how do we actually change the contents of the cell itself?
`OrionJS` uses `aldeed:meteor-tabular` to show its datatables, and it just so happens that this latter package provides a way to change the cell value: https://github.com/aldeed/meteor-tabular#example
```javascript
/lib/collections/comments.js
Comments = new orion.collection('comments', {
singularName: 'comment', // The name of one of these items
pluralName: 'comments', // The name of more than one of these items
link: {
title: 'Comments'
},
/**
* Tabular settings for this collection
*/
tabular: {
// here we set which data columns we want to appear on the data table
// in the CMS panel
columns: [
{
data: "author",
title: "Author"
},{
data: "postId",
title: "Post Title",
render: function (val, type, doc) {
var postId = val;
var postTitle = Posts.findOne(postId).title;
return postTitle;
}
},
orion.attributeColumn('createdAt', 'submitted', 'FREEDOM!!!'),
]
},
});
```
Go play around inside this function. `console.log` `val`, `type`, and `doc` to see what they are:
```javascript
{
data: "postId",
title: "Post Title",
render: function (val, type, doc) {
var postId = val;
var postTitle = Posts.findOne(postId).title;
return postTitle;
}
}
```
`data: "postId"` is critical here because that's how `val` gets its value.
After you're through go back and look at the `Comments` index page:
![enter image description here](https://lh3.googleusercontent.com/xuT9mpGBby25enedfg64fTEyuVWIXP_jFPXokNKpkx0=s0 "Screenshot from 2015-07-29 17:06:16.png")
####Meteor Tabular with Actual Templates####
Finally, onto #3. We want a column that shows a short blurb of the comment `body`, something like `Interesting project Sacha, can I...`. This is called `truncating` a string.
I want to create an actual template for this:
```html
/client/templates/orion/comments_index_blurb_cell.html
{{{ blurb }}}
```
Hold on there Skippy! Truncating a straight string that's, say, 100 characters long into one that's 15 characters long with a `...` at the end is fairly straightforward. But remember that we added Summernote and we have a *fabulous* comment?
![enter image description here](https://lh3.googleusercontent.com/8ja1upLimMWalVzUCAMWNmtULEb-xpvPzOiih5l99wg=s0 "Screenshot from 2015-07-24 18:46:03.png")
The HTML for this comment actually looks like:
```html
You sure can Tom!!!
```
Sooo... we can't just do a simple truncate of this down to 15 characters. I mean, we can...
...IF WE'RE NUBZ!
But `pathable` is not a nub:
https://github.com/pathable/truncate
Go to `/client/javascript` and literally just chuck this script's `jquery.truncate.js` file in there. Meteor will take care of minifying and loading this script automatically onto your page, as it does with *all* javascript that's not in the `/public` folder.
And now we can go ahead and create a helper for our template:
```javascript
/client/templates/orion/comments_index_blurb_cell.js
Template.commentsIndexBlurbCell.helpers({
blurb: function(){
var blurb = jQuery.truncate(this.body, {
length: 15
});
return blurb
}
});
```
Finally, we go back to modify our `tabular` object:
```javascript
/lib/comments.js
Comments = new orion.collection('comments', {
singularName: 'comment', // The name of one of these items
pluralName: 'comments', // The name of more than one of these items
link: {
title: 'Comments'
},
/**
* Tabular settings for this collection
*/
tabular: {
// here we set which data columns we want to appear on the data table
// in the CMS panel
columns: [
{
data: "author",
title: "Author"
},{
data: "postId",
title: "Post Title",
render: function (val, type, doc) {
var postId = val;
var postTitle = Posts.findOne(postId).title;
return postTitle;
}
},{
data: "body",
title: "Comment",
tmpl: Meteor.isClient && Template.commentsIndexBlurbCell
},
orion.attributeColumn('createdAt', 'submitted', 'FREEDOM!!!'),
]
},
});
```
`data: "body"` subscribes us to the values for the `body` key so that it's available for our template.
`tmpl: Meteor.isClient && Template.commentsIndexBlurbCell` looks kind of weird but remember that this code is in `/lib`, which runs on both the client and server, and `Template` isn't defined on the server. So that's why `aldeed:meteor-tabular` requires you to do this `Meteor.isClient` thing.
WABAM!
![enter image description here](https://lh3.googleusercontent.com/_DiQmOxmyTqnDpR0lpfP3WC-D1TadvsDG08J_ApZvmg=s0 "Screenshot from 2015-07-29 17:53:03.png")
And... done.
##Dictionary (updated 7/28/2015)##
Let's go back to the Microscope main page. Say that you wanted to add a little description blurb after the word `Microscope` at the top left.
- You not only want to add a description, but you want to be able to periodically change it as well AND you want text formatting on it AND you don't want to touch any code to change it - all you want is to change it from the OrionJS admin panel from inside of an update form.
- And since I'm rolling right now, let's be able to change the `Microscope` word as well.
- AND since people like to sue, let's add a terms and conditions blurb at the bottom of every `Post Submit` page that our lawyers can periodically update with worse and worse conditions for the consumer.
Sounds like we need a collection, a schema, and a dictionary of some sort that keeps track of lolidontknowhowtoexplainjustkeepreading.
Create a new file:
```javascript
/lib/orion_dictionary.js
orion.dictionary.addDefinition('title', 'mainPage', {
type: String,
label: 'Site Title',
optional: false,
min: 1,
max: 40
});
orion.dictionary.addDefinition('description', 'mainPage',
orion.attribute('summernote', {
label: 'Site Description',
optional: true
})
);
orion.dictionary.addDefinition('termsAndConditions', 'submitPostPage',
orion.attribute('summernote', {
label: 'Terms and Conditions',
optional: true
})
);
```
`orion.dictionary.addDefinition()` takes three arguments:
```
orion.dictionary.addDefinition(
nameOfYourDictionaryItem,
categoryOfYourDictionaryItem,
schemaForYourDictionaryItem
);
```
You'll see how this pans out in a little bit.
```
/client/templates/includes/header.html