#if with Roles.userIsInRole not working - meteor

I am creating a modal to manage the permissions of a user. But when the modal is loaded. The console returns true from the server, if a users has the role. While {{#if hasRole}} is still showing the wrong label.
editUserPermissionsModal.html
<tbody>
{{#each roles}}
<tr>
<td>{{this.name}}</td>
<td>
{{#if hasRole}}
<span class="label label-success">Toegang</span>
{{else}}
<span class="label label-danger">Geweigerd</span>
{{/if}}
</td>
<td>
<select class="roleSelect">
{{#if hasRole}}
<option value="allow">Toestaan</option>
<option value="deny">Weigeren</option>
{{else}}
<option value="deny">Weigeren</option>
<option value="allow">Toestaan</option>
{{/if}}
</select>
</td>
</tr>
{{/each}}
</tbody>
editUserPermissionsModal.js
Template.editUserPermissionsModal.helpers({
roles: function() {
return Roles.getAllRoles();
},
hasRole: function(){
var userId = Session.get("editing_user");
var role = this.name;
Meteor.call("checkRole", userId, role, function(error, result){
if(error){
console.log("error", error);
}
if(result){
console.log(result);
return result;
}
});
}
});
Template.editUserPermissionsModal.events({
"change .roleSelect": function(event, template){
var addRole = event.target.value;
if(addRole == 'allow') {
var user = Session.get("editing_user");
var role = this.name;
Meteor.call('addRoleToUser', user, role)
}
}
});
server.js
checkRole:function(userId, role) {
return Roles.userIsInRole(userId, role);
}

This is a typical "method call within a helper" issue. You can read about it here.
Given that you need variable arguments to your method, the easiest solution in your case would probably be using David Weldon's answer and install Sashko's meteor-reactive-method package:
$ meteor add simple:reactive-method
And then:
Template.editUserPermissionsModal.helpers({
roles: function() {
return Roles.getAllRoles();
},
hasRole: function(){
var userId = Session.get("editing_user");
var role = this.name;
var result = ReactiveMethod.call("checkRole", userId, role);
console.log(result);
return result;
}
});

Related

Meteor: How can I restrict user on a chat?

Im working on a project and my last task is to implement publish and subscribe to prevent users seeing conversations they were not involved in. We were given an example code and I notice a filter on the router.
Router.route('/chat/:_id', function () {
// the user they want to chat to has id equal to
// the id sent in after /chat/...
var otherUserId = this.params._id;
// find a chat that has two users that match current user id
// and the requested user id
var filter = {$or:[
{user1Id:Meteor.userId(), user2Id:otherUserId},
{user2Id:Meteor.userId(), user1Id:otherUserId}
]};
var chat = Chats.findOne(filter);
if (!chat){// no chat matching the filter - need to insert a new one
chatId = Chats.insert({user1Id:Meteor.userId(), user2Id:otherUserId});
}
else {// there is a chat going already - use that.
chatId = chat._id;
}
if (chatId){// looking good, save the id to the session
Session.set("chatId",chatId);
}
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
I thought the filter should be on publish and then subscribe. Here is what I have now.
My HTML
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each recentMessages}}
{{> message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
{{#if currentUser}}
<form class="new-message">
<input class="input" type="text" name="text" placeholder="type a message here...">
<button class="btn btn-default">Send</button>
</form>
{{/if}}
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="message">
<div class = "container">
<div class = "row">
<div class = "username">
<img src="/{{avatar}}" class="avatar_img" >
{{username}}
said: {{messageText}}
</div>
</div>
</div>
</template>
Server Side
Meteor.startup(function () {
if (!Meteor.users.findOne()){
for (var i=1;i<9;i++){
var email = "user"+i+"#test.com";
var username = "user"+i;
var avatar = "ava"+i+".png"
console.log("creating a user with password 'test123' and username/ email: "
+ email);
Meteor.users.insert({
profile:{username:username, avatar:avatar},
emails: [{address:email}],
services:{
password:{"bcrypt" : "PASSWORD REMOVED"}}});
}
}
});
Meteor.publish("messages", function (userId) {
return Messages.find();
});
Meteor.publish("userStatus", function() {
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("userData", function(){
if(this.userId) {
return Meteor.users.find({_id: this.userId},{
fields: {'other':1, 'things': 1}});
} else {
this.ready();
}
return Meteor.users.find({ "status.online": true })
});
Client Side
Meteor.subscribe("messages");
Meteor.subscribe("userStatus");
Meteor.subscribe("userData");
// set up the main template the the router will use to build pages
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// specify the top level route, the page users see when they arrive at the site
Router.route('/', function () {
console.log("rendering root /");
this.render("navbar", {to:"header"});
this.render("lobby_page", {to:"main"});
});
Router.route('/chat/:_id', function () {
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
///
// helper functions
///
Template.message.helpers({
userName: function() {
var userId = this.userId;
var user = Meteor.users.findOne(userId);
var username = user && user.profile && user.profile.username;
var avatar = user && user.profile && user.profile.avatar;
return {
username: username,
avatar: avatar
}
}
})
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.profile.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
recentMessages: function () {
if (Session.get("hideCompleted")) {
return Messages.find({checked: {$ne: true}}, {sort: {createdAt: -1}});
} else {
return Messages.find({}, {sort: {createdAt: 1}});
}
},
hideCompleted: function () {
return Session.get("hideCompleted");
},
incompleteCount: function () {
return Tasks.find({checked: {$ne: true}}).count();
}
});
Template.chat_page.events({
// this event fires when the user sends a message on the chat page
'submit .new-message':function(event){
console.log(event);
event.preventDefault();
var text = event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
// see if we can find a chat object in the database
// to which we'll add the message
Meteor.call("sendMessage", text);
},
});
Collections
Messages = new Mongo.Collection("messages");
Shared-Methods
Meteor.methods({
sendMessage: function (messageText) {
if (! Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Messages.insert({
messageText: messageText,
createdAt: new Date(),
username: Meteor.user().profile.username,
avatar: Meteor.user().profile.avatar,
});
}
});

Meteor quick form method not fired

I have a meteor form in which i don't want the user to be able to modify the id, so i omit the field. however i add it back to be able to proceed the update. when i submit the form, it gets stuck and the server is never called..
any help appreciated, thanks!
donators.html:
<template name="dInfo">
{{> dMove}}
{{#if getDoc}}
<label for="num">Numéro</label>
<input type="text" id="num" class="form-control" placeholder="ID" value="{{getDoc._id}}" readonly>
{{> quickForm schema=nwsc omitFields="_id" id="dUpt" doc=getDoc type="method" meteormethod="sayHi" buttonContent="Soumettre" buttonClasses="btn btn-lg btn-primary btn-block"}}
<button type="button" class="btn btn-lg btn-primary btn-warning btn-block btn-del">Supprimer</button>
{{/if}}
</template>
client side:
AutoForm.hooks({
dUpt: {
before: {
method: function(doc) {
doc._id = donators.find().fetch()[cursor.get()]._id;
}
},
onError: function(i, e) {
console.log(i);
console.log(e);
}
}
});
server side:
Meteor.methods({
sayHi: function(ins) {console.log(ins);},
updateDonator: function(ins) {
if(Meteor.userId() === null)
return;
if(ins._id === undefined)
return;
if(check(ins, dSchema) === false)
return;
dSchema.clean(ins);
ins.closed = false;
donators.update({_id: ins._id}, {$set: ins});
logs.insert({user: Meteor.userId(), email: Meteor.user().username, table: 'Donators', action:'Update', item: ins._id, date: new Date()});
}
});
Nothing is ever logged by sayHi, which is never called I suppose, and no error is ever fired.
Thanks!
According to the AutoForm documentation about callback hooks, in the before event you can modify the doc and return it by either:
// Then return it or pass it to this.result()
return doc; (synchronous)
return false; (synchronous, cancel)
this.result(doc); (asynchronous)
this.result(false); (asynchronous, cancel)
Your code does not return the modified doc. Add return doc; to the before -> method:
AutoForm.hooks({
dUpt: {
before: {
method: function(doc) {
doc._id = donators.find().fetch()[cursor.get()]._id;
return doc; // Add this line
}
},
onError: function(i, e) {
console.log(i);
console.log(e);
}
}
});
If AutoForm does not get a doc object back it will cancel the operation.

Limiting each inside of each based on context

Having trouble trying to pass some sort of context into my little test chan-clone.
Problem is I have threads, with replies. Showing just the threads works great, showing threads with replies based upon the threads they come from not so much.
Question is how do I send context on the outer each. Thank you so much for the help!
meteorchan.html
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<h1>Meteor Chan - Let's do this</h1>
<form class="new-thread">
<input type="text" name="text" placeholder="New Thread" />
<button>Submit</button>
</form>
<br/>
{{#each threads}}
{{> thread}}
{{#each replies}}
{{> reply}}
{{/each}}
{{/each}}
</body>
<template name="thread">
<div class="thread">
<span>Anonymous</span> No. {{iden}}
<br>
{{text}}
</div>
<a class="reply" href="#">Reply</a> : <button class="purge">Purge</button>
<form class="new-reply {{this._id}}" style="display:none;">
<input type="text" name="text" placeholder="Add Reply" />
<input type="text" name="thread" value="{{this._id}}" style="display:none;" />
<button>Add New Reply</button>
</form>
<br>
<br>
</template>
<template name="reply">
<div class="reply">
{{text}}
<br>
{{parentThread}}
</div>
</template>
meteorchan.js
Threads = new Mongo.Collection("threads");
Replies = new Mongo.Collection("replies");
if (Meteor.isClient) {
Meteor.subscribe("threads");
Template.body.helpers({
threads: function () {
return Threads.find({}, {sort: {createdAt: -1}});
},
replies: function () {
return Replies.find({}, {sort: {createdAt: 1}});
}
});
Template.body.events({
"submit .new-thread": function (event) {
event.preventDefault();
var text = event.target.text.value;
var currentId = 0;
if(Threads.findOne({}, {sort: {createdAt: -1}})){
currentId = Threads.findOne({}, {sort: {createdAt: -1}}).iden;
}
Threads.insert({
text: text,
createdAt: new Date(),
iden: currentId + 1
});
event.target.text.value = "";
},
"submit .new-reply": function (event) {
event.preventDefault();
var text = event.target.text.value;
var thread = event.target.thread.value;
Replies.insert({
text: text,
createdAt: new Date(),
parentThread: thread
});
event.target.text.value = "";
}
});
Template.thread.events({
"click .purge": function (){
Threads.remove(this._id);
},
"submit .new-reply": function(event){
event.preventDefault();
},
"click .reply": function(){
$('.'+this._id+'.new-reply').css('display','inherit');
}
});
}
if (Meteor.isServer) {
Meteor.publish("threads", function () {
return Threads.find();
});
Meteor.publish("replies", function () {
return Replies.find();
});
}
You can refer to the context in the template helper using this.
So in your replies helper this refers to the current thread you are iterating over using the each.
You can do the following to only fetch replies for the corresponding thread:
replies: function () {
return Replies.find({
// this refers to the current thread
parentThread: this._id
}, {sort: {createdAt: 1}});
}

Meteor: collection empty after insert

I am new to meteor and got stucked and can't understand what am I doing wrong. Enlighten me please.
Here is the HTML file:
<body>
<h1>Do</h1>
{{#if activeTask}}
{{> currentTask}}
{{else}}
{{> newTask }}
{{/if}}
<div>
</div>
</body>
<template name="newTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/></label>
<input type="submit" value="Go"/>
</form>
<!--
{{> inputAutocomplete settings=settings id="msg" class="input-xlarge" placeholder="action"}}
-->
</template>
<template name="currentTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/>{{activeTask.what}}</label>
<div>4h 15m</div>
<input type="submit" value="Stop"/>
</form>
</template>
And here is the JavaScript file:
tasks = new Mongo.Collection('tasks');
if (Meteor.isClient) {
Template.body.helpers({
activeTask: function() {
var task = tasks.findOne(
{
endAt: null
},
{
sort: {
startAt: -1
}
}
);
console.log(task);
return task;
}
});
Template.newTask.events({
'submit' : function(event) {
event.preventDefault();
var now = Date.now();
var what = event.target.what.value;
tasks.insert({ what: what, startAt: now, endAt: null });
}
});
}
It successfully adds a new document into the database and logs this in the helper activeTask. One step later it logs no task at all. It has gone. But why?
If you don't have the package autopublish (https://atmospherejs.com/meteor/autopublish) installed, you need to create a publication (client-side) and subscription (server-side):
if (Meteor.isServer) {
Meteor.publish('tasks', function () {
return tasks.find();
});
}
if (Meteor.isClient) {
Meteor.subscribe('tasks');
}
I've also explained working with collections in a recent blog article.

How to combine Meteor.publish/Subscirbe and collection.allow?

I tried to write some code for understanding Meteor.publish/subscribe and collection.allow
Now It's don't have any error However I can't insert data to my collection.
testing.html
<head>
<title>testing</title>
</head>
<body>
<div class="container">
<div class="col-xs-2">
{{> loginButtons}}
</div>
<div class="col-xs-10">
{{> gettingContent}}
</div>
</div>
</body>
<template name="gettingContent">
<h1>These are all my contents.</h1>
{{#each gettingAll}}
{{title}}
{{author}}
{{/each}}
{{#if currentUser}}
<h2 class="alert">{{denie}}</h2>
<p>Title: <input type="text" id="title"/></p>
<p>Author: <input type="text" id="author"/></p>
<p><input type="button" value="Click" id="action" /></p>
{{/if}}
</template>
model.js
Content = new Meteor.Collection("Content");
Meteor.methods({
creatingContent: function(options) {
check(options, {
title: String,
author: String,
date: new Date()
});
Content.insert({
title: options.title,
author: options.author,
date: options.date
});
}
});
Content.allow({
insert: function(userId) {
if(Meteor.userId() === true){
return true;
} else {
return false;
}
}
});
clientTesting.js
Template.gettingContent.helpers({
gettingAll : function(){
return Meteor.subscribe("getAll");
}
});
var options = {
title: $("#title").val(),
author: $("#author").val(),
date: new Date()
};
Template.gettingContent.events({
'click #action' : function(event,options) {
event.preventDefault();
if(Meteor.userId() === true) {
Meteor.call("creatingContent",options);
console.log("Content was created.");
$("#title").val("");
$("#author").val("");
}else{
Session.set("deny", "You must be login for creating content.");
}
}
});
serverTesting.js
Meteor.publish('getAll', function(){
if(Content.find().count() === 0){
return Session.set("noData", "Data not found'");
} else {
return Content.find({},{sort:{'date':-1}});
}
});
I believe your problem is here: Meteor.userId() === true
By default, a Meteor userId is an alphanumeric character string. Testing for equality to true is always going to be false if the userId exists.
Try this:
if (Meteor.userId()) {
return true;
} else {
return false;
}
Or even simpler:
return !!Meteor.userId();
i didnt test this code but date: new Date() line is incorrect. It Should be Date.
check(options, {
title: String,
author: String,
date: Date
});
Okay Everyone I have fixed some code problem and just two thing I don't understand 1.How to use Meteor.subscribe with Template? and 2. How to render for showing my data. I got error like this
Exception from Deps recompute function: Error: {{#each}} currently only accepts arrays, cursors or falsey values.
testing.html
<head>
<title>testing</title>
</head>
<body>
<div class="container">
<div class="col-xs-2">
{{> loginButtons}}
</div>
<div class="col-xs-10">
{{> gettingContent}}
</div>
</div>
</body>
<template name="gettingContent">
<h1>These are all my contents.</h1>
{{#each gettingAll}}
<p>Title: {{title}}, Author: {{author}}</p>
{{/each}}
<h2 class="alert">{{denies}}</h2>
{{#if currentUser}}
<p>Title: <input type="text" id="title"/></p>
<p>Author: <input type="text" id="author"/></p>
<p><input type="button" value="Click" id="action" /></p>
{{/if}}
</template>
clientTesting
Template.gettingContent.helpers({
gettingAll : function(){
return Meteor.subscribe('getAll') && Content.find().count() ;
},
denies: function(){
return Session.get("deny");
}
});
var options1 = function(title, author){
var title = $("#title").val();
var author = $("#author").val();
if(_.isString(title) && !_.isEmpty(title) && _.isString(author) && !_.isEmpty(author) ){
var result = {title: title, author: author};
return result;
}else{
console.log("Invalid Data")
}
};
Template.gettingContent.events({
'click #action' : function(event) {
event.preventDefault();
Session.set("options", options1());
var options = Session.get("options");
if(Meteor.userId() && !_.isEmpty(options)) {
Meteor.call('creatingContent',options, function(){
console.log("Content was created.");
});
$("#title").val("");
$("#author").val("");
return Session.set("deny", "Your Data was created.");
}else {
return Session.set("deny", "Invalid Data.");
}
}
});
model.js
Content = new Meteor.Collection("Content");
Meteor.methods({
creatingContent: function(options) {
check(options, {
title: String,
author: String
});
if(options.title.length === 0)
throw new Meteor.Error(413, "noData Passed");
if(options.author.length === 0)
throw new Meteor.Error(413, "noData Passed");
Content.insert({
title: options.title,
author: options.author,
date: new Date().getTime()
});
}
});
Content.allow({
insert: function(userId) {
if(Meteor.userId() === true){
return true;
} else {
return false;
}
}
});
serverTesting.js
Meteor.publish('getAll', function(){
return Content.find({},{sort:{'date':-1}});
});

Resources