Edit Profile Documents in Web Browser
Profile Documents in the browser. Can't be done right? Well, I think I might have found a simple way round it using the combination of a simple WQO and WQS agent.
I have a Notes client database that needs converting to the web. One of the tasks is to find a way to still allow users to edit their profile documents. At first I thought it wasn't possible, then I had a thought. Why can't I open the actual Profile Form and then use a WQO agent to copy all the fields over. When the user submits the form it simply copies all the fields back to the profile and saves it.
Let's say we have a profile based on a Form called "UserSettings". It's opened in the Notes Client in the usual way. Now, in the browser I suggest using a URL like:
http://server/db.nsf/UserSettings?OpenForm
This form has a WQO agent ("Run as Web User" enabled). The important code for which is:
Set vProfileDoc = vThisDB.GetProfileDocument("UserSettings", vWebSess.EffectiveUserName) Call vProfileDoc.CopyAllItems(vThisDoc, True)
Simple, no? Just copy all the item values over! Because it's the same form, in effect, all the fields are laid out as expected and it looks like the normal profile document.
To save the profile we need to add a WQS agent ("Run as Web User" enabled). The basic code for this is similar to the above (we still need to get a handle on the Profile) but looks like this:
Call vThisDoc.CopyAllItems(vProfileDoc, True) Call vProfileDoc.Save(True, True)
Notice the CopyAllItems method has its document parameters switched. Notice also that we save the Profile document. This is important and something I forgot when I first tested the theory. Doh!
Another thing we need to do is add a SaveOptions field the Profile form. Because we're opening it like a normal form in the browser we don't want it to save. So, make the field hidden and computed then add a formula like this:
@If(@ClientType="Web"; "0"; "1")
I've done some crude testing and it all appears to work. Have I missed some detail that means it won't in reality? What normally happens is I find something like this and think "Must blog that right away!". What I should probably do is sit on it for a while and see how stable it is. This saves me looking like a fool if it isn't. Oh well, what the hell.
What struck me is that it's such a simple solution. I'm surprised I've not seen it described elsewhere. Has this been done before? If not, should I write a more detailed explanation/article and knock together a demo db for download?
I've been doing it the way you describe for (.. thinks ...) the last four or five years.
Rather than copying across all the items, I usually have some definition of which items will be saved (the rest are discarded). This is useful when you have working or display fields that you dont' want to save on the backend document.
This is actually a first stage in a "pattern" I use a lot, which is to decouple the form from the document. Usually, the form has a significant bearing upon what is stored in the document. If you decouple them, it doesn't.
You can do this with normal documents as well as profile documents.
I just found a problem though - cacheing. Is there any way round the fact that Domino caches profiles documents?
Jake, you'll have to add a hidden field on your profile doc which stores the unid of the doc.
Then refer to it in your code like
unid := @GetProfileField("ProfileDoc"; "unid")
and do eg
myfield := @GetDocField(unid; "YourField")
Caching does not matter anymore since this method gets and sets the values of the Notes profile document.
HTH
Jake
I hate profile documents! I have had so uch trouble with them apparently updating but then when readback, the old values coming up that I have given up on them.
I know it's something to do with cacheing, but I refuse to spend my time fighting Notes!
As soon as I read the headline of this entry, I thought "Jake found a clean way around the caching?! SWEET!".
You could always issue a 'dbcache flush' command from your WSQ agent? ;-)
In truth, this is why I tend to not use profiles in my web-based or hybrid apps but rather documents.
Hopefully someone out there's got a more solid solution than dropping cache just so a single databases profile changes can be seen from the web instantly!
-Chris
It's the http server task that caches the profile documents. An easy way to "refresh" the cache is to restart http... But I guess Chris' has right when saying that 'dbcache flush' works!
i've given up with profile documents on the web too. Too much caching problems. But Patrick's tip looks great, have to test it !
{Link}
I use a similar method, but instead of copying all fields I replace all fields with a specific prefix (CFG_"fieldname")
The wqs-agent creates the profile and updates the profile. The form is just a GUI to modify the values (saveoptions = 0). The default value of each field in the gui is val:= @GetProfileField("MyProfile"; @ThisName). I also include a errorcheck (@if(@iserror(val)...) for each field so I can use a default value for each field if the profile or field doesnt exist.
This will force the GUI to display the latest values from the profile.
...but I have the same problem as you Jake, the values are cached for the session. A workaround that sometimes works is to always delete the profile before updating it... but it might give you other problems (in a replicating environment).
The soluton isn't perfect so I use it only for configuration settings etc (semi-static values)
I've been doing this in an application I'm working on for a while. I have a profile form named Keyword_profile_frm. Instead of opening the profile form I open another form That I use to populate my Keywords, I have two fields on this form the "Key" field and the "KeyWordList" fields. I have a WQO and WQS agent on this form. The WQO acts the same as you've (Jake) has outlined. The only difference being that I use the CopyItem and ReplaceItemValue methods and I never have any caching issues!
e.g. (from WQS agent)
Set docProfile = curdb.GetProfileDocument ("Keyword_profile_frm")
keyname = currentPage.GetItemValue("key")(0)
Set keywordlist = currentPage.GetFirstItem("keywords")
Dim keyValue As NotesItem
Set keyValue = docProfile.GetFirstItem(keyname)
If keyValue Is Nothing Then
Call docProfile.CopyItem(keywordlist, keyname)
Set keyValue = docProfile.ReplaceItemValue(keyname,keywordlist)
Else
Call docProfile.CopyItem(keywordlist, keyname)
End If
Call docProfile.Save(True,False)
In saying that this is just used to update one profile doc with a set of key values but I can't see why i'm having no caching issues!
Actually i'm looking at that code and can't remember why I was doing the ReplaceItemValue bit! (it was 4 years ago!) hmm... may just be working by fluke;-)
Glen. Maybe cache behaviour is different if it's one main profile rather than a user-based profile?
hmm.. maybe but i cant see why. I know i've used the method on a user-based profile before and i don't remember having any issues. I still have issues with caching in the client but never on the web!
Hi Jake
I can only assume that this is for my application. Should caching be a problem we can swap to normal Notes documents to store the settings - don't waste any of your time "fixing" the caching problem.
I don't mind dumping the profile docs in the Notes client one little bit ;o)
Now you tell me ursus ;o)
wow! Jake just used his blog to change the requirements of a customer. Brilliant! :-)
Dumping the profiles is the best solution. I played with user profiles enough to learn not to use them. Sounds like everything worked itself out.
Watch out changing profile docs heavily, before you know it you've got multiple profile docs for 1 user! I've have seen mailboxes containing 5 profiledocs where there only shoud be 1!!
Dim ses As New notessession
Dim dbCur As notesdatabase
Dim docProf As notesdocument
Dim colProfs As notesdocumentcollection
Dim strForm As String
Dim n As Integer
Set dbCur = ses.currentDatabase
Set colProfs = dbCur.getprofiledocCollection
Set docProf = colProfs.getFirstDocument
n=0
While Not docProf Is Nothing
strForm = docProf.getItemValue("Form")(0)
Set docProf = colProfs.getNextDocument(docProf)
n=n+1
Print "profile doc: " + Cstr(n)
Wend
I use a web agent to open the profile doc.
Ex. /xx.nsf/WAGetProfile?OpenAgent&Name=ProfileDocName
the code for WAGetProfile
Sub Initialize
On Error Goto ERRORHANDLER
Dim session As New NotesSession
Dim db As NotesDatabase
Dim docCntxt As NotesDocument
Dim docProfile As NotesDocument
Dim sName As String
Dim sUNID As String
Dim sDBURL As String
Set db = session.CurrentDatabase
sDBURL = db.FilePath
sDBURL = Replace(sDBURL, "\", "/")
sDBURL = Replace(sDBURL, " ", "%20")
Set docCntxt = session.DocumentContext
sName = subUrlQueryString(docCntxt.Query_String(0), "Name" )
If sName = "" Then
Print |Error in Agent: | & session.CurrentAgent.name & |<br>| & docCntxt.Query_String(0) & |<br>| & |<a href="javascript: onClick=history.back()">Back</a>|
Exit Sub
End If
Set docProfile = db.GetProfileDocument(sName)
If docProfile Is Nothing Then
Print |Error in Agent: | & session.CurrentAgent.name & |<br>| & docCntxt.Query_String(0) & |<br>| & |<a href="javascript: onClick=history.back()">Back</a>|
Exit Sub
End If
docProfile.LOG = "1"
docProfile.FORM = sName
Call docProfile.Save(True, True)
sUNID = docProfile.UniversalID
Print |<html><head><script>document.location='/| & sDBURL & |/0/| & sUNID & |?OpenDocument'| & |;</script></head></html>|
Exit Sub
ERRORHANDLER:
Print |Error in Agent: | & session.CurrentAgent.name & |<br>| & |Ln: | & Str$(Erl) & | Err: | & Str$(Err) & | | & Error$ & |<br>| & |<br>| & |<a href="javascript: onClick=history.back()">Back</a>|
Err = 0
Exit Sub
End Sub
Has anybody thought about Profile-Docs's and Replication? We got three Webserver used for load balancing in our DMZ, another used as Replication-Hub and another Notes-Cluster is serving our Intranet.
When you change a Profile-Doc on one Server, the others don't recognize them till http-restart. The http-Task will cache the Profiles on every Server. There is no issue as long as the user is only on one server.
We simply don't use Profile-Documents on Web-Applications any more.
It seems like folks love to hate the profile docs. Jake got the response he was hoping for ;-) But in my opinion profile docs are sometimes quite nice if used where appropriate and you know how they work.
I don't experience problems with caching of profile documents. - Probably because I have used them in a single client-type and single author situation only. It's when you start mixing either of these or updates on several servers concurrently you may get into trouble.
Although caching occurs in the front ends (notes client session and Http web server task), profile docs can be quite safely manipulated with backend lotusscript.
I have a simple db that I used for learning, demonstration and monitoring how updating and caching works for profile docs. (You can mail me for a copy.)