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
|
||||
|
||||
* Added karma redistribution.
|
||||
|
|
|
@ -140,6 +140,7 @@ var filters = {
|
|||
|
||||
canView: function() {
|
||||
if(Session.get('settingsLoaded') && !canView()){
|
||||
console.log('cannot view')
|
||||
this.render('no_rights');
|
||||
this.stop();
|
||||
}
|
||||
|
@ -174,7 +175,6 @@ var filters = {
|
|||
hasCompletedProfile: function() {
|
||||
var user = Meteor.user();
|
||||
if (user && ! Meteor.loggingIn() && ! userProfileComplete(user)){
|
||||
// Session.set('selectedUserId', user._id);
|
||||
this.render('user_email');
|
||||
this.stop();
|
||||
}
|
||||
|
@ -187,6 +187,13 @@ var filters = {
|
|||
Router.load( function () {
|
||||
clearSeenErrors(); // set all errors who have already been seen to not show anymore
|
||||
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
|
||||
|
@ -199,6 +206,7 @@ Router.before(filters.nProgressHook, {only: [
|
|||
'posts_pending',
|
||||
'posts_digest',
|
||||
'posts_category',
|
||||
'search',
|
||||
'post_page',
|
||||
'post_edit',
|
||||
'comment_page',
|
||||
|
@ -209,9 +217,9 @@ Router.before(filters.nProgressHook, {only: [
|
|||
'all-users'
|
||||
]});
|
||||
|
||||
Router.before(filters.canView);
|
||||
Router.before(filters.hasCompletedProfile);
|
||||
Router.before(filters.isLoggedIn, {only: ['comment_reply','post_submit']});
|
||||
Router.before(filters.canView);
|
||||
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']});
|
||||
|
@ -240,30 +248,24 @@ PostsListController = RouteController.extend({
|
|||
template:'posts_list',
|
||||
waitOn: function () {
|
||||
// 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
|
||||
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 [
|
||||
Meteor.subscribe('postsList', parameters.find, parameters.options),
|
||||
Meteor.subscribe('postsListUsers', parameters.find, parameters.options)
|
||||
Meteor.subscribe('postsList', this._terms),
|
||||
Meteor.subscribe('postsListUsers', this._terms)
|
||||
]
|
||||
},
|
||||
data: function () {
|
||||
var view = this.path == '/' ? 'top' : this.path.split('/')[1],
|
||||
limit = this.params.limit || getSetting('postsPerPage', 10),
|
||||
parameters = getParameters(view, limit, this.params.slug),
|
||||
var parameters = getParameters(this._terms),
|
||||
posts = Posts.find(parameters.find, parameters.options);
|
||||
postsCount = posts.count();
|
||||
|
||||
Session.set('postsLimit', limit);
|
||||
|
||||
// get posts and decorate them with rank property
|
||||
// note: not actually used;
|
||||
// posts = posts.map(function (post, index) {
|
||||
// post.rank = index;
|
||||
// return post;
|
||||
// });
|
||||
Session.set('postsLimit', this._terms.limit);
|
||||
|
||||
return {
|
||||
postsList: posts,
|
||||
|
@ -282,16 +284,25 @@ PostsDigestController = RouteController.extend({
|
|||
template: 'posts_digest',
|
||||
waitOn: function() {
|
||||
// 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 parameters = getDigestParameters(currentDate);
|
||||
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()
|
||||
}
|
||||
return [
|
||||
Meteor.subscribe('postsList', parameters.find, parameters.options),
|
||||
Meteor.subscribe('postsListUsers', parameters.find, parameters.options)
|
||||
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');
|
||||
var parameters = getDigestParameters(currentDate);
|
||||
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)
|
||||
|
@ -349,10 +360,15 @@ UserPageController = RouteController.extend({
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//--------------------------------------------------------------------------------------------------//
|
||||
|
@ -361,6 +377,8 @@ UserPageController = RouteController.extend({
|
|||
|
||||
Router.map(function() {
|
||||
|
||||
// -------------------------------------------- Post Lists -------------------------------------------- //
|
||||
|
||||
// Top
|
||||
|
||||
this.route('posts_top', {
|
||||
|
@ -406,6 +424,12 @@ Router.map(function() {
|
|||
|
||||
// TODO: enable /category/new, /category/best, etc. views
|
||||
|
||||
// Search
|
||||
|
||||
this.route('search', {
|
||||
path: '/search/:limit?',
|
||||
controller: PostsListController
|
||||
});
|
||||
|
||||
// Digest
|
||||
|
||||
|
@ -520,9 +544,8 @@ Router.map(function() {
|
|||
path: '/all-users/:limit?',
|
||||
template: 'users',
|
||||
waitOn: function() {
|
||||
var limit = parseInt(this.params.limit) || 20,
|
||||
parameters = getUsersParameters(this.params.filterBy, this.params.sortBy, limit);
|
||||
return Meteor.subscribe('allUsers', parameters.find, parameters.options);
|
||||
var limit = parseInt(this.params.limit) || 20;
|
||||
return Meteor.subscribe('allUsers', this.params.filterBy, this.params.sortBy, limit);
|
||||
},
|
||||
data: function() {
|
||||
var limit = parseInt(this.params.limit) || 20,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Session.set('initialLoad', true);
|
||||
Session.set('today', new Date());
|
||||
Session.set('view', 'top');
|
||||
Session.set('postsLimit', getSetting('postsPerPage', 2));
|
||||
Session.set('postsLimit', getSetting('postsPerPage', 10));
|
||||
Session.set('settingsLoaded', false);
|
||||
Session.set('sessionId', Meteor.default_connection._lastSessionId);
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@
|
|||
>li{
|
||||
@include horizontal-list-item;
|
||||
margin-right:10px;
|
||||
|
||||
>a{
|
||||
color:white;
|
||||
font-size:16px;
|
||||
|
@ -154,3 +155,29 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.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;
|
||||
@include border-radius(3px);
|
||||
padding: 8px 15px;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
a, span{
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
&.active{
|
||||
border-bottom: 2px solid $blue;
|
||||
}
|
||||
&:last-child{
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.filter{
|
||||
float: left;
|
||||
|
@ -55,3 +58,9 @@ table{
|
|||
}
|
||||
|
||||
}
|
||||
.user-table{
|
||||
font-size: 13px;
|
||||
tr{
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
.nav > li.last {
|
||||
padding-right: 0; }
|
||||
/* line 139, ../sass/modules/_header.scss */
|
||||
/* line 140, ../sass/modules/_header.scss */
|
||||
.nav > li > a {
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
height: 26px;
|
||||
font-weight: normal; }
|
||||
/* line 147, ../sass/modules/_header.scss */
|
||||
/* line 148, ../sass/modules/_header.scss */
|
||||
.nav > li > a.intercom em:before {
|
||||
content: '('; }
|
||||
/* line 150, ../sass/modules/_header.scss */
|
||||
/* line 151, ../sass/modules/_header.scss */
|
||||
.nav > li > a.intercom em:after {
|
||||
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 */
|
||||
.empty-notice {
|
||||
text-align: center;
|
||||
|
@ -1621,7 +1646,7 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
|||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
padding: 8px 15px;
|
||||
font-size: 14px; }
|
||||
font-size: 13px; }
|
||||
/* line 3, ../sass/partials/_mixins.scss */
|
||||
.filter-sort:before, .filter-sort:after {
|
||||
content: "";
|
||||
|
@ -1636,14 +1661,17 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
|||
/* line 10, ../sass/modules/_users.scss */
|
||||
.filter-sort a.active, .filter-sort span.active {
|
||||
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 {
|
||||
float: left; }
|
||||
/* line 17, ../sass/modules/_users.scss */
|
||||
/* line 20, ../sass/modules/_users.scss */
|
||||
.filter-sort .sort {
|
||||
float: right; }
|
||||
|
||||
/* line 24, ../sass/modules/_users.scss */
|
||||
/* line 27, ../sass/modules/_users.scss */
|
||||
.user-list .user .user-avatar, .user-table .user .user-avatar {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
|
@ -1655,13 +1683,20 @@ input[type="submit"], button, .button, .auth-buttons #login-buttons #login-butto
|
|||
-o-border-radius: 30px;
|
||||
border-radius: 30px; }
|
||||
|
||||
/* line 38, ../sass/modules/_users.scss */
|
||||
/* line 41, ../sass/modules/_users.scss */
|
||||
table tr td {
|
||||
padding: 10px; }
|
||||
/* line 44, ../sass/modules/_users.scss */
|
||||
/* line 47, ../sass/modules/_users.scss */
|
||||
table thead tr td {
|
||||
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 */
|
||||
.user-profile .user-avatar {
|
||||
height: 80px;
|
||||
|
|
|
@ -29,8 +29,10 @@ Template.settings.events = {
|
|||
throwError("Settings have been created");
|
||||
},
|
||||
function(error) {
|
||||
if(error) console.log(error);
|
||||
if(error)
|
||||
console.log(error);
|
||||
throwError("Settings have been updated");
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<template name="toolbox">
|
||||
<div class="grid-small grid-block dialog admin toolbox">
|
||||
<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>
|
||||
<li><a href="#" class="give-invites">Give 1 Invite to Everybody</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
|
||||
<ul class="nav site-nav desktop">
|
||||
{{#if canView}}
|
||||
<li class="dropdown">
|
||||
<a class="View" href="/">{{i18n "View"}}</a>
|
||||
<div class="dropdown-menu">
|
||||
<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 hasCategories}}
|
||||
<li class="dropdown">
|
||||
|
@ -36,6 +43,9 @@
|
|||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li>
|
||||
{{> search}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{#if logo_url}}
|
||||
|
|
|
@ -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({
|
||||
site_title: function(){
|
||||
return getSetting('title');
|
||||
|
@ -60,3 +36,28 @@ Template.nav.helpers({
|
|||
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;
|
||||
},
|
||||
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);
|
||||
},
|
||||
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);
|
||||
},
|
||||
inactiveClass: function(){
|
||||
|
|
|
@ -67,6 +67,11 @@
|
|||
<div class="control-group">
|
||||
<label class="control-label">{{i18n "Email Notifications"}}</label>
|
||||
<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">
|
||||
<input id="notifications_posts" type="checkbox" name="notifications_posts" {{hasNotificationsPosts}} /> {{i18n "New Posts"}}
|
||||
</label>
|
||||
|
@ -83,7 +88,7 @@
|
|||
<h3>Invites</h3>
|
||||
<label>Invites</label>
|
||||
<div class="controls">
|
||||
<input name="invitesCount" type="text" value="{{invitesCount}}" />
|
||||
<input name="inviteCount" type="text" value="{{inviteCount}}" />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -17,14 +17,17 @@ Template.user_edit.helpers({
|
|||
profileUrl: function(){
|
||||
return Meteor.absoluteUrl()+"users/"+this.slug;
|
||||
},
|
||||
hasNotificationsUsers : function(){
|
||||
return getUserSetting('notifications.users', '', this) ? 'checked' : '';
|
||||
},
|
||||
hasNotificationsPosts : function(){
|
||||
return getUserSetting('notifications.posts') ? 'checked' : '';
|
||||
return getUserSetting('notifications.notifications_posts', '', this) ? 'checked' : '';
|
||||
},
|
||||
hasNotificationsComments : function(){
|
||||
return getUserSetting('notifications.comments') ? 'checked' : '';
|
||||
return getUserSetting('notifications.comments', '', this) ? 'checked' : '';
|
||||
},
|
||||
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.github": $target.find('[name=github]').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.comments": $('input[name=notifications_comments]: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();
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
Template.user_email.helpers({
|
||||
user: function(){
|
||||
return Meteor.user();
|
||||
}
|
||||
});
|
||||
|
||||
Template.user_email.events = {
|
||||
'submit form': function(e){
|
||||
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">
|
||||
<tr class="user">
|
||||
<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>{{getEmail}}</td>
|
||||
<td>{{postCount}}</td>
|
||||
<td>{{commentCount}}</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>{{#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>
|
||||
<td><a class="delete-link" href="#">{{i18n "Delete User"}}</a></td>
|
||||
<td>
|
||||
{{#if invites}}
|
||||
<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>
|
||||
</template>
|
|
@ -20,35 +20,22 @@ Template.user_item.helpers({
|
|||
userIsAdmin: function(){
|
||||
return isAdmin(this);
|
||||
},
|
||||
profileUrl: function () {
|
||||
getProfileUrl: function () {
|
||||
return getProfileUrl(this);
|
||||
},
|
||||
getKarma: function() {
|
||||
return Math.round(100*this.karma)/100;
|
||||
},
|
||||
getInvitedUserProfileUrl: function () {
|
||||
var user = Meteor.users.findOne(this.invitedId);
|
||||
return getProfileUrl(user);
|
||||
}
|
||||
});
|
||||
|
||||
Template.user_item.events({
|
||||
'click .invite-link': function(e, instance){
|
||||
e.preventDefault();
|
||||
var user = Meteor.users.findOne(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")
|
||||
});
|
||||
}
|
||||
});
|
||||
Meteor.call('inviteUser', instance.data._id);
|
||||
},
|
||||
'click .uninvite-link': function(e, instance){
|
||||
e.preventDefault();
|
||||
|
|
|
@ -6,8 +6,14 @@
|
|||
<tr>
|
||||
<td colspan="2"><img class="user-avatar" src="{{avatarUrl}}"/></td>
|
||||
</tr>
|
||||
{{#if isAdmin}}
|
||||
<tr>
|
||||
<td>{{i18n "Name"}}: </td>
|
||||
<td>{{i18n "ID"}}: </td>
|
||||
<td>{{_id}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
<tr>
|
||||
<td>{{i18n "Name:"}}</td>
|
||||
<td>{{profile.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -15,7 +21,7 @@
|
|||
<td>{{createdAtFormatted}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{i18n "Bio"}}: </td>
|
||||
<td>{{i18n "Bio:"}}</td>
|
||||
<td>{{profile.bio}}</td>
|
||||
</tr>
|
||||
{{#if getTwitterName}}
|
||||
|
@ -26,13 +32,13 @@
|
|||
{{/if}}
|
||||
{{#if getGitHubName}}
|
||||
<tr>
|
||||
<td>GitHub: </td>
|
||||
<td>{{i18n "GitHub"}}:</td>
|
||||
<td><a href="http://github.com/{{getGitHubName}}">{{getGitHubName}}</a></td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if site}}
|
||||
<tr>
|
||||
<td>Site: </td>
|
||||
<td>{{i18n "Site"}}:</td>
|
||||
<td><a href="{{profile.site}}">{{profile.site}}</a></td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
|
@ -41,8 +47,8 @@
|
|||
<a class="button inline" href="/users/{{slug}}/edit">{{i18n "Edit profile"}}</a>
|
||||
{{/if}}
|
||||
{{#if canInvite}}
|
||||
{{#if invitesCount}}
|
||||
<a class="button inline invite-link" href="#">{{i18n "Invite "}}({{invitesCount}} {{i18n "left"}})</a>
|
||||
{{#if inviteCount}}
|
||||
<a class="button inline invite-link" href="#">{{i18n "Invite"}} ({{inviteCount}} {{i18n "left"}})</a>
|
||||
{{else}}
|
||||
<a class="button inline disabled" href="#">{{i18n "Invite (none left)"}}</a>
|
||||
{{/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
|
||||
return Meteor.user() && Meteor.user()._id != this._id && !isInvited(this) && invitesEnabled() && canInvite(Meteor.user());
|
||||
},
|
||||
invitesCount: function() {
|
||||
return Meteor.user().invitesCount;
|
||||
inviteCount: function() {
|
||||
return Meteor.user().inviteCount;
|
||||
},
|
||||
getTwitterName: function () {
|
||||
return getTwitterName(this);
|
||||
|
@ -27,5 +27,6 @@ Template.user_profile.helpers({
|
|||
Template.user_profile.events({
|
||||
'click .invite-link': function(e, instance){
|
||||
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 'postCount'}}" href="{{sortBy 'postCount'}}">{{i18n "Posts"}}</a>
|
||||
<a class="{{activeClass 'commentCount'}}" href="{{sortBy 'commentCount'}}">{{i18n "Comments"}}</a>
|
||||
<a class="{{activeClass 'invitedCount'}}" href="{{sortBy 'invitedCount'}}">{{i18n "InvitedCount"}}</a>
|
||||
</p>
|
||||
</div>
|
||||
<table>
|
||||
|
@ -24,13 +25,13 @@
|
|||
<tr>
|
||||
<td colspan="2">{{i18n "Name"}}</td>
|
||||
<td>{{i18n "Member since"}}</td>
|
||||
<td>{{i18n "Email"}}</td>
|
||||
<td>{{i18n "Posts"}}</td>
|
||||
<td>{{i18n "Comments"}}</td>
|
||||
<td>{{i18n "Karma"}}</td>
|
||||
<td>{{i18n "Is Invited?"}}</td>
|
||||
<td>{{i18n "Is Admin?"}}</td>
|
||||
<td>{{i18n "Delete"}}</td>
|
||||
<td>{{i18n "Invites"}}</td>
|
||||
<td>{{i18n "Invited?"}}</td>
|
||||
<td>{{i18n "Admin?"}}</td>
|
||||
<td>{{i18n "Actions"}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -9,44 +9,71 @@ Notifications.allow({
|
|||
, remove: canEditById
|
||||
});
|
||||
|
||||
getNotification = function(event, properties, context){
|
||||
var notification = {};
|
||||
// the default context to display notifications is the notification sidebar
|
||||
var context = typeof context === 'undefined' ? 'sidebar' : context;
|
||||
var p = properties;
|
||||
getNotificationContents = function(notification, context){
|
||||
// the same notifications can be displayed in multiple contexts: on-site in the sidebar, sent by email, etc.
|
||||
var event = notification.event,
|
||||
p = notification.properties,
|
||||
context = typeof context === 'undefined' ? 'sidebar' : context,
|
||||
userToNotify = Meteor.users.findOne(notification.userId);
|
||||
|
||||
|
||||
switch(event){
|
||||
case 'newReply':
|
||||
notification.subject = i18n.t('Someone replied to your comment on')+' "'+p.postHeadline+'"';
|
||||
notification.text = p.commentAuthorName+i18n.t(' has replied to your comment on')+' "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId);
|
||||
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>';
|
||||
if(context === 'email')
|
||||
notification.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+i18n.t('Read more')+'</a>';
|
||||
var n = {
|
||||
subject: 'Someone replied to your comment on "'+p.postHeadline+'"',
|
||||
text: p.commentAuthorName+' has replied to your comment on "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId),
|
||||
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>'
|
||||
}
|
||||
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':
|
||||
notification.subject = i18n.t('A new comment on your post')+' "'+p.postHeadline+'"';
|
||||
notification.text = i18n.t('You have a new comment by ')+p.commentAuthorName+i18n.t(' on your post')+' "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId);
|
||||
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>';
|
||||
if(context === 'email')
|
||||
notification.html += '<p>'+p.commentExcerpt+'</p><a href="'+getPostCommentUrl(p.postId, p.commentId)+'" class="action-link">'+i18n.t('Read more')+'</a>';
|
||||
var n = {
|
||||
subject: 'A new comment on your post "'+p.postHeadline+'"',
|
||||
text: 'You have a new comment by '+p.commentAuthorName+' on your post "'+p.postHeadline+'": '+getPostCommentUrl(p.postId, p.commentId),
|
||||
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>'
|
||||
}
|
||||
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':
|
||||
notification.subject = p.postAuthorName+i18n.t(' has created a new post')+': "'+p.postHeadline+'"';
|
||||
notification.text = p.postAuthorName+i18n.t(' has created a new post')+': "'+p.postHeadline+'" '+getPostUrl(p.postId);
|
||||
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>".';
|
||||
var n = {
|
||||
subject: p.postAuthorName+' has created a new post: "'+p.postHeadline+'"',
|
||||
text: p.postAuthorName+' has created a new post: "'+p.postHeadline+'" '+getPostUrl(p.postId),
|
||||
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':
|
||||
notification.subject = i18n.t('Your account has been approved.');
|
||||
notification.text = i18n.t('Welcome to ')+getSetting('title')+'! '+i18n.t('Your account has just 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>';
|
||||
var n = {
|
||||
subject: 'Your account has been approved.',
|
||||
text: 'Welcome to '+getSetting('title')+'! Your account has just been approved.',
|
||||
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:
|
||||
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({
|
||||
|
|
|
@ -97,15 +97,17 @@ Meteor.methods({
|
|||
|
||||
if(getSetting('emailNotifications', false)){
|
||||
// notify users of new posts
|
||||
var properties = {
|
||||
var notification = {
|
||||
event: 'newPost',
|
||||
properties: {
|
||||
postAuthorName : getDisplayName(postAuthor),
|
||||
postAuthorId : post.userId,
|
||||
postHeadline : headline,
|
||||
postId : postId
|
||||
}
|
||||
var notification = getNotification('newPost', properties);
|
||||
// call a server method because we do not have access to admin users' info on the client
|
||||
Meteor.call('notifyUsers', notification, Meteor.user(), function(error, result){
|
||||
}
|
||||
// call a server method because we do not have access to users' info on the client
|
||||
Meteor.call('newPostNotify', notification, function(error, result){
|
||||
//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() {
|
||||
return Router._currentController.template;
|
||||
}
|
||||
getCurrentRoute = function() {
|
||||
return Router._currentController.path;
|
||||
}
|
||||
clearSeenErrors = function(){
|
||||
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
|
||||
return Meteor.absoluteUrl()+'posts/'+postId+'/comment/'+commentId;
|
||||
}
|
||||
getUserUrl = function(id){
|
||||
return Meteor.absoluteUrl()+'users/'+id;
|
||||
}
|
||||
getCategoryUrl = function(slug){
|
||||
return Meteor.absoluteUrl()+'category/'+slug;
|
||||
}
|
||||
slugify = function(text) {
|
||||
if(text){
|
||||
text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
|
||||
text = text.replace(/-/gi, "_");
|
||||
text = text.replace(/\s/gi, "-");
|
||||
text = text.toLowerCase();
|
||||
}
|
||||
return text;
|
||||
|
||||
}
|
||||
getShortUrl = function(post){
|
||||
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 = 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 = {
|
||||
find: {
|
||||
|
@ -12,61 +17,71 @@ getParameters = function (view, limit, category) {
|
|||
}
|
||||
}
|
||||
|
||||
switch (view) {
|
||||
switch (terms.view) {
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
// sort by _id to break ties
|
||||
$.extend(true, parameters, {options: {sort: {_id: -1}}})
|
||||
|
||||
if(typeof limit != 'undefined')
|
||||
_.extend(parameters.options, {limit: parseInt(limit)});
|
||||
|
||||
if(typeof category != 'undefined')
|
||||
_.extend(parameters.find, {'categories.slug': category});
|
||||
|
||||
// console.log(parameters.options.sort)
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// Special case for digest
|
||||
// TODO: merge back into general getParameters function
|
||||
|
||||
getDigestParameters = function (date) {
|
||||
var mDate = moment(date);
|
||||
var parameters = {
|
||||
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: {
|
||||
status: 2,
|
||||
submitted: {
|
||||
$gte: mDate.startOf('day').valueOf(),
|
||||
$lt: mDate.endOf('day').valueOf()
|
||||
$gte: terms.after,
|
||||
$lt: terms.before
|
||||
}
|
||||
},
|
||||
options: {
|
||||
sort: {sticky: -1, baseScore: -1, _id: 1}
|
||||
sort: {sticky: -1, baseScore: -1}
|
||||
}
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// sort by _id to break ties
|
||||
deepExtend(true, parameters, {options: {sort: {_id: -1}}})
|
||||
|
||||
if(typeof terms.limit != 'undefined' && !!terms.limit)
|
||||
_.extend(parameters.options, {limit: parseInt(terms.limit)});
|
||||
|
||||
if(typeof terms.category != 'undefined' && !!terms.category)
|
||||
_.extend(parameters.find, {'categories.slug': terms.category});
|
||||
|
||||
// console.log(parameters)
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
@ -99,6 +114,8 @@ getUsersParameters = function(filterBy, sortBy, limit) {
|
|||
break;
|
||||
case 'commentCount':
|
||||
sort = {commentCount: -1};
|
||||
case 'invitedCount':
|
||||
sort = {invitedCount: -1};
|
||||
}
|
||||
return {
|
||||
find: find,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
updateScore = function (item, collection, forceUpdate) {
|
||||
updateScore = function (collection, item, forceUpdate) {
|
||||
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
|
||||
// is meaningful enough. To find out, we calculate the "power" of a single vote after n days.
|
||||
|
|
10
lib/users.js
10
lib/users.js
|
@ -24,7 +24,13 @@ getDisplayNameById = function(userId){
|
|||
return getDisplayName(Meteor.users.findOne(userId));
|
||||
}
|
||||
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){
|
||||
// return twitter name provided by user, or else the one used for twitter login
|
||||
|
@ -67,7 +73,7 @@ getAvatarUrl = function(user){
|
|||
}else{
|
||||
return Gravatar.getGravatar(user, {
|
||||
d: 'http://demo.telesc.pe/img/default_avatar.png',
|
||||
s: 30
|
||||
s: 80
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
Meteor.methods({
|
||||
inviteUser: function (userId) {
|
||||
|
||||
var currentUser = Meteor.user();
|
||||
var invitedUser = Meteor.users.findOne(userId);
|
||||
var invite = {
|
||||
invited: invitedUser._id,
|
||||
invitedId: invitedUser._id,
|
||||
invitedName: getDisplayName(invitedUser),
|
||||
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(currentUser && currentUser.invitesCount > 0 && canInvite(currentUser) && !isInvited(invitedUser)){
|
||||
if(currentUser && currentUser.inviteCount > 0 && canInvite(currentUser) && !isInvited(invitedUser)){
|
||||
|
||||
// update invinting user
|
||||
Meteor.users.update(Meteor.userId(), {$inc:{invitesCount: -1}});
|
||||
Meteor.users.update(Meteor.userId(), {$push:{invites: invite}});
|
||||
Meteor.users.update(Meteor.userId(), {$inc:{inviteCount: -1}, $inc:{invitedCount: 1}, $push:{invites: invite}});
|
||||
|
||||
// update invited user
|
||||
Meteor.users.update(userId, {$set: {
|
||||
|
|
|
@ -106,6 +106,39 @@ Meteor.startup(function () {
|
|||
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("//----------------------------------------------------------------------//")
|
||||
}
|
||||
|
||||
});
|
|
@ -26,38 +26,45 @@ Meteor.methods({
|
|||
// send the notification if notifications are activated,
|
||||
// the notificationsFrequency is set to 1, or if it's undefined (legacy compatibility)
|
||||
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){
|
||||
// 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});
|
||||
if(user){
|
||||
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 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
|
||||
_.each(Meteor.users.find().fetch(), function(user, index, list){
|
||||
if(user._id !== currentUser._id && getUserSetting('notifications.posts', false, user)){
|
||||
Meteor.users.find().forEach(function(user) {
|
||||
// don't send users notifications for their own posts
|
||||
sendEmail(getEmail(user), notification.subject, notification.text, notification.html);
|
||||
if(user._id !== currentUser._id && getUserSetting('notifications.posts', false, user)){
|
||||
properties.userId = user._id;
|
||||
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,
|
||||
emails: false,
|
||||
notifications: false,
|
||||
invitesCount: false,
|
||||
inviteCount: false,
|
||||
'profile.email': false,
|
||||
'services.twitter.accessToken': 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
|
||||
return findById.count() ? findById : findBySlug;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// 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 [];
|
||||
});
|
||||
|
||||
// Publish author of the current comment
|
||||
|
@ -60,34 +62,42 @@ Meteor.publish('commentUser', function(commentId) {
|
|||
var comment = Comments.findOne(commentId);
|
||||
return Meteor.users.find({_id: comment && comment.userId}, {fields: privacyOptions});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// 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)){
|
||||
var posts = Posts.find(find, options);
|
||||
var userIds = _.pluck(posts.fetch(), 'userId');
|
||||
var parameters = getParameters(terms),
|
||||
posts = Posts.find(parameters.find, parameters.options),
|
||||
userIds = _.pluck(posts.fetch(), 'userId');
|
||||
return Meteor.users.find({_id: {$in: userIds}}, {fields: privacyOptions, multi: true});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Publish all users
|
||||
|
||||
Meteor.publish('allUsers', function(find, options) {
|
||||
Meteor.publish('allUsers', function(filterBy, sortBy, limit) {
|
||||
if(canViewById(this.userId)){
|
||||
var parameters = getUsersParameters(filterBy, sortBy, limit);
|
||||
if (!isAdminById(this.userId)) // if user is not admin, filter out sensitive info
|
||||
options = _.extend(options, {fields: privacyOptions});
|
||||
return Meteor.users.find(find, options);
|
||||
parameters.options = _.extend(parameters.options, {fields: privacyOptions});
|
||||
return Meteor.users.find(parameters.find, parameters.options);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// publish all users for admins to make autocomplete work
|
||||
// TODO: find a better way
|
||||
|
||||
Meteor.publish('allUsersAdmin', function() {
|
||||
if (isAdminById(this.userId))
|
||||
if (isAdminById(this.userId)) {
|
||||
return Meteor.users.find();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------- Posts -------------------------------------------- //
|
||||
|
@ -98,6 +108,7 @@ Meteor.publish('singlePost', function(id) {
|
|||
if(canViewById(this.userId)){
|
||||
return Posts.find(id);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Publish the post related to the current comment
|
||||
|
@ -107,29 +118,26 @@ Meteor.publish('commentPost', function(commentId) {
|
|||
var comment = Comments.findOne(commentId);
|
||||
return Posts.find({_id: comment && comment.post});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Publish a list of posts
|
||||
|
||||
Meteor.publish('postsList', function(find, options) {
|
||||
Meteor.publish('postsList', function(terms) {
|
||||
if(canViewById(this.userId)){
|
||||
options = options || {};
|
||||
var posts = Posts.find(find, options);
|
||||
|
||||
var parameters = getParameters(terms),
|
||||
posts = Posts.find(parameters.find, parameters.options);
|
||||
// console.log('//-------- Subscription Parameters:');
|
||||
// console.log(find);
|
||||
// console.log(options);
|
||||
// console.log(parameters.find);
|
||||
// console.log(parameters.options);
|
||||
// console.log('Found '+posts.fetch().length+ ' posts:');
|
||||
// posts.rewind();
|
||||
// console.log(_.pluck(posts.fetch(), 'headline'));
|
||||
// posts.rewind();
|
||||
|
||||
return posts;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------- Comments -------------------------------------------- //
|
||||
|
||||
// Publish comments for a specific post
|
||||
|
@ -138,6 +146,7 @@ Meteor.publish('postComments', function(postId) {
|
|||
if(canViewById(this.userId)){
|
||||
return Comments.find({post: postId});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Publish a single comment
|
||||
|
@ -146,12 +155,22 @@ Meteor.publish('singleComment', function(commentId) {
|
|||
if(canViewById(this.userId)){
|
||||
return Comments.find(commentId);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// -------------------------------------------- Other -------------------------------------------- //
|
||||
|
||||
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() {
|
||||
|
@ -159,10 +178,12 @@ Meteor.publish('notifications', function() {
|
|||
if(canViewById(this.userId)){
|
||||
return Notifications.find({userId:this.userId});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
Meteor.publish('categories', function() {
|
||||
if(canViewById(this.userId)){
|
||||
return Categories.find();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
|
|
@ -14,38 +14,6 @@ Meteor.methods({
|
|||
},
|
||||
giveInvites: function () {
|
||||
if(isAdmin(Meteor.user()))
|
||||
Meteor.users.update({}, {$inc:{invitesCount: 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
|
||||
Meteor.users.update({}, {$inc:{inviteCount: 1}}, {multi:true});
|
||||
}
|
||||
})
|
|
@ -1,19 +1,32 @@
|
|||
Accounts.onCreateUser(function(options, user){
|
||||
user.profile = options.profile || {};
|
||||
user.karma = 0;
|
||||
// users start pending and need to be invited
|
||||
user.isInvited = false;
|
||||
user.isAdmin = false;
|
||||
var userProperties = {
|
||||
profile: options.profile || {},
|
||||
karma: 0,
|
||||
isInvited: false,
|
||||
isAdmin: false,
|
||||
postCount: 0,
|
||||
commentCount: 0,
|
||||
invitedCount: 0
|
||||
}
|
||||
user = _.extend(user, userProperties);
|
||||
|
||||
if (options.email)
|
||||
user.profile.email = options.email;
|
||||
|
||||
if (user.profile.email)
|
||||
user.email_hash = CryptoJS.MD5(user.profile.email.trim().toLowerCase()).toString();
|
||||
if (getEmail(user))
|
||||
user.email_hash = getEmailHash(user);
|
||||
|
||||
if (!user.profile.name)
|
||||
user.profile.name = user.username;
|
||||
|
||||
// set notifications default preferences
|
||||
user.profile.notifications = {
|
||||
users: false,
|
||||
posts: false,
|
||||
comments: true,
|
||||
replies: true
|
||||
}
|
||||
|
||||
// create slug from username
|
||||
user.slug = slugify(getUserName(user));
|
||||
|
||||
|
@ -22,16 +35,39 @@ Accounts.onCreateUser(function(options, user){
|
|||
user.isAdmin = true;
|
||||
|
||||
// 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});
|
||||
|
||||
// add new user to MailChimp list
|
||||
// if user has already filled in their email, add them to MailChimp list
|
||||
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;
|
||||
});
|
||||
|
||||
getEmailHash = function(user){
|
||||
// todo: add some kind of salt in here
|
||||
return CryptoJS.MD5(getEmail(user).trim().toLowerCase() + user.createdAt).toString();
|
||||
}
|
||||
|
||||
addToMailChimpList = function(user){
|
||||
// add a user to a MailChimp list.
|
||||
// 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);
|
||||
return Math.abs(object.score - newScore);
|
||||
},
|
||||
generateEmailHash: function(){
|
||||
var email_hash = CryptoJS.MD5(getEmail(Meteor.user()).trim().toLowerCase()).toString();
|
||||
Meteor.users.update(Meteor.userId(), {$set : {email_hash : email_hash}});
|
||||
setEmailHash: function(user){
|
||||
var email_hash = CryptoJS.MD5(getEmail(user).trim().toLowerCase()).toString();
|
||||
Meteor.users.update(user._id, {$set : {email_hash : email_hash}});
|
||||
},
|
||||
addCurrentUserToMailChimpList: function(){
|
||||
addToMailChimpList(Meteor.user());
|
||||
|
|
Loading…
Add table
Reference in a new issue