I am trying to implement the .allow part of meteor in an application I'm building. Before introducing it a list was displaying comments a user entered, now the comments just flash up for a second and then disappear. The comments are still being added to the collection though.
Could anyone tell me what I'm doing wrong, I am very new to this.
Main js file:
if (Meteor.isClient) {
Meteor.startup(function () {
Meteor.subscribe("ques");
});
Template.compose.events({
'submit form': function (event) {
var $body = $('#que-body');
var $score = 1;
event.preventDefault();
Questions.insert({
body: $body.val(),
score: $score,
created_at: Date()
});
$body.val('');
}
});
Template.question.selected = function () {
return Session.equals("selected_question", this._id) ? "selected" : '';
};
Template.question.events({
'click': function () {
Session.set("selected_question", this._id);
}
});
Template.question.que = function(){
return Questions.findOne(Session.get("selected"));
};
// Deals with up-vote, down-vote, remove buttons
Template.list.events({
'click .icon-thumbs-up': function(event) {
Questions.update(Session.get("selected_question"), {$inc: {score: 1}});
},
'click .icon-thumbs-down': function(event) {
Questions.update(Session.get("selected_question"), {$inc: {score: -1}});
},
'click .icon-remove': function(event) {
Questions.remove(Session.get("selected_question"));
}
});
Template.list.questions = Questions.find({}, {sort: {score: -1, created_at: -1}});
}
if (Meteor.isServer) {
Meteor.startup(function () {
Meteor.publish("ques", function(){
return Questions.find({}, {
fields:{ }
})
});
});
}
The model.js file:
Questions = new Meteor.Collection("questions");
Questions.allow({
insert: function(userId, que){
return userId && que.owner === userId;
},
update: function(id, ques, fields, modifier){
return true;
},
remove: function(id, que){
return id && que.owner === id;
}
});
Do you mean the questions (you said comments?): Your Meteor.allow rule is basically something that says the question.owner is the current logged in user's _id. You need to insert an owner when you insert your question. This is the only way (que.owner === userId will return true):
Questions.insert({
owner: Meteor.userId(),
body: $body.val(),
score: $score,
created_at: Date()
});
Make sure you ensure that only logged in users have the chance to insert questions. Either by hiding the button or having a check just before everything is inserted too:
if(!Meteor.userId()) {
alert("You need to be logged in to post a question");
return;
}
Related
I tried to include a button (created from user event) on Sales order. Upon clicking it, Invoice will be generated. As soon as the button is hit, ther comes an error and invoice doesnt get generated. Can anyone help me with this?
//Client script
function pageInit() {
}
function csForButton(ctx) {
var rec = curr.get();
var customer = rec.getValue({ "fieldId": "customer" });
log.error({
title: 'customer',
details: customer
});
var scriptURL = url.resolveScript({
"scriptId": "customscript_button_task_sl",
"deploymentId": "customdeploy_button_task_sl"
});
console.log('scriptURL', scriptURL);
window.onbeforeunload = null;
window.open(scriptURL + '&id=' + rec.id);
}
return {
pageInit: pageInit,
csForButton: csForButton
};
//User Event Script
function beforeLoad(ctx) {
//if (context.type == context.UserEventType.VIEW) {
addbutton(ctx);
// }
}
function addbutton(ctx) {
try {
var rec = ctx.newRecord;//record object, now we can get its properties through it(e.g. fields)
var statusOfTrans = rec.getValue({ fieldId: 'status' });
log.error('statusOfTrans', statusOfTrans);
ctx.form.clientScriptFileId = "16474"
if (ctx.type == "view" && statusOfTrans == 'Pending Fulfillment') {
ctx.form.addButton({
id: 'custpage_make_invoice',
label: 'create invoice!',
functionName: 'csForButton'
});
}
}
catch (error) {
log.error('addbutton', error);
}
}
return {
beforeLoad: beforeLoad,
}
//Suitelet Script
function onRequest(ctx) {
try {
var req = ctx.request;
var res = ctx.response;
if (req.method == 'GET') {
var objRecord = rec.transform({
fromType: rec.Type.SALES_ORDER,
fromId: req.parameters.id,
toType: rec.Type.INVOICE,
isDynamic: true,
});
objRecord.save({
ignoreMandatoryFields: true
});
res.write({output: 'Invoice created'});
}
} catch (error) {
log.error('onRequest', error);
}
}
return {
'onRequest': onRequest
}
error (in suitelet):
{"type":"error.SuiteScriptError","name":"USER_ERROR","message":"You must enter at least one line item for this transaction.","stack":["anonymous(N/serverRecordService)","onRequest(/SuiteScripts/button SL.js:39)"],"cause":{"type":"internal error","code":"USER_ERROR","details":"You must enter at least one line item for this transaction.","userEvent":null,"stackTrace":["anonymous(N/serverRecordService)","onRequest(/SuiteScripts/button SL.js:39)"],"notifyOff":false},"id":"","notifyOff":false,"userFacing":false}
As Error says to include atleast 1 line but i wanted it to be generated automatically. I am new in suitescript and taking all the help from the documentation. Can anyone jst guide me what is wrong i m doing?
Thank u
You need the SO status to be Pending Billing. If the status of the SO is Pending Fulfillment, then no line items are ready to be invoiced.
I have a set of objects called 'categories' which have posts in them that belong to that category. A user is able to 'follow' a category by clicking a button that takes the data within that category object, most importantly the posts puts them in a field within Meteor.users. As it stands, the user only gets the posts that were available at the when the user clicked the button.
How do i make it so that when they 'follow' a category, any new posts that come in later, after the click event has already been done, will automatically be pushed to their user data. In other words, how do make this process reactive?
client/categories.js
Template.CategoriesMain.events({
'click .toggle-category': function(e){
var ob = $(e.target).parent().find("div").text();
var id = $.makeArray( ob );
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
server/categories.js
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'name': 1}});
} else {
this.ready();
}
});
could i do something with autorun? to the tune of this?
Template.CategoriesMain.events({
'click .toggle-category': function(e){
autorun(function() {
var ob = $(e.target).parent().find("div").text();
var id = $.makeArray( ob );
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
});
can't get it currently but try
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'name': 1}});
} else {
return null;
}
this.ready();
});
If I try to add reviews or like a recipe I get an error Error invoking Method 'addReview': Internal server error [500] debug.js:41,even though it adds reviews and likes into databases and works fine but still gives me above error.
Source code Github
add_review.js
Template.add_review.events({
'submit .add-review':function(event){
event.preventDefault();
var rating = event.target.rating.value;
var review = event.target.review.value;
var recipeId = Router.current().data()._id;
Meteor.call('addReview',rating,review,recipeId);
}
});
Template.recipes.events({
"click [data-action='addLikes']": function (event) {
event.preventDefault();
var recipe = Recipes.findOne({_id: this._id});
Meteor.call('upvote',recipe)
}
});
client/methods.js
Meteor.methods({
addReview:function(rating,review,recipeId){
if(review!=""){
Reviews.insert({
rating:rating,
review:review,
recipeId:recipeId
});
Router.go('reviews',{_id:recipeId});
FlashMessages.sendSuccess('Review Added',{ autoHide: true, hideDelay: 2000 });
}
else{
FlashMessages.sendError('Review field is empty',{ autoHide: true, hideDelay: 3000 });
}
return false;
},
upvote:function(currentRecipe){
var user = Meteor.user();
if(!user){
FlashMessages.sendError("You need to login to like this recipe", {hideDelay: 1000});
}
if (currentRecipe) {
if (_.contains(currentRecipe.voters, Meteor.userId())) {
FlashMessages.sendError("You already liked this recipe", {hideDelay: 1000});
return false;
}
Recipes.update(currentRecipe._id, {$addToSet: {voters: Meteor.userId()}, $inc: {likes: 1}});
}
}
})
server/permissions.js
RecipesImages.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fieldNames, modifier) {
return true;
},
remove: function(userId, doc) {
return false;
},
download: function(userId,doc) {
return true;
},
fetch: null
});
Recipes.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fieldNames, modifier) {
return true;
}
});
Reviews.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fieldNames, modifier) {
return true;
}
});
It seems that you are trying to use methods where they are not needed and only make things harder. Isn't this all just client code? If so, you can just use functions:
add_review.js
Template.add_review.events({
'submit .add-review':function(event){
event.preventDefault();
var rating = event.target.rating.value;
var review = event.target.review.value;
var recipeId = Router.current().data()._id;
addReview(rating,review,recipeId);
}
});
Template.recipes.events({
"click [data-action='addLikes']": function (event) {
event.preventDefault();
var recipe = Recipes.findOne({_id: this._id});
upvote(recipe)
}
});
client/methods.js
addReview = function(rating,review,recipeId){
if(review!=""){
Reviews.insert({
rating:rating,
review:review,
recipeId:recipeId
});
Router.go('reviews',{_id:recipeId});
FlashMessages.sendSuccess('Review Added',{ autoHide: true, hideDelay: 2000 });
}
else{
FlashMessages.sendError('Review field is empty',{ autoHide: true, hideDelay: 3000 });
}
return false;
};
upvote = function(currentRecipe){
var user = Meteor.user();
if(!user){
FlashMessages.sendError("You need to login to like this recipe", {hideDelay: 1000});
}
if (currentRecipe) {
if (_.contains(currentRecipe.voters, Meteor.userId())) {
FlashMessages.sendError("You already liked this recipe", {hideDelay: 1000});
return false;
}
Recipes.update(currentRecipe._id, {$addToSet: {voters: Meteor.userId()}, $inc: {likes: 1}});
}
};
#Waqar First off, in your methods.js file, which as someone else has already told you should go in the /lib folder, you should have single quotes around your function name. I've re-written your code for Meteor.methods and for the Template.add-review.events below
client/templates/reviews/add_review.js (sorry, I put each template in a separate folder along with the js file for it)
Template.add_review.events({
'submit .add-review': function(event){
var rating = event.target.rating.value;
var review = event.target.review.value;
var recipeId = this._id;
var params = {
rating: rating,
review: review,
_id: recipeId
}
Meteor.call('addReview', params);
FlashMessages.sendSuccess('Review Added'); // and any other options you want to include
Router.go('reviews',{_id:recipeId});
return false;
}
});
/lib/methods.js
Meteor.methods({
'addReview': function(params){
Reviews.insert(params);
}
});
Give that a try and see if it works. Also, put your methods.js file in the /lib folder as Stephen suggested.
I'm using the at.js '#who mentions'(https://github.com/ichord/At.js) in my Meteor chat application, and would like to do the following:
Display 'username: firstName lastName' in the #who list.
It's currently displaying username only.
The mongo collection stores the firstName in profile.firstName, lastName in profile.lastName
Current code in channel.js:
setTimeout(function () {
var dbUsers = Meteor.users.find({_id: {$ne: Meteor.userId()}}).fetch();
var users = _.sortBy(dbUsers, 'username');
users = _.pluck(users, 'username');
self.$('textarea[name=message]').atwho({
at: '#',
data: users,
}).on('shown.atwho', function(event) {
setIsMentioning(true);
}).on('hidden.atwho', function(event) {
setTimeout(function() { setIsMentioning(false); }, 100);
});
}, 500);
My new code in channel.js:
setTimeout(function () {
var dbUsers = Meteor.users.find({_id: {$ne: Meteor.userId()}}).fetch();
var users = _.map(dbUsers, function(user) { return _.pick(user, 'username', 'profile.firstName', 'profile.lastName'); });
self.$('textarea[name=message]').atwho({
at: '#',
data: users,
displayTpl: '<li>${firstName} ${lastName}, ${degree}</li>',
insertTpl: ':${username}:'
}).on('shown.atwho', function(event) {
setIsMentioning(true);
}).on('hidden.atwho', function(event) {
setTimeout(function() { setIsMentioning(false); }, 100);
});
}, 500);
Here's the publication for users in users.js:
Meteor.publish('users', function() {
if(this.userId) { // We should only send data to logged in users.
// filter by organization
var user = Meteor.users.findOne(this.userId);
if(user.profile && user.profile.organizationId)
return Meteor.users.find(
{'profile.organizationId': user.profile.organizationId},
{fields: {'username': 1, 'emails': 1, 'status': 1, 'profile.firstName': 1, 'profile.lastName': 1}}
);
else
return Meteor.users.find({}, {fields: {'username': 1, 'emails': 1, 'status': 1}});
}
this.ready();
});
Here's the users subscription in routes.js:
teamRoutes = FlowRouter.group({
prefix: '/teams/:team',
middlewares: [AccountsTemplates.ensureSignedIn],
subscriptions: function(params) {
this.register('teamChannels', Meteor.subscribe('teamChannels', params.team));
this.register('teamDirectChannels', Meteor.subscribe('teamDirectChannels', params.team));
this.register('users', Meteor.subscribe('users', params.team));
this.register('teams', Meteor.subscribe('myTeams'));
this.register('reads', Meteor.subscribe('reads'));
this.register('mentions', Meteor.subscribe('mentions'));
}
});
Would really appreciate anyone who can explain to me what I'm doing wrong! Thanks so much.
I want to automatically generate model attributes when added to a collection in backbone. I did some searching but can't seem to find any info on how to correctly handle timestamps generated on the client side. My following example works. (but probably not in all situations) Is there a better way to do this with backbone.js?
Here is the fiddle for the code below.
<div id="output"></div>
//javascript
var modelMessage = Backbone.Model.extend({
levelToInt: function () {
switch (this.get('level')) {
case "error":
return 3;
break;
case "warning":
return 2;
break;
case "info":
return 1;
break;
default:
return 0;
break;
}
}
});
var collectionMessages = Backbone.Collection.extend({
model: modelMessage,
url: "#"
});
var Messages = new collectionMessages();
Messages.listenTo(Messages, "add", function (model) {
if (!model.get('addedon')) {
model.set({
addedon: new Date().getTime(),
levelcode: model.levelToInt()
});
}
$('#output').append('<div>added model:' + JSON.stringify(model.toJSON()) + '</div>');
});
Messages.add({
level: "info",
text: "Life is good."
});
setTimeout(function () {
Messages.add({
level: "warning",
text: "you have been warned..."
});
}, 1000);
setTimeout(function () {
Messages.add({
level: "error",
text: "OMG something really bad happened!",
});
}, 2000);
I would just set up the model so that whenever it is created, you give it a timestamp. So I would add the following to your model:
var modelMessage = Backbone.Model.extend({
defaults: function() {
return {
addedon: new Date().getTime()
};
},
// ... the rest of your code
});
Then change the Messages.listenTo call to the following as well:
Messages.listenTo(Messages, "add", function (model) {
model.set({levelcode: model.levelToInt()});
$('#output').append('<div>added model:' + JSON.stringify(model.toJSON()) + '</div>');
});
Here is a fiddle: http://jsfiddle.net/xUyak/
Or as Andrew mentioned the following would work as well:
var modelMessage = Backbone.Model.extend({
initialize: function() {
this.set({
addedon: new Date().getTime(),
levelcode: this.levelToInt()
});
},
// ... the rest of your code
});
And then you would omit model.set in Message.listenTo:
Messages.listenTo(Messages, "add", function (model) {
$('#output').append('<div>added model:' + JSON.stringify(model.toJSON()) + '</div>');
});