clean up comment edit form; continue namespacing users.telescope

This commit is contained in:
Sacha Greif 2015-04-28 15:54:19 +09:00
parent 10166402ef
commit 40d38d1364
21 changed files with 177 additions and 134 deletions

View file

@ -1,21 +1,7 @@
<template name="comment_edit">
<div class="grid">
{{#with comment}}
<form class="grid-block form-horizontal">
<div class="control-group">
<label class="control-label">{{_ "comment_"}}</label>
<div class="controls comment-field" id="editor"><textarea id="body" value="" class="input-xlarge">{{body}}</textarea></div>
</div>
<div class="form-actions">
<a class="delete-link" href="/comments/deleted">{{_ "delete_comment"}}</a>
<input type="submit" class="button btn-primary" value="{{_ "submit"}}" title="(⌘+enter)" />
</div>
</form>
{{/with}}
</div>
<div class="grid grid-module">
{{> quickForm collection="Comments" doc=comment id="editCommentForm" template="bootstrap3-horizontal" label-class="control-label" input-col-class="controls" type="method-update" meteormethod="editComment"}}
{{> quickForm collection="Comments" doc=comment id="editCommentForm" template="bootstrap3-horizontal" label-class="control-label" input-col-class="controls" type="method-update" meteormethod="editComment" fields=commentFields}}
</div>
<div class="grid grid-module">

View file

@ -1,44 +1,78 @@
var editComment = function(instance) {
var comment = instance.data.comment;
var content = instance.$('#body').val();
Template.comment_edit.helpers({
commentFields: function () {
var schema = Comments.simpleSchema()._schema;
var comment = this.comment;
var fields = _.filter(_.keys(schema), function (fieldName) {
var field = schema[fieldName];
return Users.can.editField(Meteor.user(), field, comment);
});
return fields;
}
});
AutoForm.hooks({
editCommentForm: {
if(!Meteor.user())
throw i18n.t('you_must_be_logged_in');
before: {
editComment: function(modifier) {
var comment = doc;
Comments.update(comment._id, {
$set: {
body: content
// ------------------------------ Checks ------------------------------ //
if (!Meteor.user()) {
Messages.flash(i18n.t('you_must_be_logged_in'), "");
return false;
}
// ------------------------------ Callbacks ------------------------------ //
// run all post edit client callbacks on modifier object successively
comment = Telescope.callbacks.run("postEditClient", comment);
return comment;
}
},
onSuccess: function(operation, comment) {
Events.track("edit comment", {'commentId': comment._id});
Router.go('post_page', {_id: comment.postId});
},
onError: function(operation, error) {
console.log(error)
Messages.flash(error.reason.split('|')[0], "error"); // workaround because error.details returns undefined
Messages.clearSeen();
}
});
Events.track("edit comment", {'postId': comment.postId, 'commentId': comment._id});
Router.go('post_page_comment', {_id: comment.postId, commentId: comment._id});
};
}
});
// delete link
Template.comment_edit.events({
'click .delete-link': function(e){
var comment = this.comment;
e.preventDefault();
if(confirm("Are you sure?")){
Router.go("/");
Meteor.call("deleteCommentById", comment._id, function(error) {
if (error) {
console.log(error);
Messages.flash(error.reason, 'error');
} else {
Messages.flash(i18n.t('your_comment_has_been_deleted'), 'success');
}
});
}
}
});
Template.comment_edit.onRendered(function() {
var self = this;
this.$("#comment").keydown(function (e) {
if(((e.metaKey || e.ctrlKey) && e.keyCode == 13) || (e.ctrlKey && e.keyCode == 13)){
editComment(self);
// editComment(self);
}
});
});
Template.comment_edit.events({
'click input[type=submit]': function(e, instance){
e.preventDefault();
editComment(instance);
},
'click .delete-link': function(e){
var comment = this;
e.preventDefault();
if(confirm(i18n.t("are_you_sure"))){
Meteor.call('removeComment', comment._id);
Router.go('post_page', {_id: comment.postId});
Messages.flash("Your comment has been deleted.", "success");
}
}
});
});

View file

@ -72,7 +72,11 @@ Telescope.schemas.comments = new SimpleSchema({
},
postId: {
type: String,
optional: true
optional: true,
editableBy: ["owner", "admin"], // TODO: should users be able to set postId, but not modify it?
autoform: {
omit: true // never show this
}
},
userId: {
type: String,

View file

@ -59,7 +59,8 @@ Meteor.methods({
// parentCommentId
var user = Meteor.user(),
hasAdminRights = Users.is.admin(user);
hasAdminRights = Users.is.admin(user),
schema = Comments.simpleSchema()._schema;
// ------------------------------ Checks ------------------------------ //
@ -88,8 +89,8 @@ Meteor.methods({
// clear restricted properties
_.keys(comment).forEach(function (fieldName) {
var field = commentSchemaObject[fieldName];
if (!Users.can.editField(user, comment, field)) {
var field = schema[fieldName];
if (!Users.can.submitField(user, field)) {
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
}
@ -107,7 +108,8 @@ Meteor.methods({
var user = Meteor.user(),
hasAdminRights = Users.is.admin(user),
comment = Comments.findOne(commentId);
comment = Comments.findOne(commentId),
schema = Comments.simpleSchema()._schema;
// ------------------------------ Checks ------------------------------ //
@ -122,8 +124,8 @@ Meteor.methods({
// loop over each property being operated on
_.keys(operation).forEach(function (fieldName) {
var field = Posts.schema._schema[fieldName];
if (!Users.can.editField(user, comment, field)) {
var field = schema[fieldName];
if (!Users.can.editField(user, field, comment)) {
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
}
@ -136,7 +138,7 @@ Meteor.methods({
// ------------------------------ Update ------------------------------ //
Posts.update(postId, modifier);
Comments.update(commentId, modifier);
// ------------------------------ Callbacks ------------------------------ //
@ -147,9 +149,13 @@ Meteor.methods({
return Comments.findOne(commentId);
},
removeComment: function (commentId) {
deleteCommentById: function (commentId) {
var comment = Comments.findOne(commentId);
if(Users.can.edit(Meteor.user(), comment)){
var user = Meteor.user();
if(Users.can.edit(user, comment)){
// decrement post comment count and remove user ID from post
Posts.update(comment.postId, {
$inc: {commentCount: -1},
@ -158,7 +164,7 @@ Meteor.methods({
// decrement user comment count and remove comment ID from user
Meteor.users.update({_id: comment.userId}, {
$inc: {'commentCount': -1}
$inc: {'telescope.commentCount': -1}
});
// note: should we also decrease user's comment karma ?
@ -169,8 +175,11 @@ Meteor.methods({
htmlBody: 'Deleted',
isDeleted: true
}});
}else{
Messages.flash("You don't have permission to delete this comment.", "error");
}
}
});

View file

@ -47,32 +47,4 @@ Telescope.menus.register("adminMenu", [
label: 'users',
description: 'users_dashboard'
}
]);
Telescope.menus.register("userMenu", [
{
route: function () {
return Router.path('user_profile', {_idOrSlug: Meteor.user().slug});
},
label: 'profile',
description: 'view_your_profile'
},
{
route: function () {
return Router.path('user_edit', {slug: Meteor.user().slug});
},
label: 'edit_account',
description: 'edit_your_profile'
},
{
route: 'settings',
label: 'settings',
description: 'settings',
adminOnly: true
},
{
route: 'signOut',
label: 'sign_out',
description: 'sign_out'
}
]);

View file

@ -57,7 +57,7 @@ Telescope.viewParameters.userPosts = function (terms) {
Telescope.viewParameters.userUpvotedPosts = function (terms) {
var user = Meteor.users.findOne(terms.userId);
var postsIds = _.pluck(user.votes.upvotedPosts, "itemId");
var postsIds = _.pluck(user.telescope.upvotedPosts, "itemId");
return {
find: {_id: {$in: postsIds}, userId: {$ne: terms.userId}}, // exclude own posts
options: {limit: 5, sort: {postedAt: -1}}
@ -66,7 +66,7 @@ Telescope.viewParameters.userUpvotedPosts = function (terms) {
Telescope.viewParameters.userDownvotedPosts = function (terms) {
var user = Meteor.users.findOne(terms.userId);
var postsIds = _.pluck(user.votes.downvotedPosts, "itemId");
var postsIds = _.pluck(user.telescope.downvotedPosts, "itemId");
// TODO: sort based on votedAt timestamp and not postedAt, if possible
return {
find: {_id: {$in: postsIds}},

View file

@ -16,6 +16,7 @@ Package.onUse(function (api) {
'accounts-twitter',
'reactive-var',
'http',
'email',
'aldeed:simple-schema@1.3.2',
'aldeed:collection2@2.3.3',
'aldeed:autoform@5.1.2',

View file

@ -117,7 +117,7 @@ Comments.registerField(
function setNotificationDefaults (user) {
// set notifications default preferences
user.profile.notifications = {
user.telescope.notifications = {
users: false,
posts: false,
comments: true,

View file

@ -1,7 +1,7 @@
<template name="post_edit">
<div class="grid grid-module">
{{> quickForm collection="Posts" doc=post id="editPostForm" template="bootstrap3-horizontal" label-class="control-label" input-col-class="controls" type="method-update" meteormethod="editPost"}}
{{> quickForm collection="Posts" doc=post id="editPostForm" template="bootstrap3-horizontal" label-class="control-label" input-col-class="controls" type="method-update" meteormethod="editPost" fields=postFields}}
</div>
<div class="grid grid-module">

View file

@ -1,10 +1,20 @@
Template.post_edit.helpers({
postFields: function () {
var schema = Posts.simpleSchema()._schema;
var post = this.post;
var fields = _.filter(_.keys(schema), function (fieldName) {
var field = schema[fieldName];
return Users.can.editField(Meteor.user(), field, post);
});
return fields;
}
});
AutoForm.hooks({
editPostForm: {
before: {
editPost: function(modifier) {
console.log(modifier)
console.log(template)
var post = doc;
// ------------------------------ Checks ------------------------------ //
@ -17,9 +27,8 @@ AutoForm.hooks({
// ------------------------------ Callbacks ------------------------------ //
// run all post edit client callbacks on modifier object successively
post = Telescope.callbacks.postEditClient.reduce(function(result, currentFunction) {
return currentFunction(result);
}, post);
post = Telescope.callbacks.run("postEditClient", post);
return post;
}

View file

@ -96,8 +96,8 @@ Meteor.methods({
if(!hasAdminRights){
var timeSinceLastPost=timeSinceLast(user, Posts),
numberOfPostsInPast24Hours=numberOfItemsInPast24Hours(user, Posts),
var timeSinceLastPost = Users.timeSinceLast(user, Posts),
numberOfPostsInPast24Hours = Users.numberOfItemsInPast24Hours(user, Posts),
postInterval = Math.abs(parseInt(Settings.get('postInterval', 30))),
maxPostsPer24Hours = Math.abs(parseInt(Settings.get('maxPostsPerDay', 30)));
@ -123,7 +123,7 @@ Meteor.methods({
_.keys(post).forEach(function (fieldName) {
var field = schema[fieldName];
if (!Users.can.editField(user, post, field)) {
if (!Users.can.submitField(user, field)) {
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
}
@ -163,7 +163,7 @@ Meteor.methods({
_.keys(operation).forEach(function (fieldName) {
var field = schema[fieldName];
if (!Users.can.editField(user, post, field)) {
if (!Users.can.editField(user, field, post)) {
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
}

View file

@ -1,6 +1,6 @@
<template name="userAccount">
<div class="grid-small grid-module dialog user-edit">
{{> quickForm collection="Meteor.users" doc=user id="editUserForm" template="bootstrap3-horizontal" input-col-class="controls" type="update"}}
{{> quickForm collection="Meteor.users" doc=user id="editUserForm" template="bootstrap3-horizontal" input-col-class="controls" type="update" fields=userFields}}
<!-- {{#if profileIncomplete}}
<div>

View file

@ -32,7 +32,7 @@ Template.userDownvotedPosts.helpers({
var user = this;
var posts = Template.instance().posts.get().fetch();
posts = _.map(posts, function (post) {
var vote = _.findWhere(user.votes.downvotedPosts, {itemId: post._id});
var vote = _.findWhere(user.telescope.downvotedPosts, {itemId: post._id});
post.votedAt = vote.votedAt;
return post;
});

View file

@ -11,7 +11,7 @@ Template.userInfo.helpers({
return Meteor.user() && Meteor.user()._id != this._id && !Users.is.invited(this) && Telescope.utils.invitesEnabled() && Users.can.invite(Meteor.user());
},
inviteCount: function() {
return Meteor.user().inviteCount;
return Meteor.user().telescope.inviteCount;
},
getTwitterName: function () {
return Users.getTwitterName(this);

View file

@ -32,7 +32,7 @@ Template.userUpvotedPosts.helpers({
var user = this;
var posts = Template.instance().posts.get().fetch();
posts = _.map(posts, function (post) {
var vote = _.findWhere(user.votes.upvotedPosts, {itemId: post._id});
var vote = _.findWhere(user.telescope.upvotedPosts, {itemId: post._id});
post.votedAt = vote.votedAt;
return post;
});

View file

@ -40,7 +40,7 @@ Users.getAuthorName = function(item) {
};
Users.getProfileUrl = function (user) {
return this.getProfileUrlBySlugOrId(user.slug);
return this.getProfileUrlBySlugOrId(user.telescope.slug);
};
Users.getProfileUrlBySlugOrId = function (slugOrId) {
@ -134,7 +134,7 @@ Users.numberOfItemsInPast24Hours = function (user, collection) {
Users.getUserSetting = function (setting, defaultValue, user) {
var user = (typeof user == 'undefined') ? Meteor.user() : user;
var defaultValue = (typeof defaultValue == "undefined") ? null: defaultValue;
var settingValue = this.getProperty(user.profile, setting);
var settingValue = this.getProperty(user.telescope, setting);
return (settingValue == null) ? defaultValue : settingValue;
};
@ -155,7 +155,7 @@ Users.setUserSetting = function (setting, value, userArgument) {
console.log('Setting user setting "' + setting + '" to "' + value + '" for ' + Users.getUserName(user));
var find = {_id: user._id};
var field = {};
field['profile.'+setting] = value;
field['telescope.'+setting] = value;
var options = {$set: field};
var result = Meteor.users.update(find, options, {validate: false});
};

View file

@ -0,0 +1,27 @@
Telescope.menus.register("userMenu", [
{
route: function () {
return Router.path('user_profile', {_idOrSlug: Meteor.user().telescope.slug});
},
label: 'profile',
description: 'view_your_profile'
},
{
route: function () {
return Router.path('user_edit', {slug: Meteor.user().telescope.slug});
},
label: 'edit_account',
description: 'edit_your_profile'
},
{
route: 'settings',
label: 'settings',
description: 'settings',
adminOnly: true
},
{
route: 'signOut',
label: 'sign_out',
description: 'sign_out'
}
]);

View file

@ -14,29 +14,29 @@ Users.pubsub = {};
*/
Users.pubsub.publicProperties = { // true means exposed
_id: true,
commentCount: true,
createdAt: true,
email_hash: true,
isInvited: true,
karma: true,
postCount: true,
slug: true,
username: true,
'profile.username': true,
'profile.notifications': true,
'profile.bio': true,
'profile.github': true,
'profile.site': true,
'profile.twitter': true,
'services.twitter.profile_image_url': true,
'services.twitter.profile_image_url_https': true,
'services.facebook.id': true,
'services.twitter.screenName': true,
'services.github.screenName': true, // Github is not really used, but there are some mentions to it in the code
'votes.downvotedComments': true,
'votes.downvotedPosts': true,
'votes.upvotedComments': true,
'votes.upvotedPosts': true
'telescope.commentCount': true,
'telescope.email_hash': true,
'telescope.isInvited': true,
'telescope.karma': true,
'telescope.postCount': true,
'telescope.slug': true,
'telescope.username': true,
'telescope.notifications': true,
'telescope.bio': true,
'telescope.github': true,
'telescope.site': true,
'telescope.twitter': true,
'telescope.downvotedComments': true,
'telescope.downvotedPosts': true,
'telescope.upvotedComments': true,
'telescope.upvotedPosts': true
};
/**
@ -53,8 +53,8 @@ Users.pubsub.hiddenProperties = {
*/
Users.pubsub.avatarProperties = {
_id: true,
email_hash: true,
slug: true,
'telescope.email_hash': true,
'telescope.slug': true,
username: true,
'profile.username': true,
'profile.github': true,

View file

@ -49,7 +49,7 @@ Users.controllers.edit = RouteController.extend({
},
data: function() {
// if there is no slug, default to current user
var user = !!this.params.slug ? Meteor.users.findOne({slug: this.params.slug}) : Meteor.user();
var user = !!this.params.slug ? Meteor.users.findOne({"telescope.slug": this.params.slug}) : Meteor.user();
return {
user: user
};
@ -89,7 +89,7 @@ Meteor.startup(function () {
// Only allow users with permissions to see the user edit page.
if (Meteor.user() && (
Users.is.admin(Meteor.user()) ||
this.params.slug === Meteor.user().slug
this.params.slug === Meteor.user().telescope.slug
)) {
this.next();
} else {

View file

@ -4,12 +4,12 @@ Accounts.onCreateUser(function(options, user){
var userProperties = {
profile: options.profile || {},
karma: 0,
isInvited: false,
postCount: 0,
commentCount: 0,
invitedCount: 0,
votes: {
telescope: {
karma: 0,
isInvited: false,
postCount: 0,
commentCount: 0,
invitedCount: 0,
upvotedPosts: [],
downvotedPosts: [],
upvotedComments: [],
@ -24,14 +24,14 @@ Accounts.onCreateUser(function(options, user){
// if email is set, use it to generate email hash
if (Users.getEmail(user))
user.email_hash = Users.getEmailHash(user);
user.telescope.email_hash = Users.getEmailHash(user);
// set username on profile
if (!user.profile.username)
user.profile.username = user.username;
// create slug from username
user.slug = Telescope.utils.slugify(Users.getUserName(user));
user.telescope.slug = Telescope.utils.slugify(Users.getUserName(user));
// if this is not a dummy account, and is the first user ever, make them an admin
user.isAdmin = (!user.profile.isDummy && Meteor.users.find({'profile.isDummy': {$ne: true}}).count() === 0) ? true : false;

View file

@ -23,6 +23,7 @@ Package.onUse(function (api) {
'lib/callbacks.js',
'lib/modules.js',
'lib/helpers.js',
'lib/menu.js',
'lib/pubsub.js',
'lib/routes.js'
], ['client', 'server']);