XPage DynamicViewPanel link bug, or developer blindness? - xpages

Preamble: the last time I had an issue with something from the extension libraries, I immediately posted a bug report on openNTF.org. It then turned out that I had missed a property setting that made everything work perfectly. To prevent this issue from happening again, I decided to post the question here first, can anyone reproduce the error that I am having, or am I missing a property on the xe:DynamicViewPanel control?
tested ExtLib versions tested:
901v00_06.20140424-0600
901v00_02.20131212-1115
Domino/Notes version used Domino 9.0.1
Steps to reproduce
Create a dashboard XPage application. - data is stored in a different database.
Using a dynamicViewPanel control, access a database that lies ON THE SAME SERVER and show information from that database in the view.
Click on a document link in the dynamic view panel and look at the URL in the browser. When the dynamicViewPanel is pointing to a view in a different database hosted on the same server as the dashboard, the url looks like this : http://path/Portal.nsf/%24%24OpenDominoDocument.xsp?databaseName=CN=Maestro/O=hol-dev/C=DE!!path/Portal.nsf&documentId=A6727D8AF7D2FE19C1257C9E0034A14C&action=editDocument
The appropriate Xpage defined by the form name of the document being clicked on will open and the data saved in that document will even be loaded.
Click on a button that performs any action with the defined document datasource. It can be as simple as printing a string to the server when notesXspDoc.isNewNote(). You will get an error message saying that (in the case above) NotesXSPDocument.isNewNote() null. This means that the variable IS NOT null, but for some reason you are getting a null error response.
This error will only happen when the dashboard and the data database are on the same domino server. As soon as one of them are moved, it will work perfectly AND the URL will be correctly written; the server name of the databaseName parameter will be written in the abbreviated way.
In an attempt to understand/fix this issue, I took a closer look at the code in the Extension Library class com.ibm.xsp.extlib.component.dynamicview.UIDynamicViewPanel. There, I found the call to super(XspViewPanel).getDocumentUrl(). My guess is that the error lies in that function, but that is not a class that is defined by the ExtLibs. At the very least I would be interested to know what is written there.
educated guesswork
I am pretty certain of the following: The page when first open loads because key information is saved in the requestScope. This information is not not passed on to the java DominoDocument object which is created by the NotesXspDocument javascript 'class'. When (in the background) the DomDoc.restoreDocument() function is called, it tries to locate the database CN (as returned by the param.get("databaseName") function) and fails. Thus, no further function can be called on the object since an exception would always be thrown. (This is all an educated guess on my part)
My Questions clearly stated
Is this a known bug and I am just not finding it in my google searches?
Am I just not seeing a property of the ViewPanel that would fix this error?
Is there a better work around other than setting the "ignoreUrlParams" property of the document data source and calculating the server/documentID?
Thank you very much for all assistance given!
EDIT
Changing the "ignoreRequestParams" property to true and calculating the databaseName and the documentID does not make a difference at all. I am really confused as to why it does not work, I would think that calculating the source information and not paying any attention to the request parameters would solve it, but i am having the same issues. Any further assistance would be greatly appreciated!

After a great deal of testing, deleting functions and re-adding them, I think I have the answer I sought. I cannot really say why this is, and I find it a crappy design if my thoughts are correct.
I found that when I commented out a certain javascript line which opened the database in a background java class to find out the user's current roles, it worked again. Upon further investigation, when I took out the java lines that recycled the source database, the dashboard worked again. This means, however, that the background c++ object is still existing in the background somewhere. In this project, it seems to only be an issue when the source DB and dashboard DB are hosted on the same server.
I am going to answer my question with "No, this is not a bug in the dynamicViewPanel", but rather a questionable design from IBM itself. Either we should be recycling our Notes objects, or we should not be recycling our Notes objects, or we should be given a very detailed description of when and when not to recycle. Maybe I am just not used doing my own GC. Here is the code that was giving me a hard time (some variable names have changed to protect the anonymity of the customer):
public String getDatabaseRoles(){
Session session = null;
Database db = null;
Vector<String> roles = null;
try {
session = SessionHelper.getCurrentSession();
db = DatabaseController.getApplicationInstance().getDbFile().getDatabase();
roles = db.queryAccessRoles(session.getEffectiveUserName());
} catch (NullDatabaseException e) {
// already logged
} catch (NotesException e) {
ErrorWriter.getSessionInstance().writeError(new Date(), ErrorLevel.ERROR, "EprDatabaseController.getDatabaseRoles()",
e);
} catch (NullSessionException e) {
// already logged
} finally {
//if(db != null) try {db.recycle();} catch(NotesException e) {} //commented out due to issues when portal is on same server as data.
//if(session != null) try {session.recycle();} catch(NotesException e) {}
}
StringBuilder roleStr = new StringBuilder();
if(roles != null){
for(String role : roles){
if(!StringHelper.isNullOrEmpty(role)){
roleStr.append(role);
roleStr.append(":");
}
}
}
ErrorWriter.getSessionInstance().writeError(new Date(), ErrorLevel.TRACE, "DatabaseController.getEPRDatabaseRoles()",
"Database Roles: " + roleStr.toString());
return roleStr.toString();
}
Again, just so workarounds are already answered for future readers,
have the source database on a different server.
set the ignoreUrlParams property of the document datasource and calculate the the documentID and databaseName correctly inserting the server name as abbreviated.
This not being the true answer of my question, I am just putting this here for future user ease.

