/*global define */
define(['jquery', 'underscore', 'backbone', 'models/UserModel'],
function($, _, Backbone, UserModel) {
'use strict';
/**
* @class UserGroup
* @classdesc The collection of Users that represent a DataONE group
* @classcategory Collections
*/
var UserGroup = Backbone.Collection.extend(
/** @lends UserGroup.prototype */{
// Reference to this collection's model.
model: UserModel,
//Custom attributes of groups
groupId: "",
name: "",
nameAvailable: null,
url: function(){
return MetacatUI.appModel.get("accountsUrl") + encodeURIComponent(this.groupId);
},
comparator: "lastName", //Sort by last name
initialize: function(models, options) {
if((typeof models == "undefined") || !models)
var models = [];
if(typeof options !== "undefined"){
//Save our options
$.extend(this, options);
this.groupId = options.groupId || "";
this.name = options.name || "";
this.pending = (typeof options.pending === "undefined") ? false : options.pending;
//If raw data is passed, parse it to get a list of users to be added to this group
if(options.rawData){
//Get a list of UserModel attributes to add to this collection
var toAdd = this.parse(options.rawData);
//Create a UserModel for each user
_.each(toAdd, function(modelAttributes){
//Don't pass the raw data to the UserModel creation because it is redundant-
//We already parsed the raw data when we called add() above
var rawDataSave = modelAttributes.rawData;
modelAttributes.rawData = null;
//Create the model then add the raw data back
var member = new UserModel(modelAttributes);
member.set("rawData", rawDataSave);
models.push(member);
});
}
}
//Add all our models to this collection
this.add(models);
},
/*
* Gets the group from the server. Options object uses the BackboneJS options API
*/
getGroup: function(options){
if(!this.groupId && this.name){
this.groupId = "CN=" + this.name + ",DC=dataone,DC=org";
}
this.fetch(options);
return this;
},
/*
* Fetches the group info from the server. Should not be called directly - use getGroup() instead
*/
fetch: function (options) {
options = options || { silent: false, reset: false, remove: false };
options.dataType = "xml";
options.error = function(collection, response, options){
//If this group is not found, then the name is available
if((response.status == 404) && (response.responseText.indexOf("No Such Object") > -1)){
collection.nameAvailable = true;
collection.trigger("nameChecked", collection);
}
}
return Backbone.Collection.prototype.fetch.call(this, options);
},
/*
* Backbone.js override - parses the XML reponse from DataONE and creates a JSON representation that will
* be used to create UserModels
*/
parse: function(response, options){
if(!response) return;
//This group name is not available/already taken
this.nameAvailable = false;
this.trigger("nameChecked", this);
var group = $(response).find("group subject:contains('" + this.groupId + "')").parent("group"),
people = $(response).find("person"),
collection = this,
toAdd = new Array(),
existing = this.pluck("username");
if(!people.length)
people = $(group).find("hasMember");
//Make all existing usernames lowercase for string matching
if(existing.length) existing = _.invoke(existing, "toLowerCase");
this.name = $(group).children("groupName").text();
_.each(people, function(person){
//The tag name is "hasMember" if we retrieved info about this group from the group nodes only
if(person.tagName == "hasMember"){
var username = $(person).text();
//If this user is already in the group, skip adding it
if(_.contains(existing, username.toLowerCase())) return;
var user = new UserModel({ username: username }),
userAttr = user.toJSON();
toAdd.push(userAttr);
}
//The tag name is "person" if we retrieved info about this group through the /accounts service, which includes all nodes about all members
else{
//If this user is not listed as a member of this group, skip it
if($(person).children("isMemberOf:contains('" + collection.groupId + "')").length < 1)
return;
//Username of this person
var username = $(person).children("subject").text();
//If this user is already in the group, skip adding it
if(_.contains(existing, username.toLowerCase())) return;
//User attributes - pass the full response for the UserModel to parse
var userAttr = new UserModel({username: username}).parseXML(response);
//Add to collection
toAdd.push(userAttr);
}
});
return toAdd;
},
/*
* An alternative to Backbone sync
* - will send a POST request to DataONE CNIdentity.createGroup() to create this collection as a new DataONE group
* or
* - will send a PUT request to DataONE CNIdentity.updateGroup() to update this existing DataONE group
*
* If this group is marked as pending, then the group is created, otherwise it's updated
*/
save: function(onSuccess, onError){
if(this.pending && (this.nameAvailable == false)) return false;
var memberXML = "",
ownerXML = "",
collection = this;
//Create the member and owner XML
this.forEach(function(member){
//Don't list yourself as an owner or member (implied)
if(MetacatUI.appUserModel == member) return;
var username = member.get("username") ? member.get("username").trim() : null;
if(!username) return;
memberXML += "" + username + "";
if(collection.isOwner(member))
ownerXML += "" + username + "";
});
//Create the group XML
var groupXML =
''
+ ''
+ '' + this.groupId + ''
+ '' + this.name + ''
+ memberXML
+ ownerXML
+ '';
var xmlBlob = new Blob([groupXML], {type : 'application/xml'});
var formData = new FormData();
formData.append("group", xmlBlob, "group");
// AJAX call to update
$.ajax({
type: this.pending? "POST" : "PUT",
cache: false,
contentType: false,
processData: false,
xhrFields: {
withCredentials: true
},
headers: {
"Authorization": "Bearer " + MetacatUI.appUserModel.get("token")
},
url: MetacatUI.appModel.get("groupsUrl"),
data: formData,
success: function(data, textStatus, xhr) {
if(typeof onSuccess != "undefined")
onSuccess(data);
collection.pending = false;
collection.nameAvailable = null;
collection.getGroup();
},
error: function(xhr, textStatus, error) {
if(typeof onError != "undefined")
onError(xhr);
}
});
return true;
},
/*
* For pending groups only (those in the creation stage)
* Will check if the given name/id is available
*/
checkName: function(name){
//Only check the name for pending groups
if(!this.pending) return;
//Reset the name and ID
this.name = name || this.name;
this.groupId = null;
this.nameAvailable = null;
//Get group info/check name availablity
this.getGroup({ add: false });
},
/*
* Retrieves the UserModels that are rightsHolders of this group
*/
getOwners: function(){
var groupId = this.groupId;
return _.filter(this.models, function(user){
return _.contains(user.get("isOwnerOf"), groupId);
});
},
/*
* Shortcut function - will check if a specified User is an owner of this group
*/
isOwner: function(model){
if(typeof model === "undefined") return false;
if(this.pending && (model == MetacatUI.appUserModel)) return true;
var usernames = [];
_.each(this.getOwners(), function(user){ usernames.push(user.get("username")); });
return _.contains(usernames, model.get("username"));
}
});
return UserGroup;
});