async = require 'async' mongoose = require 'mongoose' Handler = require '../commons/Handler' AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent' Clan = require './Clan' EarnedAchievement = require '../achievements/EarnedAchievement' EarnedAchievementHandler = require '../achievements/earned_achievement_handler' LevelSession = require '../levels/sessions/LevelSession' LevelSessionHandler = require '../levels/sessions/level_session_handler' User = require '../users/User' UserHandler = require '../users/user_handler' ClanHandler = class ClanHandler extends Handler modelClass: Clan jsonSchema: require '../../app/schemas/models/clan.schema' allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'] hasAccess: (req) -> return true if req.method is 'GET' return false if req.method is 'POST' and req.body?.type is 'private' and not req.user?.isPremium() req.method in @allowedMethods or req.user?.isAdmin() hasAccessToDocument: (req, document, method=null) -> return false unless document? return true if req.user?.isAdmin() return true if (method or req.method).toLowerCase() is 'get' return true if document.get('ownerID')?.equals req.user?._id false makeNewInstance: (req) -> instance = super(req) instance.set 'ownerID', req.user._id instance.set 'members', [req.user._id] instance.set 'dashboardType', 'premium' if req.body?.type is 'private' instance delete: (req, res, clanID) -> @getDocumentForIdOrSlug clanID, (err, clan) => return @sendDatabaseError res, err if err return @sendNotFoundError res unless clan return @sendForbiddenError res unless @hasAccessToDocument(req, clan) memberIDs = clan.get('members') Clan.remove {_id: clan.get('_id')}, (err) => return @sendDatabaseError res, err if err User.update {_id: {$in: memberIDs}}, {$pull: {clans: clan.get('_id')}}, {multi: true}, (err) => return @sendDatabaseError(res, err) if err @sendNoContent(res) AnalyticsLogEvent.logEvent req.user, 'Clan deleted', clanID: clanID, type: clan.get('type') getByRelationship: (req, res, args...) -> return @joinClan(req, res, args[0]) if args[1] is 'join' return @leaveClan(req, res, args[0]) if args[1] is 'leave' return @getMemberAchievements(req, res, args[0]) if args[1] is 'member_achievements' return @getMembers(req, res, args[0]) if args[1] is 'members' return @getMemberSessions(req, res, args[0]) if args[1] is 'member_sessions' return @getPublicClans(req, res) if args[1] is 'public' return @removeMember(req, res, args[0], args[2]) if args.length is 3 and args[1] is 'remove' super(arguments...) joinClan: (req, res, clanID) -> return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() try clanID = mongoose.Types.ObjectId(clanID) catch err return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan return @sendDatabaseError(res, err) unless clanType = clan.get('type') return @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium() Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) => return @sendDatabaseError(res, err) if err User.update {_id: req.user._id}, {$addToSet: {clans: clanID}}, (err) => return @sendDatabaseError(res, err) if err @sendSuccess(res) AnalyticsLogEvent.logEvent req.user, 'Clan joined', clanID: clanID, type: clanType leaveClan: (req, res, clanID) -> return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() try clanID = mongoose.Types.ObjectId(clanID) catch err return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) => return @sendDatabaseError(res, err) if err User.update {_id: req.user._id}, {$pull: {clans: clanID}}, (err) => return @sendDatabaseError(res, err) if err @sendSuccess(res) AnalyticsLogEvent.logEvent req.user, 'Clan left', clanID: clanID, type: clan.get('type') getMemberAchievements: (req, res, clanID) -> # TODO: add tests memberLimit = 200 Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID User.find {_id: {$in: memberIDs}}, 'nameLower', {sort: {nameLower: 1}}, (err, users) => return @sendDatabaseError(res, err) if err memberIDs = [] for user in users memberIDs.push user.id break unless memberIDs.length < memberLimit EarnedAchievement.find {user: {$in: memberIDs}}, 'achievementName user', (err, documents) => return @sendDatabaseError(res, err) if err? cleandocs = (EarnedAchievementHandler.formatEntity(req, doc) for doc in documents) @sendSuccess(res, cleandocs) getMembers: (req, res, clanID) -> # TODO: add tests Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan memberIDs = clan.get('members') ? [] User.find {_id: {$in: memberIDs}}, 'name nameLower points', {sort: {nameLower: 1}}, (err, users) => return @sendDatabaseError(res, err) if err cleandocs = (UserHandler.formatEntity(req, doc) for doc in users) @sendSuccess(res, cleandocs) getMemberSessions: (req, res, clanID) -> # TODO: add tests # TODO: restrict information returned based on clan type memberLimit = 200 Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID User.find {_id: {$in: memberIDs}}, 'name', {sort: {name: 1}}, (err, users) => return @sendDatabaseError(res, err) if err memberIDs = [] for user in users memberIDs.push user.id break unless memberIDs.length < memberLimit LevelSession.find {creator: {$in: memberIDs}}, 'changed codeLanguage creator creatorName levelID levelName playtime state submittedCodeLanguage', (err, documents) => return @sendDatabaseError(res, err) if err? cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents) @sendSuccess(res, cleandocs) getPublicClans: (req, res) -> # Return 100 public clans, sorted by member count, created date query = [{ $match : {type : 'public'} }] query.push {$project : {_id: 1, name: 1, slug: 1, type: 1, description: 1, members: 1, memberCount: {$size: "$members"}, ownerID: 1}} query.push {$sort: { memberCount: -1, _id: -1 }} query.push {$limit: 100} Clan.aggregate(query).exec (err, documents) => return @sendDatabaseError(res, err) if err @sendSuccess(res, documents) removeMember: (req, res, clanID, memberID) -> return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous() try clanID = mongoose.Types.ObjectId(clanID) memberID = mongoose.Types.ObjectId(memberID) catch err return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err return @sendNotFoundError(res) unless clan return @sendForbiddenError res unless @hasAccessToDocument(req, clan) return @sendForbiddenError(res) if clan.get('ownerID').equals memberID Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) => return @sendDatabaseError(res, err) if err User.update {_id: memberID}, {$pull: {clans: clanID}}, (err) => return @sendDatabaseError(res, err) if err @sendSuccess(res) AnalyticsLogEvent.logEvent req.user, 'Clan member removed', clanID: clanID, type: clan.get('type'), memberID: memberID module.exports = new ClanHandler()