Can this user Edit this document?
Monday, November 16, 2009 at 7:37PM Sometimes, we develop applications in Notes. Well, most of my time I do. And sometimes, I find that I just cant figure something out. I'm convinced that there's some magic-bullet function that allows us to quickly do something. For instance, today, I wanted to show something on a web or notes form, but only if the user was allowed to edit this form. Now, remember the first rule of lazy programming: Do as little as possible. So after quite un-lazily searching for stuff, I chatted with Julian and he mentioned that he had to code something like this too. Horrible, I thought. Oh well.
So lets create a field using @formula, which will give us "1" for 'this user is allowed to edit this document', otherwise "0"
rem {if he's lower than author, say no. Higher than author, say 'yes' };
thislevel := @userAccess(DbName);
@if(thisLevel < 3; @return("0"); thisLevel > 3; @return ("1"); "");
rem { Lets get a list of allowed authors for this document } ;
allowedAuthors := @name([Canonicalize]; @Author);
@if (allowedAuthors *= @UserNamesList; @return("1"); "");
Rem {default to 'no' };
"0";
So whats the magic ?
- @userNamesList returns all the users name variants, as well as his hierachical name wildcards, a global wildcard and (on a server) all the groups he belongs to - including nesting, and all the roles for this database. Its like a babel fish. One suspects this is exactly how the database itself works out permissions. Its far too useful to exist otherwise!
- The *= (permuted equality) operator compares every permutation of both lists and if one or more pair is equal, returns true. Pass in two lists and if one item is in both sides - bingo.
- @Author returns a list of all allowed authors for this form. Watch out as its not in canonicalized format.
- @return allows me to bale out as quickly as possible without having to evaluate the rest of the formula.
So there you have it. 15 years into notes and this still made me stop and think. Mostly think:
- Formula language is dammed powerful.
- I wish Formula language had a debugger!
Lotus Notes 
Reader Comments (11)
What about this one?
thislevel := @userAccess(DbName);
allowedAuthors := @name([Canonicalize]; @Author);
@if(thisLevel < 3; "0";
thisLevel > 3; "1";
allowedAuthors *= @UserNamesList; "1";
"0")
@1 Slower, because Bill's code uses @return to avoid the unnecessary calculations of @Name and the permuted list compare. you could rewrite to
@If((thisLevel:=@UserAccess(@DBName)[1]) < 3; @Return("0");
thisLevel > 3; @Return("1");
(@Name([Canonicalize]; @Author)) *= @UserNamesList; "1";
"0")
But I think this is best for maintainability:
REM {if he's lower than author, say no. Higher than author, say 'yes' };
thislevel := @UserAccess(@DbName)[1];
@If(thisLevel < 3; @Return("0"); thisLevel > 3; @Return ("1"); "");
REM { Lets get a list of allowed authors for this document } ;
allowedAuthors := @Name([Canonicalize]; @Author);
@If (allowedAuthors *= @UserNamesList; @Return("1"); "");
REM {default to 'no' };
"0";
Nice implementation there Bill. Very slick use of the permutation operator too! A couple things:
I think that first formula line needs to be:
thislevel := @TextToNumber(@UserAccess(@DbName; [AccessLevel]));
@UserAccess returns a text list by default.
Second, you might want to check on the case where @Author returns "". I think that in that case an Author would *not* be able to edit the doc, but the *= might return true? I could be wrong. Good to test though.
Also cool that @UserNamesList returns Roles. I didn't realize that ('cause a Role can be used for Author access too).
1 & 2 - yes - I wanted maintainable code - stuff that you could look at in a years time and still understand.
http://www.billbuchan.com/web.nsf/emoticons/DLYH-5MZVLU/$File/wink.gif" />
3. Yes - I'd forgotten about the Authors Empty case. And of course, I've just stumbled across it here. Drat!
---* Bill
@2 - I thought that the R6 Engine did lazy evaluation, i.e. in this case the last bit of the formula only gets evaluated if really needed, like an iif. At least that's what I understood from this:
{ http://www.ibm.com/developerworks/lotus/library/ls-meet_dev_Damien_Katz/index.html" rel="nofollow" target ="blank">Link }
But then I made some tests and lo! the bits I thought would not evaluate did in fact evaluate. Upon careful re-reading of the page though, damien says "Any @function that only operates on its direct arguments and does not rely on state information from anywhere else (@UpperCase, for example) can be lazily evaluated".
Pooh. That speeds up things which are fast in any case. Looks like @Return, long spurned, will become a friend.
Well, learn a new one every day.
Hi .. thanks for this great post ...
http://www.billbuchan.com/web.nsf/emoticons/DLYH-5N3GJ8/$File/shocked.gif" />
http://www.billbuchan.com/web.nsf/emoticons/DLYH-5N3GL7/$File/undecided.gif" />
Andrew is completely right ...
Coding myself lotus for now almost 17 years .. and this is definitely a smart way to get accesslevel ...
but one thing for newbies ...
Remember that this just works if you have only ONE Author-Field. If you have more than one the first accessible Author-Field will be catched ...
Well I wound prefer something like @CanEditThisDocument function in @Formula, that would be nice.
My formula language needs a little refresher; but isn't this approach currently setting a blank status for any poor unfortunate who has Author (3) level access?
Should the first line not be...
@if(thisLevel < 3; @return("0"); thisLevel >= 3; @return ("1"); "");
...or am I being as stupid as I look.
TD.
Tom - the lack of return value on that @if means that it doesnt leap out of the formula (using @return) but evaluates the next part of the formula language stuff.
Basically if he's a reader, thats a 'no' and if he's editor or above thats a 'yes'. And if he's author, then we go huntin for names.
---* Bill
Hi Bill, like yourself I am an ex Acegy/SCS developer.
What really gets me is the idiots (and I have just spent several months working with one!) who decide that building their own class is better than one line of formula language. Let's not forget where we all came from!
I like this kind of approach. I've done similar types of things with long, drawn-out hide-when formulas. I've found it so much easier to use @If, @Do, and @Return statements in hide-whens that are more complicated than just XyzField != "Yes"
Thanks for sharing Bill. Always appreciate your input. :)