define(['underscore',
'jquery',
'backbone',
"models/AccessRule"],
function(_, $, Backbone, AccessRule){
/**
* @class AccessRuleView
* @classdesc Renders a single access rule from an object's access policy
* @classcategory Views
* @screenshot views/AccessRuleView.png
*/
var AccessRuleView = Backbone.View.extend(
/** @lends AccessRuleView.prototype */{
/**
* The type of View this is
* @type {string}
*/
type: "AccessRule",
/**
* The HTML tag name for this view's element
* @type {string}
*/
tagName: "tr",
/**
* The HTML classes to use for this view's element
* @type {string}
*/
className: "access-rule",
/**
* The AccessRule model that is displayed in this view
* @type {AccessRule}
*/
model: undefined,
/**
* If true, this view represents a new AccessRule that hasn't been added to the AccessPolicy yet
* @type {boolean}
*/
isNew: false,
/**
* If true, the user can change the AccessRule via this view.
* If false, the AccessRule will just be displayed.
* @type {boolean}
*/
allowChanges: true,
/**
* The events this view will listen to and the associated function to call.
* @type {Object}
*/
events: {
"keypress .search input" : "listenForEnter",
"click .add.icon" : "updateModel",
"change .access select" : "updateModel"
},
/**
* Is executed when a new AccessRuleView is created
* @param {Object} options - A literal object with options to pass to the view
*/
initialize: function(options){
},
/**
* Renders a single Access Rule
*/
render: function(){
try{
this.$el.empty();
//If there's no model, exit now since there's nothing to render
if( !this.model ){
return;
}
//Get the subjects that should be hidden
var hiddenSubjects = MetacatUI.appModel.get("hiddenSubjectsInAccessPolicy");
//If this AccessRule is for a subject that should be hidden,
if( Array.isArray(hiddenSubjects) &&
_.contains(hiddenSubjects, this.model.get("subject") ) ){
var usersGroups = _.pluck(MetacatUI.appUserModel.get("isMemberOf"), "groupId");
//If the current user is not part of this hidden group or is not the hidden user
if( !_.contains(hiddenSubjects, MetacatUI.appUserModel.get("username")) &&
!_.intersection(hiddenSubjects, usersGroups).length){
//Remove this view
this.remove();
//Exit
return;
}
}
if( this.isNew ){
//If we aren't allowing changes to this AccessRule, then don't display
// anything for new AcccessRule rows
if( !this.allowChanges ){
return;
}
this.$el.addClass("new");
//Create a text input for adding a subject or name
var label = $(document.createElement("label"))
.attr("for", "search")
.text("Search by name, ORCID, or group name")
.addClass("subtle"),
input = $(document.createElement("input"))
.attr("type", "text")
.attr("name", "search")
.attr("placeholder", "e.g. Lauren Walker"),
hiddenInput = $(document.createElement("input"))
.attr("type", "hidden")
.attr("name", "subject")
.addClass("hidden"),
searchCell = $(document.createElement("td"))
.addClass("search")
.attr("colspan", "2")
.append(label, input, hiddenInput),
view = this;
//Setup the autocomplete widget for the input so users can search for people and groups
input.autocomplete({
source: function(request, response){
var beforeRequest = function(){
//loadingSpinner.show();
}
var afterRequest = function(){
//loadingSpinner.hide();
}
return MetacatUI.appLookupModel.getAccountsAutocomplete(request, response, beforeRequest, afterRequest)
},
select: function(e, ui) {
e.preventDefault();
var value = ui.item.value;
hiddenInput.val(value);
input.val(value);
view.updateSubject();
},
position: {
my: "left top",
at: "left bottom",
of: input,
collision: "flip"
},
appendTo: searchCell,
minLength: 2
});
this.$el.append( searchCell );
}
else{
try{
if( this.$el.is(".new") ){
this.$el.removeClass("new");
}
//Create elements for the 'Name' column of this table row
var subject = this.model.get("subject"),
icon;
//If the subject is public, don't display an icon
if( subject == "public" ){
icon = "";
}
//If this is a group subject, display the group icon
else if( this.model.isGroup() ){
icon = $(document.createElement("i")).addClass("icon icon-on-left icon-group");
}
//If this is a username, display the user icon
else{
icon = $(document.createElement("i")).addClass("icon icon-on-left icon-user");
}
//Get the user or group's name - or use the subject, as a backup
var name = this.model.get("name") || subject;
//Display "You" next to the user's own name, for extra helpfulness
if( subject == MetacatUI.appUserModel.get("username") ){
name += " (You)";
}
//Create an element for the name
var nameEl = $(document.createElement("span")).text(name);
this.$el.append($(document.createElement("td")).addClass("name").append(icon, nameEl) );
}
catch(e){
console.error("Couldn't render the name column of the AccessRuleView: ", e);
}
try{
//If this subject is an ORCID, display the ORCID and ORCID icon
if( subject.indexOf("orcid") >= 0 ){
//Create the "subject/orcid" column
var orcidImg = $(document.createElement("img")).attr("src", MetacatUI.root + "/img/orcid_64x64.png").addClass("orcid icon icon-on-left"),
orcid = $(document.createElement("span")).text( this.model.get("subject") );
this.$el.append($(document.createElement("td")).addClass("subject").append(orcidImg, orcid) );
}
else{
//For other subject types, don't show an ORCID icon
this.$el.append($(document.createElement("td")).addClass("subject").text( this.model.get("subject") ));
}
}
catch(e){
console.error("Couldn't render the subject column of the AccessRuleView: ", e);
}
}
try{
if( this.allowChanges ){
//Create the access/permission options select dropdown
var accessOptions = $(document.createElement("select"));
//Create option elements for each access rule type that is enabled in the app
_.mapObject(MetacatUI.appModel.get("accessRuleOptions"), function(isEnabled, optionType){
if( isEnabled ){
var option = $(document.createElement("option")).attr("value", optionType).text( MetacatUI.appModel.get("accessRuleOptionNames")[optionType] );
//If this is the access type enabled in this AccessRule, then select this option
if( this.model.get(optionType) ){
option.prop("selected", "selected");
}
accessOptions.append(option);
}
}, this);
}
else{
//Create an element to display the access type
var accessOptions = $(document.createElement("span"));
//Create option elements for each access rule type that is enabled in the app
_.mapObject(MetacatUI.appModel.get("accessRuleOptions"), function(isEnabled, optionType){
//If this is the access type enabled in this AccessRule, then select this option
if( this.model.get(optionType) ){
accessOptions.text( MetacatUI.appModel.get("accessRuleOptionNames")[optionType] )
.attr("title", "This cannot be changed.");
}
}, this);
}
//Create the table cell and add the access options element
this.$el.append($(document.createElement("td")).addClass("access").append(accessOptions) );
}
catch(e){
console.error("Couldn't render the access column of the AccessRuleView: ", e);
}
//Render the Remove column of the table
try{
if( this.isNew ){
var addIcon = $(document.createElement("i"))
.addClass("add icon icon-plus")
.attr("title", "Add this access");
//Create an empty table cell for "new" blank rows
this.$el.append($(document.createElement("td")).addClass("add-rule").append(addIcon));
}
else{
//Only display a remove icon if we are allowing changes to this AccessRule
if( this.allowChanges ){
//Create a remove icon
var userType = this.model.isGroup()? "group" : "person",
removeIcon = $(document.createElement("i"))
.addClass("remove icon icon-remove")
.attr("title", "Remove access for this " + userType);
//Create a table cell and append the remove icon
this.$el.append($(document.createElement("td")).addClass("remove-rule").append(removeIcon) );
}
else{
//Add an empty table cell so the other rows don't look weird, if they have remove icons
this.$el.append($(document.createElement("td")));
}
}
}
catch(e){
console.error("Couldn't render a remove button for an access rule: ", e);
}
//If there is no name set on this model, listen to when it may be set, and update the view
if( !this.model.get("name") ){
this.listenToOnce(this.model, "change:name", this.updateNameDisplay);
}
//Listen to changes on the access options and update the view if they are changed
this.listenTo(this.model, "change:read change:write change:changePermission", this.updateAccessDisplay);
//When the model is removed from the collection, remove this view
this.listenTo(this.model, "remove", this.onRemove);
//Attach the AccessRule model to the view element
this.$el.data("model", this.model);
this.$el.data("view", this);
}
catch(e){
console.error(e);
//Don't display a message to the user since this view is pretty small. Just remove it from the page.
this.$el.remove();
}
},
/**
* Update the name in this view with the name from the model
*/
updateNameDisplay: function(){
//If there is no name set on the model, exit now, so that we don't show an empty string or falsey value
if( !this.model.get("name") ){
return;
}
var name = this.model.get("name");
//Display "You" next to the user's own name, for extra helpfulness
if( this.model.get("subject") == MetacatUI.appUserModel.get("username") ){
name += " (You)";
}
//Find the name element and update the text content
this.$(".name span").text(name);
},
/**
* Update the AccessRule model with the selected access option
*/
updateAccess: function(){
try{
//Get the value of the dropdown
var selection = this.$(".access select").val();
//If nothing was selected for some reason, exit now
if( !selection ){
return;
}
if( selection == "read" ){
this.model.set("read", true);
this.model.set("write", null);
this.model.set("changePermission", null);
}
else if( selection == "write" ){
this.model.set("read", true);
this.model.set("write", true);
this.model.set("changePermission", null);
}
else if( selection == "changePermission" ){
this.model.set("read", true);
this.model.set("write", true);
this.model.set("changePermission", true);
}
}
catch(e){
console.error(e);
}
},
/**
* Update the access in this view with the access from the model
*/
updateAccessDisplay: function(){
//Get the select dropdown menu from this view
var select = this.$(".access select");
//Update the select dropdown menu with the value from the model
if( this.model.get("changePermission") ){
select.val("changePermission");
}
else if( this.model.get("write") ){
select.val("write");
}
else{
select.val("read");
}
},
/**
* Update the subject of the AccessRule
*/
updateSubject: function(){
//Get the subject from the hidden text input, which is populated from the
// jQueryUI autocomplete widget
var subject = this.$(".search input.hidden").val();
//If the hidden input doesn't have a value, get the value from the visible input
if( !subject ){
subject = this.$(".search input:not(.hidden)").val();
}
//If there is no subject typed in, exit
if( !subject ){
return;
}
//Set the subject on the model
this.model.set("subject", subject);
this.isNew = false;
this.render();
},
/**
* Updates the model associated with this view
*/
updateModel: function(){
//Update the access and the subject
this.updateAccess();
this.updateSubject();
},
/**
* Remove this AccessRule from the AccessPolicy
*/
onRemove: function(){
//If it is the rightsHolder of the object, don't remove the view
if(this.model.get("dataONEObject") && this.model.get("dataONEObject").get("rightsHolder") == this.model.get("subject")){
return;
}
//Remove this view from the page
this.remove();
},
/**
* Handles when the user has typed at least one character in the name search input
* @param {Event} e - The keypress event
*/
listenForEnter: function(e){
try{
if( !e ){
return;
}
//If Enter was pressed,
if( e.keyCode == 13 ){
//Update the subject on this model
this.updateSubject();
}
}
catch(e){
MetacatUI.appView.showAlert("This group or person could not be added.", "alert-error", this.$el, 3000);
console.error("Error while listening to the Enter key in AccessRuleView: ", e);
}
}
});
return AccessRuleView;
});