• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1517
  • Last Modified:

@IsMember @UserRoles - Update Reader Field

Experts,
Users will create a document.  Users are included in "one" of 4 roles in ACL (East, West, North, South).  Documents created by users should always be read (not edited) by user (only Admin can edit docs) and a user belongs to one role, so all members of that role can also "read" (not edit) docs from each region.

What is the best way to populate (say, on compose) the DocAuthor field to make sure it captures the author, the role that author is in, and also the Admin role?  I'm thinking on query close (the button will be a submit button -- the documents will be weekly activity reports) and its important that the author username be captured just in case the user changes regions (roles).  Can anyone provide a simple solution?
0
AliciaVee
Asked:
AliciaVee
  • 3
  • 3
1 Solution
 
Bill-HansonCommented:
I use a Computed, multi-valued Authors field with the following formula:

authors := @Trim(@Unique(
      DefaultAuthors :
      AdditionalAuthors :
      AuthorRoles
));
authors

Where...
(1) DefaultAuthors is another field that contains my "back-door" entries such as "LocalDomainServers"
(2) AdditionalAuthors is another field that is normally empty, but can be used later to extend access to a document.
(3) AuthorRoles is another field that contains the names of the roles that can edit the document.

For the AuthorRoles field (computed when composed), I would put this formula:

regionRoles := "[North]":"[South]":"[East]":"[West]";
userRole := @Trim(@Replace(regionRoles; @Trim(@Replace(regionRoles; @UserRoles; "")); ""));
"[Admin]" : userRole;


Of course, you don't have to split the code into so many fields, but I find that maintaining applications designed like this much easier than putting it all in one field.
0
 
AliciaVeeAuthor Commented:
Bill -- okay, I think I follow you.  So, I will need to use this solution -- but apply it to "readers" fields, and not author fields -- correct?

I would put the following into a DocReaders field, computed when composed, mutl-value -- correct?

regionRoles := "[North]":"[South]":"[East]":"[West]";
userRole := @Trim(@Replace(regionRoles; @Trim(@Replace(regionRoles; @UserRoles; "")); ""));
"[Admin]" : userRole;

I'm not sure I understand the first part of your solution -- authors: and default -- so in this case you would have 3 other fields that have values and these values populate your authors field?  For readers, how can I use this to populate the author of the document.  Can I just add another reader field, say DefaultReader and list the author of the document (if they move from one region to another, I want them to still be able to read their own documents) and can I also add the Admin role as well as the domainservers -- what exactly is the syntax for this?  I'm not that great with roles and author /reader fields and I know it can be tricky if not done right.
0
 
AliciaVeeAuthor Commented:
Oh -- one more question.  What does @Trim do in your authors field (I know @Unique will not allow dupes...but are you trmming out excess space and does that happen with usernames and/or roles?)
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
Bill-HansonCommented:
First, I'll address the question about @Trim.  I'll respond to the other part in my next post.

Yes, @Trim simply removes empty elements from any list.  It can also trim a string by removing leading and trailing whitespace, but we are using it to trim lists of roles.

