'jquery',
'backbone',
'home/utils/backboneUtils',
'home/collections/PaginatedUserCollection',
'home/collections/FeedItemCollection',
'home/collections/FollowingForumCollection',
'home/collections/FollowingChannelCollection',
'home/collections/FollowingUserCollection',
'home/collections/MostActiveForumCollection',
'home/collections/UserManagedChannelCollection',
'core/models/Post',
'core/UniqueModel',
'home/models/Thread',
'home/models/User',
'home/models/UserModerationDetails',
'core/api',
], function (
$,
Backbone,
backboneUtils,
PaginatedUserCollection,
FeedItemCollection,
FollowingForumCollection,
FollowingChannelCollection,
FollowingUserCollection,
MostActiveForumCollection,
UserManagedChannelCollection,
CorePost,
UniqueModel,
Thread,
User,
UserModerationDetails,
api
) {
'use strict';
var
idAttribute: 'username',
var parentProto = parent.prototype;
var Post = CorePost.extend({
// TODO: unify Home's VotersUserCollection with embed's
votersCollectionClass: VotersUserCollection,
initialize: function
parentProto.initialize.call(this, attrs, options);
this.set({ state: this._getPostState() });
this.initializeRelated(
_.
user: { username: this.get('username') },
);
// this.
this.listenTo(this.user, 'change:isFollowing', this.updateFollowers);
var userFetchString = 'username:' + this.user.get('username');
var fetchOptions = {
user: userFetchString,
order: 'desc',
};
this.forumsFollowing = new Feed({
fetchOptions: fetchOptions,
}, {
collectionClass: FollowingForumCollection,
});
// channelsFollowing
// their primary forums cannot be followed (as opposed to followedChannels below,
// which contains channels followed
// ex: Disqus Picks is followed by too many people, if
// it would result in activities being copied to too many users' activity feeds.
this.channelsFollowing = new Feed({
fetchOptions: fetchOptions,
}, {
collectionClass: FollowingChannelCollection,
});
this.listenToOnce(this.channelsFollowing, 'items:sync', function () {
this.user.set({
numChannelsFollowing: this.channelsFollowing.items.length,
});
});
this.syncFollowingCommunities();
this.following = new Feed({
fetchOptions: fetchOptions,
}, {
collectionClass: FollowingUserCollection,
});
this.followers = new Feed({
fetchOptions: fetchOptions,
}, {
collectionClass: PaginatedUserCollection,
collectionUrl: api.getURL('users/listFollowers'),
});
this.mostActiveForums = new Feed({
fetchOptions: {
user: userFetchString,
limit: 5,
},
}, {
collectionClass: MostActiveForumCollection,
});
// A feed of channels the user manages: either as a mod or creator
this.managedChannels = new Feed(undefined, {
collectionClass: UserManagedChannelCollection,
collectionOptions: {
user: this.user,
},
});
// A feed of channels the user follows, either directly or via a primary forum.
// Excludes managed channels, as those are fetched in another feed.
this.followedChannels = new Feed({
fetchOptions: {
user: userFetchString,
// Endpoint expects 1 for true, 0 for false
includeFollowedPrimaryForums: 1,
excludeModerated: 1,
excludeOwned: 1,
},
}, {
collectionClass: FollowingChannelCollection,
collectionOptions: {
url: api.getURL('users/listFollowingChannels'),
},
});
// For the endpoints that expect a target argument of the forum
// <object_type>:<object_id>
// ex: {target: 'user:username:joesmith'}
var target = 'user:' + userFetchString;
this.posts = new Feed(undefined, {
collectionClass: FeedItemCollection,
collectionOptions: {
type: 'profile',
index: 'comments',
target: target,
},
});
this.favorites = new Feed(undefined, {
collectionClass: FeedItemCollection,
collectionOptions: {
type: 'profile',
index: 'favorites',
target: target,
},
});
this.threads = new Feed(undefined, {
collectionClass: FeedItemCollection,
collectionOptions: {
type: 'profile',
index: 'threads',
target: target,
},
});
this.profileFeed = new Feed(undefined, {
collectionClass: FeedItemCollection,
collectionOptions: {
type: 'profile',
target: target,
},
});
this.moderationDetails = new UserModerationDetails({
username: this.user.get('username'),
});
this.listenTo(this.user, 'change:isOnGlobalBlacklist', this.handleChangeGlobalBlacklist);
this.listenTo(this.user, 'blacklist:add', this.removeUserDiscussions);
this.listenTo(this.user, 'change:username', this.updateUsernameInChildren);
},
// On the frontend we want to treat followed channels and followed forums the
// same. Copy all the followed channels' primary forums into the followed
// forums list so they will be shown where we show followed communities.
syncFollowingCommunities: function () {
var forums = this.forumsFollowing.items;
var channels = this.channelsFollowing.items;
this.listenTo(channels, {
reset: function () {
forums.add(_.pluck(channels.models, 'primaryForum'), { at: 0 });
},
add: function (channel) {
forums.add(channel.primaryForum, { at: 0 });
},
remove: function (channel) {
forums.remove(channel.primaryForum);
},
});
// When forumsFollowing is fetched it'll reset and overwrite all the forums
// manually added
this.listenTo(forums, 'reset', function () {
forums.add(_.pluck(channels.models, 'primaryForum'), { at: 0 });
});
},
handleChangeGlobalBlacklist: function () {
if (!this.user.get('isOnGlobalBlacklist'))
return;
this.removeUserDiscussions();
},
// Pages through all the user's discussions and deletes them. If a `forum` is
// passed, it'll only delete discussions from that forum.
removeUserDiscussions: function (forum) {
// If we've already fetched this user's discussions, go ahead and start
// loading the next page with `fetchItems()`. If we haven't fetched threads
// yet, call `ensureFetched()` because this handles the logic of not
// duplicating a fetch that's in progress.
var fetchPromise = this.threads.get('fetched') ? this.threads.fetchItems() : this.threads.ensureFetched();
fetchPromise.then(_.bind(function () {
this.threads.items.each(function (threadActivity) {
var thread = threadActivity.thread;
if (forum && thread.forum.id !== forum)
return;
thread.removeThread();
});
// Repeat as long as there's more pages
if (this.threads.get('hasNext'))
this.removeUserDiscussions(forum);
}, this));
},
isPrivate: function () {
return !this.user.isSessionUser() && this.user.get('isPrivate');
},
isBlocked: function () {
return !this.user.isSessionUser() && this.user.get('isBlocked');
},
ensureFetched: function () {
// We require Session inline because of a circular dependency
// between the UserProfile and Session models
var Session = require('home/models/Session');
var session = Session.get();
session.loadPromise().always(_.bind(function () {
var isGlobalAdmin = session.user.isGlobalAdmin();
// Wait until we can tell if the user is private
// before fetching anything else
backboneUtils.getPromiseFor(this.user, 'isPrivate').then(_.bind(function () {
if (this.isPrivate() && !isGlobalAdmin)
return;
this.favorites.ensureFetched();
this.posts.ensureFetched();
this.threads.ensureFetched();
this.mostActiveForums.ensureFetched();
// Don't fetch followedChannels or managedChannels. Those are only
// shown for the session user and are fetched directly when needed.
// numChannelsFollowing is not returned by the user details API endpoint
// like both numFollowing (users) and numForumsFollowing are,
// so we need to fetch channels to get the count
this.channelsFollowing.ensureFetched();
// Only load moderation details for Disqus admins
var self = this;
if (isGlobalAdmin) {
self.moderationDetails.ensureFetched();
$.when(self.moderationDetails.fetchPromise).then(function () {
self.trigger('moderationDetailsReady');
});
}
}, this));
}, this));
this.fetchUser();
},
ensureFetchedAllChannels: function () {
this.managedChannels.ensureFetched();
this.followedChannels.ensureFetched();
},
toJSON: function () {
user: this.user.toJSON(),
following: this.following.toJSON(),
followers: this.followers.toJSON(),
mostActiveForums: this.mostActiveForums.toJSON(),
posts: this.posts.toJSON(),
threads: this.threads.toJSON(),
profileFeed: this.profileFeed.toJSON(),
moderationDetails: this.moderationDetails.toJSON(),
}, Backbone.Model.prototype.
stringifiable.thread = this.thread.toJSON();
return stringifiable;
},
* Makes the necessary fetch calls in order to render the profile
* view for this user.
*/
fetchUser:
var $wrapper = $('<div>').html(this.get('message'));
return $wrapper.find('a[href]').map(function (__, el) {
return $(el).attr('href');
});
}, function () {
// get ids along with all the rest of the user details, whereas
// it's possible to have just a username with no other details.
if (!this.user.get('username'))
throw new Error('UserProfile requires a username or id in order to be fetched');
if (this.user.shouldFetch())
}),
getThreadId: function () {
return this.
},
this.following.fetchItems({ reset: true });
},
fetchForumsFollowing: function () {
this.forumsFollowing.fetchItems({ reset: true });
},
fetchChannelsFollowing: function () {
this.channelsFollowing.fetchItems({ reset: true });
},
/**
* Updates the list of this user's followers when the
* session user follows/unfollows this user.
*/
updateFollowers: function () {
var Session = require('home/models/Session');
var session = Session.get();
// Don't add an anonymous user to the followers list.
//
// (If an anon user tried to follow someone, they'll be prompted
// to login. That happens elsewhere and may happen after this
// handler)
if (session.isAnonymous())
return;
var items = this.followers.get('items');
if (this.
items.add(session.user);
else
items.remove(session.user);
},
/**
* Updates an object's username.
* Meant
return 'spam';
if (this.get('isDeleted'))
return 'deleted';
// Once a post is flagged to
*
* @param {Object} _model -
* @param {string} username - the new username
* @returns {void}
*/
updateUsername: function (_model, username) {
this.set('username', username);
},
/**
* Updates the username for children.
* Meant to be used as an event listener, listening to changes to this.user.username.
*
* Doesn't need to update `this.managedChannels` because
*
* @param {Object} _model - the Backbone model that was updated
* @param {string} username - the new username
* @returns {void}
*/
updateUsernameInChildren: function (_model, username) {
//
// The ones that use `username:<username>` are initialized with `fetchOptions`.
// The ones that use `user:username:<username>` are initialized with `collectionOptions`.
var targetUsername = 'username:'
var usernameObj = { user: targetUsername };
var objsUsingFetchOptions = [this.forumsFollowing, this.channelsFollowing, this.following,
this.followers, this.mostActiveForums, this.followedChannels];
_.each(objsUsingFetchOptions, function (feed) {
feed.set('fetchOptions', _.extend(feed.get('fetchOptions'), usernameObj));
});
targetUsername = 'user:' + targetUsername;
var uniqueTogether = { target: targetUsername };
var objsUsingUniqueTogether = [this.posts, this.favorites, this.threads, this.profileFeed];
_.each(objsUsingUniqueTogether, function (feed) {
feed.items.updateUniqueTogether(uniqueTogether);
});
this.moderationDetails.set('username', username); // Just has a reference to username
if (this.get('isFlagged') && !this.get('isApproved'))
return 'flagged';
if (!this.get('isApproved'))
return 'pending';
return 'visible';
},
});
CorePost.withAuthor.call(Post.prototype, UniqueModel.boundModel(User));
// TODO - apply Post.withMediaCollection mixin
UniqueModel.
return
});
// https://c.disquscdn.