mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 10:01:40 -05:00
merging with master
This commit is contained in:
commit
b2afbe6ac7
36 changed files with 698 additions and 314 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
## v0.7.3
|
||||||
|
|
||||||
|
* Refactored notifications.
|
||||||
|
* Added notifications for new users creation.
|
||||||
|
|
||||||
|
## v0.7.2
|
||||||
|
|
||||||
|
* Added basic search (thanks Ry!).
|
||||||
|
|
||||||
## v0.7.1
|
## v0.7.1
|
||||||
|
|
||||||
* Added karma redistribution.
|
* Added karma redistribution.
|
||||||
|
|
|
@ -140,6 +140,7 @@ var filters = {
|
||||||
|
|
||||||
canView: function() {
|
canView: function() {
|
||||||
if(Session.get('settingsLoaded') && !canView()){
|
if(Session.get('settingsLoaded') && !canView()){
|
||||||
|
console.log('cannot view')
|
||||||
this.render('no_rights');
|
this.render('no_rights');
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,6 @@ var filters = {
|
||||||
hasCompletedProfile: function() {
|
hasCompletedProfile: function() {
|
||||||
var user = Meteor.user();
|
var user = Meteor.user();
|
||||||
if (user && ! Meteor.loggingIn() && ! userProfileComplete(user)){
|
if (user && ! Meteor.loggingIn() && ! userProfileComplete(user)){
|
||||||
// Session.set('selectedUserId', user._id);
|
|
||||||
this.render('user_email');
|
this.render('user_email');
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,14 @@ var filters = {
|
||||||
|
|
||||||
Router.load( function () {
|
Router.load( function () {
|
||||||
clearSeenErrors(); // set all errors who have already been seen to not show anymore
|
clearSeenErrors(); // set all errors who have already been seen to not show anymore
|
||||||
Session.set('categorySlug', null);
|
Session.set('categorySlug', null);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Before Hooks
|
// Before Hooks
|
||||||
|
@ -198,7 +205,8 @@ Router.before(filters.nProgressHook, {only: [
|
||||||
'posts_best',
|
'posts_best',
|
||||||
'posts_pending',
|
'posts_pending',
|
||||||
'posts_digest',
|
'posts_digest',
|
||||||
'posts_category',
|
'posts_category',
|
||||||
|
'search',
|
||||||
'post_page',
|
'post_page',
|
||||||
'post_edit',
|
'post_edit',
|
||||||
'comment_page',
|
'comment_page',
|
||||||
|
@ -209,9 +217,9 @@ Router.before(filters.nProgressHook, {only: [
|
||||||
'all-users'
|
'all-users'
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
Router.before(filters.canView);
|
||||||
Router.before(filters.hasCompletedProfile);
|
Router.before(filters.hasCompletedProfile);
|
||||||
Router.before(filters.isLoggedIn, {only: ['comment_reply','post_submit']});
|
Router.before(filters.isLoggedIn, {only: ['comment_reply','post_submit']});
|
||||||
Router.before(filters.canView);
|
|
||||||
Router.before(filters.isLoggedOut, {only: ['signin', 'signup']});
|
Router.before(filters.isLoggedOut, {only: ['signin', 'signup']});
|
||||||
Router.before(filters.canPost, {only: ['posts_pending', 'comment_reply', 'post_submit']});
|
Router.before(filters.canPost, {only: ['posts_pending', 'comment_reply', 'post_submit']});
|
||||||
Router.before(filters.canEditPost, {only: ['post_edit']});
|
Router.before(filters.canEditPost, {only: ['post_edit']});
|
||||||
|
@ -240,30 +248,24 @@ PostsListController = RouteController.extend({
|
||||||
template:'posts_list',
|
template:'posts_list',
|
||||||
waitOn: function () {
|
waitOn: function () {
|
||||||
// take the first segment of the path to get the view, unless it's '/' in which case the view default to 'top'
|
// take the first segment of the path to get the view, unless it's '/' in which case the view default to 'top'
|
||||||
var view = this.path == '/' ? 'top' : this.path.split('/')[1];
|
|
||||||
var limit = this.params.limit || getSetting('postsPerPage', 10);
|
|
||||||
// note: most of the time this.params.slug will be empty
|
// note: most of the time this.params.slug will be empty
|
||||||
var parameters = getParameters(view, limit, this.params.slug);
|
this._terms = {
|
||||||
|
view: this.path == '/' ? 'top' : this.path.split('/')[1],
|
||||||
|
limit: this.params.limit || getSetting('postsPerPage', 10),
|
||||||
|
category: this.params.slug,
|
||||||
|
query: Session.get("searchQuery")
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
Meteor.subscribe('postsList', parameters.find, parameters.options),
|
Meteor.subscribe('postsList', this._terms),
|
||||||
Meteor.subscribe('postsListUsers', parameters.find, parameters.options)
|
Meteor.subscribe('postsListUsers', this._terms)
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
var view = this.path == '/' ? 'top' : this.path.split('/')[1],
|
var parameters = getParameters(this._terms),
|
||||||
limit = this.params.limit || getSetting('postsPerPage', 10),
|
|
||||||
parameters = getParameters(view, limit, this.params.slug),
|
|
||||||
posts = Posts.find(parameters.find, parameters.options);
|
posts = Posts.find(parameters.find, parameters.options);
|
||||||
postsCount = posts.count();
|
postsCount = posts.count();
|
||||||
|
|
||||||
Session.set('postsLimit', limit);
|
Session.set('postsLimit', this._terms.limit);
|
||||||
|
|
||||||
// get posts and decorate them with rank property
|
|
||||||
// note: not actually used;
|
|
||||||
// posts = posts.map(function (post, index) {
|
|
||||||
// post.rank = index;
|
|
||||||
// return post;
|
|
||||||
// });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
postsList: posts,
|
postsList: posts,
|
||||||
|
@ -282,16 +284,25 @@ PostsDigestController = RouteController.extend({
|
||||||
template: 'posts_digest',
|
template: 'posts_digest',
|
||||||
waitOn: function() {
|
waitOn: function() {
|
||||||
// if day is set, use that. If not default to today
|
// if day is set, use that. If not default to today
|
||||||
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : Session.get('today');
|
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : Session.get('today'),
|
||||||
var parameters = getDigestParameters(currentDate);
|
terms = {
|
||||||
|
view: 'digest',
|
||||||
|
after: moment(currentDate).startOf('day').valueOf(),
|
||||||
|
before: moment(currentDate).endOf('day').valueOf()
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
Meteor.subscribe('postsList', parameters.find, parameters.options),
|
Meteor.subscribe('postsList', terms),
|
||||||
Meteor.subscribe('postsListUsers', parameters.find, parameters.options)
|
Meteor.subscribe('postsListUsers', terms)
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : Session.get('today');
|
var currentDate = this.params.day ? new Date(this.params.year, this.params.month-1, this.params.day) : Session.get('today'),
|
||||||
var parameters = getDigestParameters(currentDate);
|
terms = {
|
||||||
|
view: 'digest',
|
||||||
|
after: moment(currentDate).startOf('day').valueOf(),
|
||||||
|
before: moment(currentDate).endOf('day').valueOf()
|
||||||
|
},
|
||||||
|
parameters = getParameters(terms);
|
||||||
Session.set('currentDate', currentDate);
|
Session.set('currentDate', currentDate);
|
||||||
return {
|
return {
|
||||||
posts: Posts.find(parameters.find, parameters.options)
|
posts: Posts.find(parameters.find, parameters.options)
|
||||||
|
@ -349,8 +360,13 @@ UserPageController = RouteController.extend({
|
||||||
data: function() {
|
data: function() {
|
||||||
var findById = Meteor.users.findOne(this.params._idOrSlug);
|
var findById = Meteor.users.findOne(this.params._idOrSlug);
|
||||||
var findBySlug = Meteor.users.findOne({slug: this.params._idOrSlug});
|
var findBySlug = Meteor.users.findOne({slug: this.params._idOrSlug});
|
||||||
return {
|
if(typeof findById !== "undefined"){
|
||||||
user: (typeof findById == "undefined") ? findBySlug : findById
|
// redirect to slug-based URL
|
||||||
|
Router.go(getProfileUrl(findById), {replaceState: true});
|
||||||
|
}else{
|
||||||
|
return {
|
||||||
|
user: (typeof findById == "undefined") ? findBySlug : findById
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -361,6 +377,8 @@ UserPageController = RouteController.extend({
|
||||||
|
|
||||||
Router.map(function() {
|
Router.map(function() {
|
||||||
|
|
||||||
|
// -------------------------------------------- Post Lists -------------------------------------------- //
|
||||||
|
|
||||||
// Top
|
// Top
|
||||||
|
|
||||||
this.route('posts_top', {
|
this.route('posts_top', {
|
||||||
|
@ -406,6 +424,12 @@ Router.map(function() {
|
||||||
|
|
||||||
// TODO: enable /category/new, /category/best, etc. views
|
// TODO: enable /category/new, /category/best, etc. views
|
||||||
|
|
||||||
|
// Search
|
||||||
|
|
||||||
|
this.route('search', {
|
||||||
|
path: '/search/:limit?',
|
||||||
|
controller: PostsListController
|
||||||
|
});
|
||||||
|
|
||||||
// Digest
|
// Digest
|
||||||
|
|
||||||
|
@ -520,9 +544,8 @@ Router.map(function() {
|
||||||
path: '/all-users/:limit?',
|
path: '/all-users/:limit?',
|
||||||
template: 'users',
|
template: 'users',
|
||||||
waitOn: function() {
|
waitOn: function() {
|
||||||
var limit = parseInt(this.params.limit) || 20,
|
var limit = parseInt(this.params.limit) || 20;
|
||||||
parameters = getUsersParameters(this.params.filterBy, this.params.sortBy, limit);
|
return Meteor.subscribe('allUsers', this.params.filterBy, this.params.sortBy, limit);
|
||||||
return Meteor.subscribe('allUsers', parameters.find, parameters.options);
|
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
var limit = parseInt(this.params.limit) || 20,
|
var limit = parseInt(this.params.limit) || 20,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Session.set('initialLoad', true);
|
Session.set('initialLoad', true);
|
||||||
Session.set('today', new Date());
|
Session.set('today', new Date());
|
||||||
Session.set('view', 'top');
|
Session.set('view', 'top');
|
||||||
Session.set('postsLimit', getSetting('postsPerPage', 2));
|
Session.set('postsLimit', getSetting('postsPerPage', 10));
|
||||||
Session.set('settingsLoaded', false);
|
Session.set('settingsLoaded', false);
|
||||||
Session.set('sessionId', Meteor.default_connection._lastSessionId);
|
Session.set('sessionId', Meteor.default_connection._lastSessionId);
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
height:26px;
|
height:26px;
|
||||||
margin-right:10px;
|
margin-right:10px;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
color:white;
|
color:white;
|
||||||
&:before{
|
&:before{
|
||||||
color:white(0.5);
|
color:white(0.5);
|
||||||
content:"Welcome, ";
|
content:"Welcome, ";
|
||||||
|
@ -136,6 +136,7 @@
|
||||||
>li{
|
>li{
|
||||||
@include horizontal-list-item;
|
@include horizontal-list-item;
|
||||||
margin-right:10px;
|
margin-right:10px;
|
||||||
|
|
||||||
>a{
|
>a{
|
||||||
color:white;
|
color:white;
|
||||||
font-size:16px;
|
font-size:16px;
|
||||||
|
@ -153,4 +154,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.search{
|
||||||
|
position: relative;
|
||||||
|
.search-field{
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
line-height: 1.3;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 0px;
|
||||||
|
width: 100px;
|
||||||
|
background: white;
|
||||||
|
&:focus{
|
||||||
|
outline:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.empty{
|
||||||
|
.search-field{
|
||||||
|
background: white(0.1);
|
||||||
|
&:focus{
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.search-clear{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,13 +3,16 @@
|
||||||
background: $lightest-grey;
|
background: $lightest-grey;
|
||||||
@include border-radius(3px);
|
@include border-radius(3px);
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
a, span{
|
a, span{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
&.active{
|
&.active{
|
||||||
border-bottom: 2px solid $blue;
|
border-bottom: 2px solid $blue;
|
||||||
}
|
}
|
||||||
|
&:last-child{
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.filter{
|
.filter{
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -54,4 +57,10 @@ table{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.user-table{
|
||||||
|
font-size: 13px;
|
||||||
|
tr{
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -34,4 +34,4 @@
|
||||||
@import "partials/mobile";
|
@import "partials/mobile";
|
||||||
|
|
||||||
@import "themes/default";
|
@import "themes/default";
|
||||||
@import "themes/telescope";
|
@import "themes/telescope";
|
||||||
|
|
|
@ -822,20 +822,45 @@ body.pageslide-open {
|
||||||
/* line 50, ../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/lists/_horizontal-list.scss */
|
/* line 50, ../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/lists/_horizontal-list.scss */
|
||||||
.nav > li.last {
|
.nav > li.last {
|
||||||
padding-right: 0; }
|
padding-right: 0; }
|
||||||
/* line 139, ../sass/modules/_header.scss */
|
/* line 140, ../sass/modules/_header.scss */
|
||||||
.nav > li > a {
|
.nav > li > a {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
font-weight: normal; }
|
font-weight: normal; }
|
||||||
/* line 147, ../sass/modules/_header.scss */
|
/* line 148, ../sass/modules/_header.scss */
|
||||||
.nav > li > a.intercom em:before {
|
.nav > li > a.intercom em:before {
|
||||||
content: '('; }
|
content: '('; }
|
||||||
/* line 150, ../sass/modules/_header.scss */
|
/* line 151, ../sass/modules/_header.scss */
|
||||||
.nav > li > a.intercom em:after {
|
.nav > li > a.intercom em:after {
|
||||||
content: ')'; }
|
content: ')'; }
|
||||||
|
|
||||||
|
/* line 158, ../sass/modules/_header.scss */
|
||||||
|
.search {
|
||||||
|
position: relative; }
|
||||||
|
/* line 160, ../sass/modules/_header.scss */
|
||||||
|
.search .search-field {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
line-height: 1.3;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 0px;
|
||||||
|
width: 100px;
|
||||||
|
background: white; }
|
||||||
|
/* line 168, ../sass/modules/_header.scss */
|
||||||
|
.search .search-field:focus {
|
||||||
|
outline: none; }
|
||||||
|
/* line 173, ../sass/modules/_header.scss */
|
||||||
|
.search.empty .search-field {
|
||||||
|
background: rgba(255, 255, 255, 0.1); }
|
||||||
|
/* line 175, ../sass/modules/_header.scss */
|
||||||
|
.search.empty .search-field:focus {
|
||||||
|
background: white; }
|
||||||
|
/* line 179, ../sass/modules/_header.scss */
|
||||||
|
.search.empty .search-clear {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
/* line 1, ../sass/modules/_posts.scss */
|
/* line 1, ../sass/modules/_posts.scss */
|
||||||
.empty-notice {
|
.empty-notice {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -1621,7 +1646,7 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
||||||
-o-border-radius: 3px;
|
-o-border-radius: 3px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
font-size: 14px; }
|
font-size: 13px; }
|
||||||
/* line 3, ../sass/partials/_mixins.scss */
|
/* line 3, ../sass/partials/_mixins.scss */
|
||||||
.filter-sort:before, .filter-sort:after {
|
.filter-sort:before, .filter-sort:after {
|
||||||
content: "";
|
content: "";
|
||||||
|
@ -1636,14 +1661,17 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
||||||
/* line 10, ../sass/modules/_users.scss */
|
/* line 10, ../sass/modules/_users.scss */
|
||||||
.filter-sort a.active, .filter-sort span.active {
|
.filter-sort a.active, .filter-sort span.active {
|
||||||
border-bottom: 2px solid #7ac0e4; }
|
border-bottom: 2px solid #7ac0e4; }
|
||||||
/* line 14, ../sass/modules/_users.scss */
|
/* line 13, ../sass/modules/_users.scss */
|
||||||
|
.filter-sort a:last-child, .filter-sort span:last-child {
|
||||||
|
margin-right: 0; }
|
||||||
|
/* line 17, ../sass/modules/_users.scss */
|
||||||
.filter-sort .filter {
|
.filter-sort .filter {
|
||||||
float: left; }
|
float: left; }
|
||||||
/* line 17, ../sass/modules/_users.scss */
|
/* line 20, ../sass/modules/_users.scss */
|
||||||
.filter-sort .sort {
|
.filter-sort .sort {
|
||||||
float: right; }
|
float: right; }
|
||||||
|
|
||||||
/* line 24, ../sass/modules/_users.scss */
|
/* line 27, ../sass/modules/_users.scss */
|
||||||
.user-list .user .user-avatar, .user-table .user .user-avatar {
|
.user-list .user .user-avatar, .user-table .user .user-avatar {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
@ -1655,13 +1683,20 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
||||||
-o-border-radius: 30px;
|
-o-border-radius: 30px;
|
||||||
border-radius: 30px; }
|
border-radius: 30px; }
|
||||||
|
|
||||||
/* line 38, ../sass/modules/_users.scss */
|
/* line 41, ../sass/modules/_users.scss */
|
||||||
table tr td {
|
table tr td {
|
||||||
padding: 10px; }
|
padding: 10px; }
|
||||||
/* line 44, ../sass/modules/_users.scss */
|
/* line 47, ../sass/modules/_users.scss */
|
||||||
table thead tr td {
|
table thead tr td {
|
||||||
font-weight: bold; }
|
font-weight: bold; }
|
||||||
|
|
||||||
|
/* line 61, ../sass/modules/_users.scss */
|
||||||
|
.user-table {
|
||||||
|
font-size: 13px; }
|
||||||
|
/* line 63, ../sass/modules/_users.scss */
|
||||||
|
.user-table tr {
|
||||||
|
border-bottom: 1px solid #eee; }
|
||||||
|
|
||||||
/* line 2, ../sass/modules/_user-profile.scss */
|
/* line 2, ../sass/modules/_user-profile.scss */
|
||||||
.user-profile .user-avatar {
|
.user-profile .user-avatar {
|
||||||
height: 80px;
|
height: 80px;
|
||||||
|
|
|
@ -29,8 +29,10 @@ Template.settings.events = {
|
||||||
throwError("Settings have been created");
|
throwError("Settings have been created");
|
||||||
},
|
},
|
||||||
function(error) {
|
function(error) {
|
||||||
if(error) console.log(error);
|
if(error)
|
||||||
throwError("Settings have been updated");
|
console.log(error);
|
||||||
|
throwError("Settings have been updated");
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
<template name="toolbox">
|
<template name="toolbox">
|
||||||
<div class="grid-small grid-block dialog admin toolbox">
|
<div class="grid-small grid-block dialog admin toolbox">
|
||||||
<h2>Toolbox</h2>
|
<h2>Toolbox</h2>
|
||||||
<h4>Updates</h4>
|
|
||||||
<ul>
|
|
||||||
<!-- <li><a href="#" class="update-categories">Update Categories</a></li> -->
|
|
||||||
<li><a href="#" class="update-user-profiles">Update User Profiles</a></li>
|
|
||||||
<!-- <li><a href="#" class="update-posts-slugs">Update Posts Slugs</a></li> -->
|
|
||||||
</ul>
|
|
||||||
<h4>Tools</h4>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#" class="give-invites">Give 1 Invite to Everybody</a></li>
|
<li><a href="#" class="give-invites">Give 1 Invite to Everybody</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -2,13 +2,20 @@
|
||||||
<header class="header grid">
|
<header class="header grid">
|
||||||
|
|
||||||
<a id="mobile-menu" href="#menu" class="mobile mobile-button menu"><i class="icon-menu"></i><span>{{i18n "Menu"}}</span></a>
|
<a id="mobile-menu" href="#menu" class="mobile mobile-button menu"><i class="icon-menu"></i><span>{{i18n "Menu"}}</span></a>
|
||||||
|
|
||||||
<ul class="nav site-nav desktop">
|
<ul class="nav site-nav desktop">
|
||||||
{{#if canView}}
|
{{#if canView}}
|
||||||
<li><a class="top" href="/top">{{i18n "Top"}}</a></li>
|
<li class="dropdown">
|
||||||
<li><a class="new" href="/new">{{i18n "New"}}</a></li>
|
<a class="View" href="/">{{i18n "View"}}</a>
|
||||||
<li><a class="best" href="/best">{{i18n "Best"}}</a></li>
|
<div class="dropdown-menu">
|
||||||
<li><a class="digest" href="/digest">{{i18n "Digest"}}</a></li>
|
<ul role="menu" aria-labelledby="dLabel">
|
||||||
|
<li><a class="top" href="/top">{{i18n "Top"}}</a></li>
|
||||||
|
<li><a class="new" href="/new">{{i18n "New"}}</a></li>
|
||||||
|
<li><a class="best" href="/best">{{i18n "Best"}}</a></li>
|
||||||
|
<li><a class="digest" href="/digest">{{i18n "Digest"}}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if hasCategories}}
|
{{#if hasCategories}}
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
|
@ -36,8 +43,11 @@
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<li>
|
||||||
|
{{> search}}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{#if logo_url}}
|
{{#if logo_url}}
|
||||||
<h1 class="logo image" style="height:{{logo_height}}px; width:{{logo_width}}px; top:{{logo_top}}px; margin-left:{{logo_offset}}px;">
|
<h1 class="logo image" style="height:{{logo_height}}px; width:{{logo_width}}px; top:{{logo_top}}px; margin-left:{{logo_offset}}px;">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
|
@ -47,13 +57,13 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<h1 class="logo"><a href="/">{{site_title}}</a></h1>
|
<h1 class="logo"><a href="/">{{site_title}}</a></h1>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if canView}}
|
{{#if canView}}
|
||||||
<ul class="nav user-nav desktop">
|
<ul class="nav user-nav desktop">
|
||||||
<li><a id="submit" class="submit button" href="/submit">{{i18n "Post"}}</a></li>
|
<li><a id="submit" class="submit button" href="/submit">{{i18n "Post"}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="auth-buttons">
|
<div class="auth-buttons">
|
||||||
{{loginButtons align="right"}}
|
{{loginButtons align="right"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,27 +1,3 @@
|
||||||
Template.nav.events = {
|
|
||||||
'click #logout': function(event){
|
|
||||||
event.preventDefault();
|
|
||||||
Meteor.logout();
|
|
||||||
},
|
|
||||||
'click #mobile-menu': function(event){
|
|
||||||
event.preventDefault();
|
|
||||||
$('body').toggleClass('mobile-nav-open');
|
|
||||||
},
|
|
||||||
'click .login-header': function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
Router.go('/account');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.nav.rendered=function(){
|
|
||||||
if(!Meteor.user()){
|
|
||||||
$('.login-link-text').text(i18n.t("Sign Up/Sign In"));
|
|
||||||
}else{
|
|
||||||
$('#login-buttons-logout').before('<a href="/users/'+Meteor.user().slug+'" class="account-link button">'+i18n.t("View Profile")+'</a>');
|
|
||||||
$('#login-buttons-logout').before('<a href="/account" class="account-link button">'+i18n.t("Edit Account")+'</a>');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.nav.helpers({
|
Template.nav.helpers({
|
||||||
site_title: function(){
|
site_title: function(){
|
||||||
return getSetting('title');
|
return getSetting('title');
|
||||||
|
@ -59,4 +35,29 @@ Template.nav.helpers({
|
||||||
categoryLink: function () {
|
categoryLink: function () {
|
||||||
return getCategoryUrl(this.slug);
|
return getCategoryUrl(this.slug);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.nav.rendered=function(){
|
||||||
|
|
||||||
|
if(!Meteor.user()){
|
||||||
|
$('.login-link-text').text("Sign Up/Sign In");
|
||||||
|
}else{
|
||||||
|
$('#login-buttons-logout').before('<a href="/users/'+Meteor.user().slug+'" class="account-link button">View Profile</a>');
|
||||||
|
$('#login-buttons-logout').before('<a href="/account" class="account-link button">Edit Account</a>');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.nav.events = {
|
||||||
|
'click #logout': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
Meteor.logout();
|
||||||
|
},
|
||||||
|
'click #mobile-menu': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('body').toggleClass('mobile-nav-open');
|
||||||
|
},
|
||||||
|
'click .login-header': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
Router.go('/account');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
5
client/views/common/search.html
Normal file
5
client/views/common/search.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template name="search">
|
||||||
|
<div class="search {{searchQueryEmpty}}">
|
||||||
|
<input id="search" type="search" class="search-field" placeholder="search" value="{{searchQuery}}">
|
||||||
|
</div>
|
||||||
|
</template>
|
43
client/views/common/search.js
Normal file
43
client/views/common/search.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
Template.search.helpers({
|
||||||
|
searchQuery: function () {
|
||||||
|
return Session.get("searchQuery");
|
||||||
|
},
|
||||||
|
searchQueryEmpty: function () {
|
||||||
|
return !!Session.get("searchQuery") ? '' : 'empty';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.search.preserve({
|
||||||
|
'input#search': function (node) { return node.id; }
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.search.events = {
|
||||||
|
'keyup, search .search-field': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var val = $(e.target).val(),
|
||||||
|
$search = $('.search');
|
||||||
|
if(val==''){
|
||||||
|
// if search field is empty, just do nothing and show an empty template
|
||||||
|
$search.addClass('empty');
|
||||||
|
Session.set('searchQuery', '');
|
||||||
|
}else{
|
||||||
|
// if search field is not empty, add a delay to avoid firing new searches for every keystroke
|
||||||
|
delay(function(){
|
||||||
|
Session.set('searchQuery', val);
|
||||||
|
$search.removeClass('empty');
|
||||||
|
// if we're not already on the search page, go to it
|
||||||
|
if(getCurrentRoute().indexOf('search') == -1)
|
||||||
|
Router.go('/search');
|
||||||
|
}, 500 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// see: http://stackoverflow.com/questions/1909441/jquery-keyup-delay
|
||||||
|
var delay = (function(){
|
||||||
|
var timer = 0;
|
||||||
|
return function(callback, ms){
|
||||||
|
clearTimeout (timer);
|
||||||
|
timer = setTimeout(callback, ms);
|
||||||
|
};
|
||||||
|
})();
|
|
@ -6,7 +6,7 @@ Template.notification_item.helpers({
|
||||||
return this.properties;
|
return this.properties;
|
||||||
},
|
},
|
||||||
notificationHTML: function(){
|
notificationHTML: function(){
|
||||||
return getNotification(this.event, this.properties).html;
|
return getNotificationContents(this).html;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,8 @@ Template.post_item.helpers({
|
||||||
return _.include(this.upvoters, user._id);
|
return _.include(this.upvoters, user._id);
|
||||||
},
|
},
|
||||||
userAvatar: function(){
|
userAvatar: function(){
|
||||||
if(author=Meteor.users.findOne(this.userId), {reactive: false})
|
var author = Meteor.users.findOne(this.userId, {reactive: false});
|
||||||
|
if(!!author)
|
||||||
return getAvatarUrl(author);
|
return getAvatarUrl(author);
|
||||||
},
|
},
|
||||||
inactiveClass: function(){
|
inactiveClass: function(){
|
||||||
|
|
|
@ -67,6 +67,11 @@
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n "Email Notifications"}}</label>
|
<label class="control-label">{{i18n "Email Notifications"}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
{{#if isAdmin}}
|
||||||
|
<label class="checkbox">
|
||||||
|
<input id="notifications_users" type="checkbox" name="notifications_users" {{hasNotificationsUsers}} /> New Users
|
||||||
|
</label>
|
||||||
|
{{/if}}
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input id="notifications_posts" type="checkbox" name="notifications_posts" {{hasNotificationsPosts}} /> {{i18n "New Posts"}}
|
<input id="notifications_posts" type="checkbox" name="notifications_posts" {{hasNotificationsPosts}} /> {{i18n "New Posts"}}
|
||||||
</label>
|
</label>
|
||||||
|
@ -83,7 +88,7 @@
|
||||||
<h3>Invites</h3>
|
<h3>Invites</h3>
|
||||||
<label>Invites</label>
|
<label>Invites</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input name="invitesCount" type="text" value="{{invitesCount}}" />
|
<input name="inviteCount" type="text" value="{{inviteCount}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -17,14 +17,17 @@ Template.user_edit.helpers({
|
||||||
profileUrl: function(){
|
profileUrl: function(){
|
||||||
return Meteor.absoluteUrl()+"users/"+this.slug;
|
return Meteor.absoluteUrl()+"users/"+this.slug;
|
||||||
},
|
},
|
||||||
|
hasNotificationsUsers : function(){
|
||||||
|
return getUserSetting('notifications.users', '', this) ? 'checked' : '';
|
||||||
|
},
|
||||||
hasNotificationsPosts : function(){
|
hasNotificationsPosts : function(){
|
||||||
return getUserSetting('notifications.posts') ? 'checked' : '';
|
return getUserSetting('notifications.notifications_posts', '', this) ? 'checked' : '';
|
||||||
},
|
},
|
||||||
hasNotificationsComments : function(){
|
hasNotificationsComments : function(){
|
||||||
return getUserSetting('notifications.comments') ? 'checked' : '';
|
return getUserSetting('notifications.comments', '', this) ? 'checked' : '';
|
||||||
},
|
},
|
||||||
hasNotificationsReplies : function(){
|
hasNotificationsReplies : function(){
|
||||||
return getUserSetting('notifications.replies') ? 'checked' : '';
|
return getUserSetting('notifications.replies', '', this) ? 'checked' : '';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -45,10 +48,11 @@ Template.user_edit.events = {
|
||||||
"profile.twitter": $target.find('[name=twitter]').val(),
|
"profile.twitter": $target.find('[name=twitter]').val(),
|
||||||
"profile.github": $target.find('[name=github]').val(),
|
"profile.github": $target.find('[name=github]').val(),
|
||||||
"profile.site": $target.find('[name=site]').val(),
|
"profile.site": $target.find('[name=site]').val(),
|
||||||
|
"profile.notifications.users": $('input[name=notifications_users]:checked').length, // only actually used for admins
|
||||||
"profile.notifications.posts": $('input[name=notifications_posts]:checked').length,
|
"profile.notifications.posts": $('input[name=notifications_posts]:checked').length,
|
||||||
"profile.notifications.comments": $('input[name=notifications_comments]:checked').length,
|
"profile.notifications.comments": $('input[name=notifications_comments]:checked').length,
|
||||||
"profile.notifications.replies": $('input[name=notifications_replies]:checked').length,
|
"profile.notifications.replies": $('input[name=notifications_replies]:checked').length,
|
||||||
"invitesCount": parseInt($target.find('[name=invitesCount]').val())
|
"inviteCount": parseInt($target.find('[name=inviteCount]').val())
|
||||||
};
|
};
|
||||||
|
|
||||||
var old_password = $target.find('[name=old_password]').val();
|
var old_password = $target.find('[name=old_password]').val();
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
Template.user_email.helpers({
|
||||||
|
user: function(){
|
||||||
|
return Meteor.user();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Template.user_email.events = {
|
Template.user_email.events = {
|
||||||
'submit form': function(e){
|
'submit form': function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -26,16 +32,3 @@ Template.user_email.events = {
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.user_email.profileIncomplete = function() {
|
|
||||||
return Meteor.user() && !this.loading && !userProfileComplete(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Template.user_email.user = function(){
|
|
||||||
var current_user=Meteor.user();
|
|
||||||
if(Session.get('selectedUserId') && !current_user.loading && current_user.isAdmin){
|
|
||||||
return Meteor.users.findOne(Session.get('selectedUserId'));
|
|
||||||
}else{
|
|
||||||
return current_user;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,33 @@
|
||||||
<template name="user_item">
|
<template name="user_item">
|
||||||
<tr class="user">
|
<tr class="user">
|
||||||
<td><span class="user-avatar" style="background-image:url({{avatarUrl}});"></span></td>
|
<td><span class="user-avatar" style="background-image:url({{avatarUrl}});"></span></td>
|
||||||
<td><a href="{{profileUrl}}">{{displayName}}</a></td>
|
<td>
|
||||||
|
<a href="{{getProfileUrl}}">{{displayName}}</a>
|
||||||
|
<br/>
|
||||||
|
<a href="mailto:{{getEmail}}">{{getEmail}}</a>
|
||||||
|
</td>
|
||||||
<td>{{createdAtFormatted}}</td>
|
<td>{{createdAtFormatted}}</td>
|
||||||
<td>{{getEmail}}</td>
|
|
||||||
<td>{{postCount}}</td>
|
<td>{{postCount}}</td>
|
||||||
<td>{{commentCount}}</td>
|
<td>{{commentCount}}</td>
|
||||||
<td>{{getKarma}}</td>
|
<td>{{getKarma}}</td>
|
||||||
<td>{{#if isInvited}}<a class="uninvite-link" href="#"><i class="icon-check"></i>{{i18n "Uninvite"}}</a>{{else}}<a href="#" class="invite-link">{{i18n "Invite"}}</a>{{/if}}</td>
|
<td>
|
||||||
<td>{{#if userIsAdmin}}<a class="unadmin-link" href="#"><i class="icon-check unadmin-link"></i>{{i18n "Unadmin"}}</a>{{else}}<a href="#" class="admin-link">{{i18n "Make admin"}}</a>{{/if}}</td>
|
{{#if invites}}
|
||||||
<td><a class="delete-link" href="#">{{i18n "Delete User"}}</a></td>
|
<h4>{{i18n "Invited"}} {{invitedCount}} {{i18n "users"}}:</h4>
|
||||||
|
<ul>
|
||||||
|
{{#each invites}}
|
||||||
|
<li><a href="{{getInvitedUserProfileUrl}}">{{invitedName}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
<p>({{inviteCount}} {{i18n "invites left"}})</p>
|
||||||
|
</td>
|
||||||
|
<td>{{#if isInvited}}<i class="icon-check"></i>{{/if}}</td>
|
||||||
|
<td>{{#if userIsAdmin}}<i class="icon-check"></i>{{/if}}</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li>{{#if isInvited}}<a class="uninvite-link" href="#">{{i18n "Uninvite"}}</a>{{else}}<a href="#" class="invite-link">{{i18n "Invite"}}</a>{{/if}}</li>
|
||||||
|
<li>{{#if userIsAdmin}}<a class="unadmin-link" href="#">{{i18n "Unadmin"}}</a>{{else}}<a href="#" class="admin-link">{{i18n "Make admin"}}</a>{{/if}}</li>
|
||||||
|
<li><a class="delete-link" href="#">{{i18n "Delete User"}}</a></li>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
|
@ -20,35 +20,22 @@ Template.user_item.helpers({
|
||||||
userIsAdmin: function(){
|
userIsAdmin: function(){
|
||||||
return isAdmin(this);
|
return isAdmin(this);
|
||||||
},
|
},
|
||||||
profileUrl: function () {
|
getProfileUrl: function () {
|
||||||
return getProfileUrl(this);
|
return getProfileUrl(this);
|
||||||
},
|
},
|
||||||
getKarma: function() {
|
getKarma: function() {
|
||||||
return Math.round(100*this.karma)/100;
|
return Math.round(100*this.karma)/100;
|
||||||
|
},
|
||||||
|
getInvitedUserProfileUrl: function () {
|
||||||
|
var user = Meteor.users.findOne(this.invitedId);
|
||||||
|
return getProfileUrl(user);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.user_item.events({
|
Template.user_item.events({
|
||||||
'click .invite-link': function(e, instance){
|
'click .invite-link': function(e, instance){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var user = Meteor.users.findOne(instance.data._id);
|
Meteor.call('inviteUser', instance.data._id);
|
||||||
Meteor.users.update(user._id,{
|
|
||||||
$set:{
|
|
||||||
isInvited: true
|
|
||||||
}
|
|
||||||
}, {multi: false}, function(error){
|
|
||||||
if(error){
|
|
||||||
throwError();
|
|
||||||
}else{
|
|
||||||
Meteor.call('createNotification', {
|
|
||||||
event: 'accountApproved',
|
|
||||||
properties: {},
|
|
||||||
userToNotify: user,
|
|
||||||
userDoingAction: Meteor.user(),
|
|
||||||
sendEmail: getSetting("emailNotifications")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
'click .uninvite-link': function(e, instance){
|
'click .uninvite-link': function(e, instance){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -6,16 +6,22 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><img class="user-avatar" src="{{avatarUrl}}"/></td>
|
<td colspan="2"><img class="user-avatar" src="{{avatarUrl}}"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{#if isAdmin}}
|
||||||
|
<tr>
|
||||||
|
<td>{{i18n "ID"}}: </td>
|
||||||
|
<td>{{_id}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "Name"}}: </td>
|
<td>{{i18n "Name:"}}</td>
|
||||||
<td>{{profile.name}}</td>
|
<td>{{profile.name}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "Member since"}}: </td>
|
<td>{{i18n "Member since"}}:</td>
|
||||||
<td>{{createdAtFormatted}}</td>
|
<td>{{createdAtFormatted}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "Bio"}}: </td>
|
<td>{{i18n "Bio:"}}</td>
|
||||||
<td>{{profile.bio}}</td>
|
<td>{{profile.bio}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{#if getTwitterName}}
|
{{#if getTwitterName}}
|
||||||
|
@ -26,13 +32,13 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if getGitHubName}}
|
{{#if getGitHubName}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>GitHub: </td>
|
<td>{{i18n "GitHub"}}:</td>
|
||||||
<td><a href="http://github.com/{{getGitHubName}}">{{getGitHubName}}</a></td>
|
<td><a href="http://github.com/{{getGitHubName}}">{{getGitHubName}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if site}}
|
{{#if site}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Site: </td>
|
<td>{{i18n "Site"}}:</td>
|
||||||
<td><a href="{{profile.site}}">{{profile.site}}</a></td>
|
<td><a href="{{profile.site}}">{{profile.site}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -41,8 +47,8 @@
|
||||||
<a class="button inline" href="/users/{{slug}}/edit">{{i18n "Edit profile"}}</a>
|
<a class="button inline" href="/users/{{slug}}/edit">{{i18n "Edit profile"}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if canInvite}}
|
{{#if canInvite}}
|
||||||
{{#if invitesCount}}
|
{{#if inviteCount}}
|
||||||
<a class="button inline invite-link" href="#">{{i18n "Invite "}}({{invitesCount}} {{i18n "left"}})</a>
|
<a class="button inline invite-link" href="#">{{i18n "Invite"}} ({{inviteCount}} {{i18n "left"}})</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class="button inline disabled" href="#">{{i18n "Invite (none left)"}}</a>
|
<a class="button inline disabled" href="#">{{i18n "Invite (none left)"}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -13,8 +13,8 @@ Template.user_profile.helpers({
|
||||||
// if the user is logged in, the target user hasn't been invited yet, invites are enabled, and user is not viewing their own profile
|
// if the user is logged in, the target user hasn't been invited yet, invites are enabled, and user is not viewing their own profile
|
||||||
return Meteor.user() && Meteor.user()._id != this._id && !isInvited(this) && invitesEnabled() && canInvite(Meteor.user());
|
return Meteor.user() && Meteor.user()._id != this._id && !isInvited(this) && invitesEnabled() && canInvite(Meteor.user());
|
||||||
},
|
},
|
||||||
invitesCount: function() {
|
inviteCount: function() {
|
||||||
return Meteor.user().invitesCount;
|
return Meteor.user().inviteCount;
|
||||||
},
|
},
|
||||||
getTwitterName: function () {
|
getTwitterName: function () {
|
||||||
return getTwitterName(this);
|
return getTwitterName(this);
|
||||||
|
@ -27,5 +27,6 @@ Template.user_profile.helpers({
|
||||||
Template.user_profile.events({
|
Template.user_profile.events({
|
||||||
'click .invite-link': function(e, instance){
|
'click .invite-link': function(e, instance){
|
||||||
Meteor.call('inviteUser', instance.data.user._id);
|
Meteor.call('inviteUser', instance.data.user._id);
|
||||||
|
throwError('Thanks, user has been invited.')
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -17,6 +17,7 @@
|
||||||
<a class="{{activeClass 'username'}}" href="{{sortBy 'username'}}">{{i18n "Username"}}</a>
|
<a class="{{activeClass 'username'}}" href="{{sortBy 'username'}}">{{i18n "Username"}}</a>
|
||||||
<a class="{{activeClass 'postCount'}}" href="{{sortBy 'postCount'}}">{{i18n "Posts"}}</a>
|
<a class="{{activeClass 'postCount'}}" href="{{sortBy 'postCount'}}">{{i18n "Posts"}}</a>
|
||||||
<a class="{{activeClass 'commentCount'}}" href="{{sortBy 'commentCount'}}">{{i18n "Comments"}}</a>
|
<a class="{{activeClass 'commentCount'}}" href="{{sortBy 'commentCount'}}">{{i18n "Comments"}}</a>
|
||||||
|
<a class="{{activeClass 'invitedCount'}}" href="{{sortBy 'invitedCount'}}">{{i18n "InvitedCount"}}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
|
@ -24,13 +25,13 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">{{i18n "Name"}}</td>
|
<td colspan="2">{{i18n "Name"}}</td>
|
||||||
<td>{{i18n "Member since"}}</td>
|
<td>{{i18n "Member since"}}</td>
|
||||||
<td>{{i18n "Email"}}</td>
|
|
||||||
<td>{{i18n "Posts"}}</td>
|
<td>{{i18n "Posts"}}</td>
|
||||||
<td>{{i18n "Comments"}}</td>
|
<td>{{i18n "Comments"}}</td>
|
||||||
<td>{{i18n "Karma"}}</td>
|
<td>{{i18n "Karma"}}</td>
|
||||||
<td>{{i18n "Is Invited?"}}</td>
|
<td>{{i18n "Invites"}}</td>
|
||||||
<td>{{i18n "Is Admin?"}}</td>
|
<td>{{i18n "Invited?"}}</td>
|
||||||
<td>{{i18n "Delete"}}</td>
|
<td>{{i18n "Admin?"}}</td>
|
||||||
|
<td>{{i18n "Actions"}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -9,44 +9,71 @@ Notifications.allow({
|
||||||
, remove: canEditById
|
, remove: canEditById
|
||||||
});
|
});
|
||||||
|
|
||||||
getNotification = function(event, properties, context){
|
getNotificationContents = function(notification, context){
|
||||||
var notification = {};
|
// the same notifications can be displayed in multiple contexts: on-site in the sidebar, sent by email, etc.
|
||||||
// the default context to display notifications is the notification sidebar
|
var event = notification.event,
|
||||||
var context = typeof context === 'undefined' ? 'sidebar' : context;
|
p = notification.properties,
|
||||||
var p = properties;
|
context = typeof context === 'undefined' ? 'sidebar' : context,
|
||||||
|
userToNotify = Meteor.users.findOne(notification.userId);
|
||||||
|
|
||||||
|
|
||||||
switch(event){
|
switch(event){
|
||||||
case 'newReply':
|
case 'newReply':
|
||||||
notification.subject = i18n.t('Someone replied to your comment on')+' "'+p.postHeadline+'"';
|
var n = {
|
||||||
notification.text = p.commentAuthorName+i18n.t(' has replied to your comment on')+' "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId);
|
subject: 'Someone replied to your comment on "'+p.postHeadline+'"',
|
||||||
notification.html = '<p><a href="'+getUserUrl(p.commentAuthorId)+'">'+p.commentAuthorName+'</a>'+i18n.t(' has replied to your comment on')+' "<a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+p.postHeadline+'</a>"</p>';
|
text: p.commentAuthorName+' has replied to your comment on "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId),
|
||||||
if(context === 'email')
|
html: '<p><a href="'+getProfileUrlById(p.commentAuthorId)+'">'+p.commentAuthorName+'</a> has replied to your comment on "<a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+p.postHeadline+'</a>"</p>'
|
||||||
notification.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+i18n.t('Read more')+'</a>';
|
}
|
||||||
break;
|
if(context == 'email')
|
||||||
|
n.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">Read more</a>';
|
||||||
|
break;
|
||||||
|
|
||||||
case 'newComment':
|
case 'newComment':
|
||||||
notification.subject = i18n.t('A new comment on your post')+' "'+p.postHeadline+'"';
|
var n = {
|
||||||
notification.text = i18n.t('You have a new comment by ')+p.commentAuthorName+i18n.t(' on your post')+' "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId);
|
subject: 'A new comment on your post "'+p.postHeadline+'"',
|
||||||
notification.html = '<p><a href="'+getUserUrl(p.commentAuthorId)+'">'+p.commentAuthorName+'</a> left a new comment on your post "<a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+p.postHeadline+'</a>"</p>';
|
text: 'You have a new comment by '+p.commentAuthorName+' on your post "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId),
|
||||||
if(context === 'email')
|
html: '<p><a href="'+getProfileUrlById(p.commentAuthorId)+'">'+p.commentAuthorName+'</a> left a new comment on your post "<a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+p.postHeadline+'</a>"</p>'
|
||||||
notification.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+i18n.t('Read more')+'</a>';
|
}
|
||||||
break;
|
if(context == 'email')
|
||||||
|
n.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">Read more</a>';
|
||||||
|
break;
|
||||||
|
|
||||||
case 'newPost':
|
case 'newPost':
|
||||||
notification.subject = p.postAuthorName+i18n.t(' has created a new post')+': "'+p.postHeadline+'"';
|
var n = {
|
||||||
notification.text = p.postAuthorName+i18n.t(' has created a new post')+': "'+p.postHeadline+'" '+getPostUrl(p.postId);
|
subject: p.postAuthorName+' has created a new post: "'+p.postHeadline+'"',
|
||||||
notification.html = '<a href="'+getUserUrl(p.postAuthorId)+'">'+p.postAuthorName+'</a>'+i18n.t(' has created a new post')+': "<a href="'+getPostUrl(p.postId)+'" class="action-link">'+p.postHeadline+'</a>".';
|
text: p.postAuthorName+' has created a new post: "'+p.postHeadline+'" '+getPostUrl(p.postId),
|
||||||
break;
|
html: '<a href="'+getProfileUrlById(p.postAuthorId)+'">'+p.postAuthorName+'</a> has created a new post: "<a href="'+getPostUrl(p.postId)+'" class="action-link">'+p.postHeadline+'</a>".'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'accountApproved':
|
case 'accountApproved':
|
||||||
notification.subject = i18n.t('Your account has been approved.');
|
var n = {
|
||||||
notification.text = i18n.t('Welcome to ')+getSetting('title')+'! '+i18n.t('Your account has just been approved.');
|
subject: 'Your account has been approved.',
|
||||||
notification.html = i18n.t('Welcome to ')+getSetting('title')+'!<br/> '+i18n.t('Your account has just been approved.')+' <a href="'+Meteor.absoluteUrl()+'">'+i18n.t('Start posting.')+'</a>';
|
text: 'Welcome to '+getSetting('title')+'! Your account has just been approved.',
|
||||||
break;
|
html: 'Welcome to '+getSetting('title')+'!<br/> Your account has just been approved. <a href="'+Meteor.absoluteUrl()+'">Start posting.</a>'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'newUser':
|
||||||
|
var n = {
|
||||||
|
subject: 'New user: '+p.username,
|
||||||
|
text: 'A new user account has been created: '+p.profileUrl,
|
||||||
|
html: 'A new user account has been created: <a href="'+p.profileUrl+'">'+p.username+'</a>'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return notification;
|
|
||||||
|
// if context is email, append unsubscribe link to all outgoing notifications
|
||||||
|
if(context == 'email'){
|
||||||
|
n.to = getEmail(userToNotify);
|
||||||
|
n.text = n.text + '\n\n Unsubscribe from all notifications: '+getUnsubscribeLink(userToNotify);
|
||||||
|
n.html = n.html + '<br/><br/><a href="'+getUnsubscribeLink(userToNotify)+'">Unsubscribe from all notifications</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
|
|
@ -97,15 +97,17 @@ Meteor.methods({
|
||||||
|
|
||||||
if(getSetting('emailNotifications', false)){
|
if(getSetting('emailNotifications', false)){
|
||||||
// notify users of new posts
|
// notify users of new posts
|
||||||
var properties = {
|
var notification = {
|
||||||
postAuthorName : getDisplayName(postAuthor),
|
event: 'newPost',
|
||||||
postAuthorId : post.userId,
|
properties: {
|
||||||
postHeadline : headline,
|
postAuthorName : getDisplayName(postAuthor),
|
||||||
postId : postId
|
postAuthorId : post.userId,
|
||||||
|
postHeadline : headline,
|
||||||
|
postId : postId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var notification = getNotification('newPost', properties);
|
// call a server method because we do not have access to users' info on the client
|
||||||
// call a server method because we do not have access to admin users' info on the client
|
Meteor.call('newPostNotify', notification, function(error, result){
|
||||||
Meteor.call('notifyUsers', notification, Meteor.user(), function(error, result){
|
|
||||||
//run asynchronously
|
//run asynchronously
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
92
lib/deepExtend.js
Normal file
92
lib/deepExtend.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// see: http://stackoverflow.com/questions/9399365/deep-extend-like-jquerys-for-nodejs
|
||||||
|
deepExtend = function () {
|
||||||
|
var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
|
||||||
|
i = 1,
|
||||||
|
length = arguments.length,
|
||||||
|
deep = false,
|
||||||
|
toString = Object.prototype.toString,
|
||||||
|
hasOwn = Object.prototype.hasOwnProperty,
|
||||||
|
push = Array.prototype.push,
|
||||||
|
slice = Array.prototype.slice,
|
||||||
|
trim = String.prototype.trim,
|
||||||
|
indexOf = Array.prototype.indexOf,
|
||||||
|
class2type = {
|
||||||
|
"[object Boolean]": "boolean",
|
||||||
|
"[object Number]": "number",
|
||||||
|
"[object String]": "string",
|
||||||
|
"[object Function]": "function",
|
||||||
|
"[object Array]": "array",
|
||||||
|
"[object Date]": "date",
|
||||||
|
"[object RegExp]": "regexp",
|
||||||
|
"[object Object]": "object"
|
||||||
|
},
|
||||||
|
jQuery = {
|
||||||
|
isFunction: function (obj) {
|
||||||
|
return jQuery.type(obj) === "function"
|
||||||
|
},
|
||||||
|
isArray: Array.isArray ||
|
||||||
|
function (obj) {
|
||||||
|
return jQuery.type(obj) === "array"
|
||||||
|
},
|
||||||
|
isWindow: function (obj) {
|
||||||
|
return obj != null && obj == obj.window
|
||||||
|
},
|
||||||
|
isNumeric: function (obj) {
|
||||||
|
return !isNaN(parseFloat(obj)) && isFinite(obj)
|
||||||
|
},
|
||||||
|
type: function (obj) {
|
||||||
|
return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
|
||||||
|
},
|
||||||
|
isPlainObject: function (obj) {
|
||||||
|
if (!obj || jQuery.type(obj) !== "object" || obj.nodeType) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var key;
|
||||||
|
for (key in obj) {}
|
||||||
|
return key === undefined || hasOwn.call(obj, key)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof target === "boolean") {
|
||||||
|
deep = target;
|
||||||
|
target = arguments[1] || {};
|
||||||
|
i = 2;
|
||||||
|
}
|
||||||
|
if (typeof target !== "object" && !jQuery.isFunction(target)) {
|
||||||
|
target = {}
|
||||||
|
}
|
||||||
|
if (length === i) {
|
||||||
|
target = this;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
for (i; i < length; i++) {
|
||||||
|
if ((options = arguments[i]) != null) {
|
||||||
|
for (name in options) {
|
||||||
|
src = target[name];
|
||||||
|
copy = options[name];
|
||||||
|
if (target === copy) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
|
||||||
|
if (copyIsArray) {
|
||||||
|
copyIsArray = false;
|
||||||
|
clone = src && jQuery.isArray(src) ? src : []
|
||||||
|
} else {
|
||||||
|
clone = src && jQuery.isPlainObject(src) ? src : {};
|
||||||
|
}
|
||||||
|
// WARNING: RECURSION
|
||||||
|
target[name] = deepExtend(deep, clone, copy);
|
||||||
|
} else if (copy !== undefined) {
|
||||||
|
target[name] = copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
|
@ -8,6 +8,9 @@ getSetting = function(setting, defaultValue){
|
||||||
getCurrentTemplate = function() {
|
getCurrentTemplate = function() {
|
||||||
return Router._currentController.template;
|
return Router._currentController.template;
|
||||||
}
|
}
|
||||||
|
getCurrentRoute = function() {
|
||||||
|
return Router._currentController.path;
|
||||||
|
}
|
||||||
clearSeenErrors = function(){
|
clearSeenErrors = function(){
|
||||||
Errors.update({seen:true}, {$set: {show:false}}, {multi:true});
|
Errors.update({seen:true}, {$set: {show:false}}, {multi:true});
|
||||||
}
|
}
|
||||||
|
@ -68,18 +71,18 @@ getPostCommentUrl = function(postId, commentId){
|
||||||
// get link to a comment on a post page
|
// get link to a comment on a post page
|
||||||
return Meteor.absoluteUrl()+'posts/'+postId+'/comment/'+commentId;
|
return Meteor.absoluteUrl()+'posts/'+postId+'/comment/'+commentId;
|
||||||
}
|
}
|
||||||
getUserUrl = function(id){
|
|
||||||
return Meteor.absoluteUrl()+'users/'+id;
|
|
||||||
}
|
|
||||||
getCategoryUrl = function(slug){
|
getCategoryUrl = function(slug){
|
||||||
return Meteor.absoluteUrl()+'category/'+slug;
|
return Meteor.absoluteUrl()+'category/'+slug;
|
||||||
}
|
}
|
||||||
slugify = function(text) {
|
slugify = function(text) {
|
||||||
text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
|
if(text){
|
||||||
text = text.replace(/-/gi, "_");
|
text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
|
||||||
text = text.replace(/\s/gi, "-");
|
text = text.replace(/-/gi, "_");
|
||||||
text = text.toLowerCase();
|
text = text.replace(/\s/gi, "-");
|
||||||
|
text = text.toLowerCase();
|
||||||
|
}
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
}
|
}
|
||||||
getShortUrl = function(post){
|
getShortUrl = function(post){
|
||||||
return post.shortUrl ? post.shortUrl : post.url;
|
return post.shortUrl ? post.shortUrl : post.url;
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
|
||||||
// getParameters gives an object containing the appropriate find and options arguments for the subscriptions's Posts.find()
|
// getParameters gives an object containing the appropriate find and options arguments for the subscriptions's Posts.find()
|
||||||
|
|
||||||
getParameters = function (view, limit, category) {
|
getParameters = function (terms) {
|
||||||
|
|
||||||
|
// console.log(terms)
|
||||||
|
|
||||||
|
// note: using jquery's extend() with "deep" parameter set to true instead of shallow _.extend()
|
||||||
|
// see: http://api.jquery.com/jQuery.extend/
|
||||||
|
|
||||||
var baseParameters = {
|
var baseParameters = {
|
||||||
find: {
|
find: {
|
||||||
|
@ -12,64 +17,74 @@ getParameters = function (view, limit, category) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (view) {
|
switch (terms.view) {
|
||||||
|
|
||||||
case 'top':
|
case 'top':
|
||||||
var parameters = $.extend(true, baseParameters, {options: {sort: {sticky: -1, score: -1}}});
|
var parameters = deepExtend(true, baseParameters, {options: {sort: {sticky: -1, score: -1}}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'new':
|
case 'new':
|
||||||
var parameters = $.extend(true, baseParameters, {options: {sort: {sticky: -1, submitted: -1}}});
|
var parameters = deepExtend(true, baseParameters, {options: {sort: {sticky: -1, submitted: -1}}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'best':
|
case 'best':
|
||||||
var parameters = $.extend(true, baseParameters, {options: {sort: {sticky: -1, baseScore: -1}}});
|
var parameters = deepExtend(true, baseParameters, {options: {sort: {sticky: -1, baseScore: -1}}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'pending':
|
case 'pending':
|
||||||
var parameters = $.extend(true, baseParameters, {find: {status: 1}, options: {sort: {createdAt: -1}}});
|
var parameters = deepExtend(true, baseParameters, {find: {status: 1}, options: {sort: {createdAt: -1}}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'category': // same as top for now
|
case 'category': // same as top for now
|
||||||
var parameters = $.extend(true, baseParameters, {options: {sort: {sticky: -1, score: -1}}});
|
var parameters = deepExtend(true, baseParameters, {options: {sort: {sticky: -1, score: -1}}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'search': // search results
|
||||||
|
if(typeof terms.query != 'undefined' && !!terms.query){
|
||||||
|
var parameters = deepExtend(true, baseParameters, {
|
||||||
|
find: {
|
||||||
|
$or: [
|
||||||
|
{headline: {$regex: terms.query, $options: 'i'}},
|
||||||
|
{url: {$regex: terms.query, $options: 'i'}},
|
||||||
|
{body: {$regex: terms.query, $options: 'i'}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
// if query is empty, just return parameters that will result in an empty collection
|
||||||
|
var parameters = {find:{_id: 0}};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'digest':
|
||||||
|
var parameters = deepExtend(true, baseParameters, {
|
||||||
|
find: {
|
||||||
|
submitted: {
|
||||||
|
$gte: terms.after,
|
||||||
|
$lt: terms.before
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
sort: {sticky: -1, baseScore: -1}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by _id to break ties
|
// sort by _id to break ties
|
||||||
$.extend(true, parameters, {options: {sort: {_id: -1}}})
|
deepExtend(true, parameters, {options: {sort: {_id: -1}}})
|
||||||
|
|
||||||
if(typeof limit != 'undefined')
|
if(typeof terms.limit != 'undefined' && !!terms.limit)
|
||||||
_.extend(parameters.options, {limit: parseInt(limit)});
|
_.extend(parameters.options, {limit: parseInt(terms.limit)});
|
||||||
|
|
||||||
if(typeof category != 'undefined')
|
if(typeof terms.category != 'undefined' && !!terms.category)
|
||||||
_.extend(parameters.find, {'categories.slug': category});
|
_.extend(parameters.find, {'categories.slug': terms.category});
|
||||||
|
|
||||||
// console.log(parameters.options.sort)
|
// console.log(parameters)
|
||||||
|
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case for digest
|
|
||||||
// TODO: merge back into general getParameters function
|
|
||||||
|
|
||||||
getDigestParameters = function (date) {
|
|
||||||
var mDate = moment(date);
|
|
||||||
var parameters = {
|
|
||||||
find: {
|
|
||||||
status: 2,
|
|
||||||
submitted: {
|
|
||||||
$gte: mDate.startOf('day').valueOf(),
|
|
||||||
$lt: mDate.endOf('day').valueOf()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
sort: {sticky: -1, baseScore: -1, _id: 1}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
getUsersParameters = function(filterBy, sortBy, limit) {
|
getUsersParameters = function(filterBy, sortBy, limit) {
|
||||||
var find = {},
|
var find = {},
|
||||||
sort = {createdAt: -1};
|
sort = {createdAt: -1};
|
||||||
|
@ -99,6 +114,8 @@ getUsersParameters = function(filterBy, sortBy, limit) {
|
||||||
break;
|
break;
|
||||||
case 'commentCount':
|
case 'commentCount':
|
||||||
sort = {commentCount: -1};
|
sort = {commentCount: -1};
|
||||||
|
case 'invitedCount':
|
||||||
|
sort = {invitedCount: -1};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
find: find,
|
find: find,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
updateScore = function (item, collection, forceUpdate) {
|
updateScore = function (collection, item, forceUpdate) {
|
||||||
var forceUpdate = typeof forceUpdate !== 'undefined' ? forceUpdate : false;
|
var forceUpdate = typeof forceUpdate !== 'undefined' ? forceUpdate : false;
|
||||||
// For performance reasons, the database is only updated if the difference between the old score and the new score
|
// For performance reasons, the database is only updated if the difference between the old score and the new score
|
||||||
// is meaningful enough. To find out, we calculate the "power" of a single vote after n days.
|
// is meaningful enough. To find out, we calculate the "power" of a single vote after n days.
|
||||||
|
|
12
lib/users.js
12
lib/users.js
|
@ -24,7 +24,13 @@ getDisplayNameById = function(userId){
|
||||||
return getDisplayName(Meteor.users.findOne(userId));
|
return getDisplayName(Meteor.users.findOne(userId));
|
||||||
}
|
}
|
||||||
getProfileUrl = function(user) {
|
getProfileUrl = function(user) {
|
||||||
return '/users/' + slugify(getUserName(user));
|
return Meteor.absoluteUrl()+'users/' + slugify(getUserName(user));
|
||||||
|
}
|
||||||
|
getProfileUrlById = function(id){
|
||||||
|
return Meteor.absoluteUrl()+'users/'+ id;
|
||||||
|
}
|
||||||
|
getProfileUrlBySlug = function(slug) {
|
||||||
|
return Meteor.absoluteUrl()+'users/' + slug;
|
||||||
}
|
}
|
||||||
getTwitterName = function(user){
|
getTwitterName = function(user){
|
||||||
// return twitter name provided by user, or else the one used for twitter login
|
// return twitter name provided by user, or else the one used for twitter login
|
||||||
|
@ -67,7 +73,7 @@ getAvatarUrl = function(user){
|
||||||
}else{
|
}else{
|
||||||
return Gravatar.getGravatar(user, {
|
return Gravatar.getGravatar(user, {
|
||||||
d: 'http://demo.telesc.pe/img/default_avatar.png',
|
d: 'http://demo.telesc.pe/img/default_avatar.png',
|
||||||
s: 30
|
s: 80
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,4 +121,4 @@ getProperty = function(object, property){
|
||||||
// else return property
|
// else return property
|
||||||
return object[array[0]];
|
return object[array[0]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
inviteUser: function (userId) {
|
inviteUser: function (userId) {
|
||||||
|
|
||||||
var currentUser = Meteor.user();
|
var currentUser = Meteor.user();
|
||||||
var invitedUser = Meteor.users.findOne(userId);
|
var invitedUser = Meteor.users.findOne(userId);
|
||||||
var invite = {
|
var invite = {
|
||||||
invited: invitedUser._id,
|
invitedId: invitedUser._id,
|
||||||
invitedName: getDisplayName(invitedUser),
|
invitedName: getDisplayName(invitedUser),
|
||||||
time: new Date()
|
time: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
// if the current user is logged in, still has available invites and is himself invited (or an admin), and the target user is not invited
|
// if the current user is logged in, still has available invites and is himself invited (or an admin), and the target user is not invited
|
||||||
|
|
||||||
if(currentUser && currentUser.invitesCount > 0 && canInvite(currentUser) && !isInvited(invitedUser)){
|
if(currentUser && currentUser.inviteCount > 0 && canInvite(currentUser) && !isInvited(invitedUser)){
|
||||||
|
|
||||||
// update invinting user
|
// update invinting user
|
||||||
Meteor.users.update(Meteor.userId(), {$inc:{invitesCount: -1}});
|
Meteor.users.update(Meteor.userId(), {$inc:{inviteCount: -1}, $inc:{invitedCount: 1}, $push:{invites: invite}});
|
||||||
Meteor.users.update(Meteor.userId(), {$push:{invites: invite}});
|
|
||||||
|
|
||||||
// update invited user
|
// update invited user
|
||||||
Meteor.users.update(userId, {$set: {
|
Meteor.users.update(userId, {$set: {
|
||||||
|
|
|
@ -106,6 +106,39 @@ Meteor.startup(function () {
|
||||||
console.log("//----------------------------------------------------------------------//")
|
console.log("//----------------------------------------------------------------------//")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// migration updateUserProfiles: update user profiles with slugs and a few other properties
|
||||||
|
if (!Migrations.findOne({name: "updateUserProfiles"})) {
|
||||||
|
console.log("//----------------------------------------------------------------------//")
|
||||||
|
console.log("//------------// Starting updateUserProfiles Migration //-----------//")
|
||||||
|
console.log("//----------------------------------------------------------------------//")
|
||||||
|
|
||||||
|
var allUsers = Meteor.users.find();
|
||||||
|
console.log('> Found '+allUsers.count()+' users.\n');
|
||||||
|
|
||||||
|
allUsers.forEach(function(user){
|
||||||
|
console.log('> Updating user '+user._id+' ('+user.username+')');
|
||||||
|
|
||||||
|
// update user slug
|
||||||
|
if(getUserName(user))
|
||||||
|
Meteor.users.update(user._id, {$set:{slug: slugify(getUserName(user))}});
|
||||||
|
|
||||||
|
// update user isAdmin flag
|
||||||
|
if(typeof user.isAdmin === 'undefined')
|
||||||
|
Meteor.users.update(user._id, {$set: {isAdmin: false}});
|
||||||
|
|
||||||
|
// update postCount
|
||||||
|
var postsByUser = Posts.find({userId: user._id});
|
||||||
|
Meteor.users.update(user._id, {$set: {postCount: postsByUser.count()}});
|
||||||
|
|
||||||
|
// update commentCount
|
||||||
|
var commentsByUser = Comments.find({userId: user._id});
|
||||||
|
Meteor.users.update(user._id, {$set: {commentCount: commentsByUser.count()}});
|
||||||
|
|
||||||
|
});
|
||||||
|
Migrations.insert({name: "updateUserProfiles"});
|
||||||
|
console.log("//----------------------------------------------------------------------//")
|
||||||
|
console.log("//------------// Ending updateUserProfiles Migration //-----------//")
|
||||||
|
console.log("//----------------------------------------------------------------------//")
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
|
@ -14,7 +14,7 @@ Meteor.methods({
|
||||||
// console.log(userDoingAction);
|
// console.log(userDoingAction);
|
||||||
// console.log(properties);
|
// console.log(properties);
|
||||||
// console.log(sendEmail);
|
// console.log(sendEmail);
|
||||||
var notification= {
|
var notification = {
|
||||||
timestamp: new Date().getTime(),
|
timestamp: new Date().getTime(),
|
||||||
userId: userToNotify._id,
|
userId: userToNotify._id,
|
||||||
event: event,
|
event: event,
|
||||||
|
@ -26,38 +26,45 @@ Meteor.methods({
|
||||||
// send the notification if notifications are activated,
|
// send the notification if notifications are activated,
|
||||||
// the notificationsFrequency is set to 1, or if it's undefined (legacy compatibility)
|
// the notificationsFrequency is set to 1, or if it's undefined (legacy compatibility)
|
||||||
if(sendEmail){
|
if(sendEmail){
|
||||||
Meteor.call('sendNotificationEmail', userToNotify, newNotificationId);
|
// get specific notification content for "email" context
|
||||||
|
var contents = getNotificationContents(notification, 'email');
|
||||||
|
sendNotification(contents);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sendNotificationEmail : function(userToNotify, notificationId){
|
|
||||||
// Note: we query the DB instead of simply passing arguments from the client
|
|
||||||
// to make sure our email method cannot be used for spam
|
|
||||||
var notification = Notifications.findOne(notificationId);
|
|
||||||
var n = getNotification(notification.event, notification.properties, 'email');
|
|
||||||
var to = getEmail(userToNotify);
|
|
||||||
var text = n.text + '\n\n Unsubscribe from all notifications: '+getUnsubscribeLink(userToNotify);
|
|
||||||
var html = n.html + '<br/><br/><a href="'+getUnsubscribeLink(userToNotify)+'">Unsubscribe from all notifications</a>';
|
|
||||||
sendEmail(to, n.subject, text, html);
|
|
||||||
},
|
|
||||||
unsubscribeUser : function(hash){
|
unsubscribeUser : function(hash){
|
||||||
// TO-DO: currently, if you have somebody's email you can unsubscribe them
|
// TO-DO: currently, if you have somebody's email you can unsubscribe them
|
||||||
// A site-specific salt should be added to the hashing method to prevent this
|
// A user-specific salt should be added to the hashing method to prevent this
|
||||||
var user = Meteor.users.findOne({email_hash: hash});
|
var user = Meteor.users.findOne({email_hash: hash});
|
||||||
if(user){
|
if(user){
|
||||||
var update = Meteor.users.update(user._id, {
|
var update = Meteor.users.update(user._id, {
|
||||||
$set: {'profile.notificationsFrequency' : 0}
|
$set: {
|
||||||
|
'profile.notifications.users' : 0,
|
||||||
|
'profile.notifications.posts' : 0,
|
||||||
|
'profile.notifications.comments' : 0,
|
||||||
|
'profile.notifications.replies' : 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
notifyUsers : function(notification, currentUser){
|
newPostNotify : function(properties){
|
||||||
|
var currentUser = Meteor.users.findOne(this.userId);
|
||||||
|
console.log('newPostNotify')
|
||||||
// send a notification to every user according to their notifications settings
|
// send a notification to every user according to their notifications settings
|
||||||
_.each(Meteor.users.find().fetch(), function(user, index, list){
|
Meteor.users.find().forEach(function(user) {
|
||||||
|
// don't send users notifications for their own posts
|
||||||
if(user._id !== currentUser._id && getUserSetting('notifications.posts', false, user)){
|
if(user._id !== currentUser._id && getUserSetting('notifications.posts', false, user)){
|
||||||
// don't send users notifications for their own posts
|
properties.userId = user._id;
|
||||||
sendEmail(getEmail(user), notification.subject, notification.text, notification.html);
|
var notification = getNotificationContents(properties, 'email');
|
||||||
|
sendNotification(notification, user);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sendNotification = function (notification) {
|
||||||
|
// console.log('send notification:')
|
||||||
|
// console.log(notification)
|
||||||
|
sendEmail(notification.to, notification.subject, notification.text, notification.html);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ var privacyOptions = { // false means private
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
emails: false,
|
emails: false,
|
||||||
notifications: false,
|
notifications: false,
|
||||||
invitesCount: false,
|
inviteCount: false,
|
||||||
'profile.email': false,
|
'profile.email': false,
|
||||||
'services.twitter.accessToken': false,
|
'services.twitter.accessToken': false,
|
||||||
'services.twitter.accessTokenSecret': false,
|
'services.twitter.accessTokenSecret': false,
|
||||||
|
@ -31,6 +31,7 @@ Meteor.publish('singleUser', function(userIdOrSlug) {
|
||||||
// if we find something when treating the argument as an ID, return that; else assume it's a slug
|
// if we find something when treating the argument as an ID, return that; else assume it's a slug
|
||||||
return findById.count() ? findById : findBySlug;
|
return findById.count() ? findById : findBySlug;
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish authors of the current post and its comments
|
// Publish authors of the current post and its comments
|
||||||
|
@ -51,6 +52,7 @@ Meteor.publish('postUsers', function(postId) {
|
||||||
|
|
||||||
return Meteor.users.find({_id: {$in: users}}, {fields: privacyOptions});
|
return Meteor.users.find({_id: {$in: users}}, {fields: privacyOptions});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish author of the current comment
|
// Publish author of the current comment
|
||||||
|
@ -60,34 +62,42 @@ Meteor.publish('commentUser', function(commentId) {
|
||||||
var comment = Comments.findOne(commentId);
|
var comment = Comments.findOne(commentId);
|
||||||
return Meteor.users.find({_id: comment && comment.userId}, {fields: privacyOptions});
|
return Meteor.users.find({_id: comment && comment.userId}, {fields: privacyOptions});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish all the users that have posted the currently displayed list of posts
|
// Publish all the users that have posted the currently displayed list of posts
|
||||||
|
|
||||||
Meteor.publish('postsListUsers', function(find, options) {
|
Meteor.publish('postsListUsers', function(terms) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
var posts = Posts.find(find, options);
|
var parameters = getParameters(terms),
|
||||||
var userIds = _.pluck(posts.fetch(), 'userId');
|
posts = Posts.find(parameters.find, parameters.options),
|
||||||
|
userIds = _.pluck(posts.fetch(), 'userId');
|
||||||
return Meteor.users.find({_id: {$in: userIds}}, {fields: privacyOptions, multi: true});
|
return Meteor.users.find({_id: {$in: userIds}}, {fields: privacyOptions, multi: true});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish all users
|
// Publish all users
|
||||||
|
|
||||||
Meteor.publish('allUsers', function(find, options) {
|
Meteor.publish('allUsers', function(filterBy, sortBy, limit) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
|
var parameters = getUsersParameters(filterBy, sortBy, limit);
|
||||||
if (!isAdminById(this.userId)) // if user is not admin, filter out sensitive info
|
if (!isAdminById(this.userId)) // if user is not admin, filter out sensitive info
|
||||||
options = _.extend(options, {fields: privacyOptions});
|
parameters.options = _.extend(parameters.options, {fields: privacyOptions});
|
||||||
return Meteor.users.find(find, options);
|
return Meteor.users.find(parameters.find, parameters.options);
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// publish all users for admins to make autocomplete work
|
// publish all users for admins to make autocomplete work
|
||||||
// TODO: find a better way
|
// TODO: find a better way
|
||||||
|
|
||||||
Meteor.publish('allUsersAdmin', function() {
|
Meteor.publish('allUsersAdmin', function() {
|
||||||
if (isAdminById(this.userId))
|
if (isAdminById(this.userId)) {
|
||||||
return Meteor.users.find();
|
return Meteor.users.find();
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// -------------------------------------------- Posts -------------------------------------------- //
|
// -------------------------------------------- Posts -------------------------------------------- //
|
||||||
|
@ -98,6 +108,7 @@ Meteor.publish('singlePost', function(id) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
return Posts.find(id);
|
return Posts.find(id);
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish the post related to the current comment
|
// Publish the post related to the current comment
|
||||||
|
@ -107,29 +118,26 @@ Meteor.publish('commentPost', function(commentId) {
|
||||||
var comment = Comments.findOne(commentId);
|
var comment = Comments.findOne(commentId);
|
||||||
return Posts.find({_id: comment && comment.post});
|
return Posts.find({_id: comment && comment.post});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish a list of posts
|
// Publish a list of posts
|
||||||
|
|
||||||
Meteor.publish('postsList', function(find, options) {
|
Meteor.publish('postsList', function(terms) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
options = options || {};
|
var parameters = getParameters(terms),
|
||||||
var posts = Posts.find(find, options);
|
posts = Posts.find(parameters.find, parameters.options);
|
||||||
|
|
||||||
// console.log('//-------- Subscription Parameters:');
|
// console.log('//-------- Subscription Parameters:');
|
||||||
// console.log(find);
|
// console.log(parameters.find);
|
||||||
// console.log(options);
|
// console.log(parameters.options);
|
||||||
// console.log('Found '+posts.fetch().length+ ' posts:');
|
// console.log('Found '+posts.fetch().length+ ' posts:');
|
||||||
// posts.rewind();
|
// posts.rewind();
|
||||||
// console.log(_.pluck(posts.fetch(), 'headline'));
|
// console.log(_.pluck(posts.fetch(), 'headline'));
|
||||||
// posts.rewind();
|
|
||||||
|
|
||||||
return posts;
|
return posts;
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------- Comments -------------------------------------------- //
|
// -------------------------------------------- Comments -------------------------------------------- //
|
||||||
|
|
||||||
// Publish comments for a specific post
|
// Publish comments for a specific post
|
||||||
|
@ -138,6 +146,7 @@ Meteor.publish('postComments', function(postId) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
return Comments.find({post: postId});
|
return Comments.find({post: postId});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish a single comment
|
// Publish a single comment
|
||||||
|
@ -146,12 +155,22 @@ Meteor.publish('singleComment', function(commentId) {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
return Comments.find(commentId);
|
return Comments.find(commentId);
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// -------------------------------------------- Other -------------------------------------------- //
|
// -------------------------------------------- Other -------------------------------------------- //
|
||||||
|
|
||||||
Meteor.publish('settings', function() {
|
Meteor.publish('settings', function() {
|
||||||
return Settings.find();
|
var options = {};
|
||||||
|
if(!isAdminById(this.userId)){
|
||||||
|
options = _.extend(options, {
|
||||||
|
fields: {
|
||||||
|
mailChimpAPIKey: false,
|
||||||
|
mailChimpListId: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Settings.find({}, options);
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('notifications', function() {
|
Meteor.publish('notifications', function() {
|
||||||
|
@ -159,10 +178,12 @@ Meteor.publish('notifications', function() {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
return Notifications.find({userId:this.userId});
|
return Notifications.find({userId:this.userId});
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('categories', function() {
|
Meteor.publish('categories', function() {
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
return Categories.find();
|
return Categories.find();
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,38 +14,6 @@ Meteor.methods({
|
||||||
},
|
},
|
||||||
giveInvites: function () {
|
giveInvites: function () {
|
||||||
if(isAdmin(Meteor.user()))
|
if(isAdmin(Meteor.user()))
|
||||||
Meteor.users.update({}, {$inc:{invitesCount: 1}}, {multi:true});
|
Meteor.users.update({}, {$inc:{inviteCount: 1}}, {multi:true});
|
||||||
},
|
|
||||||
updateUserProfiles: function () {
|
|
||||||
console.log('//--------------------------//\nUpdating user profiles…')
|
|
||||||
if(isAdmin(Meteor.user())){
|
|
||||||
var allUsers = Meteor.users.find();
|
|
||||||
console.log('> Found '+allUsers.count()+' users.\n');
|
|
||||||
|
|
||||||
allUsers.forEach(function(user){
|
|
||||||
console.log('> Updating user '+user._id+' ('+user.username+')');
|
|
||||||
|
|
||||||
// update user slug
|
|
||||||
if(getUserName(user))
|
|
||||||
Meteor.users.update(user._id, {$set:{slug: slugify(getUserName(user))}});
|
|
||||||
|
|
||||||
// update user isAdmin flag
|
|
||||||
if(typeof user.isAdmin === 'undefined')
|
|
||||||
Meteor.users.update(user._id, {$set: {isAdmin: false}});
|
|
||||||
|
|
||||||
// update postCount
|
|
||||||
var postsByUser = Posts.find({userId: user._id});
|
|
||||||
Meteor.users.update(user._id, {$set: {postCount: postsByUser.count()}});
|
|
||||||
|
|
||||||
// update commentCount
|
|
||||||
var commentsByUser = Comments.find({userId: user._id});
|
|
||||||
Meteor.users.update(user._id, {$set: {commentCount: commentsByUser.count()}});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log('Done updating user profiles.\n//--------------------------//')
|
|
||||||
},
|
|
||||||
updatePostsSlugs: function () {
|
|
||||||
//TODO
|
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -1,19 +1,32 @@
|
||||||
Accounts.onCreateUser(function(options, user){
|
Accounts.onCreateUser(function(options, user){
|
||||||
user.profile = options.profile || {};
|
var userProperties = {
|
||||||
user.karma = 0;
|
profile: options.profile || {},
|
||||||
// users start pending and need to be invited
|
karma: 0,
|
||||||
user.isInvited = false;
|
isInvited: false,
|
||||||
user.isAdmin = false;
|
isAdmin: false,
|
||||||
|
postCount: 0,
|
||||||
|
commentCount: 0,
|
||||||
|
invitedCount: 0
|
||||||
|
}
|
||||||
|
user = _.extend(user, userProperties);
|
||||||
|
|
||||||
if (options.email)
|
if (options.email)
|
||||||
user.profile.email = options.email;
|
user.profile.email = options.email;
|
||||||
|
|
||||||
if (user.profile.email)
|
if (getEmail(user))
|
||||||
user.email_hash = CryptoJS.MD5(user.profile.email.trim().toLowerCase()).toString();
|
user.email_hash = getEmailHash(user);
|
||||||
|
|
||||||
if (!user.profile.name)
|
if (!user.profile.name)
|
||||||
user.profile.name = user.username;
|
user.profile.name = user.username;
|
||||||
|
|
||||||
|
// set notifications default preferences
|
||||||
|
user.profile.notifications = {
|
||||||
|
users: false,
|
||||||
|
posts: false,
|
||||||
|
comments: true,
|
||||||
|
replies: true
|
||||||
|
}
|
||||||
|
|
||||||
// create slug from username
|
// create slug from username
|
||||||
user.slug = slugify(getUserName(user));
|
user.slug = slugify(getUserName(user));
|
||||||
|
|
||||||
|
@ -22,16 +35,39 @@ Accounts.onCreateUser(function(options, user){
|
||||||
user.isAdmin = true;
|
user.isAdmin = true;
|
||||||
|
|
||||||
// give new users a few invites (default to 3)
|
// give new users a few invites (default to 3)
|
||||||
user.invitesCount = getSetting('startInvitesCount', 3);
|
user.inviteCount = getSetting('startInvitesCount', 3);
|
||||||
|
|
||||||
trackEvent('new user', {username: user.username, email: user.profile.email});
|
trackEvent('new user', {username: user.username, email: user.profile.email});
|
||||||
|
|
||||||
// add new user to MailChimp list
|
// if user has already filled in their email, add them to MailChimp list
|
||||||
addToMailChimpList(user);
|
if(user.profile.email)
|
||||||
|
addToMailChimpList(user);
|
||||||
|
|
||||||
|
// send notifications to admins
|
||||||
|
var admins = Meteor.users.find({isAdmin: true});
|
||||||
|
admins.forEach(function(admin){
|
||||||
|
if(getUserSetting('notifications.users', false, admin)){
|
||||||
|
var notification = getNotificationContents({
|
||||||
|
event: 'newUser',
|
||||||
|
properties: {
|
||||||
|
username: getUserName(user),
|
||||||
|
profileUrl: getProfileUrl(user)
|
||||||
|
},
|
||||||
|
userId: admin._id
|
||||||
|
}, 'email');
|
||||||
|
sendNotification(notification, admin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getEmailHash = function(user){
|
||||||
|
// todo: add some kind of salt in here
|
||||||
|
return CryptoJS.MD5(getEmail(user).trim().toLowerCase() + user.createdAt).toString();
|
||||||
|
}
|
||||||
|
|
||||||
addToMailChimpList = function(user){
|
addToMailChimpList = function(user){
|
||||||
// add a user to a MailChimp list.
|
// add a user to a MailChimp list.
|
||||||
// called when a new user is created, or when an existing user fills in their email
|
// called when a new user is created, or when an existing user fills in their email
|
||||||
|
@ -76,9 +112,9 @@ Meteor.methods({
|
||||||
var newScore = baseScore / Math.pow(ageInHours + 2, 1.3);
|
var newScore = baseScore / Math.pow(ageInHours + 2, 1.3);
|
||||||
return Math.abs(object.score - newScore);
|
return Math.abs(object.score - newScore);
|
||||||
},
|
},
|
||||||
generateEmailHash: function(){
|
setEmailHash: function(user){
|
||||||
var email_hash = CryptoJS.MD5(getEmail(Meteor.user()).trim().toLowerCase()).toString();
|
var email_hash = CryptoJS.MD5(getEmail(user).trim().toLowerCase()).toString();
|
||||||
Meteor.users.update(Meteor.userId(), {$set : {email_hash : email_hash}});
|
Meteor.users.update(user._id, {$set : {email_hash : email_hash}});
|
||||||
},
|
},
|
||||||
addCurrentUserToMailChimpList: function(){
|
addCurrentUserToMailChimpList: function(){
|
||||||
addToMailChimpList(Meteor.user());
|
addToMailChimpList(Meteor.user());
|
||||||
|
|
Loading…
Add table
Reference in a new issue