List view getListItemXmlAttributes method fails with child publication items

JavascriptListviewMethodsAttributesTridion

Javascript Problem Overview


I have created a JS class to populate SG/Folder list view data, when items are modified. ([As per Jaime's approach)][1] [1]:http://jaimesantosalcon.blogspot.com/2012/04/sdl-tridion-2011-data-extenders-real.html

Everything works great when I operate on items in the publication they're created in.

Ex: I open a component or page and the custom locked by column immediately updates and shows my user name.

However, when I go to a child publication and repeat that process, I get the window asking if I want to localize or edit the parent item. If I select to edit the parent window, the code doesn't work. I haven't quite figured it out yet with initial debugging. Chrome seems to swallow the error, Firefox gives me a cryptic:

>Timestamp: 6/22/2012 3:42:54 PM

>Error: uncaught exception: [Exception... "Component returned failure code: 0x80004002 (NS_NOINTERFACE) [nsIWebProgress.DOMWindow]" nsresult: "0x80004002 (NS_NOINTERFACE)" location: "JS frame :: chrome://browser/content/tabbrowser.xml :: :: line 545" data: no]

Does anyone have any initial ideas? I'll try to post some code later on...

Code from PageEx.js:

Type.registerNamespace("MyCompany.Tridion.RTFExtensions");

/*
* Constructor
*/

MyCompany.Tridion.RTFExtensions.PageEx = function (id) {
    Type.enableInterface(this, "MyCompany.Tridion.RTFExtensions.PageEx");
    this.addInterface("Tridion.ContentManager.Page", [id]);
    var p = this.properties;
    p.versionNumberString = undefined;
    p.modifiedBy = undefined;
    p.lockedBy = undefined;
    p.approvalStatus = undefined;
    p.publishDate = undefined;
    p.previousVersion = undefined;
	p.previousApprovalStatus = undefined;
	p.customModifiedDate = undefined;
	p.initialModifierUserName = undefined;
};

/*
* sends the list xml string for the item 
*/
MyCompany.Tridion.RTFExtensions.PageEx.prototype.getListItemXmlAttributes = function (customAttributes) {
    var attribs = {};
    $extUtils.getListItemXmlAttributes(customAttributes,this, attribs);
    return this.callBase("Tridion.ContentManager.Page", "getListItemXmlAttributes", [attribs]);
};


/*
* This method gets called when an item is opened from list view. node parameter has the information
* displayed in the list view as attributes. We are getting cutom data extender column information 
* from this xml node and storing it in this class member for returning it from getListItemXmlAttributes method
*/
MyCompany.Tridion.RTFExtensions.PageEx.prototype.setDataFromList = function (node, parentId, timeStamp) {
	$extUtils.setDataFromList(node,parentId,timeStamp,this);
    this.callBase("Tridion.ContentManager.Page", "setDataFromList", [node, parentId, timeStamp]);
};

/* 
* Gets item icon 
*/
MyCompany.Tridion.RTFExtensions.PageEx.prototype.getItemIcon = function () {
    var icon = this.callBase(this.defaultBase, "getItemIcon");
    return icon;
};

Code from utils.js:

// reloads the list view for the given id (used in list view data refresh when JS cant get the required data without reloading)
MyCompany.Tridion.RTFExtensions.Utilities.reloadListView = function (listTcmId) {
	var registry = $models.getListsRegistry();
	for(var key in registry)
	{
		var entry = $models.getItem(registry[key]);
		if (entry && entry.getParentId() == listTcmId)
		{
		   entry.unload();
		   return true;
		}
	}
	return false;
}

/*
* This method gets called when an item is opened from list view. node parameter has the information
* displayed in the list view as attributes. We are getting cutom data extender column information 
* from this xml node and storing it in this class member for returning it from getListItemXmlAttributes method
*/
MyCompany.Tridion.RTFExtensions.Utilities.setDataFromList = function (node, parentId, timeStamp, itemClicked) {
    var p = itemClicked.properties;
	
    if (!timeStamp || timeStamp > itemClicked.getTimeStamp()) {
        var tmp;
        if (tmp = node.getAttribute('Version')) {
            p.versionNumberString = tmp;
			p.previousVersion = tmp;
        }
        if (tmp = node.getAttribute('ModifiedBy')) {
            p.modifiedBy = tmp;
			p.initialModifierUserName = tmp;
        }
        if (tmp = node.getAttribute('LockedBy')) {
            p.lockedBy = tmp;
        }
        if (tmp = node.getAttribute('ApprovalStatus')) {
            p.approvalStatus = tmp;
			p.previousApprovalStatus = tmp;
        }
        if (tmp = node.getAttribute('PublishDate')) {
            p.publishDate = tmp;
        }
		if (p.customModifiedDate === undefined) {
			if (tmp = node.getAttribute('Modified')) {
				p.customModifiedDate = tmp;
			}
		}
    }
}

/*
* sends the list xml string for the item in the list view.
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListItemXmlAttributes = function (customAttributes, listViewObject,attribs) {
    var p = listViewObject.properties;
	$extUtils.getListViewItemLockedByName(p,listViewObject);
	
    if (customAttributes) {
        for (var attr in customAttributes) {
            attribs[attr] = customAttributes[attr];
        }
    }

	attribs["Version"] = $extUtils.getListViewItemUpdatedVersion(p,listViewObject);
	//modified name has to come after the version update...
	$extUtils.getListViewItemModifiedByName(p,listViewObject);
    attribs["ApprovalStatus"] = $extUtils.getListViewItemApprovalStatus(p,listViewObject);	
    attribs["PublishDate"] = $extUtils.getListViewItemPublishDate(p,listViewObject);

	//set default values
	if (p.versionNumberString != undefined) {
		var iResult = p.versionNumberString.localeCompare(p.previousVersion);
		if (p.previousVersion === undefined || iResult > 0) {
			//it's been updated!
			p.previousVersion = p.versionNumberString;
			p.previousApprovalStatus = p.approvalStatus;

			//also need to update modified date
			p.customModifiedDate = $extUtils.getListViewItemUpdatedModifiedDate(p,listViewObject);
			p.initialModifierUserName = p.modifiedBy;
		}

	}
	attribs["Modified"] = p.customModifiedDate;
	attribs["LockedBy"] = p.lockedBy;
    attribs["ModifiedBy"] = p.modifiedBy;

};

/*
* This method sets the property of the Revisor owner on the item in the list view. however, if it's not the current user
* we have no way to look that up in JS so we have to reload the list view.
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemModifiedByName = function (p,listViewObject) {
    var p = listViewObject.properties;
    var xmlDoc = listViewObject.getXmlDocument();
    if (xmlDoc) {
		//modifier should always exist...
        var modifierId = $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:VersionInfo/tcm:Revisor/@xlink:title");
        if (modifierId != undefined) {
			var u = Tridion.UI.UserSettings.getJsonUserSettings(true);
			if (modifierId == u.User.Data.Name) {
				var strDescription = u.User.Data.Description.split('(');
				p.modifiedBy = strDescription[0];
				return;
			} else {
				//we're in trouble...
				//let's hope it's the initial modifier we had...
				if (p.previousVersion == p.versionNumberString) {
					//whew...
					p.modifiedBy = p.initialModifierUserName;
					return;
				}

				if (!$extUtils.reloadListView(listViewObject.getOrganizationalItemId())) {
					//hrm. something failed on the reload? not sure what else to do:
					p.modifiedBy = modifierId;
				}
			}
		} else {
			//shouldn't ever happen.
			p.modifiedBy = "";
			return;
		}
    }
	
};

/*
* This method sets the property of the lock owner on the item in the list view. however, if it's not the current user
* we have no way to look that up in JS so we have to reload the list view.
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemLockedByName = function (p,listViewObject) {
    var xmlDoc = listViewObject.getXmlDocument();
    if (xmlDoc) {
		//this will be user id. no sense getting tcmid... can't look it up without async call
		var lockedUserId = $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:VersionInfo/tcm:ItemLock/tcm:User/@xlink:title");
        if (lockedUserId != undefined) {
			//see if it's the current user. most likely...
			var u = Tridion.UI.UserSettings.getJsonUserSettings(true);
			if (lockedUserId == u.User.Data.Name) {
				var strDescription = u.User.Data.Description.split('(');
				p.lockedBy = strDescription[0];
				return;
			}
			//it's not the current user. no synch way to do what we want, plus the JS call doesn't get the workflow version anyway. refresh the parent view
			if (!$extUtils.reloadListView(listViewObject.getOrganizationalItemId())) {
				//hrm. something failed on the reload? not sure what else to do:
				p.lockedBy = lockedUserId;
			}
        } else {
			//clear it out since there's no lock owner
			p.lockedBy = "";
		}
    }
};

/*
* Gets the ApprovalStatus from the item
* This makes absolutely no sense... but for some reason the approval status gets wiped out when this method
* enters. so I had to use a "previous approval status" variable to maintain it. no idea why. I don't see anything
* else that should be touching it... but clearly something clears it out.
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemApprovalStatus = function (p,listViewObject) {
	//check if the item has actually been modified.
	if (p.versionNumberString != p.previousVersion) {
		var xmlDoc = listViewObject.getXmlDocument();
		if (xmlDoc) {
			p.approvalStatus = $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:Data/tcm:ApprovalStatus/@xlink:title");
		}
	} else {
		p.approvalStatus = p.previousApprovalStatus;
	}
    if (p.approvalStatus === undefined || p.approvalStatus.toUpperCase() == 'UNAPPROVED') {
		var foo = p.approvalStatus;
        p.approvalStatus = 'WIP';
    }
	return p.approvalStatus;
};


/*
* Gets the PublishDate from the item list view
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemPublishDate = function (p,listViewObject) {
	//modification won't alter publish date.
    var p = listViewObject.properties;
    return p.publishDate;
};


/*
*	get the modified date for the workflow version, overwrite OOB since that uses last major version
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemUpdatedModifiedDate = function (p,listViewObject) {
    var xmlDoc = listViewObject.getXmlDocument();
	var modDate = $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:VersionInfo/tcm:RevisionDate");
	return modDate;
}


/*
* Gets the updated Version information from the item
*/
MyCompany.Tridion.RTFExtensions.Utilities.getListViewItemUpdatedVersion = function (p,listViewObject) {
    var p = listViewObject.properties;
    var xmlDoc = listViewObject.getXmlDocument();
	var newVersionString = undefined;
    if (xmlDoc) {
        newVersionString = String.format("{0}.{1}", $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:VersionInfo/tcm:Version"), $xml.getInnerText(xmlDoc, "/tcm:*/tcm:Info/tcm:VersionInfo/tcm:Revision"));
    }
	if (newVersionString != undefined) {
		//want to ensure we're getting a LATER version than we had (because it will try to load the non-workflow version afterwards...
		var iResult = newVersionString.localeCompare(p.previousVersion);
		if (p.previousVersion === undefined || iResult > 0) {
			p.versionNumberString = newVersionString;
		} else {
			p.versionNumberString = p.previousVersion;
		}
	} else {
		p.versionNumberString = p.previousVersion;
	}
    return p.versionNumberString;
};



function launchPopup(winURL, winName, winFeatures, winObj) {
    // this will hold our opened window
    var theWin;
    // first check to see if the window already exists
    if (winObj != null) {
        // the window has already been created, but did the user close it?
        // if so, then reopen it. Otherwise make it the active window.
        if (!winObj.closed) {
            winObj.focus();
            return winObj;
        }
        // otherwise fall through to the code below to re-open the window
    }
    // if we get here, then the window hasn't been created yet, or it
    // was closed by the user.
    theWin = window.open(winURL, winName, winFeatures);
    return theWin;
}

var $extUtils = MyCompany.Tridion.RTFExtensions.Utilities;

Javascript Solutions


Solution 1 - Javascript

> [Exception... "Component returned failure code: 0x80004002 > (NS_NOINTERFACE) [nsIWebProgress.DOMWindow]"

This means that there was no window assigned to the nsIWebProgress object. So it has nowhere to display data.

> nsresult: "0x80004002 (NS_NOINTERFACE)" location: "JS frame :: > chrome://browser/content/tabbrowser.xml :: :: line 545" data: no]

This is telling you what file is associated with that error. and what line it faulted on.

But the real key is the NS_NOINTERFACE error. Which means the interface has not been registered.

You are using Type.enableInterface(). Is that a custom method you are declaring somewhere else ? I don't see it. You might want to change that to .registerInterface()

See this link Type Class and Type.registerInterface()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWarner SoditusView Question on Stackoverflow
Solution 1 - JavascriptTech SavantView Answer on Stackoverflow