Related

'Migrate Routes' fail when upgrading from Orchard 1.3.9 to Orchard 1.7

I'm in the process of upgrading an Orchard site from 1.3.9 to 1.7.
I'm using the Upgrade module, and when attempting to upgrade Routes (last tab) the code fails, at the point where it attempts to access a newly created AutoroutePart.
Specifically, inside RouteController.IndexPOST() :
// migrating parts
_contentDefinitionManager.AlterTypeDefinition(contentType,
builder => builder
.WithPart("AutoroutePart")
.WithPart("TitlePart"));
// force the first object to be reloaded in order to get a valid AutoroutePart
_orchardServices.ContentManager.Clear();
This code snippet is supposed to assign an AutoroutePart and a TitlePart to the type at hand (let's say it's a Page but obviously any other type in the checkbox list of the Routes tab). But it fails to do so. Because later on the assignment
var autoroutePart = ((ContentItem)content).As<AutoroutePart>();
fails and autoroutePart is null, and then further down the code fails with an Object reference not set... error when it attempts to access autoroutePart.ContentItem.Id.
Eventually I was able to bypass it by manually entering a record in table Settings_ContentTypePartDefinitionRecord that maps a Page (ContentType ID 2) to Autoroute and Title part IDs but that seems awkward and error-prone, and I must have missed something (not to mention having to apply this hack to the rest of the content types).
What can cause AlterTypeDefinition to not apply these records in the database?
I was able to get this to work by using the new transaction system.
The idea is that ITransactionManager.RequireNew() is called before and after each piece of code that needs to be committed to the database. A few of the calls in my code example are redundant but I figure this code is only going to be used once so rather than try to optimize unneeded calls away, I would leave them in to indicate where transactions start and end.
Providing a diff as a gist.
https://gist.github.com/harmony7/10621982

Sharepoint 2007 Webpart variables problem on reload

I'm having trouble with the webparts variables... I came from standard ASP language, so, to me, store variables in session and other parts is the common way to do everything =)
Now i had to create a webpart, the wp has to write a graph from parameter and i cannot understand HOW variables works: i cannot understand WHEN saved and WHEN erased and other thing like this!
Let me explain: i have a web part with the configuration toolbar on the right in which i put the values.. Everytime a button is pressed or a value in the dropdown list changes, it raises an event which causes the "CreateChild" function..
Many times the data is "stored", other time they are not!
That's the way i used to store value (in the ApplyChanges override function):
WPChartGenerator wpParent = (WPChartGenerator)this.ParentToolPane.SelectedWebPart;
wpParent.WebUrl = txtWebUrl.Text.Trim();
And in the CreateChild event i get the value like:
WPChartGenerator wpParent = (WPChartGenerator)this.ParentToolPane.SelectedWebPart;
this.ddlWeb = new DropDownList();
this.ddlWeb.ID = "ddlweb" + wpParent.ID;
ddlWeb.SelectedValue = wpParent.WebService;
Now.. Sometimes this works, for example, when i push a button I invoke in the code of the button and then the code to store every value.. In some case (like buttons) this works, in other (like dropdown list index changed event) this fails and i found every object in the wpParent equal to it's initial value.
Another thing i noticed, is that in certain cases when an event is triggered, the first thing to be executed (even first than the event's associated code) il CreateChild (even first than OnLoad!!!)
Can anybody tell me what I'm doing in the wrong way? Do anybody has a good tutoria for this matter?
Thanks & sorry 4 my School level English =)
Forget to say that every variable has been implemented as a Property, like that:
[WebBrowsable(false)]
[Personalizable(PersonalizationScope.Shared)]
[WebPartStorage(Storage.Shared)]
public string WebUrl
{
get
{
return this.webUrl;
}
set
{
this.webUrl = value;
}
}
I can't see all the code there so I don't really know what you're doing wrong (i.e. do you actually have an ApplyChanges method) but from the way you've worded your question it sounds like you really need to start at the beginning, follow one of these walkthrough tutorials and make sure you understand the basics and then start adding in the code for your project.
MSDN - Creating Web Parts for SharePoint (VS2010)
MSDN - Walkthrough: Creating a Basic SharePoint Web Part (WSS 3.0)
Developing SharePoint 2007 Web Parts

Get self defined AfterProperties column in Event Receiver