In this case, we have a list of known roles (regionRoles), and a list of roles that is not known until runtime (@UserRoles).  What we need to know is which one of the regionRoles is present in UserRoles.  To do this we need to perform a logical AND operation on the two lists.  Unfortunately, @Formula language does not include such an operation (the & operator will raise an error if you try to use it with lists).  So we have to do the logic manually.  Since "R AND U" can be converted to "!R OR !U", we can use @Replace to invert the lists and combine them (R=The Known Region Roles and U=The User's Roles).  I'll break down the formula above to make this more clear:


regionRoles := "[North]":"[South]":"[East]":"[West]";

This line is self explanatory; it's just a list of our known region roles.


invertedRegions:= @Trim(@Replace(regionRoles; @UserRoles; ""));

I broke this code out of the longest line in the example above to make the logic clearer.  It removes any of the user's roles from the known regionRoles.  So if the user is a member of these roles ("[Admin]":"[South]":"[EditHelp]"), then invertedRegions will now contain ("[North]":"[East]":"[West]").  The call to @Trim removes the "holes" left by the @Replace operation.


userRole := @Trim(@Replace(regionRoles; invertedRegions ; ""));

Now, all we need to do is to remove this list from the known region roles, and we'll have the role(s) common to both lists.  Again, the call to @Trim removes the "holes" left by the @Replace operation.


"[Admin]" : userRole;

This final line just adds the "[Admin]" role to the list.


One this to note is that if the user is (for some reason) a member of more than one region role, the result will contain all matching role names.
0
 
Bill-HansonCommented:
Now, about the security fields.

I missed a critical piece of information in your original post, but I think I understand now (I didn't realize that you wanted to hide documents from other regions).

The quick and dirty method would be to create a DocAuthors and DocReaders field (computed when composed) with these formulas:

DocAuthors
"[Admin]"

DocReaders
regionRoles := "[North]":"[South]":"[East]":"[West]";
invertedRegions := @Trim(@Replace(regionRoles; @UserRoles; ""));
userRole := @Trim(@Replace(regionRoles; invertedRegions; ""));
"[Admin]" : userRole : @UserName

This will work, but the problem with this approach is that it is hard to change later if your users start asking for more security features.

I've developed a document security framework that is very easy to maintain.  It involves using several dependent fields to compute the access dynamically at runtime.  Here is my full specification.

------ Document Security Framework ----------

CreatedBy: NAMES,CWC (CWC=Computed when composed)
@UserName

ModifiedBy: NAMES,C (C=Computed)
value := @ThisValue;
@If(
      value = ""; @UserName;
      @ClientType = "None"; value;
      @UserName)

DefaultAuthors: NAMES,C,MULTI
"LocalDomainServers" :
"LotusNotesProgrammers" :
"LotusNotesAdministrators" :
"[EditAll]"

DefaultReaders: NAMES,C,MULTI
"[ViewAll]"

AdditionalAuthors: NAMES,MULTI
This is an optional field.  You don't even need to include it on your form if you don't need it.  This is the field that you use to extend access to the document.  Use your imagination with this field.  Some possibilities are: Make it an editable field on the form so that users can extend access to anyone they want by themselves.  Make it a computed field to add additional values without messing with the existing framework code.

AdditionalReaders: NAMES,MULTI
This is an optional field.  Same as AdditionalAuthors, but restricts access rather than extending it.

AuthorRoles: TEXT,CFD,MULTI (CFD=Computed For Display)
This is an optional field.  List any roles that are required to edit the document.

ReaderRoles: TEXT,CFD,MULTI
This is an optional field.  List any roles that are required to read the document.

DocAuthors: AUTHORS,C,MULTI
authors := @Trim(@Unique(
      CreatedBy :
      DefaultAuthors :
      AdditionalAuthors :
      AuthorRoles
));
authors

DocReaders: READERS,C,MULTI
readers := @Trim(@Unique(
      DocAuthors :
      DefaultReaders :
      ReaderRoles :
      AdditionalReaders
));
@If(
      ReaderRoles != ""; readers;
      AdditionalReaders != ""; readers;
      "")

---------------------------------------------

That's it.  10 fields that make my life MUCH easier.  I keep a form in a Design Database that has these fields defined.  Then when I create a new form, I just copy and paste them into the new form and make a few minor modifications to AdditionalAuthors, AdditionalReaders, AuthorRoles, and ReaderRoles.  I very rarely need to modify the other fields.

One thing to notice is that all fields are TEXT (or NAMES, which is treated as TEXT).  I never have to manipulate AUTHORS or READERS items in code!

Another cool thing is that you can write general code that uses the framework.  For example, I use this hide-formula on 99% of my "Edit Form" buttons:

@If(
    @TextToNumber(@Subset(@UserAccess(@DbName); 1)) < 3; @True;
    AuthorRoles = ""; @False;
    AuthorRoles = @UserRoles; @False;
@True)

---------------------------------------------

Using this framework, all that you would need to do is to modify the AuthorRoles and ReaderRoles fields...

AuthorRoles
"[Admin]"

ReaderRoles
regionRoles := "[North]":"[South]":"[East]":"[West]";
invertedRegions := @Trim(@Replace(regionRoles; @UserRoles; ""));
@Trim(@Replace(regionRoles; invertedRegions; ""));
0
 
AliciaVeeAuthor Commented:
Bill -- wow!  This is awesome stuff!  Very very informative and helpful for someone at my skill level.  Thanks so much for sharing your knowledge -- I will put it to good use.

Okay -- so I've got the reader feilds working perfectly!  Thanks for all of your help.  On to part two of this task -- getting the alerts to work based on documents not found, which you've also answered.
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now