define(['underscore',
'jquery',
'backbone',
'models/portals/PortalModel',
"models/portals/PortalImage",
"collections/Filters",
'views/EditorView',
"views/SignInView",
"views/portals/editor/PortEditorSectionsView",
"views/portals/editor/PortEditorImageView",
"text!templates/loading.html",
"text!templates/portals/editor/portalEditor.html",
"text!templates/portals/editor/portalEditorSubmitMessage.html",
"text!templates/portals/editor/portalLoginPage.html"
],
function(_, $, Backbone, Portal, PortalImage, Filters, EditorView, SignInView,
PortEditorSectionsView, ImageEdit, LoadingTemplate, Template,
portalEditorSubmitMessageTemplate, LoginTemplate){
/**
* @class PortalEditorView
* @classdesc A view of a form for creating and editing DataONE Portal documents
* @classcategory Views/Portals/Editor
* @name PortalEditorView
* @extends EditorView
* @constructs
*/
var PortalEditorView = EditorView.extend(
/** @lends PortalEditorView.prototype */{
/**
* The type of View this is
* @type {string}
*/
type: "PortalEditor",
/**
* The short name OR pid for the portal
* @type {string}
*/
portalIdentifier: "",
/**
* The PortalModel that is being edited
* @type {Portal}
*/
model: undefined,
/**
* The currently active editor section. e.g. Data, Metrics, Settings, etc.
* @type {string}
*/
activeSectionLabel: "",
/**
* When a new portal is being created, this is the label of the section that will be active when the editor first renders
* @type {string}
*/
newPortalActiveSectionLabel: (MetacatUI.appModel.get("portalDefaults") ? MetacatUI.appModel.get("portalDefaults").newPortalActiveSectionLabel : "") || "Settings",
/**
* References to templates for this view. HTML files are converted to Underscore.js templates
*/
template: _.template(Template),
loadingTemplate: _.template(LoadingTemplate),
loginTemplate: _.template(LoginTemplate),
// Over-ride the default editor submit message template (which is currently
// used by the metadata editor) with the portal editor version
editorSubmitMessageTemplate: _.template(portalEditorSubmitMessageTemplate),
/**
* An array of Backbone Views that are contained in this view.
* @type {Backbone.View[]}
*/
subviews: [],
/**
* A reference to the PortEditorSectionsView for this instance of the PortEditorView
* @type {PortEditorSectionsView}
*/
sectionsView: null,
/**
* The text to use in the editor submit button
* @type {string}
*/
submitButtonText: "Save",
/**
* A jQuery selector for the element that the PortEditorSectionsView should be inserted into
* @type {string}
*/
portEditSectionsContainer: ".port-editor-sections-container",
/**
* A jQuery selector for the element that the portal logo image uploader
* should be inserted into
* @type {string}
*/
portEditLogoContainer: ".logo-editor-container",
/**
* A jQuery selector for links to view this portal
* @type {string}
*/
viewPortalLinks: ".view-portal-link",
/**
* A temporary name to use for portals when they are first created but don't have a label yet.
* This name should only be used in views, and never set on the model so it doesn't risk getting
* serialized and saved.
* @type {string}
*/
newPortalTempName: "new",
/**
* The events this view will listen to and the associated function to call.
* This view will inherit events from the parent class, EditorView.
* @type {Object}
*/
events: _.extend(EditorView.prototype.events, {
"focusout .basic-text" : "updateBasicText",
"click .section-links-toggle-container" : "toggleSectionLinks"
}),
/**
* Is executed when a new PortalEditorView is created
* @param {Object} options - A literal object with options to pass to the view
*/
initialize: function(options){
EditorView.prototype.initialize.call(this, options);
//Reset arrays and objects set on this View, otherwise they will be shared across intances, causing errors
this.subviews = new Array();
this.sectionsView = null;
if(typeof options == "object"){
// initializing the PortalEditorView properties
this.portalIdentifier = options.portalIdentifier ? options.portalIdentifier : undefined;
this.activeSectionLabel = options.activeSectionLabel || "";
}
},
/**
* Renders the PortalEditorView
*/
render: function(){
//Execute the superclass render() function, which will add some basic Editor functionality
EditorView.prototype.render.call(this);
$("body").addClass("Portal");
// Display a spinner to indicate loading until model is created.
this.$el.html(this.loadingTemplate({
msg: "Retrieving portal details..."
}));
//Create the model
this.createModel();
// An existing portal should have a portalIdentifier already set
// from the router, that does not equal the newPortalTempName ("new"),
// plus a seriesId or label set during createModel()
if (
(this.model.get("seriesId") || this.model.get("label"))
&&
(this.portalIdentifier && this.portalIdentifier != this.newPortalTempName)
){
var view = this;
this.listenToOnce(this.model, "change:isAuthorized", function(){
if (this.model.get("isAuthorized")) {
// When an existing model has been synced render the results
view.stopListening(view.model, "sync", view.renderPortalEditor);
view.listenToOnce(view.model, "sync", view.renderPortalEditor);
// If the portal model already exists - fetch it.
view.model.fetch();
// Listens to the focus event on the window to detect when a user
// switches back to this browser tab from somewhere else
// When a user checks back, we want to check for log-in status
MetacatUI.appView.listenForActivity();
// Determine the length of time until the user's current token expires
// Asks to sign in in case of time out
MetacatUI.appView.listenForTimeout();
}
else {
// generate error message
var msg = MetacatUI.appModel.get("portalEditNotAuthEditMessage");
//Show the not authorized error message
MetacatUI.appView.showAlert(msg, "alert-error non-fixed", this.$el);
}
});
// Check if the user is Authorized to edit the portal
this.authorizeUser();
}
//If there is no portal identifier given, this is a new portal.
else {
// if the user is not signed in, display the sign in view
if ( MetacatUI.appUserModel.get("tokenChecked") && !MetacatUI.appUserModel.get("loggedIn")) {
this.showSignIn();
}
else{
//Check the user's quota to create a new Portal
this.listenToOnce(MetacatUI.appUserModel, "change:isAuthorizedCreatePortal", function(){
if( MetacatUI.appUserModel.get("isAuthorizedCreatePortal") ){
// Start new portals on the settings tab
this.activeSectionLabel = this.newPortalActiveSectionLabel;
// Render the default model if the portal is new
this.renderPortalEditor();
}
else{
//If the user doesn't have quota left, display this message
if( MetacatUI.appUserModel.get("portalQuota") == 0 ){
var errorMessage = MetacatUI.appModel.get("portalEditNoQuotaMessage");
}
//Otherwise, display a more generic error message
else{
var errorMessage = MetacatUI.appModel.get("portalEditNotAuthCreateMessage");
}
//Hide the loading icon
this.hideLoading();
//Show the error message
MetacatUI.appView.showAlert(errorMessage, "alert-error non-fixed", this.$el);
}
//Reset the isAuthorizedCreatePortal attribute
MetacatUI.appUserModel.set("isAuthorizedCreatePortal", null);
});
//If the user authentication hasn't been checked yet, then wait for it
if ( !MetacatUI.appUserModel.get("tokenChecked") ) {
this.listenTo(MetacatUI.appUserModel, "change:tokenChecked", function(){
if( MetacatUI.appUserModel.get("loggedIn") ){
//Check if this user is authorized to create a new portal
MetacatUI.appUserModel.isAuthorizedCreatePortal();
}
//If the user is not logged in, show the sign in buttons
else if( !MetacatUI.appUserModel.get("loggedIn") ){
this.showSignIn();
}
});
return;
}
//If the user is logged in,
else if( MetacatUI.appUserModel.get("loggedIn") ){
//Check if this user is authorized to create a new portal
MetacatUI.appUserModel.isAuthorizedCreatePortal();
}
//If the user is not logged in, show the sign in buttons
else if( !MetacatUI.appUserModel.get("loggedIn") ){
this.showSignIn();
}
}
}
return this;
},
/**
* Renders the portal editor view once the portal view is created
*/
renderPortalEditor: function() {
var view = this;
//Check if this is a plus portal
if( MetacatUI.appModel.get("dataonePlusPreviewMode")){
var sourceMN = this.model.get("datasource");
//Check if the portal source node is from the active alt repo OR is
// configured as a Plus portal.
if( !this.model.isNew() &&
(typeof sourceMN != "string" ||
(sourceMN != MetacatUI.appModel.get("defaultAlternateRepositoryId") &&
!_.findWhere(MetacatUI.appModel.get("dataonePlusPreviewPortals"),
{ datasource: sourceMN, seriesId: this.model.get("seriesId") }))) ){
//Get the name of the source member node
var sourceMNName = "original data repository",
mnURL = "";
if( typeof sourceMN == "string" ){
var sourceMNObject = MetacatUI.nodeModel.getMember(sourceMN);
if( sourceMNObject ){
sourceMNName = sourceMNObject.name;
//If there is a baseURL string
if( sourceMNObject.baseURL ){
//Parse out the origin of the baseURL string. We want to crop out the /metacat/d1/mn parts.
mnURL = sourceMNObject.baseURL.substring(0, sourceMNObject.baseURL.lastIndexOf(".")) +
sourceMNObject.baseURL.substring(sourceMNObject.baseURL.lastIndexOf("."),
sourceMNObject.baseURL.indexOf("/", sourceMNObject.baseURL.lastIndexOf(".")));
}
}
}
//Show a message that the portal can be found on the repository website.
var message = $(document.createElement("h3")).addClass("center stripe");
message.text("The " + this.model.get("name") + " " + MetacatUI.appModel.get("portalTermSingular") +
" can be edited in the ");
if(mnURL){
message.append( $(document.createElement("a"))
.attr("href", mnURL)
.attr("target", "_blank")
.text(sourceMNName) );
}
else{
message.append(sourceMNName);
}
this.$el.html(message);
return;
}
}
// Add the template to the view and give the body the "Editor" class
this.$el.html(this.template({
name: this.model.get("name"),
submitButtonText: this.submitButtonText,
primaryColor: this.model.get("primaryColor"),
secondaryColor: this.model.get("secondaryColor"),
accentColor: this.model.get("accentColor"),
primaryColorTransparent: this.model.get("primaryColorTransparent"),
secondaryColorTransparent: this.model.get("secondaryColorTransparent"),
accentColorTransparent: this.model.get("accentColorTransparent")
}));
//Render the editor controls
this.renderEditorControls();
//Hide the Save controls
this.hideControls();
//Remove the rendering class from the body element
$("body").removeClass("rendering");
// On mobile where the section-links-toggle-container is set to fixed,
// hide the portal navigation element when user scrolls down,
// show again when the user scrolls up.
MetacatUI.appView.prevScrollpos = window.pageYOffset;
$(window).off("scroll");
$(window).scroll(_.throttle(view.handleScroll, 400));
// Functions to perform when the window is resized
var onResize = function(){
// Auto-resize the portal title
$("textarea.portal-title").trigger("windowResize");
// Ensure that the menu is always shown when switching from mobile to full width
view.toggleSectionLinks();
}
$(window).off("resize");
$( window ).resize(_.throttle(onResize, 400));
// Auto-resize the height of the portal title field on user-input and on
// window resize events.
this.$("textarea.portal-title").each(function () {
this.style.height = '0px'; // note: textfield MUST have a min-height set
this.style.height = (this.scrollHeight) + 'px';
}).on('input windowResize', function () {
this.style.height = '0px'; // note: textfield MUST have a min-height set
this.style.height = (this.scrollHeight) + 'px';
});
// Get the portal identifier
// or set it to a default value in the case that it's a new portal
var portalIdentifier = this.portalIdentifier;
if(!portalIdentifier){
portalIdentifier = this.newPortalTempName;
}
//Create a view for the editor sections
this.sectionsView = new PortEditorSectionsView({
model: this.model,
activeSectionLabel: this.activeSectionLabel,
newPortalTempName: this.newPortalTempName
});
//Save the PortEditorSectionsView as a subview
this.subviews.push(this.sectionsView);
//Attach a reference to this view
this.sectionsView.editorView = this;
//Add the view element to this view
this.$(this.portEditSectionsContainer).html(this.sectionsView.el);
//Render the sections view
this.sectionsView.render();
//If this portal is a free trial DataONE Plus portal, then display some messaging
this.renderSubscriptionInfo();
//Show the required fields for this editor
this.renderRequiredIcons(MetacatUI.appModel.get("portalEditorRequiredFields"));
// Insert the logo editor
this.renderLogoEditor();
// When the collection definition is changed, show the Save button
var definition = this.model.get("definition"),
definitionEvents = "update change";
this.stopListening(definition, definitionEvents);
this.listenTo(definition, definitionEvents, function(model, record){
// Don't show the controls for the addition of an empty filter model, or the
// controls will show right away when we add a new blank query rule
if(record && record.changes && record.changes.added && record.changes.added.length){
if(record.changes.added[0].isEmpty && record.changes.added[0].isEmpty()){
return
}
}
this.showControls()
});
// On mobile, hide section tabs a moment after page loads so
// users notice where they are
setTimeout(function () {
view.toggleSectionLinks();
}, 700);
//Show a link to view the portal, if it is not a new portal
if( !this.model.isNew() ){
var viewURL = MetacatUI.root + "/" + MetacatUI.appModel.get("portalTermPlural") +"/" + portalIdentifier;
//Update the view URL in any other portal view links
this.$(this.viewPortalLinks).attr("href", viewURL).show();
}
else{
//Remove the href attribute and hide the link
this.$(this.viewPortalLinks).attr("href", "").hide();
}
},
/**
* Create a PortalModel object
*/
createModel: function(){
// Look up the portal document seriesId by its registered name if given
if ( this.portalIdentifier && this.portalIdentifier != this.newPortalTempName) {
// Create a new portal model with the identifier
this.model = new Portal({
label: this.portalIdentifier,
edit: true
});
// Save the original label in case a user changes it. During URL
// validation, the original label will always be shown as available.
// TODO: if user navigates to portal using a SID or PID, we will need
// to get the matching label and then save it to the model
this.model.set("originalLabel", this.portalIdentifier);
// Otherwise, create a new portal
} else {
// Create a new, default portal model
this.model = new Portal({
//Set the isNew attribute so the model will execute certain functions when a Portal is new
isNew: true,
rightsHolder: MetacatUI.appUserModel.get("username"),
isAuthorized_read: true,
isAuthorized_write: true,
isAuthorized_changePermission: true,
edit: true
});
}
// set listeners on the new model
this.setListeners();
},
/**
* The authorizeUser function checks if the current user is authorized
* to edit the given PortalModel. If not, a message is displayed and
* the view doesn't render anything else.
*
* If the user isn't logged in at all, don't check for authorization and
* display a message and login button.
*/
authorizeUser: function() {
//If the user authentication hasn't been checked yet, wait for it to finish.
if( !MetacatUI.appUserModel.get("tokenChecked") ){
this.listenToOnce(MetacatUI.appUserModel, "change:tokenChecked", this.authorizeUser);
return;
}
//If the user authentication has been checked and they are not logged in, then display the Sign In buttons
else if ( MetacatUI.appUserModel.get("tokenChecked") && !MetacatUI.appUserModel.get("loggedIn") ){
//Remove the loading message
this.hideLoading();
// show the sign in view
this.showSignIn();
return;
}
else{
//If the seriesId hasn't been found yet, but we have the label
if( !this.model.get("seriesId") && !this.model.get("latestVersion") && this.model.get("label") ){
//When the seriesId or latest pid is found, come back to this function
this.listenToOnce(this.model, "change:seriesId", this.authorizeUser);
this.listenToOnce(this.model, "latestVersionFound", this.authorizeUser);
//If the portal isn't found, display a 404 message
this.listenToOnce(this.model, "notFound", this.showNotFound);
//Get the seriesId or latest pid
this.model.getSeriesIdByLabel();
return;
}
else{
//Remove the listeners for the seriesId and latest pid
this.stopListening(this.model, "change:seriesId", this.authorizeUser);
this.stopListening(this.model, "latestVersionFound", this.authorizeUser);
}
// checking for the write Permission
this.model.checkAuthority("write");
}
},
/**
* Hides the loading
*/
hideLoading: function() {
// Find the loading object and remove it.
if (this.$el.find(".loading")) {
this.$el.find(".loading").remove();
}
},
/**
* toggleSectionLinks - show or hide the section links. Used for the
* mobile/small screen view of the portal.
*/
toggleSectionLinks: function(e){
try{
// Don't close the menu if the user clicked the dropdown for the rename/delete menu,
// or something within that menu. Also do not close when the user clicked to update
// the tab name in a content editable element.
if(e && e.target){
if(
$(e.target).closest(".section-menu-link").length ||
$(e.target).closest(".dropdown-menu").length ||
$(e.target).attr("contentEditable") == "true"
){
return
}
}
var tabs = this.$("#portal-section-tabs");
if(!tabs){
return
}
// Only toggle the section links on mobile. On mobile, the
// ".show-sections-toggle" is visible.
if(this.$(".show-sections-toggle").is(":visible")){
tabs.slideToggle();
// If not on mobile, the section tabs should always be visible
} else {
tabs.show();
}
} catch(e){
console.error("Failed to toggle section links, error message: " + e);
}
},
/**
* renderLogoEditor - Creates a new PortalImage model for the portal logo if
* one doesn't exist already, then inserts an ImageEdit view into the
* portEditLogoContainer.
*/
renderLogoEditor: function() {
try {
// If the portal has no logo, add the default model for one
if(!this.model.get("logo")){
this.model.set("logo", new PortalImage({
label: "logo",
nodeName: "logo"
})
);
};
// Add the image view (incl. uploader) for the portal logo
this.logoEdit = new ImageEdit({
model: this.model.get("logo"),
imageUploadInstructions: "Drag & drop a logo or click to upload",
imageWidth: 100,
imageHeight: 100,
minWidth: 64,
minHeight: 64,
maxHeight: 300,
maxWidth: 300,
nameLabel: false,
urlLabel: false,
imageTagName: "img",
removeButton: false
});
this.$(this.portEditLogoContainer).append(this.logoEdit.el);
this.logoEdit.render();
this.logoEdit.editorView = this;
this.listenTo(this.model.get("logo"), "change", this.showControls);
} catch (e) {
console.error("Logo editor view could not be rendered. Error message: " + e);
}
},
/**
* When a simple text input field loses focus, the corresponding model
* attribute is updated with the value from the input field
*
* @param {Event} [e] - The focusout event
*/
updateBasicText: function(e){
if(!e) return false;
//Get the category, new value, and model
var category = $(e.target).attr("data-category"),
value = $(e.target).val(),
model = $(e.target).data("model") || this.model;
//We can't update anything without a category
if(!category) return false;
//Clean up the value string so it's valid for XML
value = this.model.cleanXMLText(value);
//If the value is an empty string,
if( typeof value == "string" && !value.length ){
//Remove the value from the input
$(e.target).val("");
}
//If the value is only spaces,
else if( typeof value == "string" && !value.trim().length ){
//Remove the value from the input
$(e.target).val("");
//Update the model as if this is an empty string
value = "";
}
//Get the current value
var currentValue = model.get(category);
//Insert the new value into the array
if( Array.isArray(currentValue) ){
//Find the position this text input is in
var position = $(e.target)
.parents("div.text-container")
.first()
.children("div")
.index( $(e.target).parent() );
//Set the value in that position in the array
currentValue[position] = value;
//Set the changed array on this model
model.set(category, currentValue);
model.trigger("change:" + category);
}
//Update the model if the current value is a string
else if(typeof currentValue == "string" || !currentValue){
model.set(category, value);
model.trigger("change:" + category);
}
//TODO: Add another blank text input (write addBasicText function)
// if($(e.target).is(".new") && value != '' && category != "title"){
// $(e.target).removeClass("new");
// this.addBasicText(e);
// }
},
/**
* When the object is saved successfully, tell the user.
* @param {object} savedObject - the object that was successfully saved
*/
saveSuccess: function(savedObject){
var identifier = this.model.get("label") || this.model.get("seriesId") || this.model.get("id"),
viewURL = MetacatUI.root + "/"+ MetacatUI.appModel.get("portalTermPlural") +"/" + identifier;
var message = this.editorSubmitMessageTemplate({
messageText: "Your changes have been submitted.",
viewURL: viewURL,
buttonText: "View your " + MetacatUI.appModel.get("portalTermSingular")
});
MetacatUI.appView.showAlert(message, "alert-success", this.$el, null, {remove: true});
//Update the view URL in any other portal view links
this.$(this.viewPortalLinks).attr("href", viewURL).show();
this.hideSaving();
this.removeValidation();
// Update the path in case the user selected a new portal label
this.sectionsView.updatePath();
// Reset the original label (note: this MUST occur AFTER updatePath())
this.model.set("originalLabel", this.model.get("label"));
},
/**
* When the Portal model has been flagged as invalid, show the validation error messages
*/
showValidation: function(){
//First clear all the error messaging
this.removeValidation();
var errors = this.model.validationError;
_.each(errors, function(errorMsg, category){
var categoryEls = this.$("[data-category='" + category + "']");
//The label category is unique, because it is duplicated in the PortalImage, which can cause bugs
if( category == "label" ){
categoryEls = this.$(".change-label-container [data-category='label']");
var settingsView = _.findWhere(this.sectionsView.subviews, {type: "PortEditorSettings"});
//Show the "change label" elements so the validation will appear
settingsView.changeLabel();
}
//Get the elements that have views attached to them
var elsWithViews = _.filter(categoryEls, function(el){
return ( $(el).data("view") &&
$(el).data("view").showValidation &&
!$(el).data("view").isNew );
});
//If at least one element of this category has a view,
if(elsWithViews.length){
//Use the view's showValidation function, if it exists.
_.each(elsWithViews, function(el){
var view = $(el).data("view");
if( view && view.showValidation ){
view.showValidation();
}
});
}
else{
//Show the validation message
this.showValidationMessage(categoryEls, errorMsg);
}
}, this);
if(errors){
MetacatUI.appView.showAlert("Provide the content flagged below before submitting.",
"alert-error",
this.$el,
null,
{
remove: true
});
//Hide the saving styling
this.hideSaving();
}
},
/**
* Shows a validation error message and adds error styling to the given elements
* @param {jQuery} elements - The elements to add error styling and messaging to
* @param {string} errorMsg - The error message to display
*/
showValidationMessage: function(elements, errorMsg){
//Show the error message
elements.filter(".notification").addClass("error").text(errorMsg);
//Add the error class to inputs
var inputs = elements.filter("textarea, input").addClass("error");
//Show the validation message in the portal sections
if( this.sectionsView ){
this.sectionsView.showValidation(elements);
}
},
/**
* Removes all the validation error styling and messaging from this view
*/
removeValidation: function(){
EditorView.prototype.removeValidation.call(this);
this.$(".section-link-container.error, input.error, textarea.error").removeClass("error");
},
/**
* Show Sign In buttons
*/
showSignIn: function(){
// Messsage if the user is trying to edit an existing portal
var title = "Sign in with your ORCID to edit this portal"
// Message to create a portal if the portal is new
if (this.model.get("isNew")) {
title = "<strong>You're one step away from the portal builder</strong><br>Start by signing in with your ORCID"
}
this.$el.html(this.loginTemplate({
title: title,
portalInfoLink: MetacatUI.appModel.get("portalInfoURL"),
portalImageSrc: MetacatUI.root + "/img/portals/portal-data-page-example.png",
altText: "Screen shot of a portal data page for a climate research lab. The page shows a search bar, customized filters, and a map of the the geographic area the data covers."
}));
},
/**
* The DataONE Plus Subscription if fetched from Bookkeeper and the status of the
* Subscription is rendered on the page.
* Subviews in this view should have their own renderSubscriptionInfo() function
* that inserts subscription details into the subview.
*/
renderSubscriptionInfo: function(){
if( MetacatUI.appModel.get("enableBookkeeperServices") ){
if( MetacatUI.appUserModel.get("loggedIn") && MetacatUI.appUserModel.get("dataoneSubscription") ){
//Show the free trial message for this portal, if the subscription is in a free trial
var subscription = MetacatUI.appUserModel.get("dataoneSubscription"),
isFreeTrial = false;
//If the Subscription is in free trial mode
if( subscription && subscription.isTrialing() ){
if( MetacatUI.appModel.get("dataonePlusPreviewMode") ){
//If this portal is not in the configured list of Plus portals
var trialExceptions = MetacatUI.appModel.get("dataonePlusPreviewPortals");
isFreeTrial = !_.findWhere(trialExceptions, { seriesId: this.model.get("seriesId") });
}
else{
isFreeTrial = true;
}
if( isFreeTrial ){
//Show a free trial message in the editor footer
var freeTrialMessage = "This " + MetacatUI.appModel.get("portalTermSingular") + " is a free preview of " + MetacatUI.appModel.get("dataonePlusName");
var messageEl = $(document.createElement("span"))
.addClass("free-trial-message")
.text(freeTrialMessage)
.prepend( $(document.createElement("i")).addClass("dataone-plus-icon-container") );
this.$("#editor-footer").prepend(messageEl);
// Update the label element to randomly generated label
// And disable the input
var labelEL = $('.label-input-text');
labelEL.val(this.model.get("label"));
//When the Portal Model label is changed, update the input
this.listenTo(this.model, "change:label", function(){
$('.label-input-text').val(this.model.get("label"));
});
labelEL.attr("disabled", "disabled");
//Remove the "Change URL" button that toggles the label input
this.$(".btn.change-label").remove();
// Show edit label message if the edit button is disabled
var editLabelMessage = "Create a custom " + MetacatUI.appModel.get("portalTermSingular") + " name for the URL when your free preview of " +
"<i class='dataone-plus-icon-container'></i>" + MetacatUI.appModel.get("dataonePlusName") + " ends.";
var messageContainer = this.$(".label-container .notification").html(editLabelMessage).addClass("free-trial");
if( !messageContainer.is(":visible") ){
messageContainer.detach().appendTo( this.$(".change-label-container") );
}
//Insert the DataONE Plus icon
var viewRef = this;
require(["text!templates/dataonePlusIcon.html"], function(iconTemplate){
viewRef.$(".dataone-plus-icon-container").html(iconTemplate);
});
}
}
}
else{
this.listenTo( MetacatUI.appUserModel, "change:dataoneSubscription", this.renderSubscriptionInfo );
}
}
},
/**
* @inheritdoc
*/
isAccessPolicyEditEnabled: function(){
if( !MetacatUI.appModel.get("allowAccessPolicyChanges") ){
return false;
}
if( !MetacatUI.appModel.get("allowAccessPolicyChangesPortals") ){
return false;
}
let limitedTo = MetacatUI.appModel.get("allowAccessPolicyChangesPortalsForSubjects");
if( Array.isArray(limitedTo) && limitedTo.length ){
return _.intersection(limitedTo, MetacatUI.appUserModel.get("allIdentitiesAndGroups")).length > 0;
}
else{
return true;
}
},
/**
* If the given portal doesn't exist, display a Not Found message.
*/
showNotFound: function(){
this.hideLoading();
var notFoundMessage = $(document.createElement("p")).text("The " + MetacatUI.appModel.get("portalTermSingular") + " ");
notFoundMessage.append( $(document.createElement("span")).text(this.model.get("label") || this.portalIdentifier) )
.append( $(document.createElement("span")).text(" doesn't exist.") );
MetacatUI.appView.showAlert(notFoundMessage, "alert-error non-fixed", this.$el, undefined, { remove: true });
},
/**
* This function is called whenever the window is scrolled.
*/
handleScroll: function() {
try {
var menu = $(".section-links-toggle-container")[0],
editorFooter = this.$("#editor-footer")[0],
editorFooterHeight = editorFooter ? editorFooter.offsetHeight : 0,
menuHeight = menu ? menu.offsetHeight : 0,
hiddenHeight = (menuHeight * -1) + editorFooterHeight,
currentScrollPos = window.pageYOffset;
if(!menu){
return
}
if(MetacatUI.appView.prevScrollpos >= currentScrollPos) {
// when scrolling upward
menu.style.bottom = editorFooterHeight + "px";
} else {
// when scrolling downward
menu.style.bottom = hiddenHeight + "px";
}
MetacatUI.appView.prevScrollpos = currentScrollPos;
} catch (error) {
console.log("There was an error adjusting menu position on scroll. Error details: " + error);
}
},
/**
* @inheritdoc
*/
onClose: function(){
//Call the superclass onClose() function
EditorView.prototype.onClose.call(this);
//Remove the Portal class from the body element
$("body").removeClass("Portal");
//Remove the scroll and resize listener
$(window).off("scroll");
$(window).off("resize");
//Close and remove all of the subviews
_.invoke(this.subviews, "onClose");
_.invoke(this.subviews, "remove");
//Reset the subviews array
this.subviews = new Array();
//Reset the sectionsView reference
this.sectionsView = null;
},
});
return PortalEditorView;
});