Vulcan/lib/router.js

711 lines
17 KiB
JavaScript
Raw Normal View History

2013-10-09 20:59:42 +09:00
/*
2013-10-10 11:41:11 +09:00
//--------------------------------------------------------------------------------------------------//
//---------------------------------------- Table Of Contents ---------------------------------------//
//--------------------------------------------------------------------------------------------------//
---------------------------------------------------------------
# Config #
---------------------------------------------------------------
//
2013-10-09 20:59:42 +09:00
---------------------------------------------------------------
2013-10-10 11:41:11 +09:00
# Filters #
2013-10-09 20:59:42 +09:00
---------------------------------------------------------------
2013-10-10 11:41:11 +09:00
isLoggedIn
isLoggedOut
isAdmin
canView
canPost
canEditPost
canEditComment
hasCompletedProfile
---------------------------------------------------------------
2013-10-25 11:54:19 +09:00
# Controllers #
---------------------------------------------------------------
2013-10-10 11:41:11 +09:00
2013-10-25 11:54:19 +09:00
PostsListController
PostPageController
2013-10-10 11:41:11 +09:00
---------------------------------------------------------------
# Routes #
---------------------------------------------------------------
2013-10-09 20:59:42 +09:00
1) Paginated Lists
----------------------
Top
New
Best
Pending
Categories
2) Digest
--------------------
Digest
3) Posts
--------------------
Post Page
Post Page (scroll to comment)
Post Edit
Post Submit
4) Comments
--------------------
Comment Page
Comment Edit
Comment Submit
5) Users
--------------------
User Profie
User Edit
Forgot Password
Account
All Users
Unsubscribe (from notifications)
Sign Up
Sign In
6) Misc Routes
--------------------
Settings
2013-10-09 22:21:28 +09:00
Categories
2013-10-09 20:59:42 +09:00
Toolbox
7) Server-side
--------------------
API
RSS
2013-10-09 21:34:55 +09:00
2013-10-09 20:59:42 +09:00
*/
2014-02-18 14:46:53 +09:00
// uncomment to disable FastRender
// var FastRender = {RouteController: RouteController, onAllRoutes: function() {}};
2013-10-10 11:41:11 +09:00
//--------------------------------------------------------------------------------------------------//
//--------------------------------------------- Config ---------------------------------------------//
//--------------------------------------------------------------------------------------------------//
2014-02-18 14:46:53 +09:00
var preloadSubscriptions = ['categories', 'settings', 'currentUser'];
2013-10-06 08:37:17 +09:00
Router.configure({
2013-10-12 11:44:29 +09:00
layoutTemplate: 'layout',
2013-10-06 09:33:00 +09:00
loadingTemplate: 'loading',
notFoundTemplate: 'not_found',
2014-02-18 14:46:53 +09:00
waitOn: function () {
return _.map(preloadSubscriptions, function(sub){
Meteor.subscribe(sub);
});
}
2013-10-06 08:37:17 +09:00
});
2013-10-10 11:41:11 +09:00
//--------------------------------------------------------------------------------------------------//
//--------------------------------------------- Filters --------------------------------------------//
//--------------------------------------------------------------------------------------------------//
var filters = {
2013-10-13 10:27:49 +09:00
nProgressHook: function () {
if (this.ready()) {
NProgress.done();
} else {
NProgress.start();
this.stop();
}
},
2013-10-24 20:30:05 +09:00
resetScroll: function () {
var scrollTo = window.currentScroll || 0;
$('body').scrollTop(scrollTo);
$('body').css("min-height", 0);
},
2013-10-13 10:27:49 +09:00
isLoggedIn: function() {
if (!(Meteor.loggingIn() || Meteor.user())) {
2013-11-11 22:07:19 +01:00
throwError(i18n.t('Please Sign In First.'))
this.render('signin');
2013-10-13 10:27:49 +09:00
this.stop();
}
},
isLoggedOut: function() {
if(Meteor.user()){
this.render('already_logged_in');
this.stop();
}
},
isAdmin: function() {
if(!this.ready()) return;
if(!isAdmin()){
2013-11-11 22:07:19 +01:00
throwError(i18n.t("Sorry, you have to be an admin to view this page."))
2013-10-13 10:27:49 +09:00
this.render('no_rights');
2013-10-25 10:04:34 +09:00
this.stop();
2013-10-13 10:27:49 +09:00
}
},
canView: function() {
if(!this.ready()) return;
if(!canView()){
console.log('cannot view')
2013-10-13 10:27:49 +09:00
this.render('no_rights');
this.stop();
}
},
2013-10-12 11:44:29 +09:00
canPost: function () {
if(!this.ready()) return;
if(!canPost()){
2013-11-11 22:07:19 +01:00
throwError(i18n.t("Sorry, you don't have permissions to add new items."))
this.render('no_rights');
this.stop();
}
},
2013-10-12 11:44:29 +09:00
canEditPost: function() {
if(!this.ready()) return;
// Already subscribed to this post by route({waitOn: ...})
2013-10-12 11:44:29 +09:00
var post = Posts.findOne(this.params._id);
if(!currentUserCanEdit(post)){
2013-11-11 22:07:19 +01:00
throwError(i18n.t("Sorry, you cannot edit this post."))
2013-10-12 11:44:29 +09:00
this.render('no_rights');
this.stop();
2013-10-10 11:41:11 +09:00
}
},
2013-10-12 11:44:29 +09:00
canEditComment: function() {
if(!this.ready()) return;
// Already subscribed to this commit by CommentPageController
2013-10-12 11:44:29 +09:00
var comment = Comments.findOne(this.params._id);
if(!currentUserCanEdit(comment)){
2013-11-11 22:07:19 +01:00
throwError(i18n.t("Sorry, you cannot edit this comment."))
2013-10-12 11:44:29 +09:00
this.render('no_rights');
this.stop();
2013-10-10 11:41:11 +09:00
}
2013-10-13 10:27:49 +09:00
},
hasCompletedProfile: function() {
if(!this.ready()) return;
2013-10-13 10:27:49 +09:00
var user = Meteor.user();
if (user && ! userProfileComplete(user)){
2013-10-13 10:27:49 +09:00
this.render('user_email');
this.stop();
}
2013-10-12 11:44:29 +09:00
}
2013-10-10 11:41:11 +09:00
}
if(Meteor.isClient){
2013-10-24 14:36:20 +09:00
// Load Hooks
2013-11-16 14:01:00 +09:00
Router.load( function () {
clearSeenErrors(); // set all errors who have already been seen to not show anymore
Session.set('categorySlug', null);
2013-11-16 14:01:00 +09:00
// if we're not on the search page itself, clear search query and field
if(getCurrentRoute().indexOf('search') == -1){
Session.set('searchQuery', '');
$('.search-field').val('').blur();
}
});
2013-10-15 12:35:05 +09:00
// Before Hooks
// Use nProgress on every route that has to load a subscription
Router.before(filters.nProgressHook, {only: [
'posts_top',
'posts_new',
'posts_best',
'posts_pending',
'posts_digest',
'posts_category',
'search',
'post_page',
'post_edit',
'comment_page',
'comment_edit',
'comment_reply',
'user_edit',
'user_profile',
'all-users',
'logs'
]});
Router.before(filters.canView);
Router.before(filters.hasCompletedProfile);
Router.before(filters.isLoggedIn, {only: ['comment_reply','post_submit']});
Router.before(filters.isLoggedOut, {only: ['signin', 'signup']});
Router.before(filters.canPost, {only: ['posts_pending', 'comment_reply', 'post_submit']});
Router.before(filters.canEditPost, {only: ['post_edit']});
Router.before(filters.canEditComment, {only: ['comment_edit']});
Router.before(filters.isAdmin, {only: ['posts_pending', 'all-users', 'settings', 'categories', 'toolbox', 'logs']});
// After Hooks
Router.after(filters.resetScroll, {except:['posts_top', 'posts_new', 'posts_best', 'posts_pending', 'posts_category', 'all-users']});
Router.after( function () {
2014-02-18 14:46:53 +09:00
analyticsInit(); // will only run once thanks to _.once()
analyticsRequest() // log this request with mixpanel, etc
});
2013-10-24 14:36:20 +09:00
// Unload Hooks
//
}
2013-10-15 12:35:05 +09:00
2013-10-25 11:54:19 +09:00
//--------------------------------------------------------------------------------------------------//
//------------------------------------------- Controllers ------------------------------------------//
//--------------------------------------------------------------------------------------------------//
// Controller for all posts lists
2013-10-25 11:54:19 +09:00
2014-01-12 22:58:07 +05:30
PostsListController = FastRender.RouteController.extend({
2013-10-25 11:54:19 +09:00
template:'posts_list',
waitOn: function () {
2013-10-25 11:59:00 +09:00
// take the first segment of the path to get the view, unless it's '/' in which case the view default to 'top'
// note: most of the time this.params.slug will be empty
2013-11-17 11:42:17 +09:00
this._terms = {
view: this.path == '/' ? 'top' : this.path.split('/')[1],
limit: this.params.limit || getSetting('postsPerPage', 10),
2014-01-12 22:58:07 +05:30
category: this.params.slug
};
if(Meteor.isClient) {
this._terms.query = Session.get("searchQuery");
}
2014-01-12 22:58:07 +05:30
2013-10-25 11:54:19 +09:00
return [
2013-11-17 11:42:17 +09:00
Meteor.subscribe('postsList', this._terms),
Meteor.subscribe('postsListUsers', this._terms)
2013-10-25 11:54:19 +09:00
]
},
data: function () {
2013-11-17 11:42:17 +09:00
var parameters = getParameters(this._terms),
posts = Posts.find(parameters.find, parameters.options);
postsCount = posts.count();
2013-11-16 17:44:33 +09:00
2013-11-17 11:42:17 +09:00
Session.set('postsLimit', this._terms.limit);
2013-10-28 13:35:20 +09:00
2013-10-25 11:54:19 +09:00
return {
postsList: posts,
postsCount: postsCount
2013-10-25 11:54:19 +09:00
}
},
after: function() {
var view = this.path == '/' ? 'top' : this.path.split('/')[1];
Session.set('view', view);
2013-10-25 11:54:19 +09:00
}
});
// Controller for post digest
2014-01-12 22:58:07 +05:30
PostsDigestController = FastRender.RouteController.extend({
template: 'posts_digest',
waitOn: function() {
// if day is set, use that. If not default to today
2014-01-12 22:58:07 +05:30
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : new Date(),
terms = {
view: 'digest',
after: moment(currentDate).startOf('day').valueOf(),
before: moment(currentDate).endOf('day').valueOf()
}
return [
Meteor.subscribe('postsList', terms),
Meteor.subscribe('postsListUsers', terms)
]
},
data: function() {
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : Session.get('today'),
terms = {
view: 'digest',
after: moment(currentDate).startOf('day').valueOf(),
before: moment(currentDate).endOf('day').valueOf()
},
parameters = getParameters(terms);
Session.set('currentDate', currentDate);
return {
posts: Posts.find(parameters.find, parameters.options)
}
}
});
// Controller for post pages
2013-10-25 11:54:19 +09:00
2014-01-12 22:58:07 +05:30
PostPageController = FastRender.RouteController.extend({
2013-10-25 11:54:19 +09:00
template: 'post_page',
waitOn: function () {
return [
Meteor.subscribe('singlePost', this.params._id),
2013-10-26 10:50:49 +09:00
Meteor.subscribe('postComments', this.params._id),
2013-10-25 11:54:19 +09:00
Meteor.subscribe('postUsers', this.params._id)
];
},
data: function () {
return {postId: this.params._id};
},
after: function () {
window.queueComments = false;
window.openedComments = [];
// TODO: scroll to comment position
}
});
// Controller for comment pages
2013-10-25 11:54:19 +09:00
2014-01-12 22:58:07 +05:30
CommentPageController = FastRender.RouteController.extend({
waitOn: function() {
return [
Meteor.subscribe('singleComment', this.params._id),
Meteor.subscribe('commentUser', this.params._id),
Meteor.subscribe('commentPost', this.params._id)
]
},
data: function() {
return {
comment: Comments.findOne(this.params._id)
}
},
after: function () {
window.queueComments = false;
}
});
// Controller for user pages
2014-01-12 22:58:07 +05:30
UserPageController = FastRender.RouteController.extend({
waitOn: function() {
return Meteor.subscribe('singleUser', this.params._idOrSlug);
},
data: function() {
var findById = Meteor.users.findOne(this.params._idOrSlug);
var findBySlug = Meteor.users.findOne({slug: this.params._idOrSlug});
if(typeof findById !== "undefined"){
// redirect to slug-based URL
Router.go(getProfileUrl(findById), {replaceState: true});
}else{
return {
user: (typeof findById == "undefined") ? findBySlug : findById
}
}
}
2013-11-05 09:32:21 +09:00
});
//--------------------------------------------------------------------------------------------------//
//--------------------------------------------- Routes ---------------------------------------------//
//--------------------------------------------------------------------------------------------------//
2013-10-09 21:34:55 +09:00
2013-10-06 08:37:17 +09:00
Router.map(function() {
2013-10-06 09:33:00 +09:00
2013-11-16 14:01:00 +09:00
// -------------------------------------------- Post Lists -------------------------------------------- //
2013-10-09 20:59:42 +09:00
// Top
2013-10-24 20:46:14 +09:00
this.route('posts_top', {
path: '/',
controller: PostsListController
});
this.route('posts_top', {
2013-10-24 20:46:14 +09:00
path: '/top/:limit?',
controller: PostsListController
});
2013-10-09 22:21:28 +09:00
2013-10-09 20:59:42 +09:00
// New
this.route('posts_new', {
2013-10-24 20:46:14 +09:00
path: '/new/:limit?',
controller: PostsListController
});
2013-10-09 22:21:28 +09:00
2013-10-09 20:59:42 +09:00
// Best
this.route('posts_best', {
2013-10-24 20:30:05 +09:00
path: '/best/:limit?',
controller: PostsListController
});
2013-10-09 22:21:28 +09:00
2013-10-09 20:59:42 +09:00
// Pending
this.route('posts_pending', {
2013-10-24 20:51:45 +09:00
path: '/pending/:limit?',
controller: PostsListController
});
2013-10-09 22:21:28 +09:00
2013-10-09 20:59:42 +09:00
// Categories
2013-10-26 11:18:18 +09:00
this.route('posts_category', {
2013-10-24 21:02:42 +09:00
path: '/category/:slug/:limit?',
controller: PostsListController,
2013-10-15 12:15:32 +09:00
after: function() {
Session.set('categorySlug', this.params.slug);
}
});
2013-10-12 13:01:16 +09:00
2013-10-15 12:35:05 +09:00
// TODO: enable /category/new, /category/best, etc. views
2013-11-16 14:01:00 +09:00
// Search
this.route('search', {
2013-11-16 16:53:38 +09:00
path: '/search/:limit?',
2013-11-16 14:01:00 +09:00
controller: PostsListController
});
2013-10-15 12:35:05 +09:00
2013-10-09 11:32:36 +09:00
// Digest
2013-10-09 11:21:23 +09:00
this.route('posts_digest', {
2013-10-06 09:33:00 +09:00
path: '/digest/:year/:month/:day',
controller: PostsDigestController
2013-10-06 09:33:00 +09:00
});
2013-10-06 08:37:17 +09:00
2013-10-26 11:18:18 +09:00
this.route('posts_digest', {
path: '/digest',
controller: PostsDigestController
});
2013-10-12 11:44:29 +09:00
// -------------------------------------------- Post -------------------------------------------- //
2013-10-09 11:32:36 +09:00
// Post Page
this.route('post_page', {
path: '/posts/:_id',
2013-10-25 11:54:19 +09:00
controller: PostPageController
2013-10-09 20:11:58 +09:00
});
2013-10-26 11:18:18 +09:00
this.route('post_page', {
path: '/posts/:_id/comment/:commentId',
2013-10-25 11:54:19 +09:00
controller: PostPageController,
after: function () {
// TODO: scroll to comment position
}
});
2013-10-09 12:39:05 +09:00
2013-10-09 11:32:36 +09:00
// Post Edit
2013-10-06 09:17:37 +09:00
this.route('post_edit', {
path: '/posts/:_id/edit',
waitOn: function () {
2013-10-06 09:17:37 +09:00
return Meteor.subscribe('singlePost', this.params._id);
},
data: function() {
return {postId: this.params._id};
2014-01-12 22:58:07 +05:30
},
fastRender: true
2013-10-06 09:17:37 +09:00
});
2013-10-09 11:32:36 +09:00
2013-10-09 12:39:05 +09:00
// Post Submit
this.route('post_submit', {path: '/submit'});
2013-10-12 11:44:29 +09:00
// -------------------------------------------- Comment -------------------------------------------- //
2013-10-09 11:32:36 +09:00
// Comment Page
2013-10-09 11:46:44 +09:00
this.route('comment_page', {
2013-10-09 12:39:05 +09:00
path: '/comments/:_id',
controller: CommentPageController
2013-10-09 12:39:05 +09:00
});
// Comment Reply
this.route('comment_reply', {
path: '/comments/:_id/reply',
controller: CommentPageController,
2013-10-23 10:40:29 +08:00
after: function() {
window.queueComments = false;
2013-10-09 11:46:44 +09:00
}
});
2013-10-09 11:32:36 +09:00
// Comment Edit
2013-10-09 12:39:05 +09:00
this.route('comment_edit', {
path: '/comments/:_id/edit',
controller: CommentPageController,
after: function() {
window.queueComments = false;
}
2013-10-09 12:39:05 +09:00
});
2013-10-14 11:37:37 +09:00
// -------------------------------------------- Users -------------------------------------------- //
2013-10-09 20:50:26 +09:00
// User Profile
this.route('user_profile', {
2013-10-24 11:04:27 +09:00
path: '/users/:_idOrSlug',
controller: UserPageController
2013-10-09 20:50:26 +09:00
});
// User Edit
this.route('user_edit', {
path: '/users/:_idOrSlug/edit',
controller: UserPageController
2013-10-09 20:50:26 +09:00
});
// Account
this.route('account', {
path: '/account',
template: 'user_edit',
data: function() {
return {
user: Meteor.user()
}
}
});
// Forgot Password
this.route('forgot_password');
2013-10-09 20:50:26 +09:00
// All Users
2013-10-25 09:54:40 +09:00
this.route('all-users', {
path: '/all-users/:limit?',
template: 'users',
2013-10-09 22:16:47 +09:00
waitOn: function() {
var limit = parseInt(this.params.limit) || 20;
return Meteor.subscribe('allUsers', this.params.filterBy, this.params.sortBy, limit);
2013-10-09 22:16:47 +09:00
},
data: function() {
2013-11-04 22:05:19 +09:00
var limit = parseInt(this.params.limit) || 20,
2013-11-08 09:47:23 +09:00
parameters = getUsersParameters(this.params.filterBy, this.params.sortBy, limit),
filterBy = (typeof this.params.filterBy === 'string') ? this.params.filterBy : 'all',
sortBy = (typeof this.params.sortBy === 'string') ? this.params.sortBy : 'createdAt';
2013-10-25 09:54:40 +09:00
Session.set('usersLimit', limit);
2013-10-09 22:16:47 +09:00
return {
2013-11-04 22:05:19 +09:00
users: Meteor.users.find(parameters.find, parameters.options),
2013-11-08 09:47:23 +09:00
filterBy: filterBy,
sortBy: sortBy
2013-10-09 22:16:47 +09:00
}
2014-01-12 22:58:07 +05:30
},
fastRender: true
2013-10-09 22:16:47 +09:00
});
2013-10-09 20:50:26 +09:00
2013-10-09 20:26:58 +09:00
// Unsubscribe (from notifications)
this.route('unsubscribe', {
path: '/unsubscribe/:hash',
data: function() {
return {
hash: this.params.hash
}
}
});
2013-10-09 20:50:26 +09:00
2013-10-09 20:53:55 +09:00
// User Sign-Up
this.route('signup');
2013-10-09 20:59:42 +09:00
2013-10-09 20:53:55 +09:00
// User Sign-In
this.route('signin');
2013-10-14 11:37:37 +09:00
// -------------------------------------------- Other -------------------------------------------- //
2013-10-09 22:21:28 +09:00
// Categories
this.route('categories');
2013-10-09 20:53:55 +09:00
// Settings
this.route('settings');
// Loading (for testing purposes)
this.route('loading');
2013-10-09 20:53:55 +09:00
// Toolbox
this.route('toolbox');
2013-11-22 14:20:47 +09:00
// Search Logs
this.route('logs', {
path: '/logs/:limit?',
waitOn: function () {
var limit = this.params.limit || 100;
2014-01-12 22:58:07 +05:30
if(Meteor.isClient) {
Session.set('logsLimit', limit);
}
2013-11-22 14:20:47 +09:00
return Meteor.subscribe('searches', limit);
},
data: function () {
return Searches.find({}, {sort: {timestamp: -1}});
2014-01-12 22:58:07 +05:30
},
fastRender: true
2013-11-22 14:20:47 +09:00
});
// -------------------------------------------- Server-Side -------------------------------------------- //
this.route('api', {
where: 'server',
path: '/api',
action: function() {
this.response.write(serveAPI());
this.response.end();
}
});
this.route('apiWithParameter', {
where: 'server',
path: '/api/:limit',
action: function() {
this.response.write(serveAPI(this.params.limit));
this.response.end();
}
});
// RSS
this.route('feed', {
where: 'server',
path: '/feed.xml',
action: function() {
this.response.write(serveRSS());
this.response.end();
}
});
// Link Out
this.route('out', {
where: 'server',
path: '/out',
action: function(){
var query = this.request.query;
if(query.url){
var post = Posts.findOne({url: query.url});
if(post){
Posts.update({_id: post._id}, {$inc: {clicks: 1}});
}
this.response.writeHead(302, {'Location': query.url});
this.response.end();
}
}
});
2013-10-06 08:37:17 +09:00
});
2014-01-12 22:58:07 +05:30
// adding common subscriptions that's need to be loaded on all the routes
// notification does not included here since it is not much critical and
// it might have considerable amount of docs
if(Meteor.isServer) {
FastRender.onAllRoutes(function() {
2014-02-18 14:46:53 +09:00
var router = this;
_.each(preloadSubscriptions, function(sub){
router.subscribe(sub);
});
2014-01-12 22:58:07 +05:30
});
}