i want to show an error message, whenever the user adds an item.
So i use code similar to that:
public override void ItemAdding(SPItemEventProperties properties)
{
properties.Cancel = true;
properties.ErroMessage = properties.AfterProperties[columnname];
}
Unfortunately it only work, when the columname ist an default column.
For self defined columns it doesn't work.
I get this error message:
event receiver has canceled the request
I tried everything:
The columnname is right, the column is not null,..
If i write a console application getting this columns, everything will work well.
Only in the event receiver it doesn't work.
Can anybody help? Would be great!
hopefully not too stupid a question but are you using a column name that SharePoint recognizes? The reason I ask is that you mention that these are self defined columns. When you define a column via the UI, the column name behind the scenes ends up as something similar but not the same.
This page gives you a bit of an over view of DisplayName vs InternalName. And as the author mentions, things get even more complicated when you rename a column via the UI.

SharePoint web part storage with multiple instances of web part on same page

I have a custom web part I've built, that I'm attempting to place three instances of on a single page. What I've noticed is that when I have a single instance on the page, the custom properties save and work appropriately. When I then add two more instances of the same custom web part to the page, the following web parts do not save their custom property values. I figure I must be doing something wrong when it comes to the WebPartStorage attribute. Any thoughts?
[Browsable(true), Category("Miscellaneous"),
WebPartStorage(Storage.Personal),
FriendlyName("List"), Description("List")]
public String List { get; set; }
Have you tried Storage.Shared rather than Storage.Personal?
This would help to isolate the personalisation store as the cause of the problem.
Additionally try removing the FriendlyName and Description attributes.
Interestingly enough, I ended up removing the default value and description attributes and my scenario began working. Not a very helpful explanation, I know, but maybe it will help someone else in the future.

Get Sharepoint store URL in Outlook?

I'm trying to write an Outlook 2007 VSTO add-in that lets you do some stuff with Sharepoint webservices. I'd really like to make it as simple as possible for the users; ideally, all that they would have to do is connect a Sharepoint list to Outlook. From there, my add-in would ideally grab the actual Sharepoint URL from the list and do its thing. Unfortunately, I can't seem to find where Outlook stores this information while running.
The best solution I've been able to find is to read in the files found in C:\Documents and Settings(username)\Local Settings\Application Data\Microsoft\Outlook*.sharing.xml.obi.
However, these files are only updated when you close Outlook. That means that the user would have to connect to the list, restart Outlook, and then things would work. I'd rather not have things get to that level.
It's almost like the information just magics its way into the sharing.xml.obi files. I've Googled, I've used OutlookSpy and in desperation I've used mfcmapi.exe, all to no avail. Where the heck does Outlook store this?
You can use the object model (or direct MAPI calls) to query this information from the outlook folder. First use the .isSharePointFolder property to locate your folder. The URL of the SharePoint List in outlook is then stored as the subject of a "hidden" message in the associated contents table.
Bonus tip: If you are not using it already, get yourself a copy of the excellent OutlookSpy. It makes figuring this kind of stuff out so much easier.
With Paul-Jan's pointer, I've figured this out. Since I hate it when I only find indirect hints while googling, here's exactly the code you need:
private string getSharepointURL(Microsoft.Office.Interop.Outlook.Folder SharepointFolder)
{
if (!SharepointFolder.IsSharePointFolder)
throw new Exception("Cannot get the SharePoint URL of " + SharepointFolder.FullFolderPath + ", because it is not a SharePoint folder.");
return (string)((object[])SharepointFolder.GetTable("", 1).FindRow("[From] = SharePoint").GetValues())[1];
}
That may be the ugliest return statement I've ever written. Here's what it does:
Calls Outlook.Folder.GetTable("",1). The first argument is a filter, which means "anything", and the second argument is equivalent to olHiddenItems. (I couldn't find the actual enumeration)
Gets the next row of that table whose sender (the [From] field) is "SharePoint". The information we want is always held in this hidden message.
Gets the values of that hidden message. This comes back as an object, but is secretly an object[].
Casts the values to an object[].
Gets the second item of the values, which is the url.
Casts the url to a string.
Fortunately, you can do all these steps yourself in OutlookSpy. That was a real help in figuring out how to get at this precious nugget of information.
Well, here is what I use... (C#3/VS2008, Outlook2007)
Outlook.Folder folder = GetSomeSpFolder(); // requirement :)
// Things to look for here, not in the columns by default
// the values can be found in OutlookSpy or perhaps MSDN (haha)
// this value refers to the SP site (not the full URL)
var SHARING_REMOTE_STORE_UID = "http://schemas.microsoft.com/mapi/id/{00062040-0000-0000-C000-000000000046}/8A48001E";
var table = folder.GetTable("[From] = SharePoint", Outlook.OlTableContents.olHiddenItems);
// setup columns to look through
table.Columns.RemoveAll();
table.Columns.Add(SHARING_REMOTE_STORE_UID);
if (!table.EndOfTable) {
var row = table.GetNextRow();
var siteURL = row[SHARING_REMOTE_STORE_UID];
Marshal.ReleaseComObject(row);
} else {
// No matching entry ...
}
Marshal.ReleaseComObject(table);
Also check out http://msdn.microsoft.com/en-us/library/bb176406.aspx
Happy coding!

Resources