Skip to content

Instantly share code, notes, and snippets.

@psaia
Created September 30, 2013 14:17
Show Gist options
  • Save psaia/6764490 to your computer and use it in GitHub Desktop.
Save psaia/6764490 to your computer and use it in GitHub Desktop.
mongoose = require 'mongoose'
helpers = require '../libs/helpers'
events = require "../libs/events"
config = require "../config"
async = require "async"
types =
STATUS: "status"
INTRODUCTION: "introduction"
NOTIFICATION: "notification"
populateMap = {}
populateMap[types.STATUS] = "by"
populateMap[types.INTRODUCTION] = "user1 user2 mutual"
populateMap[types.NOTIFICATION] = "for context_user context_user_2"
Story = new mongoose.Schema
created: { type: Date, default: Date.now, required: true }
scope:
public: { type: Boolean, default: false, required: true }
network: { type: Boolean, default: false, required: true }
connections: { type: Boolean, default: true, required: true }
custom: { type: Boolean, default: false, required: true }
scopedUsers: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
deleted: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
type: { type: String, required: true, enum: [types.STATUS, types.INTRODUCTION, types.NOTIFICATION] }
story: { type: mongoose.Schema.Types.ObjectId, required: true }
getDoc = (type, _id, fn) ->
Introduction = require "./introduction"
Status = require "./status"
Notification = require "./notification"
switch type
when types.STATUS then _Model = Status
when types.INTRODUCTION then _Model = Introduction
when types.NOTIFICATION then _Model = Notification
# Populate the document with necessary properies.
_Model.findOne({ _id: _id }).populate(populateMap[type]).exec (err, doc) ->
return fn err if err
return fn null, false if not doc
if type is types.STATUS # If the document is a status:
doc.by.populate "avatar", (err, u) ->
return fn err if err
doc.by = u
fn err, doc
else if type is types.NOTIFICATION # If the document is a notification:
async.waterfall [
(done) ->
return fn(null, false) if not doc.for
doc.for.populate "avatar", (err, u) ->
return done err if err
doc.for = u
done()
(done) ->
return done() if not doc.context_user
doc.context_user.populate "avatar", (err, u) ->
return done err if err
doc.context_user = u
done()
(done) ->
return done() if not doc.context_user_2
doc.context_user_2.populate "avatar", (err, u) ->
return done err if err
doc.context_user_2 = u
done(null)
], (err, result) -> fn err, doc
else if type is types.INTRODUCTION # If the document is a introduction:
async.waterfall [
(done) ->
doc.user1.populate "avatar", (err, u) ->
return done err if err
doc.user1 = u
done()
(done) ->
doc.user2.populate "avatar", (err, u) ->
return done err if err
doc.user2 = u
done()
(done) ->
doc.mutual.populate "avatar", (err, u) ->
return done err if err
doc.mutual = u
done()
], (err, result) -> fn err, doc
userPermitted = (type, doc, story, owner, viewer, isOwner, pub, network) ->
Introduction = require "./introduction"
Status = require "./status"
Notification = require "./notification"
if type is types.NOTIFICATION and not pub # Only show notifications if you are looking at your own wall.
return false if doc.for._id.equals(owner._id) and doc.type is Notification.types.FRIEND_REGISTERED and doc.context_user and doc.context_user._id and doc.context_user._id.equals(owner._id) # This is rare. Because of merging, this prevents a notification about yourself.
return doc.for._id.equals owner._id # ... And if they belong to you.
if type is types.STATUS # Statuses are way more complicated.
if pub and isOwner # If your looking at your own PUBLIC wall.
if doc.by._id.equals(owner._id) # Only show statuses that you made and that are public.. as others would see it.
return true if story.scope.public or story.scope.network or story.scope.connections
else if pub and not isOwner # If you are looking at someone elses wall.
if doc.by._id.equals(owner._id) # If the owner of the page made the status.
return true if story.scope.public # If it's public, always show.
if story.scope.connections # If public for owner's connections...
return true if helpers.objectIdExists viewer._id, owner.connections # Make sure the viewer is in the connections.
else if story.scope.network # If public to owner's network...
return true if helpers.objectIdExists viewer._id, network # Make sure the viewer is in the owner's network.
else if story.scope.custom
return true if helpers.objectIdExists viewer._id, story.scopedUsers # Make sure the viewer is in the allowed scope of users.
else if not pub # You are looking at YOUR OWN wall.
return true if doc.by._id.equals(owner._id) # If I own it, obviously.
return true if story.scope.public # Which is like never.
if story.scope.connections # If scope is for connection only.
return true if helpers.objectIdExists doc.by._id, owner.connections # Make sure the doc is in the owner's connections.
else if story.scope.network # If scope is for connection only.
return true if helpers.objectIdExists doc.by._id, network # Make sure the doc owner is in the owner's network.
else if story.scope.custom # If the story is only for specific users.
return true if helpers.objectIdExists owner._id, story.scopedUsers # Make sure the owner is in the scope of users.
if type is types.INTRODUCTION # So are introductions... However, all introductions are same level of privacy. Better.
if pub # If looking at a public wall.
return false if doc.type is "request" # Never show requests publicly.
if doc.user1_accepted and doc.user2_accepted and doc.type isnt "request"
if doc.user1._id.equals(owner._id) or doc.user2._id.equals(owner._id) or doc.mutual._id.equals(owner._id) # Only show accepted requests.
return true if isOwner or helpers.objectIdExists viewer._id, owner.connections # Make sure it's either the owner or the viewer is in the owner's connections.
else if isOwner
if doc.type is "request"
if owner._id.equals(doc.mutual._id)
return true
if owner._id.equals(doc.user1._id)
return true
return false
if doc.user1._id.equals(owner._id) or doc.user2._id.equals(owner._id)
if doc.user1_ignored or doc.user2_ignored
return false
else
return true
else if doc.mutual._id.equals(owner._id)
return true
Story.statics.types = types
Story.statics.list = (owner, viewer, pub = false, args = {}, fn = ->) ->
options = helpers.defaults args,
limit: 10
offset: 0
type: false
isOwner = owner._id.toString() is viewer._id.toString()
list = []
query =
deleted:
$nin: [owner._id]
if pub
query.$or = [
{ type: types.INTRODUCTION }
{ type: types.STATUS }
]
if options.type and isOwner
if types[options.type.toUpperCase()]
delete query.$or if query.$or
query.type = options.type
reduce = (stories, network) ->
counter = 0
addedStories = 0
next = ->
story = stories[counter]
if story
getDoc story.type, story.story, (err, doc) -> # Get full populated relevant document.
return fn err if err
return next(counter++) if not doc
if userPermitted story.type, doc, story, owner, viewer, isOwner, pub, network
doc.story_id = story._id
list.push doc
addedStories++
next counter++
else
next counter++
else # At the end, return array.
fn null, list.slice(options.offset, options.limit+options.offset)
next()
owner.getNetworkBasic (err, network) ->
Model.find query, null, { sort: { created: -1 } }, (err, stories) ->
return fn err if err
process.nextTick -> reduce stories, network
# Create the model.
module.exports = Model = mongoose.model 'Story', Story
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment