Publishing all Meteor.users doesn't work - meteor

I'm trying to publish all the usernames to the clients, even if not signed in. For that, on the server I have:
Meteor.publish("users", function() {
return Meteor.users.find({}, {fields : { username : 1 } });
});
And on the client:
Meteor.subscribe("users");
However, when I try to access the Meteor.users collection, I find nothing there.
(This is essentially the same as the question here: Listing of all users in the users collection not working first time with meteor js, only without checking the roles for admin first. Still doesn't seem to work..)
I'm probably missing something silly..

I find the same issue, and after doing a research i find this package, i think it may should help you.
Take a look and hope it help you
Update
First move the subscription to the /lib folder, just to make sure its the first thing meteor do when start, also change a little bit the subscription like this on the /lib, folder.
Tracker.autorun(function() {
if(Meteor.isClient) {
if (!Meteor.user()) {
console.log("sorry you need to be logged in to subscribe this collection")
}else{
Meteor.subscribe('users');
}
}
});
For better security we just subscribe to the users collection when the client its logged in

This code outputs all the usernames to the clients, even if not signed in (in this case on /users page):
server/publications.js:
Meteor.publish("userlist", function () {
return Meteor.users.find({},{fields:{username:1}});
});
client/users_list.js:
Template.usersList.helpers({
users: function () {
return Meteor.users.find();
}
});
client/users_list.html:
<template name="usersList">
{{#each users}}
{{username}}
{{/each}}
</template>
lib/router.js (using iron:router package):
Router.route('/users', {
name: 'usersList',
waitOn: function(){
return Meteor.subscribe("userlist");
}
});
Hope it helps.

Related

How to show correct profile picture Meteorjs

I am using CollectionFs to Upload profile pictures. Uploading and storing the image is successful. I can insert and show the image alright for one user but the
problem is:
For multiple users, when a user visit other users profiles, He sees his own picture rather than seeing the profile owner's picture!
I understand its the mongo query I have in my helper function thats causing the issue but can't just get it to work no matter how many "This._id" I Try.
Here is the javaScript
Router.route('show',{
path:'/list/:_id',
data: function(){
return Main_database.findOne({_id: this.params._id});
}
});
Template.upload.events({
'change #exampleInput':function(event, template){
var file = $('#exampleInput').get(0).files[0];
fsFile = new FS.File(file);
fsFile.metadata = {ownerId:Meteor.userId()}
Images.insert(fsFile,function(err,result){
if(!err){
console.log("New images inserted")
}
})
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId':Meteor.userId()});
}
});
And here is the html:
<template name="upload">
<div class="container">
<div class="row">
<input type="file"
id="exampleInput">
</div>
</div>
</template>
<template name="profile">
{{#each profilePic}}
<img src="{{this.url}}"
height="400" width="400"
class="img-circle">
{{/each}}
</template>
Thanks
B.s : after following the answer given, I attached the photo in the profile.xxx field. But its still showing the wrong picture. The mongo query is still showing the wrong picture.
here is the code,
Router.route('show',{
path:'/list/:_id',
data: function(){
return Main_database.findOne({_id: this.params._id});
}
});
Template.upload.events({
'change #exampleInput':function(event, template){
var file = $('#exampleInput').get(0).files[0];
newFile = new FS.File(file);
newFile.metadata = {'ownerId':Meteor.userId()};
Images.insert(newFile,function(err,result){
if(!err){
console.log(result._id);
Meteor.users.update(Meteor.userId(),{
$set: {
'profile.profilePic': result._id
}
});
}
});
}
})
// .....................profile pic.............
Template.profile.helpers({
profilePicture: function () {
return Images.find({'_id':Meteor.user().profile.profilePic});
}
});
Finally was able to do it. Being a beginner, I was stuck at uploading images and then showing them for my users for days. Tried almost each method out there, none worked. asked everywhere, none of the answer worked. Finally , a dead simple package from cosio55:autoform-cloudinary worked like magic!!! Just take a look at the problems I faced while using these packages:
1. cfs:ui
{{#with FS.GetFile "images" selectedImageId}}
// image url
{{/with}}
problem:
with this was I couldn't get the selectedImageId .
2. cfs:gridfs
problem :
grid fs stores image in a separate collection. My user list uses iron router to show the user list form another collection. Image was getting uploaded into the images collection. But For the love of my life, I couldn't show them correctly. Each user was seeing his own picture rather than the profile owner's picture. happened because of a wrong mongo query but I couldn't get the right query. Tried attaching the photo in the profile.profilePicture, but same problem of wrong image stayed.
And I had to put the upload photo in a separate page and NOT in the autoform.
3. lepozepo:cloudinary
Problem:
Image uploaded fine. But had problem getting /storing the image url. Couldn't get
And I had to put the upload photo in a separate page and NOT in the autoform.
public_id ????? Got lost there.
4. autoform-file by yogiben
same problem as GridFs.
Finally with this cosio55:autoform-cloudinarypackage took me just a minute to figure things out. A minute vs days of other big name packages!!!!
:smiley:
<div> <img src=" {{image}}" alt=""> Image </div>
just add {{image} in the img source and thats it. The image url is stored in the same collection autoform stores everything.
Cheers Mates.
For the profilePic it will return the same user profile image, instead you should do a query by _id and not metadata.ownerId
To do that you should have a reference for the image in users collection when you insert the image something like:
Images.insert(file, function (err, res) {
if (!err) {
Meteor.users.update(Meteor.userId(),{
$set: {
'profile.profilePic': res._id,
}
});
});
And when you need to display the image you can do something like:
Template.profile.helpers({
profilePic: function () {
return Images.find({'_id':Meteor.user().profile.profilePic});
}
});
First things first: Meteor.user() and Meteor.userId() always shows data for CURRENTLY logged-in user.
So when user wants to see his own profile, it is right to use your current Template helper like this:
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId':Meteor.userId()});
}
});
But when user goes to another user's profile, you should fetch that user's info like this: Meteor.user("another-user-id");
So how to do this? Let's suppose that you have set routing in you Meteor app with Iron Router or Flow Router and for user profile page you have set-up route path something like this: /user/:_id.
Now, you want to publish only this user's data like this in your publications on the server:
Meteor.publish("userData", function(userId){
return = Meteor.users.find({"_id": userId});
});
...and subscribe to this publication on client (we'll use Template-level subscriptions!)
With Iron Router:
var userId;
Template.profile.onCreated(function() {
var self = this;
self.autorun(function() {
userId = Router.current().params._id;
self.subscribe("userData", userId);
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId': userId});
}
});
With FlowRouter:
var userId;
Template.profile.onCreated(function() {
var self = this;
self.autorun(function() {
userId = FlowRouter.getParam("_id");
self.subscribe("userData", userId);
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId': userId});
}
});

Ability to update collections on front-end in Meteor

I'm struggling to get my head around the ability to edit any collection that's available to the front-end, and how to prevent it - and if this is a feature only available to Mongol.
Mongol states:
… because Mongol is a debugOnly package, it does not compile to production code.
Which is great, but as I'm new to Meteor I'm not sure if Mongol is just an interface in this scenario, or if the ability to update is something always available to the front-end (and Mongol is just making it easier).
My scenario is that I have a form submission page that grabs the profile of an associated Meteor.user to display their name along with the form:
HTML
<template name="form">
<h2>Submission for: {{ user.profile.name }}</h2>
<form id="brief">
…
</form>
</template>
Route
Router.route('/form/:_id', {
loadingTemplate: 'loading',
waitOn: function () {
return Meteor.subscribe('forms', this.params._id);
},
action: function () {
this.render('form', {
data: {
_id: this.params._id,
form: function() {
return Forms.findOne({});
},
user: function() {
return Meteor.users.findOne({});
}
}
});
}
});
Publication
Meteor.publishComposite('forms', function(formId) {
return {
find: function() {
return Forms.find({_id: formId});
},
children: [
{
find: function(form) {
return Meteor.users.find({_id: form.userId}, {fields: {profile:1}});
}
}
]
};
});
This works perfectly - however using the Mongol console I can update, duplicate and remove the user. Naturally in a production environment I wouldn't want this to be possible - is this something only available because Mongol is there, or could a determined user achieve the same thing without Mongol?
If they can, how do I prevent/work with it?
Edit: It's also elaborated on here: https://github.com/msavin/Mongol/blob/master/documentation/SECURITY.md
Given how they refer to 'special methods' I'm assuming that's what allows this to happen, and that the ability to directly update the fields isn't ordinarily available to the front-end. If anyone's able to confirm that would be ace!
Yes, Mongol uses a backdoor solution (in debug/dev only) to access and change your mongo docs in the db. This means it wont be included in your production code. As far as client side operations on the DB, Meteor restricts updating, removing, and inserting to the server although you can use Meteor's allow/deny rules to allow the client to update a DB collection. However, allow/deny rules need to be very tight to ensure the client can not alter data they should not be able to. For this reason, most people stick to using server side DB changes that are fired by meteor.methods that the client can initialize.
Since it is a debugOnly package as long as you don't deploy to production in "debug mode" it is safe.

Route returning blank page [METEOR]

I am trying to create a route for a user profile page, but when I visit the route it shows up as a completely blank page and with no errors in the terminal. Nothing whatsoever is shown, including static HTML. Here's the code:
routes.js
Router.route('/user/:_id', function () {
this.render('user');
}, {
name: 'user',
data: function(){
return Users.findOne({_id: this.params._id})
}
});
user.html
<template name="user">
<p>hello</p>
</template>
At the moment, I am using the default user accounts package and have not added any publication or subscription code.
Are you sure Users is an existing collection?
At the moment, I am using the default user accounts package and have
not added any publication or subscription code.
In that case, with autopublish enabled, your problem is probably solved by changing
data: function(){
return Users.findOne({_id: this.params._id})
}
into:
data: function(){
return Meteor.users.findOne({_id: this.params._id})
}
although it's strange this doesn't throw an error in your console...
Not sure if this is the reason, but I think that with multiple options for the route, you should incapsulate this.render in an action parameter. Something like this:
Router.route('/user/:_id', {
name: 'user',
data: function() {
return Users.findOne({_id: this.params._id})
},
action: function () {
this.render('user');
}
});
Source
I get an error in the terminal: "Users is not defined":
http://meteorpad.com/pad/eciFidhwHmLhjWmF3/Leaderboard
In your data function, try substituting Meteor.users.findOne({_id: this.params._id})
If you fix the HackPad I listed, Meteor.users won't work since the current version of HackPad doesn't support a late enough version of Meteor with Meteor.users. However, if you comment out your data function, you should at least see the page.

I misunderstood Session what is a good alternative?

Currently I'm creating an app with meteor en learning it while building. I try to incorporate Sessions instead of writing everything to the database (what I was doing). In my understanding Session is a global object which stores key-value pairs and is reactive. Therefore I thought it would a great choice to use for template rendering specifics in my simple game. My goal is small game, and the different steps will be renderend in a template for each player based on certain action they made.
I rewrote my app and wanted to use Session in this way (simplified of course).
My template:
<template name="gameRoom">
<button id='click'>click</button>
{{#if lastAction}}
{{>waiting}}
{{/if}}
</template>
Template.gameRoom.events({
lastAction: function() {
return Session.get('lastAction') === Meteor.userId();
};
})
Template.gameRoom.helpers({
'click #click' : function() {
Session.set('lastAction', Meteor.userId());
};
})
However this doesn't work they way I thought it would work. It looks like that each Session is individual for each user (what makes sense of course considering it's (sort-of) a replacement of cookies).
So my question is:
Is there a good alternative for Sessions? Do i really need to have a
alternative or is using a database ok for this? What about a local database?
Your events and helpers functions are backwards, you're missing a couple curly braces, and your event key (the button's ID) is wrong. Try this:
Template.gameRoom.helpers({
lastAction: function() {
return Session.equals('lastAction', Meteor.userId());
}
});
Template.gameRoom.events({
'click #click': function() {
Session.set('lastAction', Meteor.userId());
}
});
Edit: from what you are trying to do, it might make sense to do something like this:
Actions = new Meteor.Collection('actions');
if (Meteor.isClient) {
Template.gameRoom.events({
'click #click': function() {
Actions.insert({userId: Meteor.userId()});
}
});
Template.gameRoom.helpers({
lastAction: function() {
var lastAction = Actions.findOne() || {};
return lastAction.userId === Meteor.userId();
}
});
}

How to restrict a route to an admin user in Meteor?

as an iOS developer primarily, I'm very new to webdev. I'm looking into Meteor and have some questions regarding routing -- my apologies if they are very easy.
I am using the Meteor Router package to create routes, but I would like to have some pages only accessible to the admin user.
Meteor.Router.add({
'/' : 'home',
'/admin' : 'admin'
});
So I have a simple route setup as above, but I'm not sure how to restrict access to the /admin route.
Is it as simple as something like this? What would be a good way to restrict the route to the /admin page and show a warning or perhaps even redirect them back to the / page?
Thank you!
client.html
<head>
<title>My App</title>
</head>
<body>
{{renderPage}}
</body>
<template name="home">
{{greeting}}
</template>
<template name="admin">
{{greeting}}
</template>
client.js
Template.admin.greeting = function () {
var currentUser = Meteor.user();
if (null !== currentUser && 'admin' === currentUser.username) {
return "Hello Admin!";
}
else{
return "Sorry, only admins can see this page";
}
};
The best way to restrict access to a route is with the router itself (rather than pushing the problem to your controller). You have a couple of choices in how you do this:
Routing Function
You could make the /admin route look like:
'/admin': function() {
return {
as: 'admin',
to: function() {
if (Meteor.user() && Meteor.user().username === 'admin') {
return 'admin';
} else {
return 'unauthorized';
}
}
};
}
I'm assuming you have an unauthorized template that renders a 403 page or something informative.
Filter
Alternatively, you can leave your original /admin route as it was and add a filter:
Meteor.Router.filters({
'needsAdmin': function(page) {
if (Meteor.user() && Meteor.user().username === 'admin') {
return page;
} else {
return 'unauthorized';
}
}
});
and use it like so:
Meteor.Router.filter('needsAdmin', {only: 'admin'});
Personally, I like the filter option because it's reusable and it's a little more obvious what's going on.
Another solution is to use Roles package and make sure the user has the 'admin' role before serving data.
$ mrt add roles
Then you can check for roles like with a nice syntax:
if(!Roles.userIsInRole(Meteor.user(), ['admin'])) {
// Redirect...
}
Roles is integrated with the Meteor accounts system and plays nicely with most of the accounts packages.
If you are looking to manage accounts (create/delete Roles and add/remove Roles from a given user) I've created the package Accounts Admin UI. The README has a quickstart and some some notes on how to integrate this with other routing packages.
$ mrt add accounts-admin-ui-bootstrap-3
Use the and parameter:
Meteor.Router.add({
'/admin': { to: 'admin', and: function() {
if (!Meteor.user() || Meteor.user().name != 'admin'){
Meteor.Router.to('/');
}
}}
});
Everyone here has made great points on how to protect an admin panel on the router level. Another possibility is to skip the router all together. I've recently done this with Meteor Candy, a drop-in admin package for Meteor.
The idea is, you could create a Reactive-Dict to hold the state of the admin interface. If you place it into a package, you can ensure that it never collides with your application code. And with the new Dynamic Imports feature, you can virtually keep it off the client until its needed.
Here's how that might work:
<template name="adminPanel">
{{#if show}}
{{> adminPanelUI}}
{{/if}}
</template>
AdminUI = new ReactiveDict();
Meteor.defer(function () {
Blaze.render(Template.MeteorCandy, document.body);
});
Template.adminPanel.helpers({
show: function () {
if (AdminUI.get('show')) {
return true;
}
}
})
On top of that, all you would need is to define the occasion that sets "show" to a truth-y value.

Resources