List view getListItemXmlAttributes method fails with child publication items
Asked Answered
N

1

356

I have created a JS class to populate SG/Folder list view data, when items are modified. (As per Jaime's approach) 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;
Nickolenicks answered 22/6, 2012 at 22:58 Comment(19)
I get such error message most often from inserting something into a DOM illegally Any update on that code? Without that it will be pretty difficult to give you a more concrete answer.Ballinger
Let me see how I can post the code. it's pretty big. I'll try to trim out the irrelevant stuff if I can. I don't get any errors in the logs on the CME server. Firefox is the only browser that gives the error. the others show no error just don't populate the "custom" columns with the data.Nickolenicks
@FrankvanPuffelen, any thoughts? I'm stumped... tried watching things with the JS debugger and not seeing what is going on here. any ideas would be most appreciated.Nickolenicks
Hey Warner, sorry I missed that you added the code. Does debugging it give any indication as to which line is causing the problem? It would really help if we could reduce this from "somewhere in these 250+ lines" to "somewhere around this line".Ballinger
np. that's my fault for not notifying you. I unfortunately haven't found where that error occurs. it doesn't appear to be in this code at all, but the product code that then uses the XML that gets populated. I could be wrong, but I just haven't seen it cause the error. I'll experiment with it some more and try to provide an update.Nickolenicks
@FrankvanPuffelen, what I think I'm seeing is the error show in the console before it even calls my setDataFromList method, which is very bizarre to me. maybe I'm not seeing what I think I'm seeing, though. thoroughly puzzled right now. :(Nickolenicks
So could it be that the error message in Firefox is unrelated to the problem you're seeing? If your PageEx object gets created, I'd start with a breakpoint and see where it came from (and where it leads). If your object isn't even created, you might have a problem in the way your ModelFactory is set up. I'd consider debugging there then, possibly from when the list is requested (somewhere in $models.getRegistry()). Remember: you have all the JavaScript source in separate files too, so use that to find good places to break into.Ballinger
@FrankvanPuffelen (and anyone else brave enough to investigate): update. the Mozilla error seems bogus... I worked with someone way more experienced... and we saw the custom JS code work as expected (populating additional attributes for the item). it all shows fine until this gets processed: Tridion.ContentManager.ListTcmItems.prototype.itemUpdated this.setXml($xml.getOuterXml(xmlDoc)); this.fireEvent("itemupdate", oldId ? {itemId: id,oldId: oldId} : {itemId: id}); Then it doesn't update the list view. we're thinking it is trying to populate the item in the parent publication (not the child)Nickolenicks
Let me know if that makes any sense and how we could diagnose any further.Nickolenicks
Hi Warner - did you manage to close this issue?Victoria
nope. never got any solution on this. :( thanks for checking, though ;)Nickolenicks
This is currently unresolved on Bugzilla. Relevant mozilla codeCrisper
This probably has nothing to do with it, but in your class initialiser for MyCompany.Tridion.RTFExtensions.PageEx you are setting everything to undefined. This may cause an issue as you are defining an attribute then telling it that it is undefined - which doesn't make sense. It is better to set the initial value to null if you don't want a value. As I said this probably doesn't matter, unless other code is checking for defined keys..Cruciferous
Do you have to use Firefox? I mean like you could use the other browsers. It could be the browser's problem.Geniagenial
@Yahs Hef - actually, yes... our content author group "standardized" on Firefox.Nickolenicks
Maybe this could help : w3schools.com/js/js_strict.asp Also, it may not fix your issue, but the only undefined checks I've seen work are typeof something === 'undefined' with ===, ==, !=, or !== Also, you could try to use setTimeout to execute setXml after getOuterXmlComeon
@WarnerSoditus - is there a bug or something blocking it from accessing in Firefox? You could also see the Inspect Elements and see if there is any difference. But that would take too long and sometimes there is no difference.Geniagenial
Add console.log(...) statements. Even when stepping through the JS debugger in Chrome/Firefox, the console.log statements give me visual assertions. As another comment stated, I recommend changing all the undefined assignments to null. Usually there is something in the console logs that there was a failure. Anywhere that you are attempting to assess/manipulate a DOM (or jQuery) object, make a console log entry of it's current value and then the value after assessment/manipulation. This might help you determine if the code is attempting to assess an empyt (undefined) object or property.Slobber
You are missing semi-colons at the end of some of your function delcarations. You might want to fix that and see if that helps at all. whenever declaring a function with var something = function () {}; <-- you need a semi-colon after the }Firestone
F
2

[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()

Firestone answered 13/5, 2015 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.