/*global define */ define(['jquery', 'underscore', 'backbone', 'models/UserModel'], function($, _, Backbone, UserModel) { 'use strict'; /* * UserGroup Collection * The collection of Users that represent a DataONE group */ var UserGroup = Backbone.Collection.extend({ // 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; });