So I have two collections, 'Posts' and 'Locations' and a simple html form for users to add themselves into the Posts collection. Once added, I display a list of all users but also want to add some extra data to this list if they have attributes which appear in the Locations collection.
So, as an example, the user form has Name, Age, Location and the Locations collection has information on the location they live in, such as "London, Capital, UK". If a user from London enters their details I'd like to display the details they entered on the form along with "You live in a Capital city" if their location features in the Locations collection.
So, some code to insert the details from the form:
post_submit.js:
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
name: $(e.target).find('[id=usersName]').val().toUpperCase();,
age: $(e.target).find('[id=age]').val(),
location: $(e.target).find('[id=location]').val().toUpperCase(),
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return throwError(error.reason);
Router.go('postPage', {_id: result._id});
});
}
});
and in posts.js
Meteor.methods({
postInsert: function(postAttributes) {
var post = _.extend(postAttributes, {
submitted: new Date(),
});
var postId = Posts.insert(post);
return {
_id: postId
};
}
});
But how do I define the subscription to the Locations collection and where do I call the 'lookup' to that collection in order to lookup the location and then enable me to display that in some other portion of the page. I'm sure this is pretty simple, I'm struggling to connect the dots though.
You'll have to make a new publication that publishes the Posts and then publishes the Locations as a "dependency". There is a package that makes this very simple. copleykj:simple-publish will help you accomplish this. The samples in the readme should be enough to get you started.
Related
MeteorJS newbie here. I have a collection of "posts" which has fields: title, createdAt, body, userId. Although I only have two users, there seems to be a different userId for each post. I basically just want to display the blog posts, along with their respective user's e-mails. My current implementation is as follows and it is displaying the email of the current user, not the owner of the blog post's email:
"click .main-feed-post" : function(event) {
...
document.getElementById('post-view-email').innerHTML = Meteor.users.findOne({_id: this.userId}).emails[0].address;
...
}
That is currently outputting only the logged-in user's email address. The previous is for the viewing individual blog posts. In addition, I have a main feed where I list all the blog posts and their respective owner's emails:
{{#each posts}}
<li class="main-feed-post">
{{title}}
<div class="main-feed-post-data">
<label>BY</label> {{getUserEmail}} <label>AT</label> {{formattedDate}}
</div>
</li>
...
getUserEmail : function() {
return Meteor.users.findOne({_id: this.userId}).emails[0].address;
}
{{/each}}
This is outputting nothing, unless it is the current user's email. Ideally, I would add a username field to the user object and display that instead of the userId or email. I'm not sure how to implement this using the accounts-ui and accounts-password packages. Any help is welcome! Thanks in advance!
Inside Server:
Meteor.publish("allUsers", function () {
return Meteor.users.find({});
});
Meteor.publish("allUserData", function () {
return Meteor.users.find({}, {fields: {"emails.address": 1}});
});
Inside Client:
Meteor.subscribe("allUsers");
Meteor.subscribe("allUserData");
I have a Friends table that contains userIds for a user list of friends. The user column is a pointer to the User table and the friendId does the same.
I'm attempting to find all of the users that equal the "friendId" for a particular user and send notifications to those users.
I've added a "user" column to my Installations table which is a pointer to "User" so that I can find my installations for specific users and send a push notification.
The issue I am having is I an unable to link those queries together to get send my push notifications to my list of friends.
Any suggestions are helpful.
My current cloud code
Parse.Cloud.define("pushCheckIn", function(request, response) {
// Find users near with pending messages
var friendList = Parse.Object.extend("Friends");
var username = request.params.username;
var location = request.params.location;
var userQuery = new Parse.Query(friendList);
userQuery.equalTo("friendId", Parse.User);
// Find devices associated with these users
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery("user", userQuery);
var alertMsg = username + " checked-in at " + location;
Parse.Push.send({
where: pushQuery,
data: {
alert: alertMsg,
sound: "beep-shinymetal.caf",
title: alertMsg
}
}, {
success: function() {
response.success(alertMsg);
// Push was successful
},
error: function(error) {
response.error("push failed");
}
});
});
Your description of your tables is complicated and I don't fully understand, however I think that new Parse.Query(friendList); should rather be new Parse.Query("Friends"); and userQuery.equalTo("friendId", Parse.User); doesn't make sense at all, if it is string you should use userQuery.equalTo("friendId", yourFriendIdString);
If you try to explain your tables better, I can try to give you better advice :)
The Installations table can not be queried directly without using the master key. To query the installations table, use
Parse.Cloud.useMasterKey();
before executing the query
I have a form that tried to update meteor.users with extra information about users with the following helper
Template.Profile.events({
'submit form': function(e) {
e.preventDefault();
var post = {
firstName: $(e.target).find('[name=firstname]').val()
};
Meteor.users.update( { _id: Meteor.userId() }, { $set: { 'firstName': post.firstName }} );
}
});
however, i get update failed: Access denied
Another question is, I am wondering whether I should do extra update straight to Meteor.users collection or should I have a seperate collection to store these data.
thanks
Due to the fact that you are trying to set an attribute directly on the base user object, you are receiving the 'Access denied' error. According to the Meteor documentation for Meteor.users:
By default, the current user's username, emails, and profile are
published to the client.
This means that you can update any of those user attributes, but if you want to add additional ones, it is best to add them to one of these already existing fields. I would suggest adding something like `firstName' to the profile attribute. In this case, your code would look something like this:
Meteor.users.update({_id: Meteor.userId()}, {$set: {'profile.firstName': post.firstName}});
I'm new to Emberjs, and I'm trying to get my head around how to get data out of my model.
Here's the data structure as its being returned from my server
candidate: {
image: [image],
user: {
name: [User Name]
},
agent: {
phone: [phone]
team: {
name: [Team Name]
}
user: {
name: [User Name]
}
}
}
I cant get ember to recognize any associations more than one level deep.
heres my candidate controller
App.CandidateController = Ember.ObjectController.extend({
location: function() {
var address = this.get('address');
var city = this.get('city');
var state = this.get('state');
var zip = this.get('zip');
return address + ' ' + city + ' ' + state + ', ' + zip;
}.property('address', 'city', 'state', 'zip')
});
App.CandidateSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
agent: {embedded: 'always'},
user: {embedded: 'always'}
}
});
This allows me to get one level deep, but I need two or three levels of association.
Is there a different way to go about it?
I thought organizing everything on the server the way I want it displayed, and just returning the data as its supposed to be rendered to ember, but I have a feeling I'll run into problems when trying to update the models.
Any help is appreciated.
/****** UPDATE *******/
I have refactored my code and I am now returning data from the serializer like this:
{
candidate: {
// relevant fields
},
agent: {
//relevant fields
},
team: {
// relevant fields
}
user: [
{ // candidate user fields },
{ // agent user fields }
]
};
However, data is still not available in my template. In the Ember chrome extension, in the data tab, I get
candidate: (1)
agent: (0)
team: (0)
user (2)
I can see candidate and user data, but not agent or team data. In my candidate model, I have:
App.Candidate = DS.Model.extend({
// other fields
user_id: DS.attr('number'),
agent_id: DS.attr('number'),
user: DS.belongsTo('user'),
agent: DS.belongsTo('agent')
});
It doesn't seem like the belongsTo association actually does anything.
So the first issue is that I'm not getting the correct data, the second issue, and the one that makes me think I am going about this incorrectly, is that I have two users information that I need to display in the template. The first is user information associated with the candidate, and the second is user information that is associated with the agent. Both data need to appear in the same template. Since there is no hierarchy to the data, how would the template know which user info to display in each location?
Again, I think Im thinking about this whole thing incorrectly.
Thanks for your help.
Ember data expects models in a JSON response to be flat, and it's the job of the Serializer to transform your server response into that format. Associations usually are done by ids. The second level is not working because Ember needs to turn each level into an Ember.Object in order to observe property changes.
It might help to look over the JSON conventions portion of the guides. You can also plug your models into the ember data model maker and see what your responses should look like. Finally, make sure you're using the ember-inspector Chrome extension. When I am debugging ember-data related issues, it's usually easiest to just stop after the model hook returns, look in the inspector's store tab, and examine what data has been loaded.
I'm looking at this example of modeling a blog system using javascript, code snippet copied as below:
var user = Parse.User.current();
// Make a new post
var Post = Parse.Object.extend("Post");
var post = new Post();
post.set("title", "My New Post");
post.set("body", "This is some great content.");
post.set("user", user);
post.save(null, {
success: function(post) {
// Find all posts by the current user
var query = new Parse.Query(Post);
query.equalTo("user", user);
query.find({
success: function(usersPosts) {
// userPosts contains all of the posts by the current user.
}
});
}
});
It basically creates a post object and sets the current user object to its user field. To show all blog posts by the current user, it queries all blog posts with the user field set to the current user.
But since the User table by default is read only to all users, wouldn't this be problematic that a malicious user (X) can create random posts and "claim" that they are create by another user (Y), by setting the user field of those posts to Y as he queries from the User table? So the consequence would be that when the system shows posts for user Y, he would see all his true posts in addition to the post that was "forged" by X.
Is the mitigation that the User table needs to be ACL'd somehow? But if it is the solution, then why is the default behavior that an arbitrary user can see the entire User table?
Cloud Code is your friend here.
In this case you want a beforeSave handler that locks the user field to the currently authenticated user on new objects, and rejects the save if they're updating a post and trying to change the user field (or just using ACLs to prevent everyone except the post owner from modifying Post rows).
Something like this:
Parse.Cloud.beforeSave('Post', function(request, response) {
var post = request.object;
var user = request.user;
if (post.isNew()) {
post.set('user', user);
response.success();
} else {
// any special handling you want for updates, e.g.:
if (post.dirty('user')) {
response.error('Cannot change the owner of a Post!');
} else {
response.success();
}
}
});
My recommended approach to handling updates for something like a "Post" would be to prevent all updates. In the "Set permissions" for the class in the Data Browser I would change the following:
Update : Disabled
Delete : Disabled
To disable something just untick the "Any user can perform this action". Optionally you might want to assign a Role like "Administrator" or "Moderator" to allow those people to directly update/delete items.
These functions would then only be possible from Cloud Code when useMasterKey() is used, e.g.:
Parse.Cloud.define('deletePost', function(request, response) {
var postID = request.params.postID;
var query = new Parse.Query('post');
query.get(postID).then(function (post) {
if (post) {
// post found
var postOwner = post.get('user');
if (postOwner.id == request.user.id) {
// we let the owner delete their own posts
// NOTE: need to use the master key to modify/delete posts
Parse.Cloud.useMasterKey();
post.destroy().then(function () {
// TODO: delete all replies too?
response.success();
}, function (error) {
response.error(error);
});
} else {
response.error('Only the owner of a post can delete it!');
}
} else {
// post not found, might as well respond with success
response.success();
}
}, function (error) {
response.error(error);
}
});
But since the User table by default is read only to all users,
wouldn't this be problematic that a malicious user can create random
posts and "claim" that they are create by another user, by setting the
user field to the other user?
You can play around with curl to explore this.
IMO - you are right about world read on the _User class. So what. That is read.
When it comes to POST action, you are going to need an authenticated session as the user in question. You cant just spuuf things by claiming that u are a user that you read on the table.
try curl posts without an established session as the user. You will get a 403 or some 'illegal access' response.