Add document for current user profile meteor.js - javascript

I want to create a form to add additional profile details (address, phone number) for registered users.
This is my js file:
Template.userProfileEdit.events({
'submit form': function(event, template){
event.preventDefault();
var addressVar = template.find('#inputaddress');
var profile = {};
var fields = template.findAll('input[type="text"]');
for(var i = 0; i<fields.length; i++) {
profile[$(fields[i]).attr('name')] = $(fields[i]).val();
}
if(Meteor.users.update(Meteor.userId(), {$set: {profile: profile}})) {
$(template.find('#thirdNodal')).modal('hide');
}
}
});
Template.userProfileEdit.helpers({
address: function(){
return Meteor.user().profile.address;
}
});
Accounts.findUserByUsername = function (username) {
return Accounts.findUserByQuery({
username: username
});
};
Accounts.findUserByEmail = function (email) {
return Accounts.findUserByQuery({
email: email
});
};
And this is my html form:
<tr>
<td>Address</td>
<td>{{currentUser.profile.address}}</td>
</tr>
Does anybody know the easiest way how to insert(address) and update it? Or is it ok?

This isn't far from a working solution, but I see a couple issues.
Meteor.users.update(Meteor.userId(), {$set: {profile: profile}}) is going to override the profile object, erasing anything already stored there. Also, giving the user client-side permissions to update a user document is a security disaster waiting to happen. Ideally you should make a call to a meteor method located in your server folder instead, so that you can keep your permissions tight by validating the changes on the server.
Second, if you utilize meteor's template system (blaze) you should hardly ever have to use jQuery selectors to retrieve values. Instead, look up HTML forms so that you can use Template events for easy access to form fields upon clicking a submit button.
You should complete the Meteor tutorial which will provide you with all of this information and more.

Related

How to access Meteor user collection on server and debug correctly

When validating objects for database insert, I would like to access the Meteor.users collection to let the user know if he added an event manager to the event who is stored in the database. I extended the discover meteor book example for my needs. I know of the Meteor Accounts API, but I'm stuck at the moment how to proceed.
Here is the Meteor method
Meteor.methods({
eventInsert: function(eventAttributes) {
check(Meteor.userId(), String);
check(eventAttributes, {
title: String,
shareInPercent: Number,
eventDate: Date
});
var errors = validateEvent(eventAttributes);
if (errors.title || errors.shareInPercent || errors.eventDate)
throw new Meteor.Error('invalid-event', "Check your inputs again.");
var user = Meteor.user();
var event = _.extend(eventAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
var eventId = Events.insert(event);
return {
_id: eventId
};
}
});
validateEvent = function (event) {
var errors = {};
if (!event.title)
errors.title = "Please fill in an event title";
if (!event.eventDate)
errors.eventDate = "Please enter a date";
var eventManagersNotExisting = areEventManagersExistingUsers(event.eventManagers);
return errors;
}
areEventManagersExistingUsers = function (eventManagers) {
var eventManagersNotExisting = [];
_.each(eventManagers, function(eventManager){
var eventManagerFound = Meteor.users.findOne({username: eventManager});
});
return eventManagersNotExisting;
}
When an insert is triggered, the Meteor method is called, the validateEvent method is called in-between, which calls itself a helper method to check the users collection as seen here
var eventManagerFound = Meteor.users.findOne({username: eventManager});
If I'm right and because it should make sense, I can't put console.log() messages in the Meteor method, because the method is executed server side and the message won't appear in the browser console.
My first question therefore is, is there a way to look at log messages done on server side, similar to Node, where these messages just appear in the shell?
I read about the meteor debug and meteor shell command, but I'm not seeing any log messages appearing there, which I put into code.
Log messages in validateEvent and areEventManagersExistingUsers appear in the browser though.
Now my second question, when logging messages in validateEvent and areEventManagersExistingUsers, why do they show up in the browser console?
And the result of
var eventManagerFound = Meteor.users.findOne({username: eventManager});
is always undefined, only when eventManager is my own user, it's finding that entry.
But is it working for the Meteor.method call, and I'm seeing only a side effect of the proper execution or do I make something wrong here conceptionally?
Edit:
As said in the commentary below, to fix the console.log() when executing the Meteor method, I first had to activate preserve log in the Chrome console, because the form submit is refreshing the site so fast, I just didn't see that the site was reloaded and the log erased.
But a requirement to see the log in the browser console, is the need to have a successful submit (i.e., I had to fill all fields correctly), for that I fixed the check
check(eventAttributes, {
title: String,
shareInPercent: Number,
eventManagers: [String],
eventDate: Date
});
But a log of Meteor.users.find(); only returns my user and not all users in the database. Any idea how to get all of them without publishing the users collection?

adding data based on users login credentials (Lightswitch HTML)

I've been doing some research into how I can add data based on the login credentials. as an example scenario lets say I want a user to login to the application and then based on there login, add there hours they have done for that day, so like a timesheet application.
I don't want the user that is logged in to see any other names other
than there own.
the browse screen would show only there times they have submitted
rather than everyones aswell.
when using the insert call method in (_customnameDataService.cs) you can add in a username by associating a field within a table like below:
entity.Username = Application.User.Name
so if this is possible there must be a way of calling this in JavaScript when logging in so any help or pointers would be great help. Adding a DataItem and displaying the username would be most preferable. (using edit render code) then from this I can pass it through the hierarchy and display only the information associated with the logged in user.
follow these steps to achieve the above question:
Google GetUserName.ashx to get the code for this file to add to your
Lightswitch HTML Project.
copy the below function into the javascript file (in my case a Browse screen for my users)
function CallGetUserName(operation) {
$.ajax({
type: 'post',
data: {},
url: '../web/GetUserName.ashx',
success: operation.code(function AjaxSuccess(AjaxResult) {
operation.complete(AjaxResult);
})
});
}
For the users that can login in and access the Lightswitch Application, the user information must be stored somewhere, in my case "tbl_Users". This table has a row called username. Using the below code this enables an administrator or someone high up in the hierarchy to access all the users, and also the specific user referenced in the table to access themselves.
myapp.BrowseUsers.username_postRender = function (element,
contentItem) {
msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(PromiseResult) {
if (PromiseResult == 'TestUser' || PromiseResult == 'administrator') {
} else {
contentItem.value = PromiseResult;
}
});
};
What is actually happening?
The function is calling the GetUserName.ashx file, which in turn retrieves the current user logged in. (from the aspnet_Users table which is automatically created) I have used a foreign key to associated my table (tbl_Users) and aspnet_Users together.
in the debug or release environment if you were to add a data item (string) and display this information, it would return ("TestUser")
myapp.BrowseUsers.displayUsername_postRender = function (element,
contentItem) {
msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(PromiseResult)
{
element.innerText = PromiseResult;
}); };

How do I display this collection?

I am trying to make profile page and, I need to display the profilename and bio to a template. The problem is I can't get the Id of each profile object. If I can get the Id of each profile like how it's done in the book with postId. Here The code below is the way I thought it would work but did not. If you tell me how to get the Id that would be great thanks.
Profile = new Meteor.Collection('profile');
Profile.allow({
update: ownsDocument
})
Profile.deny({
update: function(profileId, profile, fieldNames) {
return (_.without(fieldNames, 'bio').length > 0);
}
});
Accounts.onCreateUser(function(options, user){
Meteor.methods({
profile: function(postAttributes) {
var user = Meteor.user();
var profile = _.extend(_.pick(options.profile, 'bio'), {
userId: user._id,
profilename: user.username,
submitted: new Date().getTime(),
postsCount: 0, posts : []
});
var profileId = Profile.insert(profile);
return profileId;
}
});
return user;
});
In the discover meteor example, they are using a method to insert a Post and then return its id. In your case, a new Profile is being inserted inside of an asynchronous callback so there's no way for you to return the id. However, you know the userId, so you can use that to get the profile.
Server
Accounts.onCreateUser(function(options, user){
var user = Meteor.user();
var profile = _.extend(_.pick(options.profile, 'bio'), {
userId: user._id,
profilename: user.username,
submitted: new Date().getTime(),
postsCount: 0,
posts : []
});
Profile.insert(profile);
return user;
});
Client
Template.profile.profile = function () {
return Profiles.findOne({userId: Meteor.userId()});
};
You seem a little confused on some Meteor ideas.
Firstly Meteor.methods({ ... }) are functions that can be called client side using Meteor.call({ })
They should appear at the top level and not inside other functions like Accounts.onCreateUser
For this use case I don't see why you need a method at all though. All you want to do is retrieve data that you will be storing in data that is going to be sent to the client anyway.
If you use the accounts-base package then you automatically get a Meteor.users collection that would be fine for what you want to do. I don't see the need for a Profile collection
Here's a meteorpad illustrating it: http://meteorpad.com/pad/xL9C8eMpcwGs553YD
Note I store the bio in the profile section of the user. This is because a user can edit their own profile section by default and so I can do Meteor.users.update( ... ) client side.
This is just an example to show some of the concepts. It does have bad practice in it. For a start I would recommend adding the package accounts-ui and using the {{> loginButtons}} helper. It gives you the error checking and so on. The reason I didn't use it was because I wanted to show how to allow a user to enter their bio prior to creating the account and how you can use that in Accounts.onCreateUser.

Meteor - Publish/Subscribe to user data while not logged in

I've searched and read through pretty much all the questions relating to Meteor's pub/sub functionality but cant seem to find an answer that directly addresses my question.
I am learning Meteor and would like to create the following;
A user visits a page and is presented with an input box, they enter their username and if its a valid username I present them with the password input field.
The problem I seem to be having is that I cant access the Meteor.users collection unless I am logged in (the auto-publish and insecure packages have been removed).
So my question is 2 fold;
Is is possible to access the Meteor.users.find({username: 'value of input box'}) without being logged in?
If so, what do I need to do?
Here is how I have my code structured below;
/server/publications.js:
Meteor.publish("staff", function () {
return Meteor.users.find();
});
/lib/router.js:
Router.configure({
layoutTemplate: 'layout'
});
Router.map(function() {
this.route('home', {path: '/', data: function() {return Meteor.subscribe('staff');} });
});
/client/views/home/home.js:
Template.home.events({
'submit form': function(e) {
e.preventDefault();
var guest = $("input#name");
var staffMember = Meteor.users.findOne({username: $(guest).val()});
});
Unless I am logged in as any user, Meteor.users.find().fetch() always returns an empty array. Any help is greatly appreciated.
You are publishing all the Users to be visible on the client. That is not a very secure position to be in since all your user data will be visible on all the clients. The pragmatic way to solve the problem is to allow the user to see only a limited number of users, i.e. publish a different set of users, depending on the userId.
As #Hubert's answer below indicates, you also need to subscribe to the publication on the client to actually fetch the published accounts over.
You can do this checking for validity securely by creating a Method call on the server:
/server/methods.js:
Meteor.method({
validUserName: function (userName) {
check(userName, string);
// You have access to Meteor.users here.
// This assumes that mere existence of a user with the same username will
// render the entered username invalid. If there are more checks you want to
// make, they should be made here.
return !!Meteor.users.find({username: userName});
}
});
/client/views/home/home.js:
Template.home.events({
'submit form': function(e) {
e.preventDefault();
var guest = $("input#name");
var userNameEntered = $(guest).val();
Session.set('requestInProgress', true);
Meteor.call('validUserName', function (isUserNameValid) {
Session.set('requestInProgress', false);
// ... take action here.
});
});
You're publishing all your user data to everyone, now you just need to subscibe to it:
Deps.autorun(function(){
Meteor.subscribe('staff');
});
That said, make sure that in such subscription you publish only non-sensitive user data, as it will be visible to everyone. Ideally, create another subscription for that purpose and filter just the username via the fields param.

Picking up meteor.js user logout

Is there any way to pick up when a user logs out of the website? I need to do some clean up when they do so. Using the built-in meteor.js user accounts.
I'll be doing some validation using it, so I need a solution that cannot be trigger on behalf of other users on the client side - preferably something completely server side.
You may use Deps.autorun to setup a custom handler observing Meteor.userId() reactive variable changes.
Meteor.userId() (and Meteor.user()) are reactive variables returning respectively the currently logged in userId (null if none) and the corresponding user document (record) in the Meteor.users collection.
As a consequence one can track signing in/out of a Meteor application by reacting to the modification of those reactive data sources.
client/main.js :
var lastUser=null;
Meteor.startup(function(){
Deps.autorun(function(){
var userId=Meteor.userId();
if(userId){
console.log(userId+" connected");
// do something with Meteor.user()
}
else if(lastUser){
console.log(lastUser._id+" disconnected");
// can't use Meteor.user() anymore
// do something with lastUser (read-only !)
Meteor.call("userDisconnected",lastUser._id);
}
lastUser=Meteor.user();
});
});
In this code sample, I'm setting up a source file local variable (lastUser) to keep track of the last user that was logged in the application.
Then in Meteor.startup, I use Deps.autorun to setup a reactive context (code that will get re-executed whenever one of the reactive data sources accessed is modified).
This reactive context tracks Meteor.userId() variation and reacts accordingly.
In the deconnection code, you can't use Meteor.user() but if you want to access the last user document you can use the lastUser variable.
You can call a server method with the lastUser._id as argument if you want to modify the document after logging out.
server/server.js
Meteor.methods({
userDisconnected:function(userId){
check(userId,String);
var user=Meteor.users.findOne(userId);
// do something with user (read-write)
}
});
Be aware though that malicious clients can call this server method with anyone userId, so you shouldn't do anything critical unless you setup some verification code.
Use the user-status package that I've created: https://github.com/mizzao/meteor-user-status. This is completely server-side.
See the docs for usage, but you can attach an event handler to a session logout:
UserStatus.events.on "connectionLogout", (fields) ->
console.log(fields.userId + " with connection " + fields.connectionId + " logged out")
Note that a user can be logged in from different places at once with multiple sessions. This smart package detects all of them as well as whether the user is online at all. For more information or to implement your own method, check out the code.
Currently the package doesn't distinguish between browser window closes and logouts, and treats them as the same.
We had a similar, though not exact requirement. We wanted to do a bit of clean up on the client when they signed out. We did it by hijacking Meteor.logout:
if (Meteor.isClient) {
var _logout = Meteor.logout;
Meteor.logout = function customLogout() {
// Do your thing here
_logout.apply(Meteor, arguments);
}
}
The answer provided by #saimeunt looks about right, but it is a bit fluffy for what I needed. Instead I went with a very simple approach like this:
if (Meteor.isClient) {
Deps.autorun(function () {
if(!Meteor.userId())
{
Session.set('store', null);
}
});
}
This is however triggered during a page load if the user has not yet logged in, which might be undesirable. So you could go with something like this instead:
if (Meteor.isClient) {
var userWasLoggedIn = false;
Deps.autorun(function (c) {
if(!Meteor.userId())
{
if(userWasLoggedIn)
{
console.log('Clean up');
Session.set('store', null);
}
}
else
{
userWasLoggedIn = true;
}
});
}
None of the solutions worked for me, since they all suffered from the problem of not being able to distinguish between manual logout by the user vs. browser page reload/close.
I'm now going with a hack, but at least it works (as long as you don't provide any other means of logging out than the default accounts-ui buttons):
Template._loginButtons.events({
'click #login-buttons-logout': function(ev) {
console.log("manual log out");
// do stuff
}
});
You can use the following Meteor.logout - http://docs.meteor.com/#meteor_logout

Categories

Resources