mirror of
https://github.com/vale981/Vulcan
synced 2025-03-09 12:16:37 -04:00
Merge branch 'devel'
This commit is contained in:
commit
16898d4121
74 changed files with 1579 additions and 744 deletions
|
@ -25,7 +25,7 @@ meteorhacks:fast-render
|
||||||
meteorhacks:subs-manager
|
meteorhacks:subs-manager
|
||||||
meteorhacks:npm
|
meteorhacks:npm
|
||||||
|
|
||||||
aldeed:autoform@4.0.0-rc1
|
aldeed:autoform
|
||||||
aldeed:collection2
|
aldeed:collection2
|
||||||
aldeed:simple-schema
|
aldeed:simple-schema
|
||||||
|
|
||||||
|
@ -75,6 +75,6 @@ telescope-kadira
|
||||||
telescope-notifications
|
telescope-notifications
|
||||||
telescope-singleday
|
telescope-singleday
|
||||||
telescope-invites
|
telescope-invites
|
||||||
|
telescope-post-by-feed
|
||||||
|
|
||||||
# Custom Packages
|
# Custom Packages
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
METEOR@1.0.1
|
METEOR@1.0.2.1
|
||||||
|
|
190
.meteor/versions
190
.meteor/versions
|
@ -1,113 +1,112 @@
|
||||||
accounts-base@1.1.2
|
accounts-base@1.1.3
|
||||||
accounts-facebook@1.0.2
|
accounts-facebook@1.0.3
|
||||||
accounts-oauth@1.1.2
|
accounts-oauth@1.1.3
|
||||||
accounts-password@1.0.4
|
accounts-password@1.0.5
|
||||||
accounts-twitter@1.0.2
|
accounts-twitter@1.0.3
|
||||||
accounts-ui-unstyled@1.1.4
|
accounts-ui@1.1.4
|
||||||
accounts-ui@1.1.3
|
accounts-ui-unstyled@1.1.5
|
||||||
aldeed:autoform@4.1.0
|
aldeed:autoform@4.2.2
|
||||||
aldeed:collection2@2.2.0
|
aldeed:collection2@2.3.0
|
||||||
aldeed:simple-schema@1.1.0
|
aldeed:simple-schema@1.2.0
|
||||||
application-configuration@1.0.3
|
application-configuration@1.0.4
|
||||||
artwells:queue@0.0.3
|
artwells:queue@0.0.3
|
||||||
autoupdate@1.1.3
|
autoupdate@1.1.4
|
||||||
backbone@1.0.0
|
backbone@1.0.0
|
||||||
base64@1.0.1
|
base64@1.0.2
|
||||||
bengott:avatar@0.7.3
|
bengott:avatar@0.7.3
|
||||||
binary-heap@1.0.1
|
binary-heap@1.0.2
|
||||||
blaze-tools@1.0.1
|
blaze@2.0.4
|
||||||
blaze@2.0.3
|
blaze-tools@1.0.2
|
||||||
boilerplate-generator@1.0.1
|
boilerplate-generator@1.0.2
|
||||||
callback-hook@1.0.1
|
callback-hook@1.0.2
|
||||||
ccan:cssreset@1.0.0
|
ccan:cssreset@1.0.0
|
||||||
check@1.0.2
|
check@1.0.3
|
||||||
chuangbo:cookie@1.1.0
|
chuangbo:cookie@1.1.0
|
||||||
chuangbo:marked@0.3.5
|
chuangbo:marked@0.3.5
|
||||||
cmather:handlebars-server@2.0.0
|
cmather:handlebars-server@2.0.0
|
||||||
coffeescript@1.0.4
|
coffeescript@1.0.5
|
||||||
ctl-helper@1.0.4
|
ddp@1.0.13
|
||||||
ctl@1.0.2
|
deps@1.0.6
|
||||||
ddp@1.0.12
|
|
||||||
deps@1.0.5
|
|
||||||
djedi:sanitize-html@1.3.0
|
djedi:sanitize-html@1.3.0
|
||||||
ejson@1.0.4
|
ejson@1.0.5
|
||||||
email@1.0.4
|
email@1.0.5
|
||||||
facebook@1.1.2
|
facebook@1.1.3
|
||||||
fastclick@1.0.1
|
fastclick@1.0.2
|
||||||
follower-livedata@1.0.2
|
follower-livedata@1.0.3
|
||||||
fourseven:scss@1.0.0
|
fourseven:scss@1.0.0
|
||||||
geojson-utils@1.0.1
|
geojson-utils@1.0.2
|
||||||
handlebars@1.0.1
|
handlebars@1.0.2
|
||||||
html-tools@1.0.2
|
html-tools@1.0.3
|
||||||
htmljs@1.0.2
|
htmljs@1.0.3
|
||||||
http@1.0.8
|
http@1.0.9
|
||||||
id-map@1.0.1
|
id-map@1.0.2
|
||||||
iron:controller@1.0.3
|
iron:controller@1.0.6
|
||||||
iron:core@1.0.3
|
iron:core@1.0.6
|
||||||
iron:dynamic-template@1.0.3
|
iron:dynamic-template@1.0.6
|
||||||
iron:layout@1.0.3
|
iron:layout@1.0.6
|
||||||
iron:location@1.0.3
|
iron:location@1.0.6
|
||||||
iron:middleware-stack@1.0.3
|
iron:middleware-stack@1.0.6
|
||||||
iron:router@1.0.3
|
iron:router@1.0.6
|
||||||
iron:url@1.0.3
|
iron:url@1.0.6
|
||||||
jparker:crypto-core@0.1.0
|
jparker:crypto-core@0.1.0
|
||||||
jparker:crypto-md5@0.1.1
|
jparker:crypto-md5@0.1.1
|
||||||
jparker:gravatar@0.3.1
|
jparker:gravatar@0.3.1
|
||||||
jquery@1.0.1
|
jquery@1.0.2
|
||||||
json@1.0.1
|
json@1.0.2
|
||||||
kestanous:herald-email@0.4.2
|
|
||||||
kestanous:herald@1.1.3
|
kestanous:herald@1.1.3
|
||||||
launch-screen@1.0.0
|
kestanous:herald-email@0.4.2
|
||||||
less@1.0.11
|
launch-screen@1.0.1
|
||||||
livedata@1.0.11
|
less@1.0.12
|
||||||
localstorage@1.0.1
|
livedata@1.0.12
|
||||||
logging@1.0.5
|
localstorage@1.0.2
|
||||||
matb33:collection-hooks@0.7.6
|
logging@1.0.6
|
||||||
meteor-platform@1.2.0
|
matb33:collection-hooks@0.7.7
|
||||||
meteor@1.1.3
|
meteor@1.1.4
|
||||||
|
meteor-platform@1.2.1
|
||||||
meteorhacks:async@1.0.0
|
meteorhacks:async@1.0.0
|
||||||
meteorhacks:fast-render@2.1.0
|
meteorhacks:fast-render@2.1.5
|
||||||
meteorhacks:kadira-binary-deps@1.3.1
|
|
||||||
meteorhacks:kadira@2.15.1
|
meteorhacks:kadira@2.15.1
|
||||||
|
meteorhacks:kadira-binary-deps@1.3.1
|
||||||
meteorhacks:meteorx@1.2.1
|
meteorhacks:meteorx@1.2.1
|
||||||
meteorhacks:npm@1.2.1
|
meteorhacks:npm@1.2.2
|
||||||
meteorhacks:subs-manager@1.2.2
|
meteorhacks:subs-manager@1.2.2
|
||||||
minifiers@1.1.2
|
minifiers@1.1.3
|
||||||
minimongo@1.0.5
|
minimongo@1.0.6
|
||||||
mobile-status-bar@1.0.1
|
mobile-status-bar@1.0.2
|
||||||
mongo-livedata@1.0.6
|
momentjs:moment@2.8.4
|
||||||
mongo@1.0.9
|
mongo@1.0.11
|
||||||
|
mongo-livedata@1.0.7
|
||||||
mrt:cookies@0.3.0
|
mrt:cookies@0.3.0
|
||||||
mrt:jquery-hotkeys@0.0.1
|
mrt:jquery-hotkeys@0.0.1
|
||||||
mrt:mailchimp@0.4.0
|
mrt:mailchimp@0.4.0
|
||||||
mrt:moment@2.8.1
|
mrt:moment@2.8.1
|
||||||
npm-bcrypt@0.7.7
|
npm-bcrypt@0.7.7
|
||||||
npm-container@1.0.0
|
npm-container@1.0.0
|
||||||
oauth1@1.1.2
|
oauth@1.1.3
|
||||||
oauth2@1.1.1
|
oauth1@1.1.3
|
||||||
oauth@1.1.2
|
oauth2@1.1.2
|
||||||
observe-sequence@1.0.3
|
observe-sequence@1.0.4
|
||||||
ordered-dict@1.0.1
|
ordered-dict@1.0.2
|
||||||
percolatestudio:synced-cron@1.1.0
|
percolatestudio:synced-cron@1.1.0
|
||||||
rajit:bootstrap3-datepicker@1.3.1
|
rajit:bootstrap3-datepicker@1.3.1
|
||||||
random@1.0.1
|
random@1.0.2
|
||||||
reactive-dict@1.0.4
|
reactive-dict@1.0.5
|
||||||
reactive-var@1.0.3
|
reactive-var@1.0.4
|
||||||
reload@1.1.1
|
reload@1.1.2
|
||||||
retry@1.0.1
|
retry@1.0.2
|
||||||
routepolicy@1.0.2
|
routepolicy@1.0.3
|
||||||
sacha:juice@0.1.0
|
sacha:juice@0.1.1
|
||||||
sacha:spin@2.0.4
|
sacha:spin@2.0.4
|
||||||
service-configuration@1.0.2
|
service-configuration@1.0.3
|
||||||
session@1.0.4
|
session@1.0.5
|
||||||
sha@1.0.1
|
sha@1.0.2
|
||||||
softwarerero:accounts-t9n@1.0.4
|
softwarerero:accounts-t9n@1.0.5
|
||||||
spacebars-compiler@1.0.3
|
spacebars@1.0.4
|
||||||
spacebars@1.0.3
|
spacebars-compiler@1.0.4
|
||||||
spiderable@1.0.5
|
spiderable@1.0.6
|
||||||
srp@1.0.1
|
srp@1.0.2
|
||||||
standard-app-packages@1.0.3
|
standard-app-packages@1.0.4
|
||||||
stylus@1.0.5
|
stylus@1.0.6
|
||||||
tap:http-methods@0.0.23
|
tap:http-methods@0.0.23
|
||||||
tap:i18n@1.2.1
|
tap:i18n@1.2.1
|
||||||
telescope-api@0.0.0
|
telescope-api@0.0.0
|
||||||
|
@ -123,6 +122,7 @@ telescope-lib@0.2.9
|
||||||
telescope-module-share@0.0.0
|
telescope-module-share@0.0.0
|
||||||
telescope-newsletter@0.1.0
|
telescope-newsletter@0.1.0
|
||||||
telescope-notifications@0.1.0
|
telescope-notifications@0.1.0
|
||||||
|
telescope-post-by-feed@0.0.1
|
||||||
telescope-rss@0.0.0
|
telescope-rss@0.0.0
|
||||||
telescope-search@0.0.0
|
telescope-search@0.0.0
|
||||||
telescope-singleday@0.1.0
|
telescope-singleday@0.1.0
|
||||||
|
@ -130,14 +130,14 @@ telescope-tags@0.0.0
|
||||||
telescope-theme-base@0.0.0
|
telescope-theme-base@0.0.0
|
||||||
telescope-theme-hubble@0.0.0
|
telescope-theme-hubble@0.0.0
|
||||||
telescope-update-prompt@0.1.0
|
telescope-update-prompt@0.1.0
|
||||||
templating@1.0.9
|
templating@1.0.10
|
||||||
tracker@1.0.3
|
tracker@1.0.4
|
||||||
tsega:bootstrap3-datetimepicker@3.1.3_1
|
tsega:bootstrap3-datetimepicker@3.1.3_3
|
||||||
twitter@1.1.2
|
twitter@1.1.3
|
||||||
ui@1.0.4
|
ui@1.0.5
|
||||||
underscore@1.0.1
|
underscore@1.0.2
|
||||||
url@1.0.2
|
url@1.0.3
|
||||||
useraccounts:core@1.4.0
|
useraccounts:core@1.4.1
|
||||||
useraccounts:unstyled@1.4.0
|
useraccounts:unstyled@1.4.1
|
||||||
webapp-hashing@1.0.1
|
webapp@1.1.5
|
||||||
webapp@1.1.4
|
webapp-hashing@1.0.2
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
## v0.11.1 “FeedScope”
|
||||||
|
|
||||||
|
* Post submit and edit forms now submit to their respective methods directly.
|
||||||
|
* Removed `postSubmitRenderedCallbacks` and `postEditRenderedCallbacks`.
|
||||||
|
* `telescope-post-by-feed` package now lets you import posts from RSS feeds.
|
||||||
|
* Adding limit of 200 posts to post list request.
|
||||||
|
* Refactoring post and comment submit to fix latency compensation issues.
|
||||||
|
* Tags package now using Autoform.
|
||||||
|
|
||||||
## v0.11.0 “AvatarScope”
|
## v0.11.0 “AvatarScope”
|
||||||
|
|
||||||
* Added new `userCreatedCallbacks` callback hook.
|
* Added new `userCreatedCallbacks` callback hook.
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template name="settings">
|
<template name="settingsForm">
|
||||||
<div class="grid-small grid-module dialog settings">
|
<div class="grid-small grid-module dialog settings">
|
||||||
{{#if this.hasSettings}}
|
{{#if this.hasSettings}}
|
||||||
{{> quickForm collection="Settings" id="updateSettingsForm" type="update" doc=this.settings label-class="control-label" input-col-class="controls" template="telescope"}}
|
{{> quickForm collection="Settings" id="updateSettingsForm" type="update" doc=this.settings label-class="control-label" input-col-class="controls" template="telescope"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{> quickForm collection="Settings" id="updateSettingsForm" type="insert" template="telescope" label-class="control-label" input-col-class="controls"}}
|
{{> quickForm collection="Settings" id="insertSettingsForm" type="insert" template="telescope" label-class="control-label" input-col-class="controls"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
41
client/views/admin/settings_form.js
Normal file
41
client/views/admin/settings_form.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
AutoForm.hooks({
|
||||||
|
updateSettingsForm: {
|
||||||
|
|
||||||
|
before: {
|
||||||
|
update: function(docId, modifier, template) {
|
||||||
|
template.$('button[type=submit]').addClass('loading');
|
||||||
|
return modifier;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onSuccess: function(operation, result, template) {
|
||||||
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: function(operation, result, template) {
|
||||||
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AutoForm.hooks({
|
||||||
|
insertSettingsForm: {
|
||||||
|
|
||||||
|
before: {
|
||||||
|
insert: function(doc, template) {
|
||||||
|
template.$('button[type=submit]').addClass('loading');
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onSuccess: function(operation, result, template) {
|
||||||
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: function(operation, result, template) {
|
||||||
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
|
@ -6,7 +6,7 @@
|
||||||
<textarea id="comment" rows="3" autofocus="autofocus"></textarea>
|
<textarea id="comment" rows="3" autofocus="autofocus"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-submit">
|
<div class="comment-submit">
|
||||||
<input type="submit" class="btn btn-primary" value="{{_ "add_comment"}}" title="(⌘+enter)" />
|
<button type="submit" class="comment-submit-button btn btn-primary btn-submit" value="{{_ "add_comment"}}" title="(⌘+enter)">{{_ "add_comment"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,40 +6,45 @@ Template[getTemplate('comment_form')].helpers({
|
||||||
|
|
||||||
Template[getTemplate('comment_form')].events({
|
Template[getTemplate('comment_form')].events({
|
||||||
'submit form': function(e, instance){
|
'submit form': function(e, instance){
|
||||||
var $commentForm = instance.$('#comment');
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(e.target).addClass('disabled');
|
$(e.target).addClass('disabled');
|
||||||
clearSeenMessages();
|
clearSeenMessages();
|
||||||
var content = $commentForm.val();
|
|
||||||
if(getCurrentTemplate() == 'comment_reply'){
|
|
||||||
// child comment
|
|
||||||
var parentComment = this.comment;
|
|
||||||
Meteor.call('comment', parentComment.postId, parentComment._id, content, function(error, newComment){
|
|
||||||
if(error){
|
|
||||||
console.log(error);
|
|
||||||
flashMessage(error.reason, "error");
|
|
||||||
}else{
|
|
||||||
trackEvent("newComment", newComment);
|
|
||||||
Router.go('post_page_comment', {
|
|
||||||
_id: parentComment.postId,
|
|
||||||
commentId: newComment._id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
// root comment
|
|
||||||
var post = postObject;
|
|
||||||
|
|
||||||
Meteor.call('comment', post._id, null, content, function(error, newComment){
|
|
||||||
if(error){
|
var comment = {};
|
||||||
console.log(error);
|
var $commentForm = instance.$('#comment');
|
||||||
flashMessage(error.reason, "error");
|
var $submitButton = instance.$('.btn-submit');
|
||||||
}else{
|
var body = $commentForm.val();
|
||||||
trackEvent("newComment", newComment);
|
|
||||||
Session.set('scrollToCommentId', newComment._id);
|
// now that the form is latency compensated, we don't actually need to show this
|
||||||
$commentForm.val('');
|
// $commentForm.prop('disabled', true);
|
||||||
}
|
// $submitButton.addClass('loading');
|
||||||
});
|
|
||||||
|
$commentForm.val('');
|
||||||
|
|
||||||
|
var post = postObject;
|
||||||
|
|
||||||
|
comment = {
|
||||||
|
postId: post._id,
|
||||||
|
body: body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// child comment
|
||||||
|
if (getCurrentTemplate() == 'comment_reply') {
|
||||||
|
comment.parentCommentId = this.comment._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Meteor.call('submitComment', comment, function(error, newComment){
|
||||||
|
// $commentForm.prop('disabled', false);
|
||||||
|
// $submitButton.removeClass('loading');
|
||||||
|
if(error){
|
||||||
|
console.log(error);
|
||||||
|
flashMessage(error.reason, "error");
|
||||||
|
}else{
|
||||||
|
trackEvent("newComment", newComment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template name="post_edit">
|
<template name="post_edit">
|
||||||
|
|
||||||
<div class="grid grid-module">
|
<div class="grid grid-module">
|
||||||
{{> quickForm collection="Posts" doc=post id="editPostForm" template="telescope" label-class="control-label" input-col-class="controls"}}
|
{{> quickForm collection="Posts" doc=post id="editPostForm" template="telescope" label-class="control-label" input-col-class="controls" type="method" meteormethod="editPost"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-module">
|
<div class="grid grid-module">
|
||||||
|
|
|
@ -1,46 +1,32 @@
|
||||||
AutoForm.hooks({
|
AutoForm.hooks({
|
||||||
editPostForm: {
|
editPostForm: {
|
||||||
onSubmit: function(insertDoc, updateDoc, currentDoc) {
|
|
||||||
|
|
||||||
var updateObject = updateDoc;
|
before: {
|
||||||
var submit = this;
|
editPost: function(doc, template) {
|
||||||
|
|
||||||
// ------------------------------ Checks ------------------------------ //
|
var post = doc;
|
||||||
|
|
||||||
if (!Meteor.user()) {
|
// ------------------------------ Checks ------------------------------ //
|
||||||
flashMessage(i18n.t('you_must_be_logged_in'), "");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
if (!Meteor.user()) {
|
||||||
|
flashMessage(i18n.t('you_must_be_logged_in'), "");
|
||||||
// run all post edit client callbacks on updateObject object successively
|
return false;
|
||||||
updateObject = postEditClientCallbacks.reduce(function(result, currentFunction) {
|
|
||||||
return currentFunction(result);
|
|
||||||
}, updateObject);
|
|
||||||
|
|
||||||
// ------------------------------ Update ------------------------------ //
|
|
||||||
Meteor.call('editPost', currentDoc._id, updateObject, function(error, post) {
|
|
||||||
if(error){
|
|
||||||
submit.done(error);
|
|
||||||
}else{
|
|
||||||
// note: find a way to do this in onSuccess instead?
|
|
||||||
trackEvent("edit post", {'postId': post._id});
|
|
||||||
Router.go('post_page', {_id: post._id});
|
|
||||||
submit.done();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return false
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
|
// run all post edit client callbacks on post object successively
|
||||||
|
post = postEditClientCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, post);
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: function(operation, result, template) {
|
onSuccess: function(operation, post, template) {
|
||||||
// not used right now because I can't find a way to pass the "post" object to this callback
|
trackEvent("edit post", {'postId': post._id});
|
||||||
// console.log(result)
|
Router.go('post_page', {_id: post._id});
|
||||||
// trackEvent("new post", {'postId': post._id});
|
|
||||||
// if(post.status === STATUS_PENDING)
|
|
||||||
// throwError('Thanks, your post is awaiting approval.');
|
|
||||||
// Router.go('/posts/'+post._id);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: function(operation, error, template) {
|
onError: function(operation, error, template) {
|
||||||
|
@ -49,16 +35,10 @@ AutoForm.hooks({
|
||||||
clearSeenMessages();
|
clearSeenMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called at the beginning and end of submission, respectively.
|
|
||||||
// This is the place to disable/enable buttons or the form,
|
|
||||||
// show/hide a "Please wait" message, etc. If these hooks are
|
|
||||||
// not defined, then by default the submit button is disabled
|
|
||||||
// during submission.
|
|
||||||
// beginSubmit: function(formId, template) {},
|
|
||||||
// endSubmit: function(formId, template) {}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// delete link
|
||||||
Template[getTemplate('post_edit')].events({
|
Template[getTemplate('post_edit')].events({
|
||||||
'click .delete-link': function(e){
|
'click .delete-link': function(e){
|
||||||
var post = this.post;
|
var post = this.post;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template name="post_submit">
|
<template name="post_submit">
|
||||||
|
|
||||||
<div class="grid grid-module">
|
<div class="grid grid-module">
|
||||||
{{> quickForm collection="Posts" id="submitPostForm" template="telescope" label-class="control-label" input-col-class="controls"}}
|
{{> quickForm collection="Posts" id="submitPostForm" template="telescope" label-class="control-label" input-col-class="controls" type="method" meteormethod="submitPost"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
|
@ -1,54 +1,42 @@
|
||||||
AutoForm.hooks({
|
AutoForm.hooks({
|
||||||
submitPostForm: {
|
submitPostForm: {
|
||||||
onSubmit: function(insertDoc, updateDoc, currentDoc) {
|
|
||||||
|
|
||||||
var properties = insertDoc;
|
before: {
|
||||||
var submit = this;
|
submitPost: function(doc, template) {
|
||||||
|
|
||||||
// ------------------------------ Checks ------------------------------ //
|
template.$('button[type=submit]').addClass('loading');
|
||||||
|
|
||||||
if (!Meteor.user()) {
|
var post = doc;
|
||||||
flashMessage(i18n.t('you_must_be_logged_in'), 'error');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
// ------------------------------ Checks ------------------------------ //
|
||||||
|
|
||||||
// run all post submit client callbacks on properties object successively
|
if (!Meteor.user()) {
|
||||||
properties = postSubmitClientCallbacks.reduce(function(result, currentFunction) {
|
flashMessage(i18n.t('you_must_be_logged_in'), 'error');
|
||||||
return currentFunction(result);
|
return false;
|
||||||
}, properties);
|
|
||||||
|
|
||||||
// console.log(properties)
|
|
||||||
|
|
||||||
// ------------------------------ Insert ------------------------------ //
|
|
||||||
Meteor.call('submitPost', properties, function(error, post) {
|
|
||||||
if(error){
|
|
||||||
submit.done(error);
|
|
||||||
}else{
|
|
||||||
// note: find a way to do this in onSuccess instead?
|
|
||||||
trackEvent("new post", {'postId': post._id});
|
|
||||||
if (post.status === STATUS_PENDING) {
|
|
||||||
flashMessage(i18n.t('thanks_your_post_is_awaiting_approval'), 'success');
|
|
||||||
}
|
|
||||||
Router.go('post_page', {_id: post._id});
|
|
||||||
submit.done();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return false
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
|
// run all post submit client callbacks on properties object successively
|
||||||
|
post = postSubmitClientCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, post);
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: function(operation, result, template) {
|
onSuccess: function(operation, post, template) {
|
||||||
// not used right now because I can't find a way to pass the "post" object to this callback
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
// console.log(post)
|
trackEvent("new post", {'postId': post._id});
|
||||||
// trackEvent("new post", {'postId': post._id});
|
if (post.status === STATUS_PENDING) {
|
||||||
// if(post.status === STATUS_PENDING)
|
flashMessage(i18n.t('thanks_your_post_is_awaiting_approval'), 'success');
|
||||||
// throwError('Thanks, your post is awaiting approval.');
|
}
|
||||||
// Router.go('/posts/'+post._id);
|
Router.go('post_page', {_id: post._id});
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: function(operation, error, template) {
|
onError: function(operation, error, template) {
|
||||||
|
template.$('button[type=submit]').removeClass('loading');
|
||||||
flashMessage(error.message.split('|')[0], 'error'); // workaround because error.details returns undefined
|
flashMessage(error.message.split('|')[0], 'error'); // workaround because error.details returns undefined
|
||||||
clearSeenMessages();
|
clearSeenMessages();
|
||||||
// $(e.target).removeClass('disabled');
|
// $(e.target).removeClass('disabled');
|
||||||
|
@ -58,12 +46,5 @@ AutoForm.hooks({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called at the beginning and end of submission, respectively.
|
|
||||||
// This is the place to disable/enable buttons or the form,
|
|
||||||
// show/hide a "Please wait" message, etc. If these hooks are
|
|
||||||
// not defined, then by default the submit button is disabled
|
|
||||||
// during submission.
|
|
||||||
// beginSubmit: function(formId, template) {},
|
|
||||||
// endSubmit: function(formId, template) {}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -87,9 +87,13 @@ Comments.allow({
|
||||||
remove: canEditById
|
remove: canEditById
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
// ------------------------------------------ Hooks ------------------------------------------ //
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
Comments.before.insert(function (userId, doc) {
|
Comments.before.insert(function (userId, doc) {
|
||||||
if(Meteor.isServer)
|
// note: only actually sanitizes on the server
|
||||||
doc.htmlBody = sanitize(marked(doc.body));
|
doc.htmlBody = sanitize(marked(doc.body));
|
||||||
});
|
});
|
||||||
|
|
||||||
Comments.before.update(function (userId, doc, fieldNames, modifier, options) {
|
Comments.before.update(function (userId, doc, fieldNames, modifier, options) {
|
||||||
|
@ -100,76 +104,133 @@ Comments.before.update(function (userId, doc, fieldNames, modifier, options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
commentAfterSubmitMethodCallbacks.push(function (comment) {
|
||||||
|
|
||||||
|
var userId = comment.userId,
|
||||||
|
commentAuthor = Meteor.users.findOne(userId);
|
||||||
|
|
||||||
|
// increment comment count
|
||||||
|
Meteor.users.update({_id: userId}, {
|
||||||
|
$inc: {'commentCount': 1}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update post
|
||||||
|
Posts.update(comment.postId, {
|
||||||
|
$inc: {commentCount: 1},
|
||||||
|
$set: {lastCommentedAt: new Date()},
|
||||||
|
$addToSet: {commenters: userId}
|
||||||
|
});
|
||||||
|
|
||||||
|
// upvote comment
|
||||||
|
upvoteItem(Comments, comment, commentAuthor);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
// -------------------------------------- Submit Comment ------------------------------------- //
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
submitComment = function (comment) {
|
||||||
|
|
||||||
|
var userId = comment.userId; // at this stage, a userId is expected
|
||||||
|
|
||||||
|
// ------------------------------ Checks ------------------------------ //
|
||||||
|
|
||||||
|
// Don't allow empty comments
|
||||||
|
if (!comment.body)
|
||||||
|
throw new Meteor.Error(704,i18n.t('your_comment_is_empty'));
|
||||||
|
|
||||||
|
// ------------------------------ Properties ------------------------------ //
|
||||||
|
|
||||||
|
var defaultProperties = {
|
||||||
|
createdAt: new Date(),
|
||||||
|
postedAt: new Date(),
|
||||||
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
|
baseScore: 0,
|
||||||
|
score: 0,
|
||||||
|
author: getDisplayNameById(userId)
|
||||||
|
};
|
||||||
|
|
||||||
|
comment = _.extend(defaultProperties, comment);
|
||||||
|
|
||||||
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
|
// run all post submit server callbacks on comment object successively
|
||||||
|
comment = commentSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, comment);
|
||||||
|
|
||||||
|
// -------------------------------- Insert -------------------------------- //
|
||||||
|
|
||||||
|
comment._id = Comments.insert(comment);
|
||||||
|
|
||||||
|
// --------------------- Server-side Async Callbacks --------------------- //
|
||||||
|
|
||||||
|
// run all post submit server callbacks on comment object successively
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.setTimeout(function () { // use setTimeout to avoid holding up client
|
||||||
|
comment = commentAfterSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, comment);
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
// ----------------------------------------- Methods ----------------------------------------- //
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
comment: function(postId, parentCommentId, text){
|
submitComment: function(comment){
|
||||||
|
|
||||||
|
// required properties:
|
||||||
|
// postId
|
||||||
|
// content
|
||||||
|
|
||||||
|
// optional properties:
|
||||||
|
// parentCommentId
|
||||||
|
|
||||||
var user = Meteor.user(),
|
var user = Meteor.user(),
|
||||||
post = Posts.findOne(postId),
|
hasAdminRights = isAdmin(user);
|
||||||
postUser = Meteor.users.findOne(post.userId),
|
|
||||||
timeSinceLastComment = timeSinceLast(user, Comments),
|
// ------------------------------ Checks ------------------------------ //
|
||||||
commentInterval = Math.abs(parseInt(getSetting('commentInterval',15))),
|
|
||||||
now = new Date();
|
|
||||||
|
|
||||||
// check that user can comment
|
// check that user can comment
|
||||||
if (!user || !canComment(user))
|
if (!user || !canComment(user))
|
||||||
throw new Meteor.Error(i18n.t('you_need_to_login_or_be_invited_to_post_new_comments'));
|
throw new Meteor.Error(i18n.t('you_need_to_login_or_be_invited_to_post_new_comments'));
|
||||||
|
|
||||||
// check that user waits more than 15 seconds between comments
|
// ------------------------------ Rate Limiting ------------------------------ //
|
||||||
if(!this.isSimulation && (timeSinceLastComment < commentInterval))
|
|
||||||
throw new Meteor.Error(704, i18n.t('please_wait')+(commentInterval-timeSinceLastComment)+i18n.t('seconds_before_commenting_again'));
|
|
||||||
|
|
||||||
// Don't allow empty comments
|
|
||||||
if (!text)
|
|
||||||
throw new Meteor.Error(704,i18n.t('your_comment_is_empty'));
|
|
||||||
|
|
||||||
var comment = {
|
|
||||||
postId: postId,
|
|
||||||
body: text,
|
|
||||||
userId: user._id,
|
|
||||||
createdAt: now,
|
|
||||||
postedAt: now,
|
|
||||||
upvotes: 0,
|
|
||||||
downvotes: 0,
|
|
||||||
baseScore: 0,
|
|
||||||
score: 0,
|
|
||||||
author: getDisplayName(user)
|
|
||||||
};
|
|
||||||
|
|
||||||
if(parentCommentId)
|
if (!hasAdminRights) {
|
||||||
comment.parentCommentId = parentCommentId;
|
|
||||||
|
var timeSinceLastComment = timeSinceLast(user, Comments),
|
||||||
|
commentInterval = Math.abs(parseInt(getSetting('commentInterval',15)));
|
||||||
|
|
||||||
|
// check that user waits more than 15 seconds between comments
|
||||||
|
if((timeSinceLastComment < commentInterval))
|
||||||
|
throw new Meteor.Error(704, i18n.t('please_wait')+(commentInterval-timeSinceLastComment)+i18n.t('seconds_before_commenting_again'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
// ------------------------------ Properties ------------------------------ //
|
||||||
|
|
||||||
// run all post submit server callbacks on comment object successively
|
// admin-only properties
|
||||||
comment = commentSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
// userId
|
||||||
return currentFunction(result);
|
|
||||||
}, comment);
|
|
||||||
|
|
||||||
// -------------------------------- Insert ------------------------------- //
|
// if user is not admin, clear restricted properties
|
||||||
|
if (!hasAdminRights) {
|
||||||
|
delete comment.userId;
|
||||||
|
}
|
||||||
|
|
||||||
comment._id = Comments.insert(comment);
|
// if no userId has been set, default to current user id
|
||||||
|
if (!comment.userId) {
|
||||||
|
comment.userId = user._id
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
return submitComment(comment);
|
||||||
|
|
||||||
// run all post submit server callbacks on comment object successively
|
|
||||||
comment = commentAfterSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
|
||||||
return currentFunction(result);
|
|
||||||
}, comment);
|
|
||||||
|
|
||||||
// increment comment count
|
|
||||||
Meteor.users.update({_id: user._id}, {
|
|
||||||
$inc: {'commentCount': 1}
|
|
||||||
});
|
|
||||||
|
|
||||||
Posts.update(postId, {
|
|
||||||
$inc: {commentCount: 1},
|
|
||||||
$set: {lastCommentedAt: now},
|
|
||||||
$addToSet: {commenters: user._id}
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.call('upvoteComment', comment);
|
|
||||||
|
|
||||||
return comment;
|
|
||||||
},
|
},
|
||||||
removeComment: function(commentId){
|
removeComment: function(commentId){
|
||||||
var comment = Comments.findOne(commentId);
|
var comment = Comments.findOne(commentId);
|
||||||
|
|
|
@ -251,7 +251,8 @@ getPostProperties = function (post) {
|
||||||
|
|
||||||
// default status for new posts
|
// default status for new posts
|
||||||
getDefaultPostStatus = function (user) {
|
getDefaultPostStatus = function (user) {
|
||||||
if (isAdmin(user) || !getSetting('requirePostsApproval', false)) {
|
var hasAdminRights = typeof user === 'undefined' ? false : isAdmin(user);
|
||||||
|
if (hasAdminRights || !getSetting('requirePostsApproval', false)) {
|
||||||
// if user is admin, or else post approval is not required
|
// if user is admin, or else post approval is not required
|
||||||
return STATUS_APPROVED
|
return STATUS_APPROVED
|
||||||
} else {
|
} else {
|
||||||
|
@ -273,19 +274,26 @@ getPostLink = function (post) {
|
||||||
return !!post.url ? getOutgoingUrl(post.url) : getPostPageUrl(post);
|
return !!post.url ? getOutgoingUrl(post.url) : getPostPageUrl(post);
|
||||||
};
|
};
|
||||||
|
|
||||||
checkForPostsWithSameUrl = function (url) {
|
// we need the current user so we know who to upvote the existing post as
|
||||||
|
checkForPostsWithSameUrl = function (url, currentUser) {
|
||||||
|
|
||||||
// check that there are no previous posts with the same link in the past 6 months
|
// check that there are no previous posts with the same link in the past 6 months
|
||||||
var sixMonthsAgo = moment().subtract(6, 'months').toDate();
|
var sixMonthsAgo = moment().subtract(6, 'months').toDate();
|
||||||
var postWithSameLink = Posts.findOne({url: url, postedAt: {$gte: sixMonthsAgo}});
|
var postWithSameLink = Posts.findOne({url: url, postedAt: {$gte: sixMonthsAgo}});
|
||||||
|
|
||||||
if(typeof postWithSameLink !== 'undefined'){
|
if(typeof postWithSameLink !== 'undefined'){
|
||||||
Meteor.call('upvotePost', postWithSameLink);
|
upvoteItem(Posts, postWithSameLink, currentUser);
|
||||||
|
|
||||||
// note: error.details returns undefined on the client, so add post ID to reason
|
// note: error.details returns undefined on the client, so add post ID to reason
|
||||||
throw new Meteor.Error('603', i18n.t('this_link_has_already_been_posted') + '|' + postWithSameLink._id, postWithSameLink._id);
|
throw new Meteor.Error('603', i18n.t('this_link_has_already_been_posted') + '|' + postWithSameLink._id, postWithSameLink._id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when on a post page, return the current post
|
||||||
|
currentPost = function () {
|
||||||
|
return Posts.findOne(Router.current().data()._id);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------- //
|
// ------------------------------------------------------------------------------------------- //
|
||||||
// ------------------------------------------ Hooks ------------------------------------------ //
|
// ------------------------------------------ Hooks ------------------------------------------ //
|
||||||
// ------------------------------------------------------------------------------------------- //
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
@ -303,6 +311,86 @@ Posts.before.update(function (userId, doc, fieldNames, modifier, options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
postAfterSubmitMethodCallbacks.push(function (post) {
|
||||||
|
|
||||||
|
var userId = post.userId,
|
||||||
|
postAuthor = Meteor.users.findOne(userId);
|
||||||
|
|
||||||
|
// increment posts count
|
||||||
|
Meteor.users.update({_id: userId}, {$inc: {postCount: 1}});
|
||||||
|
upvoteItem(Posts, post, postAuthor);
|
||||||
|
|
||||||
|
return post;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
// --------------------------------------- Submit Post --------------------------------------- //
|
||||||
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
submitPost = function (post) {
|
||||||
|
|
||||||
|
var userId = post.userId, // at this stage, a userId is expected
|
||||||
|
user = Meteor.users.findOne(userId);
|
||||||
|
|
||||||
|
// ------------------------------ Checks ------------------------------ //
|
||||||
|
|
||||||
|
// check that a title was provided
|
||||||
|
if(!post.title)
|
||||||
|
throw new Meteor.Error(602, i18n.t('please_fill_in_a_title'));
|
||||||
|
|
||||||
|
// check that there are no posts with the same URL
|
||||||
|
if(!!post.url)
|
||||||
|
checkForPostsWithSameUrl(post.url, user);
|
||||||
|
|
||||||
|
// ------------------------------ Properties ------------------------------ //
|
||||||
|
|
||||||
|
defaultProperties = {
|
||||||
|
createdAt: new Date(),
|
||||||
|
author: getDisplayNameById(userId),
|
||||||
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
|
commentCount: 0,
|
||||||
|
clickCount: 0,
|
||||||
|
viewCount: 0,
|
||||||
|
baseScore: 0,
|
||||||
|
score: 0,
|
||||||
|
inactive: false,
|
||||||
|
sticky: false,
|
||||||
|
status: getDefaultPostStatus(),
|
||||||
|
postedAt: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
post = _.extend(defaultProperties, post);
|
||||||
|
|
||||||
|
// clean up post title
|
||||||
|
post.title = cleanUp(post.title);
|
||||||
|
|
||||||
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
|
// run all post submit server callbacks on post object successively
|
||||||
|
post = postSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, post);
|
||||||
|
|
||||||
|
// -------------------------------- Insert ------------------------------- //
|
||||||
|
|
||||||
|
post._id = Posts.insert(post);
|
||||||
|
|
||||||
|
// --------------------- Server-Side Async Callbacks --------------------- //
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.defer(function () { // use defer to avoid holding up client
|
||||||
|
// run all post submit server callbacks on post object successively
|
||||||
|
post = postAfterSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
|
return currentFunction(result);
|
||||||
|
}, post);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------- //
|
// ------------------------------------------------------------------------------------------- //
|
||||||
// ----------------------------------------- Methods ----------------------------------------- //
|
// ----------------------------------------- Methods ----------------------------------------- //
|
||||||
// ------------------------------------------------------------------------------------------- //
|
// ------------------------------------------------------------------------------------------- //
|
||||||
|
@ -313,15 +401,19 @@ postViews = [];
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
|
||||||
submitPost: function(post){
|
submitPost: function(post){
|
||||||
var title = cleanUp(post.title),
|
|
||||||
body = post.body,
|
// required properties:
|
||||||
userId = this.userId,
|
// title
|
||||||
user = Meteor.users.findOne(userId),
|
|
||||||
timeSinceLastPost=timeSinceLast(user, Posts),
|
// optional properties
|
||||||
numberOfPostsInPast24Hours=numberOfItemsInPast24Hours(user, Posts),
|
// URL
|
||||||
postInterval = Math.abs(parseInt(getSetting('postInterval', 30))),
|
// body
|
||||||
maxPostsPer24Hours = Math.abs(parseInt(getSetting('maxPostsPerDay', 30))),
|
// categories
|
||||||
postId = '';
|
// thumbnailUrl
|
||||||
|
|
||||||
|
// NOTE: the current user and the post author user might be two different users!
|
||||||
|
var user = Meteor.user(),
|
||||||
|
hasAdminRights = isAdmin(user);
|
||||||
|
|
||||||
// ------------------------------ Checks ------------------------------ //
|
// ------------------------------ Checks ------------------------------ //
|
||||||
|
|
||||||
|
@ -329,109 +421,58 @@ Meteor.methods({
|
||||||
if (!user || !canPost(user))
|
if (!user || !canPost(user))
|
||||||
throw new Meteor.Error(601, i18n.t('you_need_to_login_or_be_invited_to_post_new_stories'));
|
throw new Meteor.Error(601, i18n.t('you_need_to_login_or_be_invited_to_post_new_stories'));
|
||||||
|
|
||||||
// check that user provided a title
|
|
||||||
if(!post.title)
|
|
||||||
throw new Meteor.Error(602, i18n.t('please_fill_in_a_title'));
|
|
||||||
|
|
||||||
// check that there are no posts with the same URL
|
|
||||||
if(!!post.url)
|
|
||||||
checkForPostsWithSameUrl(post.url);
|
|
||||||
|
|
||||||
// --------------------------- Rate Limiting -------------------------- //
|
// --------------------------- Rate Limiting -------------------------- //
|
||||||
|
|
||||||
if(!isAdmin(Meteor.user())){
|
if(!hasAdminRights){
|
||||||
|
|
||||||
|
var timeSinceLastPost=timeSinceLast(user, Posts),
|
||||||
|
numberOfPostsInPast24Hours=numberOfItemsInPast24Hours(user, Posts),
|
||||||
|
postInterval = Math.abs(parseInt(getSetting('postInterval', 30))),
|
||||||
|
maxPostsPer24Hours = Math.abs(parseInt(getSetting('maxPostsPerDay', 30)));
|
||||||
|
|
||||||
// check that user waits more than X seconds between posts
|
// check that user waits more than X seconds between posts
|
||||||
if(!this.isSimulation && timeSinceLastPost < postInterval)
|
if(timeSinceLastPost < postInterval)
|
||||||
throw new Meteor.Error(604, i18n.t('please_wait')+(postInterval-timeSinceLastPost)+i18n.t('seconds_before_posting_again'));
|
throw new Meteor.Error(604, i18n.t('please_wait')+(postInterval-timeSinceLastPost)+i18n.t('seconds_before_posting_again'));
|
||||||
|
|
||||||
// check that the user doesn't post more than Y posts per day
|
// check that the user doesn't post more than Y posts per day
|
||||||
if(!this.isSimulation && numberOfPostsInPast24Hours > maxPostsPer24Hours)
|
if(numberOfPostsInPast24Hours > maxPostsPer24Hours)
|
||||||
throw new Meteor.Error(605, i18n.t('sorry_you_cannot_submit_more_than')+maxPostsPer24Hours+i18n.t('posts_per_day'));
|
throw new Meteor.Error(605, i18n.t('sorry_you_cannot_submit_more_than')+maxPostsPer24Hours+i18n.t('posts_per_day'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ Properties ------------------------------ //
|
// ------------------------------ Properties ------------------------------ //
|
||||||
|
|
||||||
// Basic Properties
|
// admin-only properties
|
||||||
properties = {
|
// status
|
||||||
title: title,
|
// postedAt
|
||||||
body: body,
|
// userId
|
||||||
userId: userId,
|
// sticky (default to false)
|
||||||
author: getDisplayNameById(userId),
|
|
||||||
upvotes: 0,
|
|
||||||
downvotes: 0,
|
|
||||||
commentCount: 0,
|
|
||||||
clickCount: 0,
|
|
||||||
viewCount: 0,
|
|
||||||
baseScore: 0,
|
|
||||||
score: 0,
|
|
||||||
inactive: false
|
|
||||||
};
|
|
||||||
|
|
||||||
// UserId
|
// if user is not admin, clear restricted properties
|
||||||
if(isAdmin(Meteor.user()) && !!post.userId){ // only let admins post as other users
|
if (!hasAdminRights) {
|
||||||
properties.userId = post.userId;
|
delete post.status;
|
||||||
|
delete post.postedAt;
|
||||||
|
delete post.userId;
|
||||||
|
delete post.sticky;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status
|
// if no post status has been set, set it now
|
||||||
if(!!post.status && isAdmin(Meteor.user())){
|
if (!post.status) {
|
||||||
// if a custom status has been set, and user is admin, use that
|
post.status = getDefaultPostStatus(user);
|
||||||
properties.status = post.status;
|
|
||||||
}else{
|
|
||||||
// else use default status
|
|
||||||
properties.status = getDefaultPostStatus(Meteor.user());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatedAt
|
// if no userId has been set, default to current user id
|
||||||
properties.createdAt = new Date();
|
if (!post.userId) {
|
||||||
|
post.userId = user._id
|
||||||
// PostedAt
|
|
||||||
if(properties.status == 2){ // only set postedAt if post is approved
|
|
||||||
if(isAdmin(Meteor.user()) && !!post.postedAt){ // if user is admin and a custom postDate has been set
|
|
||||||
properties.postedAt = post.postedAt;
|
|
||||||
}else{ // else use current time
|
|
||||||
properties.postedAt = new Date();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
post = _.extend(post, properties);
|
return submitPost(post);
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
|
||||||
|
|
||||||
// run all post submit server callbacks on post object successively
|
|
||||||
post = postSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
|
||||||
return currentFunction(result);
|
|
||||||
}, post);
|
|
||||||
|
|
||||||
// ------------------------------ Insert ------------------------------ //
|
|
||||||
|
|
||||||
// console.log(post)
|
|
||||||
post._id = Posts.insert(post);
|
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
|
||||||
|
|
||||||
// run all post submit server callbacks on post object successively
|
|
||||||
post = postAfterSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
|
||||||
return currentFunction(result);
|
|
||||||
}, post);
|
|
||||||
|
|
||||||
// ------------------------------ After Insert ------------------------------ //
|
|
||||||
|
|
||||||
// increment posts count
|
|
||||||
Meteor.users.update({_id: userId}, {$inc: {postCount: 1}});
|
|
||||||
|
|
||||||
var postAuthor = Meteor.users.findOne(post.userId);
|
|
||||||
|
|
||||||
Meteor.call('upvotePost', post, postAuthor);
|
|
||||||
|
|
||||||
return post;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
editPost: function (postId, updateObject) {
|
editPost: function (post, modifier, postId) {
|
||||||
|
|
||||||
var user = Meteor.user();
|
var user = Meteor.user();
|
||||||
|
|
||||||
// console.log(updateObject)
|
|
||||||
|
|
||||||
// ------------------------------ Checks ------------------------------ //
|
// ------------------------------ Checks ------------------------------ //
|
||||||
|
|
||||||
// check that user can edit
|
// check that user can edit
|
||||||
|
@ -440,23 +481,21 @@ Meteor.methods({
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
// run all post submit server callbacks on updateObject successively
|
// run all post submit server callbacks on modifier successively
|
||||||
updateObject = postEditMethodCallbacks.reduce(function(result, currentFunction) {
|
modifier = postEditMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
return currentFunction(result);
|
return currentFunction(result);
|
||||||
}, updateObject);
|
}, modifier);
|
||||||
|
|
||||||
console.log(updateObject)
|
|
||||||
|
|
||||||
// ------------------------------ Update ------------------------------ //
|
// ------------------------------ Update ------------------------------ //
|
||||||
|
|
||||||
Posts.update(postId, updateObject);
|
Posts.update(postId, modifier);
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
// run all post submit server callbacks on updateObject successively
|
// run all post submit server callbacks on modifier object successively
|
||||||
updateObject = postAfterEditMethodCallbacks.reduce(function(result, currentFunction) {
|
modifier = postAfterEditMethodCallbacks.reduce(function(result, currentFunction) {
|
||||||
return currentFunction(result);
|
return currentFunction(result);
|
||||||
}, updateObject);
|
}, modifier);
|
||||||
|
|
||||||
// ------------------------------ After Update ------------------------------ //
|
// ------------------------------ After Update ------------------------------ //
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ getPostCommentUrl = function(postId, commentId){
|
||||||
slugify = function(text) {
|
slugify = function(text) {
|
||||||
if(text){
|
if(text){
|
||||||
text = text.replace(/[^-_a-zA-Z0-9,&\s]+/ig, '');
|
text = text.replace(/[^-_a-zA-Z0-9,&\s]+/ig, '');
|
||||||
text = text.replace(/\s/gi, "+");
|
text = text.replace(/\s/gi, "-");
|
||||||
text = text.toLowerCase();
|
text = text.toLowerCase();
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// getPostsParameters gives an object containing the appropriate find and options arguments for the subscriptions's Posts.find()
|
// getPostsParameters gives an object containing the appropriate find and options arguments for the subscriptions's Posts.find()
|
||||||
|
|
||||||
getPostsParameters = function (terms) {
|
getPostsParameters = function (terms, user) {
|
||||||
|
|
||||||
|
var hasAdminRights = typeof user !== 'undefined' && isAdmin(user);
|
||||||
|
var maxLimit = 200;
|
||||||
|
|
||||||
// console.log(terms)
|
// console.log(terms)
|
||||||
|
|
||||||
|
@ -21,9 +24,14 @@ getPostsParameters = function (terms) {
|
||||||
deepExtend(true, parameters, {options: {sort: {_id: -1}}});
|
deepExtend(true, parameters, {options: {sort: {_id: -1}}});
|
||||||
|
|
||||||
// if there is a limit, add it too (note: limit=0 means "no limit")
|
// if there is a limit, add it too (note: limit=0 means "no limit")
|
||||||
if (typeof terms.limit !== 'undefined')
|
if (typeof terms.limit !== 'undefined')
|
||||||
_.extend(parameters.options, {limit: parseInt(terms.limit)});
|
_.extend(parameters.options, {limit: parseInt(terms.limit)});
|
||||||
|
|
||||||
|
// limit to "maxLimit" posts at most for non-admin users
|
||||||
|
if(!hasAdminRights && (parameters.options.limit == 0 || parameters.options.limit > maxLimit || !parameters.options.limit)) {
|
||||||
|
parameters.options.limit = maxLimit;
|
||||||
|
}
|
||||||
|
|
||||||
// hide future scheduled posts unless "showFuture" is set to true or postedAt is already defined
|
// hide future scheduled posts unless "showFuture" is set to true or postedAt is already defined
|
||||||
if (!parameters.showFuture && !parameters.find.postedAt)
|
if (!parameters.showFuture && !parameters.find.postedAt)
|
||||||
parameters.find.postedAt = {$lte: new Date()};
|
parameters.find.postedAt = {$lte: new Date()};
|
||||||
|
|
|
@ -4,7 +4,7 @@ Meteor.startup(function (){
|
||||||
|
|
||||||
Router.route('/settings', {
|
Router.route('/settings', {
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
template: getTemplate('settings'),
|
template: getTemplate('settingsForm'),
|
||||||
data: function () {
|
data: function () {
|
||||||
// we only have one set of settings for now
|
// we only have one set of settings for now
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -114,7 +114,8 @@ PostPageController = RouteController.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getTitle: function () {
|
getTitle: function () {
|
||||||
return this.post().title + ' - ' + getSetting('title');
|
if (!!this.post())
|
||||||
|
return this.post().title + ' - ' + getSetting('title');
|
||||||
},
|
},
|
||||||
|
|
||||||
onBeforeAction: function() {
|
onBeforeAction: function() {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
telescopeVersion = "0.11.0";
|
telescopeVersion = "0.11.1";
|
411
lib/vote.js
411
lib/vote.js
|
@ -1,226 +1,225 @@
|
||||||
|
|
||||||
// returns how much "power" a user's votes have
|
// returns how much "power" a user's votes have
|
||||||
var getVotePower = function (user) {
|
var getVotePower = function (user) {
|
||||||
// return isAdmin(user) ? 5 : 1;
|
// return isAdmin(user) ? 5 : 1;
|
||||||
return 1; // for now, leave everybody at 1 including admins; 5 is too unbalanced
|
return 1; // for now, leave everybody at 1 including admins; 5 is too unbalanced
|
||||||
};
|
};
|
||||||
|
|
||||||
var modifyKarma = function (userId, karma) {
|
var modifyKarma = function (userId, karma) {
|
||||||
Meteor.users.update({_id: userId}, {$inc: {karma: karma}});
|
Meteor.users.update({_id: userId}, {$inc: {karma: karma}});
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasUpvotedItem = function (item, user) {
|
var hasUpvotedItem = function (item, user) {
|
||||||
return item.upvoters && item.upvoters.indexOf(user._id) != -1;
|
return item.upvoters && item.upvoters.indexOf(user._id) != -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasDownvotedItem = function (item, user) {
|
var hasDownvotedItem = function (item, user) {
|
||||||
return item.downvoters && item.downvoters.indexOf(user._id) != -1;
|
return item.downvoters && item.downvoters.indexOf(user._id) != -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
var addVote = function (userId, vote, collection, upOrDown) {
|
var addVote = function (userId, vote, collection, upOrDown) {
|
||||||
var field = 'votes.' + upOrDown + 'voted' + collection;
|
var field = 'votes.' + upOrDown + 'voted' + collection;
|
||||||
var add = {};
|
var add = {};
|
||||||
add[field] = vote;
|
add[field] = vote;
|
||||||
var result = Meteor.users.update({_id: userId}, {
|
var result = Meteor.users.update({_id: userId}, {
|
||||||
$addToSet: add
|
$addToSet: add
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var removeVote = function (userId, itemId, collection, upOrDown) {
|
var removeVote = function (userId, itemId, collection, upOrDown) {
|
||||||
var field = 'votes.' + upOrDown + 'voted' + collection;
|
var field = 'votes.' + upOrDown + 'voted' + collection;
|
||||||
var remove = {};
|
var remove = {};
|
||||||
remove[field] = {itemId: itemId};
|
remove[field] = {itemId: itemId};
|
||||||
Meteor.users.update({_id: userId}, {
|
Meteor.users.update({_id: userId}, {
|
||||||
$pull: remove
|
$pull: remove
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var upvoteItem = function (collection, item) {
|
upvoteItem = function (collection, item, user) {
|
||||||
var user = Meteor.user(),
|
var user = typeof user === "undefined" ? Meteor.user() : user,
|
||||||
votePower = getVotePower(user),
|
votePower = getVotePower(user),
|
||||||
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
||||||
|
|
||||||
|
// make sure user has rights to upvote first
|
||||||
|
if (!user || !canUpvote(user, collection, true) || hasUpvotedItem(item, user))
|
||||||
|
return false;
|
||||||
|
|
||||||
// make sure user has rights to upvote first
|
// in case user is upvoting a previously downvoted item, cancel downvote first
|
||||||
if (!user || !canUpvote(user, collection, true) || hasUpvotedItem(item, user))
|
cancelDownvote(collection, item, user);
|
||||||
return false;
|
|
||||||
|
|
||||||
// in case user is upvoting a previously downvoted item, cancel downvote first
|
// Votes & Score
|
||||||
cancelDownvote(collection, item, user);
|
var result = collection.update({_id: item && item._id, upvoters: { $ne: user._id }},{
|
||||||
|
$addToSet: {upvoters: user._id},
|
||||||
|
$inc: {upvotes: 1, baseScore: votePower},
|
||||||
|
$set: {inactive: false}
|
||||||
|
});
|
||||||
|
|
||||||
// Votes & Score
|
if (result > 0) {
|
||||||
var result = collection.update({_id: item && item._id, upvoters: { $ne: user._id }},{
|
// Add item to list of upvoted items
|
||||||
$addToSet: {upvoters: user._id},
|
var vote = {
|
||||||
$inc: {upvotes: 1, baseScore: votePower},
|
itemId: item._id,
|
||||||
$set: {inactive: false}
|
votedAt: new Date(),
|
||||||
});
|
power: votePower
|
||||||
|
};
|
||||||
|
addVote(user._id, vote, collectionName, 'up');
|
||||||
|
|
||||||
if (result > 0) {
|
// extend item with baseScore to help calculate newScore
|
||||||
// Add item to list of upvoted items
|
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
||||||
var vote = {
|
updateScore({collection: collection, item: item, forceUpdate: true});
|
||||||
itemId: item._id,
|
|
||||||
votedAt: new Date(),
|
|
||||||
power: votePower
|
|
||||||
};
|
|
||||||
addVote(user._id, vote, collectionName, 'up');
|
|
||||||
|
|
||||||
// extend item with baseScore to help calculate newScore
|
// if the item is being upvoted by its own author, don't give karma
|
||||||
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
if (item.userId != user._id) {
|
||||||
updateScore({collection: collection, item: item, forceUpdate: true});
|
modifyKarma(item.userId, votePower);
|
||||||
|
|
||||||
// if the item is being upvoted by its own author, don't give karma
|
// if karma redistribution is enabled, give karma to all previous upvoters of the post
|
||||||
if (item.userId != user._id) {
|
// (but not to the person doing the upvoting)
|
||||||
modifyKarma(item.userId, votePower);
|
if (getSetting('redistributeKarma', false)) {
|
||||||
|
_.each(item.upvoters, function (upvoterId) {
|
||||||
// if karma redistribution is enabled, give karma to all previous upvoters of the post
|
// share the karma equally among all upvoters, but cap the value at 0.1
|
||||||
// (but not to the person doing the upvoting)
|
var karmaIncrease = Math.min(0.1, votePower/item.upvoters.length);
|
||||||
if (getSetting('redistributeKarma', false)) {
|
modifyKarma(upvoterId, 0.1);
|
||||||
_.each(item.upvoters, function (upvoterId) {
|
});
|
||||||
// share the karma equally among all upvoters, but cap the value at 0.1
|
|
||||||
var karmaIncrease = Math.min(0.1, votePower/item.upvoters.length);
|
|
||||||
modifyKarma(upvoterId, 0.1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log(collection.findOne(item._id));
|
}
|
||||||
return true;
|
// console.log(collection.findOne(item._id));
|
||||||
};
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
var downvoteItem = function (collection, item) {
|
downvoteItem = function (collection, item, user) {
|
||||||
var user = Meteor.user(),
|
var user = typeof user === "undefined" ? Meteor.user() : user,
|
||||||
votePower = getVotePower(user),
|
votePower = getVotePower(user),
|
||||||
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
||||||
|
|
||||||
// make sure user has rights to downvote first
|
// make sure user has rights to downvote first
|
||||||
if (!user || !canDownvote(user, collection, true) || hasDownvotedItem(item, user))
|
if (!user || !canDownvote(user, collection, true) || hasDownvotedItem(item, user))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// in case user is downvoting a previously upvoted item, cancel upvote first
|
// in case user is downvoting a previously upvoted item, cancel upvote first
|
||||||
cancelUpvote(collection, item, user);
|
cancelUpvote(collection, item, user);
|
||||||
|
|
||||||
// Votes & Score
|
// Votes & Score
|
||||||
var result = collection.update({_id: item && item._id, downvoters: { $ne: user._id }},{
|
var result = collection.update({_id: item && item._id, downvoters: { $ne: user._id }},{
|
||||||
$addToSet: {downvoters: user._id},
|
$addToSet: {downvoters: user._id},
|
||||||
$inc: {downvotes: 1, baseScore: -votePower},
|
$inc: {downvotes: 1, baseScore: -votePower},
|
||||||
$set: {inactive: false}
|
$set: {inactive: false}
|
||||||
});
|
|
||||||
|
|
||||||
if (result > 0) {
|
|
||||||
// Add item to list of downvoted items
|
|
||||||
var vote = {
|
|
||||||
itemId: item._id,
|
|
||||||
votedAt: new Date(),
|
|
||||||
power: votePower
|
|
||||||
};
|
|
||||||
addVote(user._id, vote, collectionName, 'down');
|
|
||||||
|
|
||||||
// extend item with baseScore to help calculate newScore
|
|
||||||
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
|
||||||
updateScore({collection: collection, item: item, forceUpdate: true});
|
|
||||||
|
|
||||||
// if the item is being upvoted by its own author, don't give karma
|
|
||||||
if (item.userId != user._id)
|
|
||||||
modifyKarma(item.userId, votePower);
|
|
||||||
}
|
|
||||||
// console.log(collection.findOne(item._id));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var cancelUpvote = function (collection, item) {
|
|
||||||
var user = Meteor.user(),
|
|
||||||
votePower = getVotePower(user),
|
|
||||||
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
|
||||||
|
|
||||||
// if user isn't among the upvoters, abort
|
|
||||||
if (!hasUpvotedItem(item, user))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Votes & Score
|
|
||||||
var result = collection.update({_id: item && item._id, upvoters: user._id},{
|
|
||||||
$pull: {upvoters: user._id},
|
|
||||||
$inc: {upvotes: -1, baseScore: -votePower},
|
|
||||||
$set: {inactive: false}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result > 0) {
|
|
||||||
// Remove item from list of upvoted items
|
|
||||||
removeVote(user._id, item._id, collectionName, 'up');
|
|
||||||
|
|
||||||
// extend item with baseScore to help calculate newScore
|
|
||||||
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
|
||||||
updateScore({collection: collection, item: item, forceUpdate: true});
|
|
||||||
|
|
||||||
// if the item is being upvoted by its own author, don't give karma
|
|
||||||
if (item.userId != user._id)
|
|
||||||
modifyKarma(item.userId, votePower);
|
|
||||||
}
|
|
||||||
// console.log(collection.findOne(item._id));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var cancelDownvote = function (collection, item) {
|
|
||||||
var user = Meteor.user(),
|
|
||||||
votePower = getVotePower(user),
|
|
||||||
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
|
||||||
|
|
||||||
// if user isn't among the downvoters, abort
|
|
||||||
if (!hasDownvotedItem(item, user))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Votes & Score
|
|
||||||
var result = collection.update({_id: item && item._id, downvoters: user._id},{
|
|
||||||
$pull: {downvoters: user._id},
|
|
||||||
$inc: {downvotes: 1, baseScore: votePower},
|
|
||||||
$set: {inactive: false}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result > 0) {
|
|
||||||
// Remove item from list of downvoted items
|
|
||||||
removeVote(user._id, item._id, collectionName, 'down');
|
|
||||||
|
|
||||||
// extend item with baseScore to help calculate newScore
|
|
||||||
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
|
||||||
updateScore({collection: collection, item: item, forceUpdate: true});
|
|
||||||
|
|
||||||
// if the item is being upvoted by its own author, don't give karma
|
|
||||||
if (item.userId != user._id)
|
|
||||||
modifyKarma(item.userId, votePower);
|
|
||||||
}
|
|
||||||
// console.log(collection.findOne(item._id));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// note: doesn't actually seem very useful to enable admins to vote for other users. Remove this?
|
|
||||||
var getUser = function (user) {
|
|
||||||
// only let admins specify different users for voting
|
|
||||||
// if no user is specified, use current user by default
|
|
||||||
return (isAdmin(Meteor.user()) && typeof user !== 'undefined') ? user : Meteor.user();
|
|
||||||
};
|
|
||||||
|
|
||||||
Meteor.methods({
|
|
||||||
upvotePost: function (post, user) {
|
|
||||||
return upvoteItem.call(this, Posts, post);
|
|
||||||
},
|
|
||||||
downvotePost: function (post, user) {
|
|
||||||
return downvoteItem.call(this, Posts, post);
|
|
||||||
},
|
|
||||||
cancelUpvotePost: function (post, user) {
|
|
||||||
return cancelUpvote.call(this, Posts, post);
|
|
||||||
},
|
|
||||||
cancelDownvotePost: function (post, user) {
|
|
||||||
return cancelDownvote.call(this, Posts, post);
|
|
||||||
},
|
|
||||||
upvoteComment: function (comment, user) {
|
|
||||||
return upvoteItem.call(this, Comments, comment);
|
|
||||||
},
|
|
||||||
downvoteComment: function (comment, user) {
|
|
||||||
return downvoteItem.call(this, Comments, comment);
|
|
||||||
},
|
|
||||||
cancelUpvoteComment: function (comment, user) {
|
|
||||||
return cancelUpvote.call(this, Comments, comment);
|
|
||||||
},
|
|
||||||
cancelDownvoteComment: function (comment, user) {
|
|
||||||
return cancelDownvote.call(this, Comments, comment);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
// Add item to list of downvoted items
|
||||||
|
var vote = {
|
||||||
|
itemId: item._id,
|
||||||
|
votedAt: new Date(),
|
||||||
|
power: votePower
|
||||||
|
};
|
||||||
|
addVote(user._id, vote, collectionName, 'down');
|
||||||
|
|
||||||
|
// extend item with baseScore to help calculate newScore
|
||||||
|
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
||||||
|
updateScore({collection: collection, item: item, forceUpdate: true});
|
||||||
|
|
||||||
|
// if the item is being upvoted by its own author, don't give karma
|
||||||
|
if (item.userId != user._id)
|
||||||
|
modifyKarma(item.userId, votePower);
|
||||||
|
}
|
||||||
|
// console.log(collection.findOne(item._id));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
cancelUpvote = function (collection, item, user) {
|
||||||
|
var user = typeof user === "undefined" ? Meteor.user() : user,
|
||||||
|
votePower = getVotePower(user),
|
||||||
|
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
||||||
|
|
||||||
|
// if user isn't among the upvoters, abort
|
||||||
|
if (!hasUpvotedItem(item, user))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Votes & Score
|
||||||
|
var result = collection.update({_id: item && item._id, upvoters: user._id},{
|
||||||
|
$pull: {upvoters: user._id},
|
||||||
|
$inc: {upvotes: -1, baseScore: -votePower},
|
||||||
|
$set: {inactive: false}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
// Remove item from list of upvoted items
|
||||||
|
removeVote(user._id, item._id, collectionName, 'up');
|
||||||
|
|
||||||
|
// extend item with baseScore to help calculate newScore
|
||||||
|
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
||||||
|
updateScore({collection: collection, item: item, forceUpdate: true});
|
||||||
|
|
||||||
|
// if the item is being upvoted by its own author, don't give karma
|
||||||
|
if (item.userId != user._id)
|
||||||
|
modifyKarma(item.userId, votePower);
|
||||||
|
}
|
||||||
|
// console.log(collection.findOne(item._id));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
cancelDownvote = function (collection, item, user) {
|
||||||
|
var user = typeof user === "undefined" ? Meteor.user() : user,
|
||||||
|
votePower = getVotePower(user),
|
||||||
|
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
|
||||||
|
|
||||||
|
// if user isn't among the downvoters, abort
|
||||||
|
if (!hasDownvotedItem(item, user))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Votes & Score
|
||||||
|
var result = collection.update({_id: item && item._id, downvoters: user._id},{
|
||||||
|
$pull: {downvoters: user._id},
|
||||||
|
$inc: {downvotes: 1, baseScore: votePower},
|
||||||
|
$set: {inactive: false}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
// Remove item from list of downvoted items
|
||||||
|
removeVote(user._id, item._id, collectionName, 'down');
|
||||||
|
|
||||||
|
// extend item with baseScore to help calculate newScore
|
||||||
|
item = _.extend(item, {baseScore: (item.baseScore + votePower)});
|
||||||
|
updateScore({collection: collection, item: item, forceUpdate: true});
|
||||||
|
|
||||||
|
// if the item is being upvoted by its own author, don't give karma
|
||||||
|
if (item.userId != user._id)
|
||||||
|
modifyKarma(item.userId, votePower);
|
||||||
|
}
|
||||||
|
// console.log(collection.findOne(item._id));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// note: doesn't actually seem very useful to enable admins to vote for other users. Remove this?
|
||||||
|
var getUser = function (user) {
|
||||||
|
// only let admins specify different users for voting
|
||||||
|
// if no user is specified, use current user by default
|
||||||
|
return (isAdmin(Meteor.user()) && typeof user !== 'undefined') ? user : Meteor.user();
|
||||||
|
};
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
upvotePost: function (post) {
|
||||||
|
return upvoteItem.call(this, Posts, post);
|
||||||
|
},
|
||||||
|
downvotePost: function (post) {
|
||||||
|
return downvoteItem.call(this, Posts, post);
|
||||||
|
},
|
||||||
|
cancelUpvotePost: function (post) {
|
||||||
|
return cancelUpvote.call(this, Posts, post);
|
||||||
|
},
|
||||||
|
cancelDownvotePost: function (post) {
|
||||||
|
return cancelDownvote.call(this, Posts, post);
|
||||||
|
},
|
||||||
|
upvoteComment: function (comment) {
|
||||||
|
return upvoteItem.call(this, Comments, comment);
|
||||||
|
},
|
||||||
|
downvoteComment: function (comment) {
|
||||||
|
return downvoteItem.call(this, Comments, comment);
|
||||||
|
},
|
||||||
|
cancelUpvoteComment: function (comment) {
|
||||||
|
return cancelUpvote.call(this, Comments, comment);
|
||||||
|
},
|
||||||
|
cancelDownvoteComment: function (comment) {
|
||||||
|
return cancelDownvote.call(this, Comments, comment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -224,15 +224,13 @@ postMeta = [
|
||||||
]
|
]
|
||||||
// ------------------------------ Callbacks ------------------------------ //
|
// ------------------------------ Callbacks ------------------------------ //
|
||||||
|
|
||||||
postSubmitRenderedCallbacks = [];
|
|
||||||
postSubmitClientCallbacks = [];
|
postSubmitClientCallbacks = [];
|
||||||
postSubmitMethodCallbacks = [];
|
postSubmitMethodCallbacks = [];
|
||||||
postAfterSubmitMethodCallbacks = [];
|
postAfterSubmitMethodCallbacks = []; // runs on server only in a timeout
|
||||||
|
|
||||||
postEditRenderedCallbacks = [];
|
postEditClientCallbacks = []; // loops over post object
|
||||||
postEditClientCallbacks = [];
|
postEditMethodCallbacks = []; // loops over modifier (i.e. "{$set: {foo: bar}}") object
|
||||||
postEditMethodCallbacks = []; // not used yet
|
postAfterEditMethodCallbacks = []; // loops over modifier object
|
||||||
postAfterEditMethodCallbacks = []; // not used yet
|
|
||||||
|
|
||||||
commentSubmitRenderedCallbacks = [];
|
commentSubmitRenderedCallbacks = [];
|
||||||
commentSubmitClientCallbacks = [];
|
commentSubmitClientCallbacks = [];
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -86,19 +86,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -7,7 +7,7 @@ Package.describe({
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function(api) {
|
||||||
api.use('templating@1.0.0');
|
api.use('templating@1.0.0');
|
||||||
api.use('blaze@2.0.0');
|
api.use('blaze@2.0.0');
|
||||||
api.use('aldeed:autoform@4.0.0');
|
api.use('aldeed:autoform');
|
||||||
api.use('fourseven:scss');
|
api.use('fourseven:scss');
|
||||||
// api.use('jquery');
|
// api.use('jquery');
|
||||||
// api.use('tsega:bootstrap3-datetimepicker');
|
// api.use('tsega:bootstrap3-datetimepicker');
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:autoform",
|
"aldeed:autoform",
|
||||||
"4.1.0"
|
"4.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"base64",
|
"base64",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -86,19 +86,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -40,7 +40,7 @@ $small-break: 30em;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
margin: -18px 0 0 -18px;
|
margin: -18px 0 0 -18px;
|
||||||
line-height: 32px;
|
line-height: 38px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: white;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -51,16 +51,48 @@ Meteor.methods({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// For security reason, we use a separate server-side API call to set the media object
|
// For security reason, we use a separate server-side API call to set the media object,
|
||||||
var addMediaOnSubmit = function (post) {
|
// and the thumbnail object if it hasn't already been set
|
||||||
|
|
||||||
|
// note: the following function is not used because it would hold up the post submission, use next one instead
|
||||||
|
// var addMediaOnSubmit = function (post) {
|
||||||
|
// if(post.url){
|
||||||
|
// var data = getEmbedlyData(post.url);
|
||||||
|
// if (!!data) {
|
||||||
|
// // only add a thumbnailUrl if there isn't one already
|
||||||
|
// if(!post.thumbnailUrl && !!data.thumbnailUrl)
|
||||||
|
// post.thumbnailUrl = data.thumbnailUrl
|
||||||
|
// // add media if necessary
|
||||||
|
// if(!!data.media.html)
|
||||||
|
// post.media = data.media
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return post;
|
||||||
|
// }
|
||||||
|
// postSubmitMethodCallbacks.push(addMediaOnSubmit);
|
||||||
|
|
||||||
|
// Async variant that directly modifies the post object with update()
|
||||||
|
var addMediaAfterSubmit = function (post) {
|
||||||
|
var set = {};
|
||||||
if(post.url){
|
if(post.url){
|
||||||
var data = getEmbedlyData(post.url);
|
var data = getEmbedlyData(post.url);
|
||||||
if(!!data && !!data.media.html)
|
if (!!data) {
|
||||||
post.media = data.media
|
// only add a thumbnailUrl if there isn't one already
|
||||||
|
if (!post.thumbnailUrl && !!data.thumbnailUrl) {
|
||||||
|
post.thumbnailUrl = data.thumbnailUrl;
|
||||||
|
set.thumbnailUrl = data.thumbnailUrl;
|
||||||
|
}
|
||||||
|
// add media if necessary
|
||||||
|
if (!!data.media.html) {
|
||||||
|
post.media = data.media;
|
||||||
|
set.media = data.media;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Posts.update(post._id, {$set: set});
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
postSubmitMethodCallbacks.push(addMediaOnSubmit);
|
postAfterSubmitMethodCallbacks.push(addMediaAfterSubmit);
|
||||||
|
|
||||||
// TODO: find a way to only do this is URL has actually changed?
|
// TODO: find a way to only do this is URL has actually changed?
|
||||||
var updateMediaOnEdit = function (updateObject) {
|
var updateMediaOnEdit = function (updateObject) {
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:autoform",
|
"aldeed:autoform",
|
||||||
"4.1.0"
|
"4.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:autoform",
|
"aldeed:autoform",
|
||||||
"4.1.0"
|
"4.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -82,19 +82,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -38,16 +38,10 @@ SyncedCron.add({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.startup(function() {
|
|
||||||
if(getSetting('newsletterFrequency', defaultFrequency) != 0) {
|
|
||||||
SyncedCron.start();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
getNextJob: function () {
|
getNextJob: function () {
|
||||||
var nextJob = SyncedCron.nextScheduledAtDate('scheduleNewsletter');
|
var nextJob = SyncedCron.nextScheduledAtDate('scheduleNewsletter');
|
||||||
console.log(nextJob);
|
console.log(nextJob);
|
||||||
return nextJob;
|
return nextJob;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -86,19 +86,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -14,7 +14,7 @@ commentAfterSubmitMethodCallbacks.push(function (comment) {
|
||||||
if(Meteor.isServer){
|
if(Meteor.isServer){
|
||||||
|
|
||||||
var parentCommentId = comment.parentCommentId;
|
var parentCommentId = comment.parentCommentId;
|
||||||
var user = Meteor.user();
|
var user = Meteor.users.findOne(comment.userId);
|
||||||
var post = Posts.findOne(comment.postId);
|
var post = Posts.findOne(comment.postId);
|
||||||
var postUser = Meteor.users.findOne(post.userId);
|
var postUser = Meteor.users.findOne(post.userId);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -94,19 +94,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
1
packages/telescope-post-by-feed/.gitignore
vendored
Normal file
1
packages/telescope-post-by-feed/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.build*
|
1
packages/telescope-post-by-feed/.npm/package/.gitignore
vendored
Normal file
1
packages/telescope-post-by-feed/.npm/package/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
7
packages/telescope-post-by-feed/.npm/package/README
Normal file
7
packages/telescope-post-by-feed/.npm/package/README
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
This directory and the files immediately inside it are automatically generated
|
||||||
|
when you change this package's NPM dependencies. Commit the files in this
|
||||||
|
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
|
||||||
|
so that others run the same versions of sub-dependencies.
|
||||||
|
|
||||||
|
You should NOT check in the node_modules directory that Meteor automatically
|
||||||
|
creates; if you are using git, the .gitignore file tells git to ignore it.
|
44
packages/telescope-post-by-feed/.npm/package/npm-shrinkwrap.json
generated
Normal file
44
packages/telescope-post-by-feed/.npm/package/npm-shrinkwrap.json
generated
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"he": {
|
||||||
|
"version": "0.5.0"
|
||||||
|
},
|
||||||
|
"htmlparser2": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"dependencies": {
|
||||||
|
"domhandler": {
|
||||||
|
"version": "2.3.0"
|
||||||
|
},
|
||||||
|
"domutils": {
|
||||||
|
"version": "1.5.0"
|
||||||
|
},
|
||||||
|
"domelementtype": {
|
||||||
|
"version": "1.1.3"
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.1"
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1"
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "0.10.31"
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entities": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"to-markdown": {
|
||||||
|
"version": "0.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
packages/telescope-post-by-feed/i18n/en.i18n.json
Normal file
5
packages/telescope-post-by-feed/i18n/en.i18n.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"feed_already_exists": "A feed with the same URL already exists.",
|
||||||
|
"you_need_to_login_and_be_an_admin_to_add_a_new_feed": "You need to log in and be an admin to add a new feed.",
|
||||||
|
"import_new_posts_from_feeds": "Import new posts from feeds."
|
||||||
|
}
|
21
packages/telescope-post-by-feed/lib/client/routes.js
Normal file
21
packages/telescope-post-by-feed/lib/client/routes.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
adminNav.push({
|
||||||
|
route: 'feeds',
|
||||||
|
label: 'Feeds',
|
||||||
|
description: 'import_new_posts_from_feeds'
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.startup(function () {
|
||||||
|
|
||||||
|
Router.onBeforeAction(Router._filters.isAdmin, {only: ['feeds']});
|
||||||
|
|
||||||
|
// RSS Urls Admin
|
||||||
|
|
||||||
|
Router.route('/feeds', {
|
||||||
|
name: 'feeds',
|
||||||
|
waitOn: function() {
|
||||||
|
return Meteor.subscribe('feeds');
|
||||||
|
},
|
||||||
|
template: getTemplate('feeds')
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
$light-yellow:#fffcea;
|
||||||
|
|
||||||
|
.add-feed{
|
||||||
|
background: $light-yellow;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<template name="feedItem">
|
||||||
|
<div class="grid-small grid-module">
|
||||||
|
{{> quickForm collection="Feeds" id=formId type="update" doc=this label-class="control-label" input-col-class="controls" template="telescope"}}
|
||||||
|
<a href="#" class="delete-link">Delete</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,16 @@
|
||||||
|
Meteor.startup(function () {
|
||||||
|
Template[getTemplate('feedItem')].helpers({
|
||||||
|
formId: function () {
|
||||||
|
return 'updateFeed-'+ this._id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template[getTemplate('feedItem')].events({
|
||||||
|
'click .delete-link': function(e, instance){
|
||||||
|
e.preventDefault();
|
||||||
|
if (confirm("Delete feed?")) {
|
||||||
|
Feeds.remove(instance.data._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template name="feeds">
|
||||||
|
<div class="grid-small grid-module dialog admin add-feed">
|
||||||
|
<h3>Add new feed:</h3>
|
||||||
|
{{> quickForm collection="Feeds" id="insertFeedForm" type="insert" label-class="control-label" input-col-class="controls" template="telescope"}}
|
||||||
|
</div>
|
||||||
|
{{#each feeds}}
|
||||||
|
{{> UI.dynamic template=feedItem}}
|
||||||
|
{{/each}}
|
||||||
|
</template>
|
|
@ -0,0 +1,28 @@
|
||||||
|
Meteor.startup(function () {
|
||||||
|
Template[getTemplate('feeds')].helpers({
|
||||||
|
feeds: function(){
|
||||||
|
return Feeds.find({}, {sort: {url: 1}});
|
||||||
|
},
|
||||||
|
feedItem: function () {
|
||||||
|
return getTemplate('feedItem');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template[getTemplate('feeds')].events({
|
||||||
|
'click input[type=submit]': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var url = $('#url').val();
|
||||||
|
|
||||||
|
Meteor.call('insertFeed', {url: url}, function(error, result) {
|
||||||
|
if(error){
|
||||||
|
console.log(error);
|
||||||
|
flashMessage(error.reason, "error");
|
||||||
|
clearSeenMessages();
|
||||||
|
}else{
|
||||||
|
$('#url').val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
59
packages/telescope-post-by-feed/lib/feeds.js
Normal file
59
packages/telescope-post-by-feed/lib/feeds.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
var feedSchema = new SimpleSchema({
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Url
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Feeds = new Meteor.Collection("feeds");
|
||||||
|
Feeds.attachSchema(feedSchema);
|
||||||
|
|
||||||
|
// used to keep track of which feed a post was imported from
|
||||||
|
var feedIdProperty = {
|
||||||
|
propertyName: 'feedId',
|
||||||
|
propertySchema: {
|
||||||
|
type: String,
|
||||||
|
label: 'feedId',
|
||||||
|
optional: true,
|
||||||
|
autoform: {
|
||||||
|
omit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addToPostSchema.push(feedIdProperty);
|
||||||
|
|
||||||
|
// the RSS ID of the post in its original feed
|
||||||
|
var feedItemIdProperty = {
|
||||||
|
propertyName: 'feedItemId',
|
||||||
|
propertySchema: {
|
||||||
|
type: String,
|
||||||
|
label: 'feedItemId',
|
||||||
|
optional: true,
|
||||||
|
autoform: {
|
||||||
|
omit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addToPostSchema.push(feedItemIdProperty);
|
||||||
|
|
||||||
|
Meteor.startup(function () {
|
||||||
|
Feeds.allow({
|
||||||
|
insert: isAdminById,
|
||||||
|
update: isAdminById,
|
||||||
|
remove: isAdminById
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
insertFeed: function(feedUrl){
|
||||||
|
check(feedUrl, feedSchema);
|
||||||
|
|
||||||
|
if (Feeds.findOne({url: feedSchema.url}))
|
||||||
|
throw new Meteor.Error('already-exists', i18n.t('feed_already_exists'));
|
||||||
|
|
||||||
|
if (!Meteor.user() || !isAdmin(Meteor.user()))
|
||||||
|
throw new Meteor.Error('login-required', i18n.t('you_need_to_login_and_be_an_admin_to_add_a_new_feed'));
|
||||||
|
|
||||||
|
return Feeds.insert(feedUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
9
packages/telescope-post-by-feed/lib/server/cron.js
Normal file
9
packages/telescope-post-by-feed/lib/server/cron.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
SyncedCron.add({
|
||||||
|
name: 'Post by RSS feed',
|
||||||
|
schedule: function(parser) {
|
||||||
|
return parser.text('every 30 minutes');
|
||||||
|
},
|
||||||
|
job: function() {
|
||||||
|
fetchFeeds();
|
||||||
|
}
|
||||||
|
});
|
77
packages/telescope-post-by-feed/lib/server/fetch_feeds.js
Normal file
77
packages/telescope-post-by-feed/lib/server/fetch_feeds.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
var htmlParser = Npm.require('htmlparser2');
|
||||||
|
var toMarkdown = Npm.require('to-markdown').toMarkdown;
|
||||||
|
var he = Npm.require('he')
|
||||||
|
|
||||||
|
var getFirstAdminUser = function() {
|
||||||
|
return Meteor.users.findOne({isAdmin: true}, {sort: {createdAt: 1}});
|
||||||
|
}
|
||||||
|
|
||||||
|
var handleFeed = function(error, feed) {
|
||||||
|
if (error) return;
|
||||||
|
|
||||||
|
var feedItems = _.first(feed.items, 20); // limit feed to 20 items just in case
|
||||||
|
|
||||||
|
clog('// Parsing RSS feed: '+ feed.title)
|
||||||
|
|
||||||
|
var newItemsCount = 0;
|
||||||
|
|
||||||
|
feedItems.forEach(function(item, index, array) {
|
||||||
|
|
||||||
|
// check if post already exists
|
||||||
|
if (!!Posts.findOne({feedItemId: item.id})) {
|
||||||
|
// clog('// Feed item already imported')
|
||||||
|
} else {
|
||||||
|
newItemsCount++;
|
||||||
|
|
||||||
|
var post = {
|
||||||
|
title: item.title,
|
||||||
|
body: toMarkdown(he.decode(item.description)),
|
||||||
|
url: item.link,
|
||||||
|
feedId: feed.id,
|
||||||
|
feedItemId: item.id,
|
||||||
|
userId: getFirstAdminUser()._id
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
submitPost(post);
|
||||||
|
} catch (error) {
|
||||||
|
// catch errors so they don't stop the loop
|
||||||
|
clog(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
clog('// Found ' + newItemsCount + ' new feed items')
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchFeeds = function() {
|
||||||
|
var content;
|
||||||
|
|
||||||
|
Feeds.find().forEach(function(feed) {
|
||||||
|
try {
|
||||||
|
content = HTTP.get(feed.url).content;
|
||||||
|
} catch (e) {
|
||||||
|
// just go to next url
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var feedHandler = new htmlParser.FeedHandler(handleFeed);
|
||||||
|
|
||||||
|
var parser = new htmlParser.Parser(feedHandler, {xmlMode: true});
|
||||||
|
parser.write(content);
|
||||||
|
parser.end()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
fetchFeeds: function () {
|
||||||
|
fetchFeeds();
|
||||||
|
},
|
||||||
|
testEntities: function (text) {
|
||||||
|
console.log(he.decode(text));
|
||||||
|
},
|
||||||
|
testToMarkdown: function (text) {
|
||||||
|
console.log(toMarkdown(text));
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,6 @@
|
||||||
|
Meteor.publish('feeds', function() {
|
||||||
|
if(isAdminById(this.userId)){
|
||||||
|
return Feeds.find();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
5
packages/telescope-post-by-feed/package-tap.i18n
Normal file
5
packages/telescope-post-by-feed/package-tap.i18n
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"translation_function_name": "__",
|
||||||
|
"helper_name": "_",
|
||||||
|
"namespace": "project"
|
||||||
|
}
|
64
packages/telescope-post-by-feed/package.js
Normal file
64
packages/telescope-post-by-feed/package.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
Package.describe({
|
||||||
|
summary: 'Auto post via RSS to Telescope',
|
||||||
|
version: '0.0.1',
|
||||||
|
name: 'telescope-post-by-feed'
|
||||||
|
});
|
||||||
|
|
||||||
|
Npm.depends({
|
||||||
|
'htmlparser2': '3.8.2',
|
||||||
|
'to-markdown': '0.0.2',
|
||||||
|
'he': '0.5.0'
|
||||||
|
});
|
||||||
|
|
||||||
|
Package.onUse(function(api) {
|
||||||
|
|
||||||
|
api.use([
|
||||||
|
'telescope-base',
|
||||||
|
'aldeed:simple-schema',
|
||||||
|
'aldeed:autoform',
|
||||||
|
'tap:i18n',
|
||||||
|
'fourseven:scss'
|
||||||
|
], ['client', 'server']);
|
||||||
|
|
||||||
|
api.use([
|
||||||
|
'iron:router',
|
||||||
|
'templating'
|
||||||
|
], 'client');
|
||||||
|
|
||||||
|
api.use([
|
||||||
|
'http',
|
||||||
|
'mrt:moment',
|
||||||
|
'percolatestudio:synced-cron'
|
||||||
|
], 'server');
|
||||||
|
|
||||||
|
api.add_files([
|
||||||
|
'lib/feeds.js'
|
||||||
|
], ['client', 'server']);
|
||||||
|
|
||||||
|
api.add_files([
|
||||||
|
'lib/client/routes.js',
|
||||||
|
'lib/client/scss/feeds.scss',
|
||||||
|
'lib/client/templates/feeds.js',
|
||||||
|
'lib/client/templates/feeds.html',
|
||||||
|
'lib/client/templates/feed_item.js',
|
||||||
|
'lib/client/templates/feed_item.html',
|
||||||
|
], 'client');
|
||||||
|
|
||||||
|
api.add_files([
|
||||||
|
'lib/server/fetch_feeds.js',
|
||||||
|
'lib/server/cron.js',
|
||||||
|
'lib/server/publications.js'
|
||||||
|
], ['server']);
|
||||||
|
|
||||||
|
api.add_files([
|
||||||
|
"i18n/en.i18n.json"
|
||||||
|
], ["client", "server"]);
|
||||||
|
|
||||||
|
api.export([
|
||||||
|
'Feeds'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Package.onTest(function(api) {
|
||||||
|
api.use('tinytest');
|
||||||
|
});
|
251
packages/telescope-post-by-feed/versions.json
Normal file
251
packages/telescope-post-by-feed/versions.json
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
{
|
||||||
|
"dependencies": [
|
||||||
|
[
|
||||||
|
"aldeed:autoform",
|
||||||
|
"4.2.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"aldeed:simple-schema",
|
||||||
|
"1.2.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"application-configuration",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"base64",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"binary-heap",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"blaze",
|
||||||
|
"2.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"blaze-tools",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"boilerplate-generator",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"callback-hook",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"check",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"coffeescript",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"ddp",
|
||||||
|
"1.0.12"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"deps",
|
||||||
|
"1.0.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"ejson",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"follower-livedata",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"fourseven:scss",
|
||||||
|
"1.0.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"geojson-utils",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"html-tools",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"htmljs",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"http",
|
||||||
|
"1.0.8"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id-map",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:controller",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:core",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:dynamic-template",
|
||||||
|
"1.0.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:layout",
|
||||||
|
"1.0.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:location",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:middleware-stack",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:router",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"iron:url",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"jquery",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"json",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"livedata",
|
||||||
|
"1.0.11"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"logging",
|
||||||
|
"1.0.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"meteor",
|
||||||
|
"1.1.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minifiers",
|
||||||
|
"1.1.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minimongo",
|
||||||
|
"1.0.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mongo",
|
||||||
|
"1.0.9"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mrt:moment",
|
||||||
|
"2.8.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"observe-sequence",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"ordered-dict",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"percolatestudio:synced-cron",
|
||||||
|
"1.1.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"random",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"reactive-dict",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"reactive-var",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"retry",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"routepolicy",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"session",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"spacebars",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"spacebars-compiler",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tap:http-methods",
|
||||||
|
"0.0.23"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tap:i18n",
|
||||||
|
"1.2.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"telescope-base",
|
||||||
|
"0.0.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"telescope-i18n",
|
||||||
|
"0.0.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"telescope-lib",
|
||||||
|
"0.2.9"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"templating",
|
||||||
|
"1.0.9"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tracker",
|
||||||
|
"1.0.3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"ui",
|
||||||
|
"1.0.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"underscore",
|
||||||
|
"1.0.1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"url",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"webapp",
|
||||||
|
"1.1.4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"webapp-hashing",
|
||||||
|
"1.0.1"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pluginDependencies": [],
|
||||||
|
"toolVersion": "meteor-tool@1.0.36",
|
||||||
|
"format": "1.0"
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -78,19 +78,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -78,19 +78,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
$red: #DD3416;
|
$red: #DD3416;
|
||||||
|
$light-yellow:#fffcea;
|
||||||
|
|
||||||
.post-category{
|
.post-category{
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
|
@ -12,4 +13,8 @@ $red: #DD3416;
|
||||||
background:$red;
|
background:$red;
|
||||||
color:white;
|
color:white;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-category{
|
||||||
|
background: $light-yellow;
|
||||||
}
|
}
|
|
@ -1,24 +1,9 @@
|
||||||
<template name="categories">
|
<template name="categories">
|
||||||
<div class="grid-small grid-module dialog admin">
|
<div class="grid-small grid-module dialog admin add-category">
|
||||||
<h2>Categories</h2>
|
<h3>Add new category:</h3>
|
||||||
<form class="form-block">
|
{{> quickForm collection="Categories" id="insertCategoryForm" type="insert" label-class="control-label" input-col-class="controls" template="telescope"}}
|
||||||
<h3>Add new category</h3>
|
|
||||||
<div class="control-group">
|
|
||||||
<label>Name</label>
|
|
||||||
<div class="controls"><input id="name" type="text" value="" /></div>
|
|
||||||
</div>
|
|
||||||
<div class="control-group">
|
|
||||||
<label>Order</label>
|
|
||||||
<div class="controls"><input id="order" type="number" value="" /></div>
|
|
||||||
</div>
|
|
||||||
<div class="form-actions">
|
|
||||||
<input type="submit" class="button" value="Submit" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<ul>
|
|
||||||
{{#each categories}}
|
|
||||||
{{> UI.dynamic template=categoryItem}}
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
{{#each categories}}
|
||||||
|
{{> UI.dynamic template=categoryItem}}
|
||||||
|
{{/each}}
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
<template name="categoryItem">
|
<template name="categoryItem">
|
||||||
<li>
|
<div class="grid-small grid-module">
|
||||||
<form>
|
{{> quickForm collection="Categories" id=formId type="update" doc=this label-class="control-label" input-col-class="controls" template="telescope"}}
|
||||||
<div class="control-group inline">
|
<a href="#" class="delete-link">Delete</a>
|
||||||
<a class="button submit edit-link btn" href="#">Save</a>
|
</div>
|
||||||
<div class="controls">
|
|
||||||
<input class="category-name" id="name_{{_id}}" type="text" value="{{name}}" placeholder="Name"/>
|
|
||||||
<textarea class="category-description" id="description_{{_id}}">{{description}}</textarea>
|
|
||||||
<input class="category-number" id="order_{{_id}}" type="number" value="{{order}}" placeholder="0"/>
|
|
||||||
</div>
|
|
||||||
<span class="category-slug small">Slug: /{{slug}}</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
Meteor.startup(function () {
|
Meteor.startup(function () {
|
||||||
|
Template[getTemplate('categoryItem')].helpers({
|
||||||
|
formId: function () {
|
||||||
|
return 'updateCategory-'+ this._id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Template[getTemplate('categoryItem')].events({
|
Template[getTemplate('categoryItem')].events({
|
||||||
'click .edit-link': function(e, instance){
|
'click .delete-link': function(e, instance){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var categoryId = instance.data._id;
|
if (confirm("Delete category?")) {
|
||||||
var name = $('#name_'+categoryId).val();
|
Categories.remove(instance.data._id);
|
||||||
var description = $('#description_'+categoryId).val();
|
|
||||||
var order = parseInt($('#order_'+categoryId).val());
|
|
||||||
var slug = slugify(name);
|
|
||||||
if(name){
|
|
||||||
Categories.update(categoryId,{ $set: {name: name, slug: slug, order: order, description: description}});
|
|
||||||
}else{
|
|
||||||
Categories.remove(categoryId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,29 +1,34 @@
|
||||||
// category schema
|
// category schema
|
||||||
categorySchema = new SimpleSchema({
|
categorySchema = new SimpleSchema({
|
||||||
_id: {
|
name: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true
|
optional: true,
|
||||||
|
autoform: {
|
||||||
|
rows: 3
|
||||||
|
}
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
type: Number,
|
type: Number,
|
||||||
optional: true
|
optional: true
|
||||||
},
|
},
|
||||||
slug: {
|
slug: {
|
||||||
type: String
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
type: String,
|
||||||
|
optional: true,
|
||||||
autoform: {
|
autoform: {
|
||||||
rows: 5
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Categories = new Meteor.Collection("categories", {
|
Categories = new Meteor.Collection("categories");
|
||||||
schema: categorySchema
|
Categories.attachSchema(categorySchema);
|
||||||
|
|
||||||
|
Categories.before.insert(function (userId, doc) {
|
||||||
|
// if no slug has been provided, generate one
|
||||||
|
if (!doc.slug)
|
||||||
|
doc.slug = slugify(doc.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// we want to wait until categories are all loaded to load the rest of the app
|
// we want to wait until categories are all loaded to load the rest of the app
|
||||||
|
@ -80,9 +85,9 @@ addToPostSchema.push(
|
||||||
|
|
||||||
Meteor.startup(function () {
|
Meteor.startup(function () {
|
||||||
Categories.allow({
|
Categories.allow({
|
||||||
insert: isAdminById
|
insert: isAdminById,
|
||||||
, update: isAdminById
|
update: isAdminById,
|
||||||
, remove: isAdminById
|
remove: isAdminById
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
|
|
@ -6,8 +6,10 @@ Package.onUse(function (api) {
|
||||||
'telescope-lib',
|
'telescope-lib',
|
||||||
'telescope-base',
|
'telescope-base',
|
||||||
'aldeed:simple-schema',
|
'aldeed:simple-schema',
|
||||||
|
'aldeed:autoform',
|
||||||
'tap:i18n',
|
'tap:i18n',
|
||||||
'fourseven:scss'
|
'fourseven:scss',
|
||||||
|
'matb33:collection-hooks'
|
||||||
], ['client', 'server']);
|
], ['client', 'server']);
|
||||||
|
|
||||||
api.use([
|
api.use([
|
||||||
|
@ -46,5 +48,12 @@ Package.onUse(function (api) {
|
||||||
"i18n/zh-CN.i18n.json",
|
"i18n/zh-CN.i18n.json",
|
||||||
], ["client", "server"]);
|
], ["client", "server"]);
|
||||||
|
|
||||||
api.export(['preloadSubscriptions', 'adminNav', 'Categories', 'addToPostSchema', 'primaryNav', 'postModules']);
|
api.export([
|
||||||
|
'preloadSubscriptions',
|
||||||
|
'adminNav',
|
||||||
|
'Categories',
|
||||||
|
'addToPostSchema',
|
||||||
|
'primaryNav',
|
||||||
|
'postModules'
|
||||||
|
]);
|
||||||
});
|
});
|
|
@ -1,8 +1,12 @@
|
||||||
{
|
{
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
[
|
||||||
|
"aldeed:autoform",
|
||||||
|
"4.2.0"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -82,19 +86,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -106,7 +110,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
@ -116,10 +120,18 @@
|
||||||
"json",
|
"json",
|
||||||
"1.0.1"
|
"1.0.1"
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"livedata",
|
||||||
|
"1.0.11"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"logging",
|
"logging",
|
||||||
"1.0.5"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"matb33:collection-hooks",
|
||||||
|
"0.7.6"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"meteor",
|
"meteor",
|
||||||
"1.1.3"
|
"1.1.3"
|
||||||
|
@ -136,6 +148,10 @@
|
||||||
"mongo",
|
"mongo",
|
||||||
"1.0.9"
|
"1.0.9"
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"mrt:moment",
|
||||||
|
"2.8.1"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"observe-sequence",
|
"observe-sequence",
|
||||||
"1.0.3"
|
"1.0.3"
|
||||||
|
|
|
@ -88,6 +88,10 @@ form, .accounts-dialog{
|
||||||
&[type="number"]{
|
&[type="number"]{
|
||||||
width: 30%;
|
width: 30%;
|
||||||
}
|
}
|
||||||
|
&:disabled{
|
||||||
|
background: #eee;
|
||||||
|
color: $light-text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input[type="text"], input[type="password"], input[type="number"], .login-form input{
|
input[type="text"], input[type="password"], input[type="number"], .login-form input{
|
||||||
height:30px;
|
height:30px;
|
||||||
|
@ -98,8 +102,9 @@ form, .accounts-dialog{
|
||||||
background:$lightest-grey;
|
background:$lightest-grey;
|
||||||
}
|
}
|
||||||
textarea{
|
textarea{
|
||||||
min-height:100px;
|
min-height:50px;
|
||||||
line-height:1.4;
|
line-height:1.4;
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
.note{
|
.note{
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
|
@ -119,23 +124,40 @@ input[type="submit"], button, .button, .btn{
|
||||||
font-size:15px;
|
font-size:15px;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
margin:0;
|
margin:0;
|
||||||
|
color: white;
|
||||||
// line-height:26px;
|
// line-height:26px;
|
||||||
// height:26px;
|
// height:26px;
|
||||||
font-weight:normal;
|
font-weight:normal;
|
||||||
&.disabled{
|
&.disabled, &.loading{
|
||||||
background:$lighter-grey;
|
background:$lighter-grey !important;
|
||||||
pointer-events:none;
|
pointer-events:none;
|
||||||
|
color: $medium-text;
|
||||||
}
|
}
|
||||||
&.inline{
|
&.inline{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
&.btn-primary{
|
&.btn-primary{
|
||||||
background:$red;
|
background:$red;
|
||||||
color:white;
|
|
||||||
&:link, &:hover, &:active, &:visited{
|
&:link, &:hover, &:active, &:visited{
|
||||||
color:white;
|
color:white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.loading {
|
||||||
|
position: relative;
|
||||||
|
color: $lighter-grey !important;
|
||||||
|
&:after{
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -10px 0 0 -10px;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
background: url('/img/loading.svg');
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input[type="search"]{
|
input[type="search"]{
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
|
|
|
@ -11,4 +11,9 @@
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.user-avatar {
|
||||||
|
.avatar-initials {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -68,7 +68,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.comment{
|
.comment{
|
||||||
margin-left:10px;
|
margin-left:0px;
|
||||||
|
.comment{
|
||||||
|
@include small{
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
@include medium-large{
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
position:relative;
|
position:relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -155,6 +163,10 @@
|
||||||
position:block;
|
position:block;
|
||||||
float:left;
|
float:left;
|
||||||
margin: 0 10px 0 10px;
|
margin: 0 10px 0 10px;
|
||||||
|
.avatar{
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
// img{
|
// img{
|
||||||
// height:40px;
|
// height:40px;
|
||||||
// width:40px;
|
// width:40px;
|
||||||
|
@ -259,7 +271,9 @@
|
||||||
.comment-field{
|
.comment-field{
|
||||||
}
|
}
|
||||||
.comment-submit{
|
.comment-submit{
|
||||||
@extend .cf;
|
}
|
||||||
|
.comment-submit-button{
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.comment-page &{
|
.comment-page &{
|
||||||
margin-left:30px;
|
margin-left:30px;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
[
|
[
|
||||||
"aldeed:simple-schema",
|
"aldeed:simple-schema",
|
||||||
"1.1.0"
|
"1.2.0"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"application-configuration",
|
"application-configuration",
|
||||||
|
@ -82,19 +82,19 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:core",
|
"iron:core",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:dynamic-template",
|
"iron:dynamic-template",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:layout",
|
"iron:layout",
|
||||||
"1.0.3"
|
"1.0.5"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:location",
|
"iron:location",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:middleware-stack",
|
"iron:middleware-stack",
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"iron:url",
|
"iron:url",
|
||||||
"1.0.3"
|
"1.0.4"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"jquery",
|
"jquery",
|
||||||
|
|
18
public/img/loading.svg
Normal file
18
public/img/loading.svg
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="24px" height="30px" viewBox="0 0 24 30" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||||
|
<rect x="0" y="10" width="4" height="10" fill="#333" opacity="0.2">
|
||||||
|
<animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0s" dur="1s" repeatCount="indefinite" />
|
||||||
|
</rect>
|
||||||
|
<rect x="8" y="10" width="4" height="10" fill="#333" opacity="0.2">
|
||||||
|
<animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.15s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.15s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.15s" dur="1s" repeatCount="indefinite" />
|
||||||
|
</rect>
|
||||||
|
<rect x="16" y="10" width="4" height="10" fill="#333" opacity="0.2">
|
||||||
|
<animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.3s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.3s" dur="1s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.3s" dur="1s" repeatCount="indefinite" />
|
||||||
|
</rect>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -1,8 +1,9 @@
|
||||||
// Publish a list of posts
|
// Publish a list of posts
|
||||||
|
|
||||||
Meteor.publish('postsList', function(terms) {
|
Meteor.publish('postsList', function(terms) {
|
||||||
|
var user = Meteor.users.findOne(this.userId);
|
||||||
if(canViewById(this.userId)){
|
if(canViewById(this.userId)){
|
||||||
var parameters = getPostsParameters(terms),
|
var parameters = getPostsParameters(terms, user),
|
||||||
posts = Posts.find(parameters.find, parameters.options);
|
posts = Posts.find(parameters.find, parameters.options);
|
||||||
|
|
||||||
// console.log('//-------- Subscription Parameters:');
|
// console.log('//-------- Subscription Parameters:');
|
||||||
|
|
3
server/start_cron.js
Normal file
3
server/start_cron.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Meteor.startup(function() {
|
||||||
|
SyncedCron.start();
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue