Lotus Notes FAQ Visit Our Sponsor!

Lotus Notes Frequently Asked Questions
(Notes/Domino 5.x)

A. Administration Tips


1. How do you update a Stored Form in old documents?

2. How do you get rid of inactive sessions on a server?
3. How do you convert Word files to Notes format?
4. Can Notes be attacked by Viruses?
5. Why is email address type-ahead so slow compared to R4?
6. When Are Database Subscriptions Checked?
7. How do you set the outgoing domain name in the SMTP MTA on a per user basis?
8. Why aren't laptop user's database designs being updated?
9. Is Notes Year 2000 Ready?
10. Why isn't my @DbLookup background macro running properly?
11. How do you bypass Enforce ACL DB security?
12. How do you run the Compact process on a schedule?
13. Can you support multiple SMTP domains using the SMTP MTA?
14. How do you warn users that a server will be shut down?
15. How do you revert the .nsf ODS back to R4?
16. How do you fix documents with bad ReaderNames fields?
17. How do you specify a new location for the desktop.dsk file?
18. What can the Lotus SMTP MTA do to prevent SPAM?
19. What Virus scanning packages are available for Notes?
20. How do you remove Stored Forms from documents?
21. Why do users get notified of new mail even though they don't have any?
22. What would change the list of servers I see in the Database Open dialog?
23. What TCP/IP port does Notes use?
24. How do you run Notes in "Kiosk" mode?
25. What can be done to optimize database performance?
26. Why can't users with Depositor access create documents even though I have enabled Public Access on the form?
27. How do you make a list of all groups a person is in?
28. Why do new documents not show up in views after a recent server crash?
29. How do you turn off Notes crash dumps (notes.rip files)?
30. How do you keep Notes from being started for mailto URLs?
31. Can you use the SMTP MTA over a dial-up link?
32. Can I set the default ACL on the N&A Book to Reader?
33. Can you keep multiple versions of Notes on the same system?
34. Can you add a database icon which launches a program?
35. What programs will back up Notes databases without needing a Notes server shutdown?
36. How do you get rid of all the deletion stubs in a database?
37. How do you get Notes to rebuild corrupted views?
38. Who can I call to find my local reseller and licensing information?
39. How do I get rid of the "Can't open view $DirectoryAssistance" error?
40. How do you fix the Mail Archiving Agent so it keeps documents in the right folders?
41. How do you manage ACL information across an organization?
42. Can fields be added to the N&A Book?
43. How do you make the infobox reappear if it doesn't show up?
44. How do you install the Notes client without user intervention?
45. How do you put a Notes Database on a CD?
46. Can you run Notes 4.6 and 5.x clients simultaneously?
47. What is involved in deploying Notes to International users?
48. How do you create a database that is usable as an address book for name lookup?
49. How do you move databases onto a different drive without the user knowing about it?
[<a href="#NT00000FC2">50. How can I use a POP3 client, such as Netscape or Eudora, to
access my mailbox in Notes?
</a>]
51. How do you unhide a database design?
52. How do you prevent groups from being expanded when sending mail?
53. How do I get rid of the encap2.ond attachment on outgoing Internet mail?
54. How do you shut the server down for backups?
55. How do I add additional N&A books to the address lookup dialog?
56. Are there any large-scale deployment guidelines?
57. How do you update the SmartIcons on all your users' workstations via LotusScript?
58. How do you add Quoted Reply support to Notes' mail templates?
59. Why won't the incremental installer work?
60. How can you have multiple users on one workstation?
61. Can you have specific notes.ini files per user?
62. How do you change how long logs are kept in log.nsf?
63. How do you limit the size of users' mail boxes?
64. How do you set up remote users with a local replica of a new database?
65. How do you refresh Private View designs on clients?
66. Why does my mail say it is sent from someone else?
67. How do you get the Notes R4 workspace in R5?
68. Can you run console commands from a server program document?
69. How do you add a disclaimer/signature to all outgoing SMTP mail?
70. Can you run scheduled server commands?
71. How do you convert an R5 database back to R4 format?
72. What are .IIB files?
73. Can Domino prevent MS viruses from getting into user's mailboxes?
74. Are agents failed over when clustering?
75. Can you force a messagebox for broadcast messages?
76. Why is a user not getting the proper roles?
77. Why do I get att1.unk or winmail.dat attachments?
78. Where is the R5.x fixlist?
79. What do I get "Database must be compacted to support the use of @AllChildren or @AllDescendents in a formula" errors?
80. How do you move a Domino server to new hardware?
81. Why do HTML/MIME messages arrive w/ images at the bottom of the message instead of inline?
82. What encryption is used by Notes?
83. How do you always show the preview pane in a user's mail database?

84. Why is outbound SMTP mail stuck in mail.box on my Windows Domino server?
85. How do you get the server to recognize changes to groups immediately?
86. Can you run SpamAssassin on a Notes server?
87. Can users recall messages they've sent?
88. Can you forward a copy of a user's mail and keep it in their mailbox?
89. How Do You Set Up Failover for Outbound SMTP Mail?
90. Why is my Notes client having trouble replicating over satellite links?
91. Can you have an Apache server handle Domino URLs on a different box?
92. How do you enable folder references for a database?
93. What can I check if my server is running too slow?
94. How Do You Rebuild Busytime?
95. How Do You Fix the "Error connecting to server " Error?

B. Books and Magazines


1. Books for Notes/Domino 5.x
2. IBM Redbooks
3. Helpful Non-Notes Books
4. Lotus Notes Magazines

C. Domino


1. Can you force a user to log in before using a database?
2. How do you replace the "No Documents Found" message in an empty view?
3. Is it possible to run a Domino server on a stand-alone PC?
4. How do you add a button that clears the fields in the form?
5. What URL do you use to create a new document in a database?
6. How do you reopen a saved new document in read mode?
7. Can you prevent the browser from prompting for a password when you use multiple web servers for your site?
8. How do you make parts of a document visible only to Web users or Notes users?
9. Can Domino use directory links to reach databases?
10. How do you customize error messages?
11. How do you display the number of search results on a page?
12. How do you reference view names with "/" and "\" in them?
13. Can you do a Query by Form?
14. Can you set Notes fields from JavaScript?
15. How do you use a URL to reference a specific document in a view?
16. How do you pass parameters to OpenAgent?
17. Why do Cascaded NABs no longer work for web authentication?
18. Can you customize the "Deleted" message?
19. Can you have Domino search a specific view instead of a database?
20. How do you delete the current document?
21. How do you validate and jump to fields which have validation errors?
22. How do you prevent people from deleting file attachments?
23. How do you remove the Domino-generated Navigation Bar from Views?
24. How do you decode parameters in URLs?
25. How do you set up Gnu JSP and Tomcat?
26. Can you create field help on a web browser?
27. Can an X.509 certificate from another CA be used with Domino?
28. How do you improve performance to web clients?
29. How do you decrease the download time for the Notes Java applets?
30. How do you remove Domino's default submit button?
31. Can you display a text field as a TEXTAREA?
32. Can you do banner ads with Domino?
33. How do you detach a file submitted by a web browser?
34. How do you add a logoff button for web users?
35. How do you make user registration immediate?
36. How do you refer to CGI variables in a $$Query*Agent?
37. How do you delete a document by the UNID?
38. How do you set up a field to autowrap when text is typed in a browser?
39. How would you secure a Domino server?
40. Can you automatically refresh when a keyword field is changed?
41. How do you prevent people from submitting a form multiple times?
42. How do you add a Javascript button which validates the form and then submits it?
43. How do you set a Cookie?
44. How do you make a field look like a password field?
45. Why is tabbed table data lost when I switch to a different tab?
46. When do you have to pay for Client Access Licenses (CALs)?
47. What @Command do you use to delete the current document?
48. Can you automatically expand sections in documents?
49. Can you change the Twistie graphics in Notes Views?
50. Are @PostedCommands Supported?
51. How do you access fields on a Parent document from an unsaved response doc?
52. How do you get rid of the margins in frames?
53. Why do you need Author access to do a search on a database?
54. Can you make a link that lets the user jump back two web pages?
55. Can Domino forms be mail-enabled?
56. Can you have a single URL that opens the user's mailbox?
57. How do you pass variables into a form via URL's?
58. Why do I get SSL errors when using MSIE?
59. Can you change the look of the search input form?
60. Can views be shown to web clients with the Notes V4 UI?
61. Can you get rid of Domino's tags?
62. How do you make a Domino site searchable by web search engines
63. How do you add context-sensitive help on Domino web pages?
64. What companies will host Domino web applications for a fee?
65. How do you open the About This Database document with a URL?
66. How do you select multiple documents in a view and run an action button on them?
67. How do you dynamically open a single category view?
68. How do you jump back to the current view from the current document?
69. How do you handle field inheritance when composing from the Web?
70. How do you create a document access counter?
71. Does Domino support Server-Side Includes (SSI)?
72. Can you attach files to a document using a web browser?
73. How do you make a button do a document refresh?
74. Can you control the HTTP headers for the output of an agent?
75. How do you re-enable WebSphere integration after a Domino upgrade?
76. Where is the API for the View and RTFapplets?
77. How do you log out a user and jump to a particular URL?
78. How do you hide attachments uploaded via the web upload file field?
79. How do you take full control of HTML generation from Domino?
80. How do you change the twistie graphics?
81. How do you check for the browser type using JavaScript?
82. How do you remove the Domino version number from the generated HTML?
83. How do you get the current user name in LotusScript?
84. Can you have custom login forms per database?
85. How do you create a DHTML Calendar Control?
86. How do you make onSubmit work with keyword refresh?
87. Can you launch the Notes client from a web page?
88. How do you display a list of response docs on the parent document?
89. Why won't views/richtext display using Java applets in MSIE?
90. How do you get rid of the ecblank.gif in generated HTML?
91. How do you switch tabs in tabbed tables?
92. How do you handle "Allow Values Not In List" dialogs on the web?
93. How do you get the relative path to the database for web URLs?
94. Can You Do AJAX Applications with Domino?
95. Can You Run PHP Pages on a Domino Server?
96. How do you make a database open a form by default from the web?

D. Education and Training


1. Computer Based Training
2. Virtual Classes via the Internet
3. Lotus Education Helpline

E. FTP Archives


1. Notes Utilities
2. Notes Related Programs or Information

F. Mail Gateways


1. OfficeVision and SNADS
2. PalmPilot
3. FAX
4. POP3
5. Migration Tools
6. MS Exchange <-> Lotus Notes
7. NetTalk (X.500, LDAP, IMAP, Exchange, cc:Mail)
8. Windows CE/Pocket PC/Windows Mobile
9. Notes <-> Rex Organizer
10. Outlook to Domino

G. Mailing Lists


1. LNotes-L
2. DominoLinux
3. Oklahoma State Notes-L
4. NotesMac
5. Oklahoma State Domino-L
6. NotesMTA-L
7. Portugese Notes Users Mailing List
8. David Stephens' Lotus Product/Event News
9. LNotes-J
10. Yahoo LNotes-L Group
11. Domino on iSeries (AS/400)
12. LNotes-L on Yahoo

H. Notes Platforms


1. How do you start Notes automatically on bootup in Solaris?
2. How do you set up a Digiboard under OS/2?
3. How do you change the new mail sound on a Mac?
4. How do you set up server proxying through MS Proxy Server?
5. Which file system should be used for the Notes server?
6. How do you install R5 on Linux?
7. How do you tune the operating system for running Notes?
8. Which platform is best for a Notes server?
9. How do you get NT to reboot automatically after the Blue Screen Of Death?
10. How do you generate a core dump for IBM to look at?
11. Where can I find info on Mac-specific (not Notes-related) problems?
12. What is a good entry-level AS/400 system?
13. How do you back up the server automatically?
14. How do you get the Notes server to run in the background?
15. Why can't Notes clients see my new Notes server running IPX?
16. How do you set up NetBIOS?
17. How do you change the new mail tune under NT?
18. How do you determine the NT Service Pack Level using LotusScript?
19. How do you set up the modem port for Solaris 2.x?
20. How do you run the Notes server in the background but still be able to control the console?
21. Can you run the Notes Client on Linux?
22. Can you run the R5 client on OS/2?
23. Why do I have a JVM problem running httpsetup?
24. How do you install and secure Domino on Linux?
25. What are common useful commands for the AS/400?
26. How do you get LS:DO working on Linux?
27. How big an AS/400 do I need for Domino?
[<a href="#NT000080B6">28.
Do I have to install the Notes/Domino Server on Windows Server?</a>]

I. Programming Tips


1. Where can I get the Notes 4.6 LotusScript Class Chart?
2. Why don't bitmaps display properly on some systems?
3. How do you write DbLookups so they work on a local database and on the server?
4. How do you display a button next to a field only when editing?
5. How do you restrict a field to contain alphanumeric characters only?
6. How do you validate that a field has alpha characters only?
7. Can you have views based on the current user name?
8. How do you categorize and count the number of documents created in a particular week?
9. How do you reduce database size by deleting unused fields?
10. From a response document, how do you update a field in the parent document?
11. How do you delete a profile document?
12. Can you have an agent send mail as a specific person?
13. How do you get the directory the current database is in?
14. How do you automatically copy an ACL from a template into a new database?
15. How do you display a number field with leading zeroes?
16. How do you add more action buttons than fit on the display?
17. How do you prevent a form from prompting to save it?
18. How do you automatically filter mail in Notes?
19. How do you create a view action button that creates responses to only work on main documents?
20. Can you start a different navigator depending on the user role?
21. How can you tell if a field has been changed in LotusScript?
22. Can you inherit fields from parts of the same form?
23. How do you do a mail merge using info from existing documents?
24. How do you generate sequential document IDs?
25. Can you show FTSearch documents with a specific view?
26. How do you make a view to use for checking for replication conflicts?
27. How do you add an Internet-style signature at the bottom of your mail messages?
28. Can you access the Clipboard in Notes?
29. How do you save a document, then create a new document in its window?
30. How do you force the value of a field to be unique?
31. How do you find the positions of a string in a text list?
32. How do I get the week ending date for a specific day?
33. Why does @Explode only give me the first item?
34. How do you delete Environment variables from the notes.ini file?
35. How do you prevent people from viewing with a different form and saving the result?
36. Can you use @UserName in a view?
37. How do you sum number fields which have not been edited and initialized?
38. Can you work around some of the size limitations in LotusScript programs?
39. How do you synchronize fields between documents created with two different forms?
40. How do you automatically format phone numbers?
41. How do you select only Response documents in a view?
42. How do you validate a credit card number?
43. Can you make a popup calendar in Notes?
44. How do you get the current access level of a user?
45. Can you open a view in full screen mode from a Navigator?
46. How do you generate a subset of list values based on the contents of another list?
47. How do you keep a running history of all entries in an RTF field?
48. Can you use @Functions in LotusScript?
49. Can you use views from the Mail template in your own databases?
50. How do you compare date/time fields using Lotuscript?
51. How do you go to the last document in a category using Lotuscript?
52. Why does my agent fail on the server with a "Error loading USE or USELSX module" error?
53. How do you create mailing labels in Notes?
54. What are the Unix syntaxes for calling DLLs from LotusScript?
55. Can you dynamically select documents per user without using a ReaderNames field?
56. How do you display documents created with a form using a layout region?
57. Why doesn't NotesUIDocument.GetFieldText return the alias for a keyword selection?
58. Why doesn't my "If documents have been created or modified" agent execute immediately?
59. How do you insert a new line in a Rich Text field using a macro?
60. How do you set the server to run agents on with a hidden database design?
61. How do you sort a document collection?
62. What HelpDesk products are available for Notes?
63. How do you force a user to use a button to save a form?
64. Can you start a program in Notes and wait for it to finish?
65. How do you access a specific element of a multi-value field?
66. How do you prevent orphan response documents?
67. How do you find out what Roles a user has in the current database?
68. How do your control an OLE-VBA object in LotusScript?
69. How do you add a response's doclink to a parent document?
70. How do you track who changed a critical field?
71. How do you find the exact version number of Notes?
72. Where can I get more information on Java access to Notes?
73. How do you put a popup calendar in a layout region?
74. How do you operate on the currently selected documents in LotusScript?
75. In what order do @Commands execute?
76. How do you display progress to the user via LotusScript?
77. How do you convert a numerical value to the equivalent written text?
78. How do you open a document in edit mode if it exists?
79. How do I remove erased fields that keep appearing in the Add Fields window?
80. How do you retrieve a column total for a category?
[<a href="#NT00000DCA">81.
How do you sort and categorize a view based on months in chronological order?</a>]
82. Can you dynamically change the color of the form?
83. Can you open a Navigator using Lotuscript?
84. How do you do @Explode in LotusScript?
85. Can you launch a URL without using InterNotes?
86. How do you get the date for Easter of a given year?
87. How do you hide the design of a database, but let the user see field names?
88. Can you access Win32 registry keys in LotusScript?
89. How do you launch an embedded object using LotusScript?
90. How do you debug into the QueryModeChange and QuerySave events in LotusScript?
91. How do I refresh a document's fields after one field is changed?
92. How do you change the title of a dialog box?
93. How do you remove an element from a Textlist field?
94. Is there any way to prevent the user from deleting parent documents with child documents?
95. How do you avoid error messages with DBLookup?

96. How do you find the elements which are unique to a list?
97. Why don't my changes to a Rich Text field show up immediately?
98. How do signatures and sections in a form work?
99. What does the @Db NoCache parameter do?
100. How do you generate unique document numbers?
101. What happened to Tools\RefreshFields that was in Notes 3.x?
102. How do you do a data import/merge using Lotuscript?
103. How do you lock a document that is being edited?
104. How can I see if the directory exists using LotusScript?
105. How can you tell if a view is Private?
106. How do you check if an RTF field is empty?
107. How do you log off a Notes user so you can force user revalidation?
108. Can you get MSIE to understand "notes://" URLs?
109. How do you calculate the week number in a year of a given date?
110. How do you create response documents from a navigator hotspot?
111. How do you provide context-sensitive help for your database?
112. Can you get a filename from the user in Lotuscript?
113. How do you make a Computed For Display Rich Text Field?
114. How do you look up all the people in a Group?
115. How do you make dynamically sized tables?
116. How do you send mail based on whether a field is modified?
117. How do you execute DOS commands from a formula?
118. How do I calculate the number of weekdays between two date fields?
119. Can I look up Multiple Keywords using @Db functions?
120. How do you prevent LotusScript errors when handling deletion stubs in document collections?
121. How do you monitor your own threads in a discussion database?
122. How do you notify users when documents are modified or added to a database?
123. How do you force the user to click an Edit action button to edit a document?
124. How do you copy all parts of a script from one form to another?
125. Can I lookup Rich Text using @Db functions?
126. How do you change fields based on the state of a separate keyword field?
127. How do you keep doc.send() from crashing a background agent?
128. How do you notify the author of a document when a response has been composed?
[<a href="#NT00001052">129. How do I make the "loser" of a replication/save conflict be the
"winner"?
</a>]
130. How can I control whether the @Db Function returns a list?
131. How do you return custom data types in LotusScript?
132. Can you create Shared Actions?
133. Can you use regular expressions for string matching?
134. Can you run an Agent from LotusScript?
135. How do you create Names, Readers, Authors fields in LotusScript?
136. Can you implement exclusive locking across replicated databases?
137. How do you do @MailSend in Lotuscript?
138. Can you dynamically hide/unhide sections of a form?
139. How do you update a field in response documents after it is changed in the parent document?
140. How do you refresh RichText fields in a UI document?
141. How do you create a preview pane in a framed UI?
142. How do you install COM?
143. How do you convert a time field to display as military time?
144. How do you display a custom image in a Notes view?
145. Can you stop the refresh view indicator for displaying if you use @Today in a view?
146. How do you load different LSX/DLLs based on which platform you are on?
147. How do you check if a document is a deletion stub?
148. Why do I get an "Object Variable Is Not Set" or "Variant is Not Set" error?
149. Is there a @Replace equivalent function in LotusScript?
150. How do you prevent jagged right table edges when merging/splitting cells?
151. How do you synchronize custom databases with PDAs?
152. What Are Domino's Field Size Limits?
153. How do you output a PDF from Notes?
154. How do you create a NotesDocumentCollection?
155. What causes "Entry Not Found In Index" Errors?

J. Programming Tips - LotusScript


1. Can LotusScript operate on Private Views?
2. Can you add a database to the replicator page?
3. Can you refresh a document in the Notes client after changing a rich text field?
4. How do you track who deleted a document?
5. How Do You Run Console Commands From Lotuscript?
6. How do you find out if a document has been foldered?
7. How do you open a File Dialog in LotusScript?
8. How do you edit a rich text field in an open document and redisplay it?
9. How do you find out if there is default view?

K. Programming Tips - @Formula


1. How do you scan an image into a rich text field using a button?

L. Web Sites


1. Companies with Notes Applications
2. Sources of Notes API Information
3. Local User Groups
4. Notes Related Web Sites
5. Mirrors Sites for the Notes FAQ
6. Non-Notes Web Sites
7. What Notes Related RSS Feeds Are There?


How do you update a Stored Form in old documents?

First, remove the old form using this technique.
Then, refresh all the affected documents using this technique.

How do you get rid of inactive sessions on a server?

On the server console, execute:

SHOW USERS DROP

This drops all inactive connections on the server. After you do that and do a regular Show Users, you will see no one or only the currently active users who are actually on the system right now. It should wipe out all the old connections, but if you do it and still see a connection with anything but 0 next to it, it's a bad or phantom session and is indicative of other problems.


How do you convert Word files to Notes format?

There is a product called Hyper*Ink (formerly called Lexstyle Publisher) from CoExtant (formerly Pantano Genesis) which can batch convert formatted Word document into hyperlinked Notes documents. Contact them at [email] sales@coextant.com.

Notes:Import Document from Pembroke International also will do this, but cannot automatically generate multiple linked Notes documents for each chapter of a Word document.


Can Notes be attacked by Viruses?

Here is a useful posting by [email] Christopher Feller:

"...Notes is an entirely different paradigm. One of the reasons viruses and attacks on internet-based systems happen so frequently, is that the initiators are anonymous. They don't have to worry about retribution or punishment. However, you don't attack a Notes server anonymously like a Firewall or FTP server. You can't even begin to touch a Notes server unless someone has created an ID for you. And as soon as an ID exists, there's an electronic trail to follow, I don't care what fields you delete.
Someone creates an anonymous mail-bomb? Fine. I'll just trace through all the servers listed in the RouteServers field to find where the initial mail came from. Then I'll check the RouteTimes/PostedDate and compare that to the Notes Log to see when it was routed through that server. Then I'll check to see who was logged in and bust them.
Worried about attacks on your N&A? Don't give anyone greater than reader access, and control the certifier tightly. Don't let just anyone create replicas/copies on the server. Also, place a mail-paste macro that does an @DocumentDelete in the N&A. (This will keep anyone from routing anything nasty to the N&A by using Mail-In database documents or by setting the mail database destination of a user in the main or secondary N&A book to be the N&A itself.) Finally, mark all documents in the
N&A as read and check the database for unread documents on a periodic basis from the local workspace. If anything new or changed is there (even if it doesn't show up in a view) you'll find it by pressing that Tab key. (For those people who are worried about a disgruntled employee making design changes to documents or forms, inherit the N&A design from an "approved" template nightly and run a nightly background macro to check for documents that have mysterious or "non-approved" form names). Oh, and don't forget to setup statistics and event reporting!

My point is simply this: due to the way Notes was designed, access is always "granted", not implicit. So, no matter what, there's always a record of activity somewhere and there always a way to deny that access.

If you have your security set up sensibly, your logging turned up to the max, and train your users to look at buttons before pressing them (highlight the button and choose Edit->Button) then you really don't have much to worry about. If you ever do encounter anything strange, you'll be able to trace it with the information contained in the offending document. Don't let panic mongers scare you away from Notes!"


Why is email address type-ahead so slow compared to R4?

There are two workaround for this so far:
1) install a directory catalog on the system and have it do lookups in this
2) type a comma (",") when you want it to auto-complete the name

It will hopefully be fixed in a future version of R5.

When Are Database Subscriptions Checked?

They are checked according to your mail poll interval.


How do you set the outgoing domain name in the SMTP MTA on a per user basis?

This technique, which can be used to host multiple domains provided you have unique names across all the domains, applies to the Lotus SMTP MTA 1.05 and higher:

You can edit the short name field in the user's Person document to include the domain name. For example, if your domain is "company.com", you can enter the user's name as "user@another.com" and the mail will be sent with that as a reply address.

To be able to receive mail from more than one domain, be sure to add the other domain to the Internet Domain Suffix field in the Global Domain document.


Why aren't laptop user's database designs being updated?

Check that the Server is given at least designer access to the Local database. A frequent error occurs like this. The database ACL is set up this way:

Default {Editor}
LocalDomainServers {Manager}
OtherDomainServers {Editor}
Admin {Manager}

Since the Server is usually in the group LocalDomainServers, it has manager
access when being evaluated against the Server's N&A Book. The problem occurs
when the Laptop user makes a replica of the DB. On the laptop, the user has a
local N&A book which is used to resolve Group Names when evaluating the ACL
when it replicates with the Server. The Server is usually not a member of the
group LocalDomainServers in that private N&A book, therefore no design changes
are passed when the replication occurs.


Is Notes Year 2000 Ready?

From Lotus' Knowledgebase Article#147238:

Lotus Notes is ready for the year 2000. In fact, support for the year 2000 has been part of the Notes architecture from the very beginning of its development. Therefore, all Notes releases, beginning with Release 1.0, fully support all year 2000 date functions, and no human intervention is necessary for Notes to continue functioning correctly when we move into the year 2000.

When discussing the impact of year 2000 dates in Lotus Notes, there are three areas of Notes date functionality to consider. These are:

1. Date entry.
2. Date calculations.
3. Notes server time synchronization.

Below are descriptions of each of these areas and explanations of how Notes handles each:

1. Date entry.

Since Release 1.0 of Notes, it has been possible to enter dates for the year 2000 and beyond simply by typing all four digits of the year. For example, "1 1 2000".

For all Notes releases prior to Release 4.5, if only two digits are typed in for the year, Notes assumes that the user means the date within the base century 1900. For example:

If the date entered is "1 1 20", Notes releases prior to Release 4.5 will internally store the year as "1920".

Beginning in Notes Release 4.5, if only two digits are typed in for the year and the two digits are a value between 50 and 99, then Notes will assume that the year is within the base century 1900. If the two digit year value entered is between 00 and 49, then Notes will assume that the century is base 2000. For example:

If the date entered is "1 1 97", Notes will internally store the year as "1997". If the date entered is "1 1 00", Notes will internally store the year as "2000".

This new feature in Notes Release 4.5 will allow data entry to be more intuitive for users as we move into the next millennium.

The only place where Notes does not make this assumption is with the @Date function. If you enter an @Date formula with a year as two digits, Notes assumes you mean the literal year that is entered. For example, @Date(94;3;16) will evaluate as 03 16 0094, when you probably intended @Date(1994;3;16) which will evaluate as 03 16 94. This is true in all Notes releases, including Notes Release 4.5.

2. Date calculations.

All calculations using pre- and post-year 2000 dates in Notes will execute correctly. Notes' internal TIMEDATE structure stores the dates in such a way that they can be manipulated in formulas in anyway, regardless of the year or any other part of the date.

Notes internally supports up to the year 32767 on 16-bit operating systems (limited by a 15-bit year quantity in our TIME structure), and the year 41247 on 32-bit operating systems (limited by a 24-bit Julian date quantity in our TIMEDATE structure), so it is well prepared not only for the year 2000 but for many millenniums beyond that.

3. Notes server time synchronization.

When a Notes server is started for the first time, it picks up the time from the operating system it is running on and then keeps its own time from then on until the server is brought down again. The Notes server already knows how to manage the year 2000, so it will automatically roll its time from December 31, 1999 at 11:59:59 PM to January 1, 2000 at 12:00:00 AM. The Notes server also knows how to work with leap years and daylight savings time, so both of these will also be handled correctly during the year 2000.


Why isn't my @DbLookup background macro running properly?

For any @DbLookup or @DbColumn that is run from a backgound macro, you need to put the ReplicaID of the database where the macro is being run into the ACL of the database which is being used for the source of the lookup.

A background macro is executed by $Chronos which doesn't have an ID by itself. That's why it uses the replica ID of the database containing the macro for authentication.

How do you bypass Enforce ACL DB security?

You can add

Disable_Local_Access_Control=1

to your notes.ini file and restart the server. The server restart is required for the setting to go into effect.


How do you run the Compact process on a schedule?

Here is an example program document you can use. Make sure the Notes directory is in your system PATH.

Basics
Program name: $COMPACT.EXE
Command line: -S 5
Server to run on: ServerName/Domain
Comments: Compact databases with more than 5 percent whitespace.

Schedule
Enabled/disabled: ENABLED
Run at times: 10:10 PM each day
Repeat interval of: 0 minutes
Days of week: Wed, Sat


Note that if you are running DAOS in Domino 8.x+, you'll have to add the -B flag for the database to shrink free space properly.

Can you support multiple SMTP domains using the SMTP MTA?

Yes, look at the FAQ on changing the outgoing domain name for the SMTP MTA.


How do you warn users that a server will be shut down?

Use the Broadcast message from the server console:

  Broadcast "Server will be shut down in 10 minutes"

This will send the warning message to all users using the server.


How do you revert the .nsf ODS back to R4?

To convert the file format (On Disk Structure) of .nsf databases back to R4's, use "compact -r <filename>". This will convert the database back. The ODS format of template files are the same so this is not needed for templates.


How do you fix documents with bad ReaderNames fields?

If you access the database locally on the server, you can reset all the bad ReaderNames fields by using an agent. ReaderNames fields are only enforced when you access the database from the server. This also applies to AuthorNames.


How do you specify a new location for the desktop.dsk file?

You can add a line to the NOTES.INI file telling Notes where to find the DESKTOP.DSK file. By default it looks in the Notes Data Directory. E.g., the following line :

  DESKTOP=C:\NEWDIR\DESKTOP.DSK

will look for the desktop.dsk file in c:\newdir.


What can the Lotus SMTP MTA do to prevent SPAM?

From a member of Lotus' SMTP team:

The term 'SPAM' covers such a broad spectrum of SMTP mail 'attacks' that there is no easy solution to the prevention of 'SPAM'. The MTA provides rudimentary capabilities to deal with 2 types of 'attack'.

1. Relays
These are messages sent to your SMTP server from an outside source that are not destined for a local recipient, but are for another external SMTP address. This results in your SMTP server 'relaying' the message onto the external destination. Before the explosion of the Internet and the use of DNS to provide 1 hop source to destination routing of messages this was an acceptable practice on the Internet. Infact the original concepts relied on it to provide source to destination routing across many interconnected hosts. However the ability to accept and transfer 'Relay' messages brings 2 problems to the modern Internet SMTP host administrator. Firstly it results in the local organization incurring transfer costs to handle messages that have no relation to that organization. Secondly, as SMTP is in general an unauthenticated protocol, it became a mechanism for the distribution of 'SPAM' mail. A host that was available to relay mail could be used as the 'distribution' point for a broadcast/offensive 'SPAM' message. Often the message would be constructed to make it look like it originated from that host domain, making to original distributor virtually anonymous. As a result, most SMTP servers directly connected to the Internet are configured to provide some measure of protection to deny the ability to relay messages through them. The initial SMTPMTA option to prevent relays was provided as a quick 'band-aid' to a growing problem.

The MTA was designed to accept and support the concept of Relay messages. This was achieved by allowing the Inbound message converter to pass any message that is not destined for a local user to the Outbound Work Queue so that the Outbound Transport could route it out. The first 'band-aid' solution was an ini variable, SMTPMTA_REJECT_RELAYS=1. This ini variable switched off the Inbound Converters ability to perform relays. If the converter found a message that was supposed to be relayed, instead it would flag an error and place the message in state that the DRTask could generate a Non-Delivery report. This solution has a number of limitations:
It cannot distinguish relays from external originators that need to be blocked and messages from local POP/IMAP clients that need to be relayed.
Often the Originator address on a Spam relay message is bogus, so the NDRs often failed an ended up DEAD in either the Inbound or Outbound WQ's, which meant more work for the Administrator to have to monitor and clean up after.
The check only occurred after the entire message has been received, which results in the hosting server incurring the cost of reading in the message.
The check only looks for messages that were destined for the Outbound WQ from the Inbound WQ. There is a mechanism of addressing that can cause an MTA to deliver the message to mail.box, and the Router to deliver it back to smtp.box. This method of addressing circumvents this relay check, and is referred to in the internet as '% hack' addressing.

The MTA team had been aware of the potential of '% hack' addressing prior to the need to reject relays, and the MTA already had an .ini variable to handle messages that had an SMTP address that results in a Notes message that will be delivered back to SMTP. This is another 'band-aid' solution. It works, but there are drawback. The ini variable is SMTP_OCH_REJECT_SMTP_ORIGINATED_MESSAGES=1. When the MTA Inbound Converter creates a message in Mail.Box it writes a field called SMTPOriginator to hold the SMTP specific Originator information for NDR purposes. This .ini variable plays off of this functionality.

It causes the Outbound converter to reject any message in SMTP.BOX that has an SMTPOriginator field (i.e originally came from SMTP). This will block the '% hack' relay case in SMTP.BOX. The downside is due to the way Notes does NDRs. When Notes sends back an NDR for a failed delivery it does not send back a new message. It will send back the original message, with all the original fields, but it adds additional information. This means that Notes NDRs to incoming SMTP messages will have an SMTPOriginator field and will be blocked by this .ini variable.


To overcome most of the limitations of the Reject_Relays ini variable the MTA implemented a new mechanism to handle the denial of relays. This is controlled by the SMTPMTA_ALLOW_KNOWN_DOMAINS=1 .ini variable. This allows the MTA to refuse any message which is deemed to be a Relay during the inbound transport session rather than in the Inbound Converter. For each incoming connection, and each incoming recipient the MTA makes a determination regarding the nature of the Connection and the nature of the Recipient, each can either be 'External' or 'Internal'. An 'Internal' Connection can send messages to both 'Internal' and 'External' Recipients. An 'External' Connection can only send to 'Internal' recipients, 'External' recipients will be rejected. The MTA determines the states as follows. The MTA knows which Internet Domains it is responsible for, from the Internet Domain Suffix list in the Global Domain Document. The Inbound Sessions Handlers have access to this list of domains. When an incoming connection is received the Session Handler can obtain the IP address of the host that is connecting to it from the IP stack. The Session Handler then uses the DNS to resolve the IP address into a host name. Once it has got the host name, it compares it to the list of 'local' domains. If the connecting host is in one of the 'local' domains, then the Connection is deemed 'Internal'. If the host name is not in a 'local' domain, or the DNS was unavailable, or unable to provide a hostname, then the Connection is not to be trusted and is deemed 'External'. As the connecting host goes through the SMTP protocol exchange, the Inbound Session handler checks each recipient address against it's list of 'local' domains. If the recipient is for one of the local domains, then it is considered 'Internal', anything else is 'External'. Once both states are know the Inbound Session handler can decide if it will accept that recipient or reject that recipients. This mechanism solves the previous problems:
It has an ability to distinguish between external relays and internal POP/IMAP clients.
It does not need to accept the message content before decided whether it is a relay or not. This reduces the network traffic and means no NDRs to clean up for the Admin.

There are, however, still some potential limitations:
Because it uses DNS reverse lookups to 'validate' an incoming connection, if local POP/IMAP clients have IP addresses that are not in the local DNS, then they cannot be seen as 'Internal'
If the incoming connection is through some Proxy Servers, this may mask the true IP address of the connecting host from the MTA, so that the MTA always sees the incoming host being the Proxy Server. If the Proxy Servers IP address resolves to a local host name, then all incoming connections are considered 'Internal'
Because the Inbound Sessions handlers do not have access to the Router Routing tables, they cannot make a further determination as to whether a '%hack' type address to a local domain is truly an inbound message or a 'Relay'. As a result this option will not prevent inbound relays using a '%hack' address to a local domain, so the previous .ini variable may need to still be implemented to prevent these relays.

2. Attacks
Whilst a Relay is technically an attack, it is primarily an attack against another system, using the local SMTP host as an unwitting carrier. It is also possible that a connecting system may attempt to deliver a message to a local address that is considered an attack. This maybe in the nature of an offensive message, or perhaps an unwanted message. Oftentimes these messages are repeated over and over continually, resulting in a degradation of the local mail service.
These types of attack are harder to prevent ahead of time, as they often have no distinguishing features that separate them from ordinary acceptable messages. There are 2 possibilities to help combat them though.

The first is to attempt to authenticate the connecting host to determine that it is a 'real' SMTP host and not an Internet client attempting to attack the server. Until true Server-Server authentication using a PKI setup is available there are a couple of mechanisms. The MTA provides one of these, however its usefulness is somewhat in question. When a host connects to the MTA, it provides a HELO/EHLO command with it's own hostname as a parameter. Early on it was thought that this could be used to 'authenticate' the connection, as most 'SPAM' attacks used a bogus name at this point. A number of vendors including the MTA implemented a check of this parameter by taking the incoming IP address, looking it up in DNS to get a host name and comparing the result to the HELO parameter. If they did not match then the connecting host is not to be trusted. Over time it has become obvious that this check is only of little value as there is no requirement that Internet SMTP hosts have the necessary reverse lookup entry in DNS. As a result this type of check can reject connections from otherwise perfectly valid SMTP hosts. Each Admin needs to bear this in mind when considering to implement this option. It is enables via the SMTPMTA_HELO_DOMAIN_VERIFY=1 ini variable.

The second option is the provide a mechanism for admins to 'blacklist' connecting hosts/domains that have previously caused problems so that they cannot cause problems in the future. The MTA provides this via the SMTPMTA_Denied_Domains ini variable. This variable takes the path to a text file as a parameter. That text file should contain a list of hosts that the Admin wishes to refuse connections from, one per line. When this is enabled, the first Inbound Session Handler will take this list and perform DNS lookups to turn it into a list of IP addresses, which is then shared with all the other handlers. Each incoming connection is then compared to the 'blacklist' of IP addresses and the connection refused if there is a match.


As can be seen the MTA provides some basic measures for 'Anti-Spam' however it is by no means complete and Admins should look to R5 for a more complete implementation and ongoing mechanisms. Please check the Release Notes for exact details of all the .ini variables, including correct spelling and in what version they were first supported in.


You can also set up ASSP as an anti-spam proxy by following Andrew Pollack's Blog Entry.


What Virus scanning packages are available for Notes?

* McAfee's GroupShield and GroupScan (includes AIX)
* Trend Micro's ScanMail for Lotus Notes (includes AIX support)
* Group-WG's WatchDog
* Cheyenne's InnocuLAN for Notes
* Symantec's Norton Antivirus for Notes (includes Unix, AS/400 and S/390; Linux soon)
* Command AntiVirus for Lotus Notes
* Panda AntiVirus for Lotus Notes
* Sophos Mail Monitor
* Die Avast! Domino Edition
* Kaspersky AntiVirus (includes Linux)
* ESET's NOD32 for Lotus Domino


How do you remove Stored Forms from documents?

Create an agent with the following code:

SELECT $TITLE="Form Name";
FIELD $TITLE:=@DeleteField;
FIELD $INFO:=@DeleteField;
FIELD $WINDOWTITLE:=@DeleteField;
FIELD $BODY:=@DeleteField;
FIELD $ACTIONS:=@DeleteField;
FIELD FORM:="Form Name";

Notes V4 has a new $ACTIONS field that must be also deleted.


Why do users get notified of new mail even though they don't have any?
The database compaction process on the server will cause the new mail flag to be set incorrectly. Turn this off and the false new mail signals should stop.

If the new mail notification indicator stays on, the NewMailSeqNum=X variable in the Notes.ini file also may have gotten out of sync with the server. Shut down Notes, erase the "X" part of the variable and then restart Notes.


What would change the list of servers I see in the Database Open dialog?

When you do a File/Database/Open, Notes will show you the servers for any database replica icons on your workspace. If you have renamed a server, it will continue to show up as long as you have an old replica icon from the old server on your workspace.
If you select "Other...", it will show you all servers known by your home server.


What TCP/IP port does Notes use?

Port 1352


How do you run Notes in "Kiosk" mode?

Adding "/kiosk" to the Notes command line will bring Notes up without menus. Unfortunately, this clips the top of action buttons currently and you can still use the mouse to grab the Notes window, so it is not as useful as it could be.
This appears to be fixed in Notes 4.61 and above.


What can be done to optimize database performance?

1. Don't have too many views - each time you change/add a document Notes will need to update every appropriate view.

2. Keep views simple - more columns means more calculation. It gets worse if the columns are sorted and worse still if the columns are categorized.

3. Don't use @Today or @Now in selection formulas - the views will never be up to date and the server will be forever recalculating them. If you need to use today's date in a selection formula then have a background macro running each day to set an environment variable in the server's notes.ini and reference this.

4. If you want to display compound information in a view column from multiple fields then calculate it in a hidden document field. The column should then reference this single field rather than carrying out the calculation.

5. To avoid the @DBColumns/@DBLookups used to generate keyword lists, etc.,
being generated at read time use something like:

@If(@IsDocBeingLoaded & !@IsNewDoc; @Unavailable; @DbColumn(""; ""; "By _Category (Main View)"))

for the formula. Editing documents will take just as long but document readers will notice a big improvement. The example above is from a keyword format formula.

6. Use column numbers not field names for lookups

7. If you are doing lots of lookups to multiple columns in a single view then append all of the data in a single column with a unique delimiter string and do a single lookup. The value returned can then be parsed with @Left/@Right/@Mid or @Explode to give you the separate field values.

8. Put 64 Mb of RAM in the server and push the buffer pool sizes to their limits. This is documented in the Knowledge Base.

An IBM Redbook is also available: Performance Considerations for Domino Applications.

A developerWorks article is also available: Troubleshooting Application Performance

Why can't users with Depositor access create documents even though I have enabled Public Access on the form?

This can happen if you have any @DbLookups in fields on the form. Even if you enable users to have Public Access to the form and to the views used by @DbLookup, you will have to give Reader access to the Replica ID in the database to the ACL. For example, you would add an ACL entry of "85255CEB:0032AC04" with Reader access.


How do you make a list of all groups a person is in?

Create view in the NAB with a selection formula of "Select (Form = "Group").
Add the field 'Members' in first column.
In the propeties box for the first column, select sorting type 'categorized' and select 'show multiple values as separate entries'.
In the second column, put the field 'ListName'.

This is not recursive, so it won't show a person in a nested group.


An alternative way (from chowell@epd.renold.com) is to:
1) Load the Name & Address Book D/Base and select the "Groups" view
2) Enable the Search Bar (View, Search Bar) - the NAB has to be full text indexed
3) Key in the name of the person you are seeking - the Groups of which they are a member are identified in the view
or use the search string:

  FIELD Members contains "username or groupname" 

so you avoid false hits on the ListOwner and LocalAdmin fields.

And another way using the Notes API from agorlenko@manu.com but this only works on R5+:

Declare Function NSFBuildNamesList Lib "NNOTES" Alias "NSFBuildNamesList" _
( Byval S As String, Byval F As Long, hNL As Long) As Integer

Declare Function OSLockObject Lib "NNOTES" Alias "OSLockObject" _
( Byval H As Long) As Long

Declare Sub OSUnlockObject Lib "NNOTES" Alias "OSUnlockObject" _
( Byval H As Long)

Declare Function OSMemFree Lib "NNOTES" Alias "OSMemFree" _
( Byval Handle As Long) As Integer

Declare Function ReadInteger Lib "MSVCRT" Alias "memcpy" _
( N As Integer, Byval P As Long, Byval B As Long) As Long

Declare Function ReadString Lib "MSVCRT" Alias "memcpy" _
( Byval S As String, Byval P As Long, Byval B As Long) As Long

Sub Initialize
        Dim session As New NotesSession
 
        Dim x As String
        Dim m As String
        Dim p As Integer
        Dim I As Integer
        Dim n As Integer
        Dim hNL As Long
        Dim GroupCount List As Integer
        Dim a As String
        Dim abook As NotesDatabase
        Dim aview As NotesView
        Dim doc As NotesDocument
        On Error Goto oops
        Set abook = session.GetDatabase("", "names.nsf")
        Set aview = abook.GetView("People")
        Set doc = aview.GetFirstDocument
        Do Until doc Is Nothing
 
 
                a = doc.FullName(0) 
 
         ' Get Names List handle (fails on R4)
 
                'On Error Resume Next
                NSFBuildNamesList a$, 0, hNL
                'On Error Goto 0
                If hNL = 0 Then
                        Print "Failed"
                        Exit Sub
                End If
 
  ' Get memory pointer
                Dim pNL As Long
                pNL = OSLockObject(hNL)
 
  ' Get number of entries, skip to first entry
                ReadInteger n%, pNL, 2
                pNL = pNL + 14
 
  ' Read the entries
                For i% = 1 To n%
                        x$ = String$(256, " ")
                        ReadString x$, pNL, 256
                        p% = Instr(x$, Chr$(0))
                        pNL = pNL + p%
                        If Not p% = 0 Then x$ = Left$(x$, p% - 1)

  ' each group is listed in x$ in this loop

                Next
 
  ' Discard the Names List
                OSUnlockObject hNL
                OSMemFree hNL
                'Exit Do
                Set doc=aview.GetNextDocument(doc)
        Loop

Exit Sub


Why do new documents not show up in views after a recent server crash?

Running UPDALL -R should fix these views. However, this doesn't work all the time.

When a server crashes, views that were marked for re-indexing and were queued for UPDATE tend to not be re-indexed. That can be a lot of views. Apparently they think they're indexed and don't mark themselves to be indexed again.

Tell anyone who thinks something hasn't been updated to do a SHIFT-F9 while in the view that seems affected (not from their workspace).


How do you turn off Notes crash dumps (notes.rip files)?

Disable Quincy (the crash dump program) by renaming the executable, QNC.EXE in the Notes directory. You can also uninstall Quincy using the command line "qnc -u".


How do you keep Notes from being started for mailto URLs?

This applies only to Windows95 and Windows NT (from a MS tech support article).

Set Microsoft Internet Explorer Options
1) In Microsoft Internet Explorer, on the View menu, click Internet Options.
2) On the Programs tab in the Messaging section, click to select Microsoft Outlook as the default mail program, and then click OK.

Set the Mailto Protocol
1) Double-click the My Computer icon.
2) On the View menu, click Options.
3) On the File Types tab click to select, URL:MailTo Protocol from the Registered File Types, and then click Edit.
4) In the Edit File Type dialog, click Edit.
5) Under Actions, click to select Open, and then click Edit.
6) Verify that the "Application used to perform action" reads "<drive>:\<path>\Outlook.exe" -c IPM.Note /m "%1". For example:

  "C:\Program Files\Outlook\Office\OUTLOOK.EXE" -c IPM.Note /m "%1"

NOTE: This is only an example of an Outlook.exe application path. Your may vary. Check to make sure it is correct on each PC.).
7) Click OK, Close, and Close.


Set the Notes.ini File Entry
1) Click the Start, point to Find, and then click Files or Folders.
2) In the Named box, type Notes.ini, and then click Find Now.
3) Double-click to open the Notes.ini file in the Search Results window.
4) Type the following line after the [Notes] header:

  NotDefaultMailTo=1

5) Close and save the Notes.ini file.


Can you use the SMTP MTA over a dial-up link?

If you have a dedicated IP address and don't have to worry about leaving your phone line connected all the time, this is no problem. You just have to set up NT or Win95 to automatically redial if the line gets disconnected. Your system is essentially on the Internet.

However, if you have a dialup PPP account with dynamic IP (what most ISP's provide), it is more complicated. You have do several things:
1) Set up Win95 or NT to automatically dial when your Notes server needs to connect to the Internet. Do this by testing it with MSIE or Netscape.
2) Configure the SMTP MTA. Test sending mail and see if Win95 or NT automatically dials up and sends mail to an Internet address.
3) Ask you ISP to save all your mail in a multidrop POP3 account at their site. Your ISP should virtual host your domain (company.com) and hold all mail going to it. Get a copy of POP3Fido and configure it to retrieve mail (this should cause Win95 or NT to autodial when needed).

If you use OS/2 or are having problems with step (1), it is easier to use a Windows-based proxy such as WinProxy. These proxies can be set up to automatically dial the Internet. An additional benefit of this method is that they will also give your company dial-on-demand access to the web or to Usenet newsgroups.

Notes R4.6 and R5 include dialup SMTP support via the ETRN command. However, it is not documented clearly that your ISP has to give you a static IP address and your ISP also has to support ETRN on their mail server.

The reason this is so complicated is that SMTP is a "push" protocol (in Notes parlance). One of the destination SMTP servers is always expected to be available. Usually, your ISP's will set up their SMTP server as a backup in case your system is down. If you bring up your connection "for long enough", the ISP's SMTP server will send all the mail it has collected for you, but this still requires a dedicated IP address. This technique is not as reliable as a "push" (outgoing mail via SMTP MTA), "pull" (via a POP3 account) technique.


Can I set the default ACL on the N&A Book to Reader?
Notes has been designed to have the default ACL set to Author with no create or delete privileges. Without this, people cannot edit the items in their person document like phone number, location, signature, picture, etc. They also cannot delete the ID file from the N&A book after installation. And worst of all, they cannot edit any groups that they have been given ownership of. You should make the default ACL set to Author (with both delete and create turned off).

Can you keep multiple versions of Notes on the same system?

Yes. On an install of a new version, you should:
1) Put the \Notes directory in your PATH.
2) For major revisions (3.x -> 4.x -> 5.x, etc.), keep a separate copy of the \Notes\Data directory because the Notes internal database format changes between each version; new major versions will upgrade old database versions to the latest which can then no longer be read by the old versions. You can point to the appropriate data directory by editing your notes.ini file; you will only need one per major revision (NoteData.3, NoteData.4, etc.)
3) After the install, move the notes.ini file into the \Notes directory.
4) Before installing a new version, rename the directory of the old version to something else (e.g., \Notes to \Notes.463).
5) Install into the same non-renamed directory (e.g., \Notes), but before doing this, copy your desktop.dsk file into this directory so it can be upgraded if needed.

To use a specific version:
1) Make sure you are not running anything from \Notes (e.g., Notes Single Logon which is nsl.exe, the mail check in R5, the Notes client, etc.)
2) Rename the current \Notes directory to whatever version (e.g., \Notes to \Notes.463) it was.
3) Rename the directory with the version you want to \Notes (e.g., \Notes.50 to \Notes).
4) Start Notes.

You have to keep the same directory structure because in later versions of Notes, there is information in the registry related to OLE automation. If you use separate directories for each version, OLE automation may not function correctly.

With the caveat about OLE automation, you can run multiple versions simultaneously if you:
1) do not put the \Notes directory in your PATH
2) run the nlnotes.exe (or the appropriate executable for your platform be replacing the first character in the filename) instead of running notes.exe
From Olivér Zsigmond (Oliver_Zsigmond@lotus.com) comes this tip on how to run the R5 server locally with multiple clients:

Rename dirs of your existing versions of Notes R3, R4.x as described above.
Move your NOTES.INI into the appropriate NOTES.EXE dir.
Modify the Directory line in NOTES.INI to show the appropriate DATA dir.

Install Domino R5 to \lotus\domino
Install Notes R5 All Clients to \lotus\notes

Both directories will have their own NOTES.INI file and DATA dir.

Start Domino server and complete the setup.
Start any of the Notes clients (R3,R4 or R5) from its directory and you can use the local R5 server. You can stop the client and start any other one without stopping the server. The new rules are that you can start only one client and you can't start the client from \lotus\domino because if you stop this client, it will stop the Domino server as well.
Because of Windows Logo requirements, the \data directory structure in R6 is more confusing. There are some files in \lotus\notes\data underneath where you installed the main \lotus\notes executables, but there are also NSF files and the notes.ini file in "\winnt\profiles\<username>\local settings\application data\lotus\notes\data". You can change to a similiar structure as R3/R4/R5 by moving the files and then changing the Directory attribute in the notes.ini to point to the new directory.

In the registry, you should update the DATADIR value in the "HKEY_USERS\<userid>\Software\Lotus\Notes\Installer" key to point to the data directory you used so that the installer knows how to uninstall and update your current files.
In addition, the following two keys should point to the data directory:
"HKEY_LOCAL_MACHINE\SOFTWARE\Lotus\Notes\6.0\DataPath"
"HKEY_LOCAL_MACHINE\SOFTWARE\Lotus\Notes\DataPath"

Note that this will probably break R6's roaming user support. Installing multiple versions of Notes is something usually done by developers, so this should be a reasonable caveat.
Mike Kemp (MikeJKemp@aol.com) uses this technique for running multiple simultaneous versions on NT/Win2K:

So I could respond quickly to user's 'phone queries I had my desktop 'admin' workstation set up so I could run several Notes clients concurrently.� The OS had to be NT (or W2K) - definitely not W9x.� The set up is along the same lines as in your article, with the difference that the start-up shortcut preferences point to the appropriate nlnotes.exe (rather than the more usual notes.exe) similar to this:

for R4.5 client :-� C:\NotesR45\nlnotes.exe =D:\NotesR45\Data\notes.ini

for R4.6 client :-� C:\NotesR46\nlnotes.exe =D:\NotesR46\Data\notes.ini

for R5.x client (my main admin tool):- C:\R5Client\Notes.exe =D:\R5Client\Data\notes.ini.

and even (as an experiment):- C:\NotesR33\_lnotes.exe =D:\Notes33\Data\notes.ini.

Note that the R5 shortcut pointed to the Notes.exe and MUST be started first.

With this structure in place I could <Alt>+<Tab> between the various clients at will and so be able to 'talk my callers' through a problem quite rapidly.� I could also check the operation of a database development intended for the environment.

The only drawback was the occasional entire machine 'lock-up' if I tried to use the Admin console in more than one client at a time - but this did not affect the servers.

I have not had the opportunity yet to try out this procedure involving ND6.


Can you add a database icon which launches a program?

There are several things you can do to run programs from the Notes desktop:

1) Use a smarticon or a button to launch a program:

  @Command([Execute]; "D:\\dir\\subdir\\executable.EXE"; "parameters")

2) If you are running a program, add this to the PostOpen event of the database script:

  Sub Postopen(Source As Notesuidatabase)
    Dim taskId As Integer
    taskId% = Shell("CALC.EXE", 1)
  End Sub

3) If your application is an OLE program, create a single document (with an embedded OLE object) in the database and set the form to automatically launch the first object in the form. Add this to the PostOpen event of the database script:
  Sub Postopen(Source As Notesuidatabase)
    Dim workspace As New NotesUIWorkspace
    Dim uidoc As NotesUIDocument
    Set uidoc = workspace.EditDocument(0)
  End Sub

What programs will back up Notes databases without needing a Notes server shutdown?

* Cheyenne ArcServe has a Notes agent which lets it back up open databases. The agent requires a "work area" of the size of your largest Notes database because it creates a replica of a database into a separate directory before backing up that database.
* Tivoli TSM (formerly IBM's ADSM) will back up open files and can back up databases incrementally.
* Veritas's Backup Exec with the Open File Option (they also have a Domino agent).
* Quadratec's Time Navigator can do full and incremental backups
* Unix's tar (and ports of it to other platforms like NT and OS/2) will back up open files.


How do you get rid of all the deletion stubs in a database?

All document deletion stubs in a database can be purged by selecting File/Database/Information/Replication and setting the cutoff date ahead two days. Once this is done, the deletion stubs are purged immediately.

NOTE: Depending upon the Notes platform, it may also be necessary to set the purge interval to one day as well as setting the cutoff date ahead. This is not required for all Notes platforms.


How do you get Notes to rebuild corrupted views?

From the server window, run:

load updall dbname -C
load updall dbname -R

Dbname is optional. If omitted it will operate on all db's.
-C builds indexes for all views that have not already been built.
-R rebuilds indexes for all views that have already been built.


Who can I call to find my local reseller and licensing information?

You can call Lotus at 1-800-782-7876 in the United States.


How do I get rid of the "Can't open view $DirectoryAssistance" error?

This was a feature added in Notes 4.5 to enable servers to provide a single person/group directory for multiple Notes domains. However, it is a common mistake to put the standard names.nsf file in the Master Address Book field of the server document. The result is the error message above.
To correct this problem, either remove names.nsf from this field, or create a new database named mab.nsf from the mab.ntf template and put mab.nsf in this field. If you really do have NABs for multiple domains on your server, you can set up Directory Assistance as specified in the on-line help.


How do you fix the Mail Archiving Agent so it keeps documents in the right folders?

There is a sample modified database on The View's web site. What it does is create a categories field with a list of the folders the document belongs in so it can carry the folder location information with the document when it is moved into the archive.

Pawel Bartuzi (pawel.bartuzi@kir.com.pl) suggested fixing Lotus' archiving agents instead.
Modifications made to mail archiving agent ("Periodic Archive", Notes 4.6.5):

In the Declarations section added:

  'PB start
  Dim strlstFolders_g List As String
  'PB end

In the ProcessDocuments procedure added an the very beginning::

  'PB start
  Dim doc_l As NotesDocument
  Dim strUNID_l As String
  Dim lngCount_l As Long

  Forall a In dbSource.Views
    If a.IsFolder Then
      If (Not a.Name Like "(*)") Or (a.Name = "($Inbox)") Then
        Set doc_l = a.GetFirstDocument
        Do While Not doc_l Is Nothing
          strUNID_l = doc_l.UniversalID
          If Iselement(strlstFolders_g(strUNID_l)) Then
            strlstFolders_g(strUNID_l) = strlstFolders_g(strUNID_l) & ";" & a.Name
          Else
            strlstFolders_g(strUNID_l) = a.Name
            lngCount_l = lngCount_l + 1
            If Not session.IsOnServer Then  Print "Reading documents... " & a.Name & " - " & lngCount_l
          End If
          Set doc_l = a.GetNextDocument(doc_l)
        Loop
      End If
    End If
  End Forall
 'PB end

and, an the beginning of the for...loop:

 'PB start
 If Not session.IsOnServer Then Print "Checking documents... " & i & " of " & numDocs
 'PB end

In the ArchiveDocument procedure, add at the very end:

 'PB start
 Dim strFolders_l As String
 Dim strFolder_l As String
 Dim intPos_l As Integer

 If Iselement(strlstFolders_g(docSource.UniversalID)) Then
   strFolders_l = strlstFolders_g(docSource.UniversalID)
   intPos_l = 0
   Do While Not Len(strFolders_l) = 0
     intPos_l = Instr(1, strFolders_l, ";")
     If intPos_l = 0 Then
       strFolder_l = strFolders_l
       strFolders_l = ""
     Else
       strFolder_l = Left$(strFolders_l, intPos_l - 1)
       strFolders_l = Right(strFolders_l, Len(strFolders_l) - (intPos_l))
     End If
     Call docArchive.PutInFolder(strFolder_l)
   Loop
 End If
 'PB end

Remarks:

- Further optimizations can be done to minimize the size of the strlstFolders_g list by creating something like a "folder dictionary": then only folder IDs would be put in the list, it could be beneficial on very large mailboxes with very long folder names.
- Modifications to the "Archive selected documents" agent are basically the same, with the exception that some global variable names differ (docSource->note, docArchive->newnote, session->s, dbSource->sourcedb), also you have to be more careful where you place modifications as there are fewer procedures in "Archive selected..." than in "Periodic Archive" and they are longer.


How do you manage ACL information across an organization?

Notes R4 allows you to set a database's properties so that ACL's are consistent across all replicas.
Notes R5 has built in tools for updating the ACL's across multiple databases.

Percussion Software has a product called Server Admin Plus that will also let you manage and audit ACL information.

Candle also has a system administration tool.

IVES Technologies has a product called ACL Reporter Updater which is a platform independent administration tool that provides security management for enterprise-wide Lotus Notes/Domino networks.


Can fields be added to the N&A Book?

There's no problem with adding fields and views, but don't change anything that is pre-existing. Do all your work in a template, and be very careful with the management of that template. There are two dangers:
(a) losing your modified template due to inadvertant replication from a newly installed server that has a standard NAMES.NTF that is newer than your custome version, and
(b) having two different templates for the NAB on different servers such that $DESIGN on each server re-inherits conflicting designs every night causing a "Design Storm" that can bring your whole network down.

Do not name it NAMES.NTF, or it may be overwritten by a software upgrade. Make sure that the template name is not the default (StdNotesAddressBoook). You can either make it a replica of the original NAMES.NTF, or a copy.

If it is a replica, be sure to remove the original, be sure to let it replicate to all servers, and be especially sure that any time a new Notes version is loaded that you merge any changes in the new version's NAMES.NTF into your template and then delete that NAMES.NTF from your server before you allow the upgraded server to replicate with any other server.

If it is a copy instead of a replica, it is best that you uncheck the setting that allows replication of the template name for your NAB and make sure that only one server in your organization is set up to inherit the NAB design from your modified template.
Just for insurance it might be a good idea to use the ACL or selective replication to insure that your main hub server never accepts NAB design changes from any other server.

For Notes R4 and R5, the preferred way of adding fields to the NAB is to use subforms.


How do you make the infobox reappear if it doesn't show up?

From Lawrence Wagner (lwagn2@dwp.ci.la.ca.us):

METHOD #1:

Using the keyboard:

1. Select File, Database Properties.
2. Hold down the ALT key and press the spacebar.
3. If a menu appears, select Move. If not, type the letter "m".
4. Use the arrow keys (i.e., the up arrow) on the keyboard to move the InfoBox back onto the screen. You should eventually see a dotted line in the form of a rectangle. Once you do, press ENTER. The InfoBox will reappear on the screen.

METHOD #2:

The NOTES.INI variable that is responsible for the position of the InfoBox on the screen is:

Win32InfoboxPos=

If you cannot find the InfoBox, modifying the values in the NOTES.INI to will also make it reappear. The values in this parameter refer to x, y coordinate locations. By changing these to numbers that fit within the pixel resolution (for example, 800x600), the InfoBox will reappear. Examples:

Win32InfoboxPos=441 79

or
Win32InfoboxPos=333 261


How do you install the Notes client without user intervention?

Thanks to mvanlone@bigfoot.com for pointing me at this IBM tech support article:

To automate an install, you create an install response file that includes all the options you want users to have. �After you create the response file, you package it with the install files and copy the files to a network for users to download.

These steps describe how to set up a workstation installation so that users only need to click a shortcut to launch SETUP.EXE, with defined parameters in the command line, to install Notes with all the customizations you choose.

1. From the Install directory, type SETUP -R. �This action creates the response file, SETUP.ISS, which is stored in your system directory. �(Depending on the operating system, this file will be placed in the�WINDOWS or WINNT directory.)
2. Go through the user setup dialogs, actually completing a normal�installation. �The response file records all the settings you specify.
3. Copy the entire Install directory to a network drive.
4. Copy the response file (SETUP.ISS) into the Install directory on the�network. �The SETUP.ISS file must be in the same directory as the rest�of the installation files.
5. Right-click the SETUP.EXE file in the installation directory, and�from the pop-up menu, select Create Shortcut. �This will create a shortcut�to SETUP.EXE in the installation directory.
6. Right-click the new shortcut, and from the pop-up menu, select�Properties. �In the Target field, enter the following at the end of the�path and file name, making sure to place a space between the SETUP.EXE�at the end of the path before the below text is added:

  -s -f1 <location of SETUP.ISS file>

where <location of SETUP.ISS file> is replaced with the full path to the file, including the filename.
This action creates a shortcut to run the Notes install program with the automated file. �The shortcut can be renamed as desired, but keep in mind that the original SETUP.EXE will still be visible in the installation directory as well. �You will want to somehow direct users as to which item in the directory they need to double-click on to run the Notes installation with the automated feature. �Although all the installation files must be in one directory, the shortcut can be placed in a different directory if desired.

Alternately, the full command can be entered into the Windows Start -->Run command window as follows:

  <Path>\SETUP.EXE -s f1 <Path>\SETUP.ISS

Note: �There is also a -f2 switch which is used to specify the location of�the log file. �The log file is created during the installation�process and if no location is specified, the log file will be put in the same directory where the .iss file was found.

Please note that the syntax is very important. �Extraneous spaces and�characters will either cause the switch to not work or to be�misinterpreted. �One customer reported that spaces between the switch�"-f1" and the start of the path caused the switch to not work�properly.

Example of Correct Syntax: � setup.exe -s -f1c:\myresponse.iss�-f2c:\logdir\mylog.log

Supporting Information:

When the Notes Installation is run silently using the steps detailed in this document, you will see no indication that the install is running. �No program windows are opened, and no dialog boxes or progress bars are displayed. �When the installation is completed, the Lotus Product Registration windows will appear on the screen, to be filled out or closed by the user.

Note: �For an in-depth look at the methods and options of the�InstallShield Silent Install, refer to the following URL, at the InstallShield (http://www.installshield.com) web site:

http://support.installshield.com/kb/default.asp?action=Display&documents_id=101901&productname=&category=&code=&documentnumber=Q101901&selectproductname=InstallShield__5&selectcategory=&selectkeyword=&documentnumber2=&old_document_number=&rn_descriptor=&contents=Creating%20a%20Silent%20Installation%20&org=search&onlyOne=yes&date_published=1/1/90


How do you put a Notes Database on a CD?

From Glenn.Thibert@thehartford.com:

The following steps are necessary to put a database onto a CD or other read-only media:

1. Select the database and choose Design - Views and make sure that all views are unhidden (i.e. do not have parentheses around their names). It is important to temporarily unhide the hidden views so that the view indexes for these views can be created (see Step 2 below).

2. Open the database and press CTRL+SHIFT+F9. This key combination will rebuild all of the views in the database. This includes both open and hidden views, as well as server-based or local databases. It is important to build the view indexes before copying the database to the CD or other read-only media as, if they are not created and stored in the NSF file prior to adding it
to the read-only media, Notes will attempt to create them and will not be able to because it cannot write to the media.

Note: If a view is not built, pressing CTRL+SHIFT+F9 will cause Notes to build the view. If the view is already built, pressing CTRL+SHIFT+F9 will cause Notes to update the view, not rebuild it.

3. Create the full-text index for the database if you intend for the database to be queried using Notes' full-text indexing capabilities. You can do this using the File - Full Text Search - Create Index command. This index must be created prior to putting the database on the read-only media for the same reason described for view indexes in Step 2 above.

Note: Most CD mounting software conforms to the ISO 9660 standard which does not allow for periods in directory names. When creating full-text indexes in Notes, it by default creates a directory with the extension .FT (period - FT) which is against the ISO 9660 regulations. For example, if your database is called DATABASE.NSF, then Notes will create a subdirectory called \DATABASE.FT underneath the directory which contains the file DATABASE.NSF.

To workaround this issue, do the following

a. Create another directory which has the same name as the database, but with no extension (i.e. \DATABASE instead of DATABASE.FT). This new directory name must be the same as the database name.

b. Copy all of the files created for the full-text index from the original directory into the new directory.

c. Delete the full-text index files from the old directory name (the name with the .FT extension) and remove the directory from the system.

Notes will now see the new directory and use the full-text index files inside of it. It does not require the .FT extension to be on the directory name. The .FT is only used as a naming convention when creating the directory for full-text indexes so that those directory names wouldn't show up along with the other directory names in the File - Open Database dialog box.

4. Do an operating system level copy (such as using the DOS or OS/2 COPY command) of the .NSF file from the writeable media which you are currently using onto the media which will be used to press the CD or other read-only media. Be sure that you do an operating system level copy during this step and not a File - Database - Copy from within Notes as using Notes to copy the
database will remove the view indexes.


Also, be aware that Notes databases on a CD can only be viewed by the same major version of Notes that the Notes database was indexed with. The view index and full text index are improved with each major version, i.e., R5 can't read R4 DB on CD, etc. To work around this with old CDs you may still want to view, you can copy the .nsf file to your local system, follow the procedure above, then copy all the files onto a new CDR because CDRW drives are so inexpensive now.


Can you run Notes 4.6 and 5.x clients simultaneously?

Yes, you can type these on the command line and change the paths appropriately:
SET NOTESPARTITION=1
START c:\notes\program\nlnotes.exe =c:\notes\data\notes.ini

Note that this is unsupported by Lotus. Use at your own risk, etc.


What is involved in deploying Notes to International users?

From a posting by [email] Kevin Urbanek:

"There are some questions you will need to answer before a decision can be made. First the legal stuff. Legally, a NA ID file can not leave the US or Canada (unless the US State Dept grants you an exception). NA Notes servers can talk to Non-NA Notes servers just fine with one exception, encrypion keys (all keys would need to be International for them to be used worldwide). Note that not all countries allow encrypted data or have rules/laws governing encryption.

Some questions:
1. Do your applications and/or users use encryption regurally? BTW: make sure to check out encryption laws in the different countries (France,Russia South Korea and others have laws governing encryption and the keys)
2. Do your users travel internationally? (i.e a NA Notes ID travels to Europe)

If you want to keep your NA setup that you currently have, when you setup the International users/servers, you will need to create a new Organizational certifier that is International. Even though you can create an International ID from a NA certifier, this International ID still carries enough of the NA encryption info (I do not know exactly what part) to make it illegal to export. So, you would end up with 2 Organizational certifiers, which means you need to cross certify the organizations. You can still use one Domain if you wanted. Depending on number of users, you may want to look at 2 Domains, one NA and one International.

If you answered "Yes" to the above questions, then you might want to think about migrating to World Wide Security (International version) or appy to the US State Dept. for an exemption. If you look to migrating, make sure you plan the migration of IDs and also review the Notes applications you have for encyption and sections. If you keep the naming conventions you have in place, Reader and Author names should not be a problem."


How do you create a database that is usable as an address book for name lookup?
You only need to have the views ($Users), ($PeopleGroupsFlat), and ($PeopleGroupsHier) in your database. You also need the ($NamesFieldLookup) view if you want people to be able to begin typing the name in the To: field and have Notes find it automatically in the address books and fill in the rest.

In the person form, you must include the following fields:

  FirstName (Type: Text)
  LastName (Type: Text)
  FullName (Type: Names)
  MailAdress (Type: Text)
  Type (Type: Text) with default value "Person"

Then add the database to the notes.ini file as a cascaded address book.


How do you move databases onto a different drive without the user knowing about it?

You can use a directory link to put all the databases into what the user sees as a subdirectory in the Notes client. In the Notes data directory, create a file named <subdir>.dir where <subdir> is the name of your subdirectory. The first line in the file is a directory name which can include a drive letter (e.g., "e:\data2"). Lines after the first line are hierarchical names for people that can go through this directory link ("e.g., */MyCompany") so you can use directory links as a security tool.

You can also use a database link. To do this, move the database to your new directory. In the Notes directory where the database used to be, create a file with the same filename as the old database. In this file, put the path to the new location of the database (e.g., "e:\data\db.nsf"). When Notes accesses this file, it will automatically look for the database using the path you specify.

If you are using OS/2, you can install the Toronto Virtual File System (TVFS) from one of the many OS/2 ftp archives. This file system allows you to merge directories and files into a "virtual" directory that you can then use for the Notes data directory.

Note that all of these methods will slow down file access a little, but the slowdown should be negligible.


[<a name="NT00000FC2">How can I use a POP3 client, such as Netscape or Eudora, to
access my mailbox in Notes?
</a>]

There are two parts to this:
1) On the server, load the POP3 Server task with "load pop3"; you can also add it to your "TASKS=" line in your notes.ini file if you'd like.
2) Create a person document for the user and fill in the HTTP/Internet password. The user will have to log in using their user name (fully qualified if you specify a Notes domain) and this password in their POP3 client.

Note that even if these users do not have a Notes ID, there is a Lotus per user charge of $30 for each Domino/POP3 mail user.


How do you unhide a database design?
There are a lot of ways to do this in Notes 3.x, but here are a few:

1) If you have multiple servers, copy the database from one server to another. This will unhide the design in the new copy. The database will still show as hidden, so change the name to a template file(ntf) and create a new database from this template. This will insure the database is truly unhidden.

2) Make the locked database a design template. Create a new database. New database inherits design from template. Refresh design from template.

3) Modify a byte in the .nsf file; this is typically done to hack an application that was sold, so this technique will not be described here.

Lotus provides a database Hide Design tool (implemented in Notes 4.x and 5.x) that secures the database design more thoroughly. It is not possible to unhide a database design in these later versions of Notes without the original unhidden template; i.e., you can't unhide it by modifying a few bytes in the .nsf file. In addition, if the original design included LotusScript files from the file system when an agent or design element was saved, you'll need these files in the right directories to modify the corresponding agent or design element.


How do you prevent groups from being expanded when sending mail?

The group expansion only happens for groups in your personal NAB. You can do the following to disable this expansion when sending mail:

Modify your memo form to include a hidden field named "ExpandPersonalGroups" with Text type and Computed when Composed with a value of "0". This will cause personal groups to NOT be expanded. Changing the value to "1" will cause the groups to expand.

The other solution is to use group alias names, but this technique only works in R3.x. For example, if you have a group named Managers make it Mgrs;Managers instead. Aliases are not expanded. That way, if you want it to NOT expand send the memo to Managers. If you DO want it to expand them, send it to Mgrs.


How do I get rid of the encap2.ond attachment on outgoing Internet mail?

These attachments are used to send Notes rich-text-format messages to other Notes users across the Internet.

In the SMTP MTA section of your server document, set the Message Content field to "Users without Lotus Notes"; you probably have this set to "Users with Lotus Notes". Your users can still manually send fully-formatted messages to Notes users via Internet mail by using Actions/SpecialOptions when creating a new mail message.


How do you shut the server down for backups?

If you are running Notes as an NT service, look at this FAQ instead.

You can use the following command to tell the Notes server to shut down:
notes server -quit
After your backup is complete, issue this command to restart the Notes server:
notes server


How do I add additional N&A books to the address lookup dialog?
In your notes.ini file, you should modify the line that reads "Names=NAMES.NSF" to read "Names=NAMES.NSF,MYNAMES.NSF". This will cause Notes to look up names in both the NAMES and MYNAMES N&A books.

Are there any large-scale deployment guidelines?
For large-scale Notes deployment, standards should be defined for:
* Notes network topology & replication strategy

* Hardware and software configurations
* Hardware and software installation guidelines
* User, group, and server naming scheme
* Notes mail integration strategy
* Notes & non-Notes data integration strategy
* Server management guidelines
* Operations guidelines
* Security and ACL guidelines
* Application design guidelines
* Application implementation guidelines
* Training guidelines
* Support guidelines
* Staffing requirements
* Group naming standards
* ACL standards
* Database location standards
* Establishing test, development and production environments
* Name and Address Book control
* Centralization or distributed ID creation
* Replication strategy


How do you update the SmartIcons on all your users' workstations via LotusScript?

If you put the following code in a button in an email message and attach all the icon files to the same message, your users can update their SmartIcons by simply clicking on the button when they receive the email:

     '======================================================================
     ' Code Documentation
     '======================================================================   
     'Originally Written By: James Fox @ Com Tech Communications
     'Originally Created On: 1/3/97
     'Last Updated By: James Fox
     'Last Updated On: 1/3/97
     
     '======================================================================
     ' Declare Variables
     '======================================================================   
     Dim session As New NotesSession
     Dim ws As New NotesUIWorkspace
     Dim uidoc As NotesUIDocument
     Dim doc As NotesDocument
     Dim rtitem As Variant
     Dim iconPath As String
     Dim retvalue As Variant
     Dim fileName As String
     On Error Goto errorRoutine     
     '======================================================================
     ' Warn User what is going to happen
     '======================================================================          
     x = Messagebox("SmartIcon Installer is about to setup the new Com Tech SmartIcons.  Your current universal SmartIcon set will be overridden with the new SmartIcon set.  Your original set will be backed up to a file called universe.bak.  Press OK to Continue", 1 + 64 + 0 + 0,"SmartIcon Installer")
     If x <> 1 Then
          x = Messagebox("SmartIcon Setup has been Aborted  Thank you.",0 + 48 + 0 + 0,"SmartIcon Setup")
          End
     End If
     
     '======================================================================
     ' Get and Set Icon Path
     '======================================================================          
     iconPath = session.GetEnvironmentString("WinNTIconPath", True) + "\"
     Chdrive Left(iconPath,3)
     Chdir iconPath 
     '======================================================================
     ' Backup current universal Set
     '=====================================================================
     fileName = Dir$(iconPath + "universe.bak", 0)
     If fileName <> "" Then
          Kill iconPath + "universe.bak"
     End If     
     Name iconPath + "universe.smi"  As iconPath + "universe.bak"
     
     '======================================================================
     ' Detach Files in memo (overwriting existing files)
     '=====================================================================     
     Set uidoc = ws.CurrentDocument
     Set doc = uidoc.Document     
     Set rtitem = doc.GetFirstItem( "Body" )     
     notesEmbeddedObject = rtitem.EmbeddedObjects
     If ( rtitem.Type = RICHTEXT ) Then
          Forall o In rtitem.EmbeddedObjects          
               objectName = o.Name
               fileName = Dir$(iconpath + objectName, 0)
               If fileName <> "" Then
                    Kill iconpath + fileName
               End If     
               Call o.ExtractFile(iconPath + objectName )
          End Forall                                        
     End If
     
     '======================================================================
     ' Notify User of SmartIcon Installer Status
     '=====================================================================          
     x = Messagebox("SmartIcon Setup has completed Successfully.  Your original smartIcon set has been saved as Universe.bak.  Please close and restart Lotus Notes to use your New SmartIcons",0 + 64 + 0 + 0,"SmartIcon Setup")
     End     
     
     '======================================================================
     ' This is the general error routine
     '=====================================================================     
errorRoutine:
     x = Messagebox("SmartIcon Setup is Incomplete.  Please advise MIS via e-mail.  Thank you.",0 + 48 + 0 + 0,"SmartIcon Setup")
     Name  iconPath + "universe.bak"  As iconPath + "universe.smi"
     End
End Sub


How do you add Quoted Reply support to Notes' mail templates?

Quoted replies let you precede each line of the mail you are replying to with a ">"; it is a standard way of indicating what you are replying to when replying to Internet mail (similiar to how people use color to indicate their replies in Notes). Here is a button you can add to the forms of the standard mail templates. Add an Action Button called "Quoted Reply" to the following forms: Memo, Reply, Reply with History. This button is hidden when: Previewed for reading, Previewed for editing, Opened for editing. The limitation is that it can only quote up to around 15K worth of text because of LotusScript's limitation with GetFormattedText.

Put the following script in the Click action:

Sub Click(Source As Button)
  Dim uiws As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Dim doc As NotesDocument
  Dim uidocReply As NotesUIDocument
  Dim rtitemBody As Variant
  Dim sBodyOriginal$
  Dim sBodyConverted$
  Dim vntMailDbFile,vntMailDbServer
  Set uidoc=uiws.CurrentDocument
  Set doc = uidoc.Document
  Set rtitemBody=doc.GetFirstItem("Body")
  sBodyOriginal=rtitemBody.GetFormattedText(False,0)
  vntMailDbServer=Evaluate("@Subset(@MailDbName;1)")
  vntMailDbFile=Evaluate("@Subset(@MailDbName;-1)")
  Set uidocReply=uiws.ComposeDocument(Cstr(vntMailDbServer(0)),Cstr(vntMailDbFile(0)), "Reply")
  sBodyConverted=ManipulateReplyText(uidoc, sBodyOriginal)
  Call uidocReply.FieldSetText("Body", sBodyConverted)
End Sub

Function ManipulateReplyText (Source As NotesUIDocument, body As String)
 'Adding > to the begining of each line of the "History text" and
 'Aligning the text Left (wrapping)
  Print "Formatting ""History"" text"
  Dim bd As Variant
  Dim note As NotesDocument
  Dim Header As NotesItem
  Dim dateItem As NotesItem
  Dim InFrom As NotesName
  Dim GetInternetFullName$, HeaderString$, pos%, tmpString$, pos1%, dont%,tmp$
  Dim y%, x%, b%, xx%, xb
  Set note=Source.Document
   'dividing the text to lines and addding the > sign
  If note.hasitem("$AdditionalHeaders") Then
     'starting here: inbound messages seem to have $AdditionalHeaders
    Set Header=note.GetFirstItem("$AdditionalHeaders")
    If Header.values(0) = "" Then
       'GetInternetFullName=note.InheritedFrom(0)
      GetInternetFullName=note.From(0)
      Goto Continue
    End If
  Else
    If Not note.HasItem("tmpAdditionalHeaders") Or
           note.tmpAdditionalHeaders(0)="" Then
       'GetInternetFullName=note.InheritedFrom(0)
      GetInternetFullName=note.From(0)
      Goto continue
    End If
    Set Header=note.Getfirstitem("tmpAdditionalHeaders")
  End If
  HeaderString=Header.values(0)
  pos=Instr(HeaderString,"From: ")
  tmpString=Mid(HeaderString,pos+6)
  pos1=Instr(tmpString,"<")
  If pos1=0 Then 'The full name will appear in (...)
    pos1=Instr(tmpString,"(")
    tmpString=Mid(tmpString,pos1+1)
    pos1=Instr(tmpString,")")
    GetInternetFullName=Mid(tmpString,1,pos1-1)
    dont=True
    Goto Continue
  End If
  tmpString = Mid(tmpString,1,pos1-1)
  pos=Instr(tmpString,|"|)
  If pos<>0 Then
    tmpString=Mid(tmpString,pos+1)
    pos=Instr(tmpString,|"|)
    GetInternetFullName=Mid(tmpString,1,pos-1)
  Else
    GetInternetFullName=tmpString
  End If

Continue:
  Set InFrom=New NotesName(GetInternetFullName)
   ' and starting here: I found that inbound messages had a PostedDate item, Not tmpSentOn
  If note.HasItem("tmpSentOn") Then
    postDate = note.tmpSentOn(0)
  Else
    Set dateItem = note.GetFirstItem("PostedDate")
    postDate = dateItem.Text
  End If
  tmp="On " & postDate & note.tmpSentOn(0) & " " & InFrom.Common & "
wrote:" & Chr(13) & Chr(10) & Chr(13) & Chr(10) & ">"
  y=1
  b=1
  For x=1 To Len(body)
    xx=Asc(Mid(body,x,1))
    If x<>Len(body) Then xb=Asc(Mid(body,x+1,1))
       'Now that we know the current and the next characters we can consider whether they will cause a line feed, so we can insert our ">".
      If xx=10 Or xx=13 Or xx=11 Or xx=12 Then
         'if this combination occurs then we skip the next one so we don't LF twice.
        If xx=10 And xb=13 Or xx=13 And xb=10 Then
          x=x+1
          tmp=tmp & Chr (xx) & Chr (xb) & ">"
        Else
          tmp=tmp & Chr(xx) & ">"
        End If
        b=1
      Else
        tmp=tmp & Mid(body,x,1)
        b=b+1
      End If
  Next
  ManipulateReplyText=tmp
End Function

Why won't the incremental installer work?

This typically happens if you install Notes/Domino optional components inconsistently. E.g., you installed Notes 4.61 with Advanced Services, but then you didn't with 4.62. When you try to install 4.63, it detects some old files from the 4.61 install that are not at the 4.62 level (failing a checksum). You can see which files these are by looking at the UPGRADE.LOG file in the Notes directory. Usually, you can keep deleting these old files and re-running the incremental installer until it works (the installer stops when it detects the first file that fails the checksum).


How can you have multiple users on one workstation?

Each user has to have a separate notes.ini file and desktop.dsk file. There is a freeware utility called SmartSwitcher that does all this for you and provides a nice user interface. It can be picked up by clicking [ftp] here. This only works for Notes 3.x

A few settings (like workspace textured background) are not preserved for Notes 4.x users in SmartSwitcher. There is a commercial utility named MultiUser Logon Utility from Rein&McBride that does handle Notes 4.x.

Another commercial utility that reads the ID files from a database is SwitchID from Sollazzo Consulting.

You can also use location documents in Notes 4.x and higher. Important caveats: 1) users share the same desktop so one user can rearrange icons/bookmarks and totally confuse the other users, 2) they will get false "you have new mail" indications, and 3) the user ID files for all the users will be in one location.

Here are some tips from Laurence Wagner (lwagn2@dwp.ci.la.ca.us) on how to do this:


Do a normal install for the first user, then do the following:
1. Change the User Preferences
Select File / Tools / User Preferences from the menu.
In the Navigator panel, with icons labeled "Basics, International, Mail, and Ports", click on Basics.
There are four check boxes to the immediate right of the Basics icon. Select the box labeled "Prompt for location".
Now when Notes is re-started, it will prompt the user to select a named location document. The original location documents given with Notes are named for a variety of network connections that these users will not need to use, but do not delete them.

2. Copying the Notes ID files to the Workstation
Get these files from your Notes admin. These can go to the Notes\Data directory or a network drive, as long as it will be available to your users. Once on the workstation, each person should log in and reset their password from the default value. This can be done after the location documents are created for each person.

3. Add an action button, labeled "Make Location Doc", to the People view of the LADWP Name and Address Book. Switch to the user's ID file before you run this. The code for the button is as follows:

Sub Click(Source As Button)
  Dim session As New Notessession
  Dim notesdirectory As String
  notesdirectory = session.GetEnvironmentString( "Directory", True)
  Dim keyfilename As String
  keyfilename = session.GetEnvironmentString( "KeyFilename" , True)
  Dim whois As String, realwho As String
  Dim workspace As New NotesUIWorkspace
  Dim doc As NotesUIDocument
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.EditDocument(False)
  Dim item As NotesItem
  Dim first As String, last As String, mailserver As String, mailfile As String
  Dim mailserver1 As String, mailfile1 As String
  Dim short As String
  Dim short1 As String
  Dim udir As String
  Dim full As String
  Dim ppass As String
  Dim thisdb As NotesDatabase

  whois = session.CommonUserName
  realwho = session.UserName
  Set thisdb = session.CurrentDatabase
  Set uidoc = workspace.EditDocument(False)
  first = uidoc.FieldGetText("FirstName")
  last = uidoc.FieldGetText("LastName")
  short = uidoc.FieldGetText("ShortName")
  mailfile = uidoc.FieldGetText("MailFile")
  spaceposition = Instr(1,mailfile, "\")
  short = Mid( mailfile, spaceposition + 1)
  mailfile1 = "mail\\" + short
  mailserver = uidoc.FieldGetText("MailServer")
  If first <> "" Then
    full = first + " " + last
  Else
    full = last
  End If
  Call uidoc.Close
  Print "got Person data"

'  Build new rec
'
  Set uidoc = workspace.ComposeDocument ( "", "names.nsf", "Location" )
  Call uidoc.FieldSetText("Name",full)
  udir = notesdirectory
  short1 = keyfilename
  Call uidoc.FieldSetText("Userid", udir + "\" + short1)
  Call uidoc.FieldSetText("Domain","LADWP")
  Call uidoc.FieldSetText("DST","1")
  Call uidoc.FieldSetText("Enabled","0")
  Call uidoc.FieldSetText("NameLookupPref","2")
  Call uidoc.FieldSetText("ExhaustiveNameLookup","1")
  Call uidoc.FieldSetText("Images","0")
  Call uidoc.FieldSetText("LocationType","0")
  Call uidoc.FieldSetText("MailFile", mailfile)
  Call uidoc.FieldSetText("MailServer", mailserver)
  Call uidoc.FieldSetText("Source","*")
  Call uidoc.FieldSetText("TimeZone","8")
  Call uidoc.FieldSetText("WebRetriever","Netscape Navigator")
  Call uidoc.Save
  Call uidoc.Close
'
'     Add the mail database to the workspace
'
  If whois = full Then
    spaceposition = Instr(1, mailserver, "/")
    If spaceposition = 0 Then
      spaceposition = 30
    End If
    mailserver1 = Left$( mailserver, spaceposition - 1)
    Call workspace.AddDatabase( mailserver1 , mailfile1 )
    Messagebox "the mail database for: " & full & Chr(10)  & " has been added to the workspace, and " & Chr(10) & "Location
Document added to Address Book"
  Else
    Messagebox "Location Document for: " & full & " added to Personal Address Book"
  End If
End Sub

A company called DNI Systems makes a workstation sharing solution called Notes Profiler. It accomodates roaming users.

Note that this is no longer necessary as of Notes 6.01. Notes 6.01 supports roaming users so their Notes desktops go with them to each machine. It also supports multiuser installs of the Notes client on a single machine so each logged on user doesn't have to install their own separate copy of Notes and waste disk space.

Can you have specific notes.ini files per user?

You can modify your Notes startup icon to use this as the command line:

  <path>notes.exe =<path>notes.ini

This uses the specified notes.ini file. You can then place this with a user's personal data files on a file server.


How do you change how long logs are kept in log.nsf?

There is a LOG= setting in the NOTES.INI. You specify
LOG=<logfile name>,<logging enable>, 0,<days to keep documents>,<document size>

Do not set document size too large or the server will not be able to compact the log database very well.


How do you limit the size of users' mail boxes?

Notes 4.x has Database Quotas that you can apply to a database; you can access these under the Database Tools of the Server Administration menu.

If you would like to warn users more directly, you can put this in the mail templates's Database Script PostOpen event:

Sub PostOpen(Source as Notesuidatabase)
     Dim db As NotesDatabase
     Dim MaxSize As Double, CurrSize As Double
     Set db=source.Database
     Maxsize = 20     'Megabytes
     CurrSize = Round( db.Size/1000000, 2)
     If CurrSize > MaxSize Then
       MsgBox "Your mail database exceeds the corporate " _
       & "maximum size of " & MaxSize & " Mb. Your mail " _
       & "database is " & CurrSize & " Mb and contains " _
       & db.AllDocuments.Count & " documents.  Please " _
       & "remove or archive old messages. If you " _
       & "require assistance, please contact IS.", 0, _
       "Mail Database Needs Archiving"
     End If
End Sub


Other techniques suggested by ThomasMcA@dawnfoods.com:
1) I created a DeleteAttachments button in our Inbox & All Docs views that *deletes* the attachments in any selected doc. This lets them keep the doc itself, but remove the large attachments.

2) I created a Size column [ @Round(@Sum(@DocLength )/1024) ] in the above views that lists the size of each document. It really helps for the users to see the size of every doc w/ attachments (Wow, it's THAT big?!?!?!? :-) Lotus added a similar column to the R5 mailbox, but they display bytes instead of KB. I think that clutters up the view, and wastes column space. The above formula shows KB, and that's what I use for the column heading - KB.

3) I created an agent that loops through all of the mail files, checks the size, and sends them and myself an e-mail that says it's too big. The agent is signed by the server, so it looks like it came from the server, not from me.

4) I added code to the Initialize event for the mail template that also checks the size of the db, and pops up a messagebox that says "your mailbox has exceeded XX megabytes".

4b) Part of me hated to have to implement this next feature, and I regularly apologize to my users for having to implement it. Since it is human nature to just click OK to the above warning, the code pauses for 10 seconds for every 5 meg. So it displays a message for 25 meg, pauses 10 secs, displays another for 30 meg, pauses, etc. So the more you abuse the system, the longer you have to wait.

5) I also added the above code (via a script library, of course) to the New Memo buttons. If the user ignores all of the above methods/warnings, they have to wait for the above messages and pauses each time they try to create a new memo.

6) I created an Attachments view in the mail template that only displays docs with attachments. A Size column displays the size of each attachment.

7) I soon discovered that even all of the above steps were not enough to stop the determined email quota abuser. So I created a new "over quota" mail template with the following features:

7a) A "before mail" agent that compares the user's quota to the size of their mailbox. If they are over their quota, a Readers field is used to hide the new message from the user (but it's still in their mailbox, so it still counts toward their quota). A message also gets deposited into their Inbox that says "You have mail being held from <User Name>. Subject: <subject>". The body of that note tells them that the message will get released after they cleanup their mailbox, and it contains links to self-help documents in our FAQ database.

7b) A scheduled agent that compares the user's quota to the size of their mailbox. If they are UNDER their quota, it unhides all hidden messages by removing the Readers field. Schedule this agent as often as your users are willing to wait for email to get released, but not so often that you overload your servers.

7c) A hidden view that shows messages that have been hidden by the "over quota" agent. This is used by the agent to loop through hidden messages, and by the admins to look for hidden messages during troubleshooting.

7d) Since this creates 2 agents for every mailbox that uses this template, I only convert a user to use this "over quota" template when I notice that they are regularly close to their quota


How do you set up remote users with a local replica of a new database?

Create a mail message to the remote users with
1) a copy of the database as an attachment
2) a database doclink

Also in this mail message, add some instructions on how to detach the attachment and where to place it (in the Notes data directory). After the attachment has been detached, the instructions should ask the user to click on the doclink. This will search for the database replica locally and open it; it will also place the database on the replication workspace page.
If the attachment is large, you should warn remote users that you are sending it to them so they don't think that the replication is hung.


How do you refresh Private View designs on clients?

Delete the database icon from the user's workspace and re-add it. Deleting the database will cause all the Private Views to be removed. When the user re-opens the database, new Private Views (with the updated design) will be created.

You can do this via a macro that is sent to users via email:

@Command([WindowWorkspace]);
@Command([FileOpenDatabase];........);
@Command([FileCloseWindow]);
@PostedCommand([EditClear]);
@PostedCommand([FileOpenDatabase];.......)


Why does my mail say it is sent from someone else?

If you have the calendar profile (Actions/CalendarTools/CalendarProfile) mail file owner set so that it doesn't match your user name, mail will be sent from that person specified in that field. The mail will also say it is really sent by you though. You will also see error messages in the Notes log ("SchedMgr: Error processing calendar profile document") if you rename a user but do not change their calendar profile.


How do you get the Notes R4 workspace in R5?

Some users who have gotten truly used to the R4 workspace with the tabs and database icons are more productive in it.

This is only needed if you installed R5 fresh instead of upgrading an R4 installation:
1. Copy over the desktop.dsk from your R4 data directory into the R5 directory.
2. Delete your bookmark.nsf file.
3. Restart R5 so it can migrate the R4 workspace and database icons into R5 bookmarks.

If you upgraded R4 to R5, you will only have to follow the next step: In your databases bookmarks, you will have a Workspace database. Right click this bookmark and click on "Set Bookmark As Home Page".

Can you run console commands from a server program document?

From kanellopoulos@byte.gr who found it in the notes.net discussion:

This Win32 program is used in program docs to execute any console command on schedule such as:

  Tell http restart
  Show tasks

Example:

  Program name:     nconsole.exe
  Command line:     SERVER/ACME "tell smtpmta compact all"
  Server to run on: SERVER/ACME

- nConsole.exenconsole.exe

You also have to have the server name in the Administrator section of the server document in the name and address book.


How do you add a disclaimer/signature to all outgoing SMTP mail?

From Matt Chant:
There's an unsupported way, which is exactly the same as the unsupported way in the MTA link. The only difference is that the database is now Mail.Box and not MTAForms.nsf.
The problem is that this is only likely to work if messages are in CD format and being converted to MIME on that particular system. As you migrate to R5 and the client is now submitting the MIME, then this won't work.
There is an outstanding enhancement request for this feature. It's not as simple as it might sound, as there are pluses and minuses to doing on a server (Hard to inject additional content into existing MIME, Can't do it for S/MIME at all )and pluses and minuses to doing on the client (Solves the MIME,S/MIME problems but then there are problems in trying to support disclaimers for only external recipients.), so it's going to take some figuring out how to achieve it cleanly in the modern Domino environment.


Can you run scheduled server commands?

Yes, this was added in R5. Use this for a program name (this example is for Wintel32 systems) in the program document:

nserver -c "<command goes here>"
Example:
nserver -c "load replica"
nserver -c "tell http restart"


How do you convert an R5 database back to R4 format?

You have to run the "compact -R" on the database. You can then rename it w/ a .ns4 extension so the R5 server will not convert it back to R5 format on the next compaction.


What are .IIB files?

These files are generated by the Lotus Incremental Installer. They are backups of the files that the Incremental Installer changed. You can delete these files if you are satisfied with the incremental upgrade. If not, run the same Incremental Installer again and it will ask if you want to uninstall the upgrade.

Note: If you're running a CF incremental, do *not* delete the IIB files. When Lotus releases another normal incremental, it will try backing out the CF incremental by using the IIB files; if they don't exist, the incremental will fail.

Can Domino prevent MS viruses from getting into user's mailboxes?

"Pre-Delivery Agents" were added to Domino R5. This lets you run an agent before a mail message is deposited into a user's mailbox. You can use this new feature in R5's mail template (mail50.ntf) to strip out all .vbs and .shs attachments from users' incoming mail. Create an agent named "Remove MS Viruses" in the R5 mail template. Set the agent to run "Before New Mail Arrives". Sign this agent with an ID that can run restricted agents on the server. Put this code in the agent's Initialize event:

Sub Initialize
  Dim session As New NotesSession
  Dim doc As NotesDocument
  Dim rtitem As Variant
  Dim p1 As Integer
  Dim p2 As Integer
  Dim filename As String

  Set doc = session.DocumentContext
  Set rtitem = doc.GetFirstItem("Body")
  If (rtitem.Type = RICHTEXT) Then
    If (Isarray(rtitem.EmbeddedObjects)) Then
      Forall obj In rtitem.EmbeddedObjects
        If (obj.Type = EMBED_ATTACHMENT) Then
          filename = Lcase(obj.Source)
          p1 = Instr(1, filename, ".vbs", 5)
          p2 = Instr(1, filename, ".shs", 5)
          If ((p1 > 0) Or (p2 > 0)) Then
            Call obj.Remove
            Call doc.Save(True, True)
          End If
        End If
      End Forall
    End If
  End If
End Sub

The most common Win32 extension types to worry about are (there may be others):
*.com, *.exe, *.bat, *.vbs, *.vbe, *.js, *.jse, *.hta, *.wsf, *.wsh, *.shs, *.scr, *.pif, *.xml, *.lnk, *.eml


In R6+, you can define server rules to remove messages that have certain extensions.


Are agents failed over when clustering?

Only "Before Mail Delivery" fail over when clustering.
The other agent types do not because they will run once the failed server comes back up.


Can you force a messagebox for broadcast messages?

In R5, if you broadcast a message preceded by "(!) ", it will show up on clients via a messagebox instead of showing up in the status bar.


Why is a user not getting the proper roles?

From Barry Wand (bwand@us.ibm.com):

The user is in an ACL as a member of a Group that has Editor access to a database and has been included in a Role that is authorized to modify a field in a Controlled Access Section. The user is also listed explicitly with Designer access. The user still cannot edit that field although other people in the same group can.

Explicit ACL entries always override Group document entries. If your Group entry has the Role assigned but your explicit entry does not then you do not have the Role assigned to you. In this case you have Designer access but without the Role assigned. You will need to adjust the ACL to assign that Role to your explicit entry also.


Why do I get att1.unk or winmail.dat attachments?

From Bjarne Sloth (bjs@BKMED.DK):

These attachments are sent from users of a Microsoft email system. The att1.unk file is received by Notes if the original message was transmitted with MIME encoding. The winmail.dat file is received if the Microsoft email system UUEncodes the message.

Here are some links for more information:
http://office.microsoft.com/assistance/2000/Olmsgfrmtprobs.aspx
http://support.microsoft.com/support/kb/articles/q136/2/04.asp

If you can get the files into a .tnf file extension, you can use the fentun "TNEF-unpacker" from:
http://www.fentun.com


Where is the R5.x fixlist?

The fixlist for all R5 versions is now online at the Notes.net site.

What do I get "Database must be compacted to support the use of @AllChildren or @AllDescendents in a formula" errors?

From turtle@fred.net:

These are almost never actually related to the use of the R5 @commands @AllChildren or @AllDescendents. What the error thinks is that you're using these R5 commands in an R4 database. However, if you encounter these errors while changing to a view in a database that is already R5 (ODS version 41) or where you know those commands are not used, it means something quite different.

This error can occur when the view index for a particular view has been corrupted. You can compact all you want, you can make new replicas or copies of the database, but the error won't go away! To fix this, I suggest altering the view selection formula (often, merely changing it from an "easy" view selection to a proper "formula" will do the trick) and saving the database. This will force the view index to be discarded and rebuilt. It may also be effective to change the view's "Discard Index" property to "after each use" (the default is "Never"). Change this, open the problem view, then change the setting back.


How do you move a Domino server to new hardware?

From colin.brown@advanticastoner.com:

The following procedure can be used when you need to move an existing Domino server to new hardware (i.e. when upgrading to a new server) without any disruption to your users. The procedure is based on a number of postings to notes.net and our own recent experience moving a R5.05 Domino server to new hardware. The basic idea is simple: install the same version of the server on the new box (but don't configure it), then move the data directory & ini file from the old machine.


1. If possible, obtain the install media for the release of Domino currently running on the server you want to move - lets call it DEV1. If this is unavailable, upgrade DEV1 to the version you DO have available. It is vital that you install the release of Domino currently running on the old machine on the new hardware. If you only have the install media for 5.07, and DEV1 is currently running 5.05, you must upgrade DEV1 to 5.07 prior to the move.

Note: It is preferable to avoid upgrading the server prior to moving it, if possible - that way you can be sure that any problems encountered after the move are not due to problems with the upgrade.

2. If the server is one of several in a Notes Domain, replicate the Domino Directory (names.nsf) with another server in the domain to ensure an up to date copy.

3. Disable replication and access to the old server (to ensure we get a frozen snapshot of the server and its data). Replication and access via Notes clients can be disabled by simply taking the Domino server down. On an NT box, as an extra precaution you can remove any shares and stop the netlogon service (this must be restarted before data can be copied to the new box).

4. Do a full backup of the old server.

5. Install Domino on the new hardware, but don't configure it.

6. Copy the data directory and the notes.ini file from the old server to the new box, replacing the data directory & ini file created during the Domino install on the new machine (if you stopped the netlogon service in step 3. you'll need to restart it so you can connect to the new server). (Note: If you have any 3rd party Domino software installed on your server, you may also need some DLLs from the program directory; it safest to just reinstall the 3rd party software)

7. If the path of the data directory on the new machine is different to the old, you need to modify the directory= line in the notes.ini file to specify the correct location of the data directory on the new machine.

8. Disconnect the old box from the network.

9. Update the DNS to resolve the server name "DEV1" to the IP address of the new server (the NT server name doesn't need to be the same as the old machine). (Note: If you're using NetBIOS for name resolution, you also have to purge the NetBIOS cache or the other machines will not find the new machine)

10. Bring the new server up, test Notes and the DNS change

11. Do a full backup of the new server

12. If you need to reconnect the old machine to the network, make sure you trash the Domino server installation prior to reconnection to prevent conflicts with the new server.

13. You're done - your server should now run happily on the new hardware with no disruption to your users.


Why do HTML/MIME messages arrive w/ images at the bottom of the message instead of inline?

If you go to the Person document in the Domino Directory, the person most likely has Mail Preference set to Prefers Notes Rich Text. If you set this to No Preference, it will come in as MIME mail with inline images.


What encryption is used by Notes?

From stharris@sd.synetics.com:

- 630 bit RSA key for Notes protocols
- 1024 bit RSA key in the web protocols (SSL and S/MIME)
- 128 bit encryption (SSL and S/MIME)
- 64 bit encryption in the Notes protocols (mail, doc encryption, session encryption, etc)

Technote 179624 - "Overview of Recent Changes in U.S. Export Regulations and Implications to
Lotus and Its Customers"

Technote 179031 -"U.S. Government Relaxed Export Restrictions"

Milestones in Notes/Domino security

Notes from Support: Notes Encryption - Locks for a Digital World

Notes and Domino security: Past, present, and future

Look for 128-bit Notes/Domino Encryption in future Releases.


How do you always show the preview pane in a user's mail database?

This has probably been one of the most requested R5 features that never made it in. Some users (mostly Outlook users) are used to having a preview pane always open when viewing mail. The R5 mail template does not have this feature. However, you can add it by adding a profile form and a few actions to your ($Inbox) folder in the mail template.

First, create a profile form named "Custom Configuration". Add a text field named "PreviewPaneDefault".

Now modify the ($Inbox) folder by adding a few actions.

Action "Tools\Set Preview Pane Off":

  @SetProfileField("Custom Configuration"; "PreviewPaneDefault";  @False;@UserName);
  @If(Form!="";
    @Command( [ShowHidePreviewPane] ; @False );
    @Return(0)
  )

For the hide-when on this action, use this formula:
  showOrHide := @GetProfileField("Custom Configuration"; "PreviewPaneDefault"; @UserName);
  @If(@IsError(showOrHide) | showOrHide = 1;
    @False;
    @True
  )

Action "Tools\Set Preview Pane On":

  @SetProfileField("Custom Configuration";"PreviewPaneDefault";@True;@UserName);
  @If(Form!="";
    @Command( [ShowHidePreviewPane] ; @True );
    @Return(0)
  )

For the hide-when on this action, use this formula:
  showOrHide := @GetProfileField("Custom Configuration"; "PreviewPaneDefault"; @UserName);
  @If(showOrHide = 1;
    @True;
    @False
  )

Finally, modify the PostOpen event for the ($Inbox) folder by using this formula to activate the preview pane depending on how the user specifies his/her preference:

  @If(@ViewTitle="($Inbox)";
    @True;
    @Return(0)
  );
  showOrHide := @GetProfileField("Custom Configuration"; "PreviewPaneDefault"; @UserName);
  @Command( [ShowHidePreviewPane] 
    @If(@IsError(showOrHide);
      @False;
      showOrHide
    )
  )

Why is outbound SMTP mail stuck in mail.box on my Windows Domino server?

From John Aylmer (John@Aylmer.com.au):

Symptoms:
Mail to external Internet domains accumulates in the Domino server's mail.box. The problem is that although Windows can resolve DNS, Domino cannot. This happens only with some Domino R5 & Windows 2000 installations. You get no error indications anywhere. It looks like a DNS problem but Windows can resolve DNS including making
Telnet connections.


To prove it is this problem, add the following parameters to your server's NOTES.INI file and restart the Domino server:

  DebugRouter=3
  Log_MailRouting=40

If you have messages queued and get a number of 'DNS resolver error' messages, then this is your problem.

One workaround in IBM's KBase is to specify the DNS server's IP directly in the registry. The disadvantage of entering this registry information is that obvious problems will result if the IP's of the DNS servers ever change and the registry edit was not clearly documented. This entry is normally blank even for machines that work OK:

  \HKEY_LOCAL_MACHINE\SYSTEM\CURRENTCONTROLSET\SERVICES\TCPIP\PARAMETERS\NameServer REG_SZ

In the data field of the NameServer key enter the IP's of the DNS Servers:
eg. 123.123.123.123, 124.124.124.124

The other fix you could use would be to use an SMTP relay server, DNS resolution is not required if you do this.


How do you get the server to recognize changes to groups immediately?
From Daniel Nashed:
"show nlcache" gives you the current status of the NameLookup Cache
"show nlcache reset" reset the NameLookup Cache

Can you run SpamAssassin on a Notes server?
This only works on Win32 systems and requires the use of a Before Mail Arrives agent and a COM object so it may not be that scaleable:
http://www.projectlounge.com/QuickPlace/spamassassin/Main.nsf


Can users recall messages they've sent?

Gregg Eldred provided the nice agent below to do this. Caveats are that the user must ask an administrator to run the agent and the administrators have to have access to everyone's mail file. A copy of the sent message must be selected for the agent to run on:

Sub Initialize
Code: Dim db As NotesDatabase, mailDb As NotesDatabase
Dim s As New NotesSession
Dim doc As NotesDocument
Dim dc As NotesDocumentcollection
Dim notesdbdir As New NotesDbDirectory("mailserver/ou")
Dim mailDoc As NotesDocument
Dim thisLog As NotesLog
counter = 0
'//evaluate the membership of the administrators group. If user does not have access, then exit the routine
eval$ ={@IsMember("Administrators";@UserNamesList)}
admin = Evaluate(eval$)
'//check security level of the person running the agent - admin level
only
If admin(0) = 0 Then
Msgbox "You do not have the required access to recall messages. Please contact your Lotus Administrator. Thank you", 0+16, "Error - Security Level"
Exit Sub
End If
'//script will error if the mailDoc is not set, so force the script to resume next on any error
On Error Goto errHandler
'//get a handle to the current database
Set db = s.CurrentDatabase
'//get the first database in the directory on the server.
Set mailDb = notesdbdir.GetFirstDatabase(DATABASE)
'//get the documented marked for removal
Set dc = db.UnprocessedDocuments
'//check to make sure that a message(s) has been selected for recall. If not, then exit sub
If dc.count = 0 Then
Msgbox "You have not selected any messages to recall. Please select the messages you wish to recall and run this agent again. Thank you.", 0+16, "Error - No Message Selected"
Exit Sub
End If
'//Due to server performance, get only one document at a time
Set doc = dc.GetFirstDocument
'//set the log file using some information from the document
Set thisLog = New NotesLog("Message Recall")
Call thisLog.OpenNotesLog("server/ou", "applog.nsf")
thisLog.LogActions = True
'//log brief information about the offending email for tracking purposes
Call thisLog.LogAction(" ********** RECALL STARTED ************* ")
Call thisLog.LogAction(db.title & " - " & doc.universalid)
'//check the database for the particular unid in question
While Not(mailDb Is Nothing)
'//if the filepath of the database has 'mail' in it, then it should be a mail file
If Instr(mailDb.FilePath,"mail") Then
'//in order to search by unid, you must open an instance of the mail file
Call mailDb.Open("", "")
'//Search for the document by unid
Set mailDoc = mailDb.GetDocumentByUNID(doc.UniversalID)
'//if the document matching is found, remove the
document
If Not(mailDoc Is Nothing) Then
Call mailDoc.Remove(True)
Call thisLog.LogAction(" -- document removed from db: " & mailDb.Title)
'//count the number of databases that we remove the document from, for verification purposes
counter = counter + 1
End If
End If
'//get the next database in the directory
Set mailDb = notesdbdir.GetNextDatabase
Wend
'//display message to the administrator
Msgbox "Recall Complete. Please review log file for validation. Thank you and have a great day, " & s.commonusername, 0+64, "Message Recall"
'//finish up the logging and then close the log file
Call thisLog.LogAction(" Agent Completed -- " & counter & " databases were accessed.")
Call thisLog.LogAction(" ********** RECALL COMPLETE ************* ")
Call thisLog.Close
errHandler:
Resume Next End Sub


Can you forward a copy of a user's mail and keep it in their mailbox?

You can use R6 Mail Rules in their mailbox, but the problem is that all the forwarded mail looks like it comes from you.

As an alternative, you can use this Before New Mail Arrives agent. However, it cannot include the Cc list directly; it has to be part of the mail message so a duplicate copy of the forwarded mail doesn't get sent to the original Cc list. This nice thing about this agent is that it retains the original "From" address so the user can reply to the sender directly.

Sub Initialize
Dim s As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim forward As NotesDocument
Dim forwardaddress As String
Dim rtitem As NotesRichTextItem
Dim rtnav As NotesRichTextNavigator
' **** set to the address that you would like to forward mail to ****
forwardaddress = "test@test.com"
Set db = s.currentdatabase
Set doc = s.DocumentContext
Set forward = New NotesDocument(db)
Call doc.CopyAllItems(forward, True)
Set rtitem = forward.GetFirstItem( "Body" )
Dim newtext As String
'navigation element in order to place header in front of body text
Set rtnav = rtitem.CreateNavigator
Call rtnav.FindFirstElement(RTELEM_TYPE_TEXTPARAGRAPH)
'determines if this is an internal message or not
Dim nn As New NotesName(doc.GetItemValue("From")(0))
Dim cc As New NotesName(doc.GetItemValue("CopyTo")(0))
Dim sto As New NotesName(doc.GetItemValue("SendTo")(0))
' Set up a header that will be attached to the email which
' specifies additional info about the original email since we include the Cc list directly
Dim testcopy As String
If doc.GetItemValue("CopyTo")(0) = "" Then
testcopy = "no one."
Else
Forall x In doc.GetItemValue("CopyTo")
If testcopy = Null Then
testcopy = x
Else
testcopy = testcopy + x + ", "
End If
End Forall
End If
If nn.IsHierarchical Then 'if it is then get their internet address
If nn.Addr821 <> Null Then
'if they have one then use this as the from address
Call rtitem.BeginInsert(rtnav)
Call rtitem.AddNewLine( 1 )
Call rtitem.AppendText( "Original message sent to " + sto.Addr821 + " and copies were sent to " + testcopy)
Call rtitem.AddNewLine( 3 )
Call rtitem.EndInsert
Call forward.RemoveItem("CopyTo")
Call forward.RemoveItem("BlindCopyTo")
Call forward.ReplaceItemValue("From", nn.Addr821)
Call forward.Save( True, True )
Else
' otherwise if this is an internal message and the internet address of ' that user is not populated, use the agent signer's return address
Call rtitem.BeginInsert(rtnav)
Call rtitem.AddNewLine( 1 )
Call rtitem.AppendText( "Original message sent to " + sto.Addr821 + " and copies were sent to " + testcopy)
Call rtitem.AddNewLine( 3 )
Call rtitem.EndInsert
Call forward.RemoveItem("CopyTo")
Call forward.RemoveItem("BlindCopyTo")
Call forward.ReplaceItemValue("iNetFrom", nn.Addr821)
Call forward.Save( True, True )
End If
Else
'otherwise this came in from the internet, so just use the from address as the inetfrom
Call rtitem.BeginInsert(rtnav)
Call rtitem.AddNewLine( 1 )
Call rtitem.AppendText( "Original message sent to " + doc.GetItemValue("SendTo")(0) + " and copies were sent to " + testcopy)
Call rtitem.AddNewLine( 3 )
Call rtitem.EndInsert
Call forward.RemoveItem("CopyTo")
Call forward.RemoveItem("BlindCopyTo")
Call forward.ReplaceItemValue("iNetFrom", doc.GetItemValue("From")(0))
Call forward.Save( True, True )
End If
forward.Send False, forwardaddress
Call forward.RemovePermanently(True) End Sub


How Do You Set Up Failover for Outbound SMTP Mail?
In your server configuration document, there is a field for setting the relay host for messages leaving the Internet domain. If you put a comma separated value in there (can be IP addresses or hostnames), the SMTP router will try all the outbound relay hosts you specify when trying to send outbound SMTP mail.

Why is my Notes client having trouble replicating over satellite links?
There is a lot more latency on satellite links. Craig Wiseman suggested these parameters for your TCP/IP adapter that connects to your satellite modem:

TCP RECEIVE WINDOW : 224360
WINDOWS SCALING : YES
TIME STAMPING : NO
SELECTIVE AKS : YES
PATH MTU DISCOVERY : YES
BLACK HOLE DETECTION : NO
MAX DUPLICATE AKS : 3
TTL : 64
MTU : 900

You can use DrTCP to make these changes.


Can you have an Apache server handle Domino URLs on a different box?
You can use something called "reverse proxy" to map the Domino URLs to an internal machine:

http://www.apacheweek.com/features/reverseproxies


How do you enable folder references for a database?
You have to do this programmatically and via the convert task.
Programmatically, you have to enable the folder references property on the database. E.g.

Dim session as NotesSession
Dim db As NotesDatabase
Set db = session.CurrentDatabase
db.FolderReferences = True

This enables Notes to track which folders documents are put into after you enable this flag. However, all the foldered documents before setting this flag will be missing their folder references. To determine which folders documents are in, you have to run this on the server console:

load convert -m <database>    
(R6 or R7)
or
load convert -e <database>    
(R5)


What can I check if my server is running too slow?

These are a few resources you can check:

- The Domino Performance Cookbook
- The Domino Performance Redbook
- Domino Performance Best Practices
- Troubleshooting TCP/IP Issues
- if you are running a cluster, do "show stat replica.cluster*" and check your cluster queue (if it's too high, your network bandwidth is too low)


How Do You Rebuild Busytime?

The cleanest way to do this is to shut down the server and delete busytime.nsf and let the server rebuild this if your busytime database is corrupted. If you have a clustered setup, you have to shut down the servers in your cluster and delete the clubusy.nsf DB.

An alternative way that may not require server reboots:
- shut down the calconn, sched, and rnrmgr tasks
- delete all documents in the busytime.nsf or clubusy.nsf DBs or just delete the DB after a "dbcache flush"
- restart the calconn, sched, and rnrmgr tasks
- instead of restarting the tasks, you may be able to do "tell sched validate" and "tell rnrmgr validate" from the console


How Do You Fix the "Error connecting to server " Error?

The Domino server when it has multiple network cards, sometimes doesn't know how to contact itself and you get messages of the form "Error connecting to server <itself>: Remote system no longer responding". Even though DNS has the IP address for the server, Domino sometimes needs this IP address put in the local hosts file directly. On Windows, this is in the \windows\system32\drivers\etc directory.


Books for Notes/Domino 5.x

You can order any of these books from Amazon Books, who will give you a 20% discount on them. All you have to do is click on the ISBN number of the book in the tables below.


Search Amazon for more books:



Title
Author
Publisher
ISBN
Reviews

Building Intranets With Lotus Notes & Domino 5.0
Steve Krantz, Eileen Rudden Maximum Press 1885068417
Developer's Guide to Lotus Notes and Domino 4.6-5.0 Curt Holmer, Brett Molotsky Wordware Publishing 1556226438
How to Plan, Develop and Implement Lotus Notes 5.0 Mike Falkner John Wiley & Sons 0471191078
Lotus Notes & Domino 5 Scalable Network Design John P. Lamb McGraw-Hill 007913792X
Easy Lotus Notes 5 Dorothy Burke MacMillan 0789718138
CLP Fast Track : Lotus Notes 5 Application Development Dennis Maione New Riders Publishing 0735708770
Easy Lotus Notes R5 Cate McCoy MacMillan 0789721066
Teach Yourself Lotus Notes and Domino 5 Development in 21 Days Jane Calabria MacMillan 0672314169
Domino System Administration : Administering Domino for Lotus Notes &
the Internet
Rob Kirkland New Riders Publishing 1562059483
Lotus Notes and Domino 5 Development Unleashed Debbie Lynd, Steve Kern MacMillan 0672314142
Lotus Notes R5 For Dummies Pat Freeland, Stephen Londergan IDG Books 0764503200
Lotus Notes R5 For Dummies Quick Reference Steve Londergan, Pat Freeland, Stephen Londergan, Paul Freeland IDG Books 0764503197
Lotus Notes 5 Certification Exam Guide Libby Schwarz McGraw-Hill 007135090X
Mastering: Lotus Notes R5 Scot Haberman, Andrew Faciani Sybex 0782121853
Developer's Guide to Lotus Notes and Domino 5.0 Curt Holmer, Brett Molotsky Wordware Publishing 1556226438
CLP Fast Track : Lotus Notes 5 Application Development Dennis Maione New Riders Publishing 0735708770
Certified Lotus Professional : Application Development Study Guide Klander, Lars Klander Sybex 0782126685
Certified Lotus Professional : System Administration Study Guide Klander, Lars Klander Sybex 0782126650
Lotus Notes and Domino 5 Bible Brown, Kenyon Brown, Carl Abrahamson IDG Books 0764545906
Lotus Notes 5 Certification Exam Guide Libby Schwarz McGraw-Hill 007135090X
Domino Development with Java Anthony S. Patton Manning 1930110049 Ken Yee


Reviews are still needed.

If you've read a good Notes book lately, [email] let the FAQ maintainer know how you liked it.


IBM Redbooks

IBM redbooks are documents written by technical specialists within IBM. The quality of the technical information is generally very good. This is a list of the available publications related to Notes:

* Developing Applications with Lotus Notes Release 4, IBM form number SG24-4618-00
* LotusScript for Visual Basic Programmers, IBM form number SG24-4856-00, Lotus part number 12498
* Secrets to Running Lotus Notes: The Decisions No One Tells You How to Make, IBM form number SG24-4875-00, Lotus part number AA0424
* Lotus Notes Release 4 in a Multiplatform Environment, IBM form number SG24-4649-00
* IBM PC Server and Lotus Notes Integration Guide, IBM form number SG24-4857-00
* Lotus Notes on AIX Systems Installation: Customization and Administration, IBM form number SG24-4694-00
* Using ADSM to Back Up Lotus Notes, IBM form number SG24-4534-00

You can order these redbooks from the Redbook Web Site.


Helpful Non-Notes Books

You can order any of these books from Amazon Books, who will give you a 20% discount on them. All you have to do is click on the ISBN number of the book in the tables below.

Search Amazon for more books:

Title
Author
Publisher
ISBN
Reviews

Enterprise.Com
Jeff Papows Perseus Books 0738200646 Ken Yee
Javascript : The Definitive Guide David Flanagan O'Reilly & Associates 1565923928
Java Servlet Programming Jason Hunter, William Crawford, Paula Ferguson O'Reilly & Associates 156592391X
Dynamic HTML : The Definitive Reference Danny Goodman O'Reilly & Associates 1565924940


Reviews are still needed.

If you've read a good Notes book lately, [email] let the FAQ maintainer know how you liked it.


Lotus Notes Magazines

The View, (800)810-1800 or (617)969-6666, $295/yr. Click [email] here to send a mail message to The View.

Lotus Notes & Domino Advisor, (800)336-6060 or (619)483-9851, $69/yr. Click [email] here to send a mail message to the Lotus Notes Advisor.

Lotus eBusiness Magazine (an on-line magazine)

Lotus Solutions Now! is a free publication on Lotus products published by Lotus.

Virtual Workgroups, (800)-227-1234, $40/yr. Not strictly a Notes magazine, but a good number of their articles describe how companies are using Notes.

netConnect, 011-44-171-221-7178, $225/yr for the printed version. This is a UK Groupware magazine with some articles on Lotus Notes. You can also [email] email them for additional info.

Group Computing, (415)348-0579, free to qualified individuals. This is a magazine that covers Notes and other groupware tools (including web based tools). [email] Contact them for more information.

Rupert's Lotus Business Week (Formerly Lotus eNews), free. A weekly email newsletter covering the business issues behind Lotus mail, groupware and web products. To subscribe, send email to [email] rupert.b@virgin.net.

Domino Power Magazine is a free monthly journal with weekly tips and daily Notes and Domino-related news.


Can you force a user to log in before using a database?

The normal way to do this is to set the database Default and Anonymous access to No Access.

With Domino 1.5b3 and later, add "&Login=1" to a Domino URL. This will force the user to log in before allowing access to a database (without requiring you to set Default and Anonymous access to No Access).


How do you replace the "No Documents Found" message in an empty view?

In the $$ViewTemplate, count the elements in the view using @Elements(@DbColumn). This will get the number of documents in the view. You can then use this in the paragraph hide-when for the embedded view to hide it. In another paragraph, you can use the opposite of this formula to unhide a custom message or passthrough HTML.


Is it possible to run a Domino server on a stand-alone PC?

Yes. You must have TCP/IP installed on the stand-alone PC; you should configure the loopback interface if you have no network card.
Configure the Domino server normally. When you run Netscape or MSIE locally, you can specify localhost, 127.0.0.1, or the system name like so: "http://localhost", "http://127.0.0.1", or "http://ntserver1" (assuming your stand-alone PC is named ntserver1).


How do you add a button that clears the fields in the form?

Add the following HTML code to your form:
<INPUT TYPE="reset" VALUE="Clear ALL">

If you want to control where this shows up, you can put the Submit button and this code in separate columns of a table with hidden borders.


What URL do you use to create a new document in a database?

The URL:

DatabaseName/FormName?OpenForm

can be used to create a document in a database. e.g., this will create a new document using the form "New Order" in the database orders.nsf in your Notes data directory:

orders.nsf/New+Order?OpenForm


How do you reopen a saved new document in read mode?

Add this to the $$Return field for the form:

  REM "By thomas@csshome.net";
  nsfpath := @ReplaceSubString(@SubSet(@DbName;-1); "\\"; "/");
  "[http://www.company.com/" + nsfpath + "/$DefaultView/" + @Text(@DocumentUniqueID) + "?OpenDocument]"


Can you prevent the browser from prompting for a password when you use multiple web servers for your site?

You can add URL redirects so the browser thinks that it is accessing only one server:

www.server.com/ ->www.server.com/index.html
www.server.com/marketing ->www.dominoserver.com/marketing.nsf/$About
www.server.com/sales ->www.iisserver.com/sales/defaul.asp

In the example above, the browser always thinks it is accessing www.server.com.


How do you make parts of a document visible only to Web users or Notes users?

Use this formula to hide pieces of Notes documents if the current user is a Web client:

  @Contains(@UserRoles; "$$WebClient")

And this to hide if the current user is a Notes client:

  !@Contains(@UserRoles; "$$WebClient")


You can also use this to control when subforms are displayed. For example, you can have a "web footer" subform which you only want displayed when documents are shown on the web. In the design of the form for the documents, you would do a Create/InsertSubform with "Insert subform based on formula" and a formula of:

  @If(@IsMember("$$WebClient"; @UserRoles); "web footer"; "")

In Notes 4.6, there is a special text attribute you can set to hide parts of a document from web users or Notes 4.6 users.


Can Domino use directory links to reach databases?

The released version of Domino 1.5 can access DIR links by default, but betas acted the opposite way. To disable directory link support, put DominoNoDirLinks=1 in your NOTES.INI file. With this setting, Domino will limit web users from using dirlinks, but allows Notes clients and the Notes server to use the dirlinks.
If you do allow Domino to use DIR links, it will not use the ACLs specified in the .dir files so you should make sure the database ACLs are set up properly for the databases you put in the directory pointed to by the DIR link.


How do you customize error messages?

This is from the Notes on-line help. Create a form with the one of the following names. On the form, there must be an editable text field named MessageString to hold the error message.

Form name
Displays to Web users when...
$$ReturnDocumentDeleted The user successfully deletes documents.
$$ReturnAuthenticationFailure The user's name and password can't be verified.
$$ReturnAuthorizationFailure The user doesn't have a high enough access level to access the database.
$$ReturnGeneralError Any other errors occur.

For the errors and deletion messages before you enter a database or if you are serving HTML files, you can create a Mapping Error Messages document in the a database created from the Notes 4.61 Domino Web Server Configuration template.


How do you display the number of search results on a page?


You will have to use JavaScript to display the number of search result links:

<SCRIPT LANGUAGE=JAVASCRIPT>  document.write(document.links.length); </SCRIPT> documents found.

You may have to substract a couple of links if you have other links on the page.


[<a name="NT00000A72">
How do you reference view names with "/" and "\" in them?</a>]

Replace "\" with "%5C" and "/" with "%2F".


Can you do a Query by Form?

Create a form named "Search Form" with simple text fields that will be used to refer to the Notes fields of the documents in the "Search View" below. Name these fields form_Field1 and form_Field2 which correspond to Notes fields named Field1 and Field2. Add a field named "SaveOptions" with a default value of "0" (with the quotes) so the form is not saved when submitted.

Put a $$Return field that is computed for display at the very top of your search form with a formula of:

  tField1 := @Trim(form_Field1);
  tField2 := @Trim(form_Field2);
  a := @If (tField1 = ""; ""; "([Field1] = " + tField1 + ")");
  b := @If(tField2 = ""; ""; "([Field2] = " + tField2 + ")");
  SearchFields := @Implode(@Trim(a:b); " AND ");
  REM " *** Empty search will return everything ***";
  SearchString := @If(SearchFields = ""; "*"; SearchFields );
  db := @Subset(@DbName; -1);
  view := "Search View";
  REM "*** Replace spaces with '+'s ***";
  SearchView := @Implode(@Explode(view; " "); "+");
  @Return("[/" + db + "/" + SearchView + "?SearchView&Query=" + SearchString + "]")

To initiate a search, provide a link somewhere that with Action hotspot of

  @Command([Compose]; "Search Form");


Can you set Notes fields from JavaScript?

On the browser the Notes fields are rendered as objects associated with the form and are accessible via JavaScript. E.g. document.forms[0].NotesField.value = "hello" would set a notes text field named "NotesField" to the value "hello".
RichText, checkboxes and radio buttons are handled as array objects, so they must be indexed (e.g. NotesField[0]). You set different properties based on the field type ( .value=, .text=, .checked=true, etc.).


How do you use a URL to reference a specific document in a view?

Create a view with the first column set to the document name. This document name can be numerical, but precede it with a non-numeric character; otherwise, Domino thinks that you are trying to reference a Notes Document ID. You can then reference the document with the URL of:

db.nsf/DocView/DocName

where DocView is the view name and DocName is the document name in the first column.


How do you pass parameters to OpenAgent?

This code will create variables for each parameter you pass to ?OpenAgent. Values for each variable will also be set:

' From Kevin Pauli (kcpauli@usa.net), Technical Directions

Dim session As New NotesSession
Set doc=session.DocumentContext
Dim data List As String
args=doc.Query_String(0)
a = Instr(args, "&" )
Do While a>0
  args = Mid(args, a+1)
  a=Instr(args,"=")
  If a>0 Then varname = Left(args,a-1) Else Exit Do
  args=Mid(args, a+1)
  a=Instr(args,"&")
  If a>0 Then value = Left(args, a-1) Else value=args
  data(varname)=value
Loop

Why do Cascaded NABs no longer work for web authentication?

In Notes 4.6.2 and above, you will have to use the Master Address Book (called Directory Assistance in R5) for web authentication. This was done to enhance security. Here are some quick setup instructions for doing this:

This following steps demonstrate how to add a secondary NAB for web authentication:
1) Create Master Address Book from template (mab45.ntf) or Directory Assistance from the R5 template (da50.ntf)
2) Create Directory Assistance document and enter field values as shown below:
Basics
Domain Type: Notes
Domain Name: Internet (this will most likely not correspond to a "real" domain, and that's fine -- it just has to be a unique name in the MAB; you can't specify more than one names.nsf for a particular "domain")
Company Name: Web Users
Enabled: Yes
Rules
Rule1: Enter asterisks for all the OU/O/C fields; Enabled - Yes; Trusted - Yes
Replicas
Replica1: ServerName - hierarchical name of server; Address Book Filename - <exact filename of secondary NAB>; Address Book Title - <exact title of secondary NAB>; Enabled - Yes
2b) Add additional documents for each of your secondary NABs
2c) Add a document for your public address book (names.nsf) if you don't have it in the MAB already.
3) There are two methods to add the Master Address Book or Directory Assistance information to the server document -- choose whichever is more convenient for your environment.
a) Edit the server document and enter the filename into the Master Address Book or Directory Assistance name field in the Basics section.
b) Open names.nsf to the Servers view; highlight the server name and select Actions -- Set Master Address (or Directory Assistance in R5) Book Information (this uses AdminP to process the request; Editor access to NAB is required to run this action).

4) Take out the original cascaded address book from the NAMES= line in notes.ini.
5) Restart the server for the MAB/DA to take effect.


Can you customize the "Deleted" message?
You can create a form named $$ReturnDocumentDeleted for the database and Domino will use that form when a document has been deleted.

Can you have Domino search a specific view instead of a database?

Use this Domino reference:

db.nsf/viewname?SearchView

You can also use
db.nsf/viewname?SearchView&query=word

to specify a specific search word or words (replace spaces with '+').


How do you delete the current document?

The easiest way to do this is to call this agent via ?OpenAgent from the current document:

Sub Initialize
  Dim s As New notessession
  Dim db As notesdatabase
  Dim rdoc, doc As notesdocument
  Dim uid  As String
  Dim returnto As String
   ' Get current document
  Set rdoc = s.DocumentContext
  Set db = rdoc.ParentDatabase
  returnto$=Left(rdoc.http_referer(0),Instr(rdoc.http_referer(0),"?")-33)
  uid$=Right(Left(rdoc.http_referer(0),Instr(rdoc.http_referer(0),"?")-1),32)
  Set  doc=db.GetDocumentByunID(uid$)
   ' Get the parent document so that you can bounce the user back to it after the deletion
  returnto$= Left(returnto$,Instr(Strconv(returnto$,2),".nsf/")+3)+"/all/"+doc.parentdocumentunid+"?Opendocument"
  Call doc.remove(True)
  Print "["+returnto$+"]"
End Sub


How do you validate and jump to fields which have validation errors?

This example will check that all required fields are filled in. If a field is not filled in, it will move the cursor in the web browser to that field.

Put this in the onClick JavaScript event of your submit button:

javascript:DocumentSave();

Place in Form

function DocumentSave()  {
  fieldList = new MakeArray(2);  //set array for how many fields to check
  fieldList[1] = document.forms[0].f_ORG_UnitName;
  fieldList[2] = document.forms[0].f_ORG_UnitAbbreviation;
  DocumentOK = FieldValidation(fieldList);
  if (DocumentOK)
    document.forms[0].submit();
}

Place in Subform (Generic Sub-form)

// By James Fox@Com Tech
//==========================================================
//This function will return the length of an array
//==========================================================
function ArrayLength(obj) {
  var result = -1;
  for (var i in obj)
    result += 1;
  return result;
}

//==========================================================
//This function will perform field validation on the current Lotus Notes document
// for all fields in the fieldsToCheck array.
//==========================================================
function FieldValidation(fieldsToCheck){
  var continueFlag = true;
  for (var i=1; i <= ArrayLength(fieldsToCheck);i++) { 
    if (fieldsToCheck[i].value == "") {
      alert("Please ensure you enter a value for all fields marked with a Star.");
      continueFlag = false;
      fieldsToCheck[i].focus();
      break;   
    }   
  }
  if (continueFlag) 
    return true;  
}


How do you prevent people from deleting file attachments?

At the bottom of your document, pass the HTML tag "<NOSCRIPT>". This will hide the attachments from the browser. However, this can cause problems if you are displaying databases with the Use JavaScript option enabled.

If you are using Domino/Notes 4.6 and later, add a field named "$V2AttachmentOptions" with the value "0" to hide the attachments from web users.


How do you remove the Domino-generated Navigation Bar from Views?

Use a view template as described in the Domino documentation.


How do you decode parameters in URLs?

Using these two functions, you can decode parameters into an array of strings that you can later parse. These two functions handle parameter characters such as spaces which had to be "escaped" to be passed as parameters.

Function unescape( thing$ ) As String

     ' From Hugh Pyle@NIP
     ' Treat thing$ as a string which came from a Web URL;
     ' un-escape it, ie: "+" becomes space, and %HH become individual characters.
     '
     Dim a$
     Dim bit$
     Dim j%
     Const hx$ = "ABCDEFabcdef1234567890"
     For j=1 To Len( thing$ )
          bit$ = Mid$( thing, j, 1 )
          If bit$="+" Then
               bit$ = " "
          Elseif bit$="%" Then
               If ( Instr( hx$, Mid$( thing, j+1, 1 ) )>0 And Instr( hx$, Mid$( thing, j+2, 1 ) )>0 ) Then
                    bit$ = Chr$( Val( "&H" & Mid$( thing, j+1, 2 ) ) )
                    j = j + 2
               End If
          End If
          a$ = a$ + bit$
     Next
     unescape = a
End Function


Function URLGetParamArray( url$, param$ ) As Variant
     ' From Hugh Pyle@NIP
     ' Find as many occurrences of "&param=" as appear in the URL, and return the value(s) as an array.
     ' Unescape the values while we're doing it.
     Dim n%, m%, c%
     Dim p As Variant
     c = 0
     Redim p( c ) As String
     n = Instr( 1, url$, "&" + param$ + "=", 5 )
     While n>0
          Redim Preserve p(c) As String
          m = Instr( n+1, url$+"&", "&")
          p(c) = unescape( Mid$( url$, n+ Len(param$) + 2, m - n - Len(param$) - 2) )
          n = Instr( m, url$, "&" + param$ + "=", 5 )
          c = c + 1
     Wend
     URLGetParamArray = p
End Function


How do you set up Gnu JSP and Tomcat?

From Calin_Tenitchi@netset.se:

The servlet manager in Domino has the following bug when serving up JSP files:
1) it serves JSP files for downloading if you append a / to the .jsp URL
2) JSP pages in subdirectories do not work
3) JSP pages with parameters do not work

For installation, suppose that Domino is installed in:
d:\lotus\domino,d:\lotus\domino\data

Create the following directories:
d:\lotus\domino\data\domino\servlet
d:\classes
d:\jsptmp

Copy "gnujsp10.jar" and "servlet-2.0-plus.jar" from the GNUJSP distribution into d:\classes

In the server document make the following changes in "Internet Protocols->Domino Engine":
Java servlet support: Domino Servlet Manager
Servlet file extensions: *.jsp

Add this line to "notes.ini":
JavaUserClasses=d:\classes\servlet-2.0-plus.jar;d:\classes\gnujsp10.jar

In d:\lotus\domino\data make a file "servlets.properties" with the following content:
servlet.gnujsp.code=org.gjt.jsp.JspServlet
servlet.gnujsp.initArgs=scratchdir=d:\\jsptmp
servlet.gnujsp.extension=jsp

Restart the server and you are ready to do JSP!


From Philip Oldham ([email] phillip@adventmm.co.uk):

Firstly download and unzip the Tomcat JSP server from <http://jakarta.apache.org/>, and make sure you have the Java Development Kit installed, available from <http://sun.java.com> . Let's assume you've already got a working Tomcat installation and that the NT system variables JAVA_HOME, CALADERA_HOME (in version 4, which is the same as TOMCAT_HOME), TOMCAT_HOME and CLASSPATH are properly set. It is also a good idea to set these up in your AUTOEXEC.BAT to be available from startup. You can verify that this is the case by typing

C:\> full path to tomcat\bin\tomcat run - Version 3.2
C:\> full path to tomcat\bin\startup - Version 4.0

If everything is set up correctly Tomcat should start and it should be possible to connect to your server on port 8080 using a browser. Try something like

http://name_of_server:8080/

which, assuming Tomcat is still in its default state will produce the examples page. You need to make sure that you have a working Tomcat installation before continuing with these instructions. If you have problems with installing or configuring Tomcat, you'd better go here: <http://jakarta.apache.org/tomcat/>.
Once you've got a working Tomcat, download the tomcat_redirector.dll from <http://free.tagish.net/domino-tomcat/index.jsp> and copy the .dll to the Domino program directory (this is the directory, which may be called something like C:\Lotus\Domino, that contains a file called nlnotes.exe). Next we need to make some registry entries. The simplest way is to edit the supplied file tomcat_redirector.reg, which initially will look like this

REGEDIT4

[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\1.0]
"log_file"="C:\\tomcat\\logs\\domino.log"
"log_level"="debug"
"worker_file"="C:\\tomcat\\conf\\workers.properties"
"worker_mount_file"="C:\\tomcat\\conf\\uriworkermap.properties"
"tomcat_start"="C:\\tomcat\\bin\\tomcat.bat start"
"tomcat_stop"="C:\\tomcat\\bin\\tomcat.bat stop"

Edit this file to reflect the location where Tomcat has been installed, i.e. replace the instances of C:\\tomcat with the appropriate path remembering to retain the double backslashes. You may also need to edit the tomcat_start and tomcat_stop to the following in version 4.

"tomcat_start"="C:\\tomcat\\bin\\startup"
"tomcat_stop"="C:\\tomcat\\bin\\shutdown"

Once you've made the necessary changes save this file and double click on it to enter it into the registry.

Note that the files referred to by the worker_file and worker_mount_file keys need to exist and contain sane values. Default Tomcat installations will have these files. Note also that recent versions of Tomcat write a file called uriworkermap.properties-auto when they start up that can be renamed uriworkermap.properties to obtain default behaviour. It is a good idea to do this before configuring the Domino server.

It is also good practice to make sure that within TOMCAT_HOME\conf\workers.properties you've changed all instances of 'localhost' to '127.0.0.1'.

Automatically Starting Tomcat

The last two registry entries above provide commands that the redirector DLL will use to start and stop Tomcat when the Domino http server starts and stops respectively. If you don't require this behaviour these two lines can be omitted (or deleted if you've already placed them in the registry).

Configuring Domino

Finally we need to configure Domino to use the DSAPI extension DLL. For those who are unfamiliar with Domino server configuration most of a server's configurable behavior is dictated by a document called the "server document" in a database called the "Public Name and Address Book" or "NAB" for short (N.B. Lotus have renamed the NAB to "Domino Directory" from Domino 5 onwards). Each Domino server will have a NAB (called names.nsf) and each NAB will have a number of server documents including one for the current server. If you have not previously configured a Domino server you may need to refer to the supplied documentation, or you may need to pass this document to your tame Domino administrator.

Assuming you know your way around a Domino server document what we're going to do is actually quite simple. Open the server document for this server, place it in Edit mode, then locate the DSAPIsection and the 'DSAPI filter file names' field on the Internet Protocols tab, HTTP sub-tab. Add "tomcat_redirector.dll" to the DSAPI field, then save and close the document.

Enabling environment memory

Another thing which needs to be done to allow tomcat to run under the Domino server, is to enable the Domino server's initial environment memory to be at its fullest. This is done by finding the CONAGENT.EXE, usually located in C:\WINDOWS\SYSTEM (or similar on NT machines), and with the Domino server NOT RUNNING, right click on the icon and choose properties. Under the memory tab, locate Initial Environment, and increase it to 4096. Then click OK.
Restart Domino

In order to get these settings to take effect and make sure that you haven't disrupted anything else you should now restart the Domino server. If the server is running as a service and you have changed any relevant system variables (JAVA_HOME, TOMCAT_HOME, CLASSPATH) since the last time you restarted the computer you should do a complete restart now because updates to system variables are not seen by services until after a reboot. If all goes well you should see something like this on the server console when the web server starts up.

29/05/2001 18:54:13 JVM: Java Virtual Machine initialized.
29/05/2001 18:54:14 Attempting to start Tomcat: C:\tomcat\bin\tomcat.bat start
Including all jars in C:\Tomcat\lib in your CLASSPATH.

Using CLASSPATH: C:\Tomcat\classes;C:\Tomcat\lib\ant.jar;C:\Tomcat\lib\servlet.jar

Starting Tomcat in new window

29/05/2001 18:54:15 Apache Tomcat Interceptor (Jakarta/DSAPI/1.0) loaded
29/05/2001 18:54:16 HTTP Web Server started

At about the same time Tomcat should open in a new window (assuming you enabled the autostart option in the registry settings). You should now be able to visit a URL that is handled by Tomcat. Something like

http://name
of server/index.jsp
This is the ROOT_TO_TOMCAT\webapps\root\ directory.

may be available, depending on how Tomcat is configured.

Many thanks to Andy Armstrong. Any additional information can be obtained from <http://free.tagish.net/domino-tomcat/index.jsp>.

Can you create field help on a web browser?

Yes, put this into the HTML attributes for the field:

onFocus="window.status='Please enter a subject'" onBlur="window.status=''

This only works on JavaScript-enabled browsers such as Netscape and MSIE.


Can an X.509 certificate from another CA be used with Domino?

You must take these steps to add an X.509 certificate from another CA:
1) Have the user install the certificate in his/her browser
2) Have the user open the certca.nsf database on your Domino server using SSL (use a URL with https://)
3) Have the user choose "Register Client Certificate"
4) The Domino administrator then has to open certca.nsf in the notes client and validate the request. This transfers processing of the request to the AdminP process which has to be running properly.
5) The Domino administrator can then wait for AdminP to process the request on its normal schedule or tell it to process the request immediate as documented in the Notes Help.


How do you improve performance to web clients?

1. Shared fields and subforms slow down the server. Try not to use too many shared fields.

2. DbLookups are cached in the Notes client but not in the Domino server. If you are doing the same DbLookup multiple times, create a computed-for-display field to hold the initial lookup and reference that lookup in other fields that would have done DbLookups.


How do you decrease the download time for the Notes Java applets?

From Brian Levine:

One way to avoid the download time associated with Java applets is to install the class files for those applets on the (browser) client machine. Following are instructions that describe how to manually install the Domino applets (Outline, View, Editor, Action Bar) locally.

Important: Once the Domino applets are installed on the client machine, these local applets will take precedence over those installed on the server. When installing a new Notes build, it is important to re-install the new class files using the procedure outlined below. Alternatively, you can delete the local class files which will cause the class files installed on the server to be used.

Browsers search for Java class files in the following order:

1. Locally, in directories as specified in the CLASSPATH environment variable.
2. Locally, in browser-specific directories.
3. On the server using the archive (.zip, .jar, .cab files) specified in the ARCHIVE or CABBASE tags.
4. On the server using the .class files if an archive is not specified or does not exist. In Internet Explorer 3.x, if a CAB file is specified and not found, an Exception is thrown and the applet is not loaded.

The archive files containing the Domino applet class files can be found in the <notes_data_directory>\domino\java directory. The archives for the view, outline, editor, and action bar applets are NVAPPLET.ZIP, OUTLINE.ZIP, EDITOR.ZIP, and ACTIONBAR.ZIP respectively. The .class files contained within these .zip files must be unzipped into the appropriate browser-specific directory as indicated in the following table. A utility such as PKUnzip or WinZip should be used. Be sure to specify that the directory structure stored in the .zip file should be maintained by adding the -d option in pkunzip or by checking "Use Folder Names" in WinZip.

After unzipping the .class files, a directory hierarchy starting with "lotus\notes" should be found in the appropriate directory.


Browser Directory
Internet Explorer 3.x Unsupported
Netscape 3.x+ (Windows 95/NT)* <netscape_install_dir>\program\java\classes Netscape 3.x+ (Mac)
<netscape_install_folder>\Java\netscape-classes Netscape 3.x+ (Unix)
class files must be installed in CLASSPATH (see below)
Internet Explorer 4.x (Windows 95) c:\windows\java\classes Internet Explorer 4.x (Windows NT)
c:\winnt\java\classes or c:\winnt\program\java\classes
Netscape 4.x Netscape 4.x also supports the JAR archive format. For Netscape 4.x on Windows only, the appropriate .jar file(s) (nvapplet.jar, outline.jar, editor.jar, actionbar.jar) can be copied to the browser-specific directory mentioned above as an alternative to the unzipped .class files.


How do you remove Domino's default submit button?

Create a button hotspot on the form. On a separate line above it , put "<!--" and add a line below it with "//-->" using the HTML paragraph style. This will comment out the Domino button on the browser.

Another way to get rid of the Domino Submit button is to create a hotspot button on the form but do not assign a name or any code to it. It will not appear when you compose the document from a browser.

In R4.6/R5/R6, you can also turn on "Use JavaScript when generating pages" in Database Properties to disable automatic submit button generation. This setting may change the behavior of your other forms.

And for your amusement and all the other variations on this, check out the
How To Hide A Submit Button web site :-)


Can you display a text field as a TEXTAREA?

From Howard Katz (HKatz@FocusedManagement.com):

Simply code your text field as "Allow multi-values", then on the Options tab, check "New line" for "Separate values when user enters" and "Display separate values with". This plain text field will then be displayed as a TEXTAREA. You can set the HTML attributes of the field to adjust the size of the TEXTAREA.


From Justin Freeman:
Note that you'll need separate web and Notes forms to do this: Add some passthru HTML text where you want the textarea to show up:

Then create a hidden computed field at the bottom of the page. Set the value to "comments" (the same name as the field).


Can you do banner ads with Domino?

This technique works well if your site is done in a Notes database (it won't work w/ static HTML files that are served by Domino):
1. Create a database for your ads.
2. In the database's ad view, create the HTML for the image, text, and hyperlink.
3. In each document you want to put an ad on, use a @DbLookup with an @Random to select one of the items from the view.
4. Use passthrough HTML using "[" and "]" to pass the HTML through.

You can also put readernames fields on the ad documents so authenticated users get appropriate ads that are matched to their profiles.


How do you detach a file submitted by a web browser?

The attachment comes in as a Notes V2 attachment. The following code would extract the attachment into a file named "file.web":

  Dim Item as NotesItem
  Dim Object as NotesEmbeddedObject
  Dim Doc as NotesDocument
  Set Item = Doc.GetFirstItem("$FILE")
  Dim ItemName As String
  ItemName = Item.Values(0)
  Set Object = Doc.GetAttachment(ItemName)
  Call Object.ExtractFile("file.web")

If you need to extract multiple items:

  Forall Item In Doc.Items
    If (Item.Name = "$FILE") Then
      ' Process one item
    End If
  End Forall

If you need to check whether any of these are in a Rich Text field, use the NotesRichTextItem's GetEmbeddedObject method or look in the EmbeddedObjects property of the NotesRichTextItem.


How do you add a logoff button for web users?

Using the username:password@www.company.com URL does not work because the browser thinks your realm is "username:password@www.company.com" instead of "www.company.com".

You can use a Java servlet to pass a 401 exception to the browser to log someone off of TestRealm (in most cases, this is "www.company.com" or "/"):

// From Terry Courtney
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class LogOff extends HttpServlet {
  public void init(ServletConfig config)
  throws ServletException
  {
    super.init(config) ;
  }

  public void doGet (HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
  {
    String auth ;

    // get output streem
    ServletOutputStream out = res.getOutputStream() ;
    // get authorization from header (if it exists, user is logged in)
    auth = req.getHeader("Authorization") ;
    if (auth != null) {
      // force prompt of login
      res.setContentType("text/html");
      res.setHeader("WWW-Authenticate", "WWW-Authenticate: basic realm=\"/TestRealm\"") ;
      res.setStatus(401, "401 Unauthorized") ;
    }

    out.println("Not logged in.") ;

  } // end doGet

} // end class LogOff

When you call this servlet w/ a URL of http://www.company.com/LogOff, it will send the right HTTP headers back to the browser to trick the browser into thinking that the user did not authenticate with this Realm. This technique will tell your browser it is no longer authenticated, but only if you are using Basic Authentication. R5 has Session Authentication which can be used to autologoff a user after a period of inactivity.

This servlet can also be written as a LotusScript agent which is run from a web page:
Print "Status: 401"
Print |WWW-Authenticate: Basic realm="/

TestRealm
"|
Print
Print "Logged out."


In R5, you can turn on session authentication and then use this URL: http://www.company.com/db.nsf?Logoff


How do you make user registration immediate?

There are several ways to do this. These techniques involve updating the index of the appropriate NAB which contains the newly registered users:

Open the Person Document View in the Browser
When you register a new Person document in the NAB, change the $$return variable to open the new person document with this:

  "[/names.nsf/($users)/" + @ReplaceSubString(fullname; " "; "+") + "]"

This will cause the index of that view to be updated so that the next username look up will work.

Run the updall Program
In the Terminate Event of the registration form, you can add the following LotusScript:

  Dim progname As String
  progname = "d:\notes\nupdall names.nsf"
  taskID = Shell (progname, SHELL_MAX_FOCUS)

You will have to adjust the updall name and path as appropriate for your system.

Do a View Lookup with the Notes API
Add this to your Declarations section:

  ' NAMELookup and OSMemFree are used in EnsureUserInNAB.
  ' NOTE: if you are running this app on a platform other than
  ' Windows 32 Intel, then you will have to change the name of the
  ' dll in these two declarations. Look in your Notes executable
  ' directory for *notes.dll to see what it is called, or for the proper
  ' form of shared library on your platform.

  Declare Sub NAMELookup Lib "nnotes.dll" _
  (Byval serverName As Integer, _
   Byval flags As Integer, _
   Byval numNameSpaces As Integer, _
   Byval nameSpaces As String, _
   Byval numNames As Integer, _
   Byval names As String, _
   Byval numItems As Integer, _
   Byval items As String, _
   rethBuffer As Long _
  )

  Declare Sub OSMemFree Lib "nnotes.dll" (Byval hBuffer As Long)

Now create a new sub:

  ' This sub is used to guarantee that the NAB view of users is up-to-date
after adding a new
  ' user. Domino 1.5 does not force the view to be up-to-date when
authenticating users, so
  ' we'll do it ourselves so the user can start working right away.

  Sub EnsureUserInNAB(Byval fullname As String)
    Dim hBuf As Long 
    Call NAMELookup(0, 0, 1, "$Users", 1, fullname, 1, "HTTPPassword", hBuf)
    Call OSMemFree(hBuf)
  End Sub

Then, once you have modified all of the documents in the NAB you are going to modify, call EnsureUserInNAB with the user's full name. The NAB will be up-to-date.
This technique is used in Lotus' Domino Registration sample.


How do you refer to CGI variables in a $$Query*Agent?

If you'd like to operate on a CGI variable in your agent, you must include a text field that is named the same as the CGI variable on your form. You can then use "Session.DocumentContext" in your $$QueryOpenAgent or $$QuerySaveAgent to access the CGI variable like so:

  Dim session As New NotesSession
  Dim context As NotesDocument
  Set context = session.DocumentContext
  Print "HTTP_User_Agent = " & context.HTTP_User_Agent(0)

This will display the web browser being used.


How do you delete a document by the UNID?

If you are using a $$QuerySaveAgent you can get the unid from the NotesSession.DocumentContext.UniversalID.
If you are running an agent with ?OpenAgent, append &UNID=docid to the end of the URL, where docid is either on the document that you are working with, or, is computed by Domino with @DocumentUniqueID and use the following as the delete agent:

Dim s As New NotesSession
Dim db As NotesDatabase
Dim tempdoc As NotesDocument
Dim realdoc As NotesDocument
Dim RealUNID As String
Dim readerlist As NotesItem
Dim templist As NotesItem

Set tempdoc = s.DocumentContext
Set db = s.CurrentDatabase
RealUNID$ = Right$(tempdoc.QUERY_STRING(0),32)
Set realdoc = db.GetDocumentByUNID(RealUNID$)

Call realdoc.remove(True)


How do you set up a field to autowrap when text is typed in a browser?

Add the HTML tag "WRAP=Virtual" in the help description of the rich text field.

You can also use the tags COLS="xx" and ROWS="yy" to set the width and height of the field.

For Notes 4.6 and above, put these tags in the HTML attributes for the field.


How would you secure a Domino server?

This is what KEY Enterprise Solutions normally recommends for its clients:

* Put the Domino server in a separate organization in case some of your databases have */org in the ACL, then cross certify the Domino server with internal servers
* Put a password on the Notes server ID in case someone manages to steal it off your site
* Encrypt all databases w/ the Notes server ID in case someone manages to steal your databases
* Set all databases to enforce local security in case someone manages to get the database and the server ID
* Turn off database browsing for web clients so people can't reach databases you didn't mean to publish
* Set all databases to Default No Access so only validated people can get into databases
* Activate SSL (whether self-certified or Verisign) to secure your network traffic to web clients
* Turn on network encryption when Notes clients talk to the Domino server over the Internet
* Protect all views you don't want accessed because web users can use the 3PaneUI parameter to see all your database views.
* If you are using the Domino server as a Notes server, put files you don't want Web users to access in a directory with a DirLink and disable Domino's DirLink support to allow Notes users to use the DirLinks but not Web users.

Also from GroupAware:
* Name your databases and URL's carefully. If users manage to go over hidden link or manage to browse your system, they may be able to guess what you are planning from URL names.


Can you automatically refresh when a keyword field is changed?

From hellerr@msnotes.wustl.edu:

Enable the Database Property "Web access:Use Javascript when generating pages". In your form, place an Action Button with the formula:
@Command([ViewRefreshFields])
This MUST be the FIRST action button, because this trick depends upon its position, which must not change.
Open the form from a browser.
View the Document(Page) source in your browser.
You will see something like this where the first button's code is:
<A HREF="javascript:_doClick('94bb0ce58b790fd086256713005f5721/$ACTIONS/0.1e')">
Copy it to the clipboard.
Now, for each keywords field that you want to refresh on a click (like selecting a radio button or checkbox), paste it into the HTML Attributes so the syntax looks like this:
"OnClick=\"_doClick(\'94bb0ce58b790fd086256713005f5721/$ACTIONS/0.1e\')\""
For fields that need to cause a refresh on change rather than click, change OnClick to say OnChange.
Now you can hide that Action button, but never remove it.

From michael.holmstrom@datavis.se

:

You can also call

  _doClick('$Refresh', this, '_self', '')

from the onChange event of a web field.

How do you prevent people from submitting a form multiple times?

By using a META tag, you can set the page to expire on a date that has already passed, and use the Pragma parameter to tell the browser not to cache the file. This will prevent the user from hitting the Back button on the browser and resubmitting the page multiple times. Put this code into the $$HTMLHead field:

"<META HTTP-EQUIV=\"expires\" content=\"Wednesday, 27-Dec-95 05:29:10 GMT\"><META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"

Note that this does not work w/ MSIE. MSIE needs an HTTP header to be set.


How do you add a Javascript button which validates the form and then submits it?

Your checkForm function should have the code to validate as well as submit. Your button should be of the type "button" and not submit. If the validation fails make an early exit from the function.

Add the following code in the $$HTMLHead field:

"<SCRIPT LANGUAGE=\"JavaScript\"> 
function checkForm() {
  if (document.forms[0].Name.value == \"\") {
      alert(\"You must enter your name\");
      return false;
  }
  document.forms[0].submit();
  return true;
}
</script>"

Add this code for a submit button as passthrough HTML or with the HTML style:

  <INPUT TYPE="Button" VALUE="Submit" onClick="checkForm(0)">

Now that you've added a special button, you'll have to remove the default one that Domino generates via the FAQ on how to remove the submit button.


How do you set a Cookie?

This is an example which you can place in a $$HTMLHead field:

"<META HTTP-EQUIV=\"SET-COOKIE\" CONTENT=\"cookiename=" + Cookievalue +"; path=/;
expires=Thu, 01-Jan-2009 12:00:00 GMT\">"


How do you make a field look like a password field?

If you put <INPUT TYPE=password> in the Help description of a field, the characters your user enters will be masked by the "*" character.

In Notes 4.6 and above, add "TYPE=password" to the HTML attributes of the field.


Why is tabbed table data lost when I switch to a different tab?

You have not enabled the "Generate HTML for all Fields" option in the Form Properties dialog box.


When do you have to pay for Client Access Licenses (CALs)?

This is a really confusing topic, even if you try to look up what it means to Microsoft IIS or Netscape or Groupwise. This is how I understand it (have a lawyer review Lotus' CAL documentation if you need to confirm):

The web CAL is only for direct intranet/extranet users, i.e. users who have a very close relationship with your company. Examples would be your company inventory app that distributors use to collaborate w/ you. Another example is if your employees acces their HR records over the web.

For Internet users who are logging in to participate in discussion forums on your site, there is no charge because they are not directly related to your company.

Note that most people think IIS is free. IIS itself is free, but usually you need a back end database since IIS does not have a built-in DB; this is where Microsoft gets you to pay more for buying and using SQL Server. For RDBMS CALs, the only way to get around them is to use one RDBMS login for everything and then use appropriate SELECT statements to mask data for each user. Once you start adding real DB username/passwords, you are also subject to CALs for the RDBMS. Note that you will be able to use this same technique in Notes R5 since it supports sessions.

Note: apparently, the CAL policy was changed sometime in March, 1999. The above description applies to CALs from last year. The current CAL policy as written seems to say that you need a CAL for any authenticated user (even if not an employee or distributor) unless you want to spend $25K on a Server Access License. This is patently stupid. This FAQ will be rewritten as soon as I can figure out what the new policy really means. Sorry for any confusion the previous FAQ may have caused. The direct link to Lotus' CAL information is http://www.lotus.com/dominocal.


What @Command do you use to delete the current document?
@Command([EditClear])

Can you automatically expand sections in documents?

You can use the ExpandSection argument after OpenDocument in a URL as follows:

  ...?OpenDocument&ExpandSection=1,3

This will expand the first and third section in the referenced document.


Can you change the Twistie graphics in Notes Views?

In the Icons directory for Domino, e.g., c:\notes\data\domino\icons, you will find the files expand.gif and collapse.gif. These are the GIFs used for the twisties displayed in Notes views for the web. Note that you will change the twisties for all views displayed on the web by changing these two files.


Are @PostedCommands Supported?

Domino treats @Commands and @PostedCommands the same way.


How do you access fields on a Parent document from an unsaved response doc?

Create a computed for display field named QUERY_STRING on your form with the formula "". You can then access any fields from the parent using this in a second, computed field:

  parent := @Word(@Right(@Uppercase(QUERY_STRING);"&ParentUNID=");"&";1);
  valueNow := @GetDocField(parent; "Field1");


How do you get rid of the margins in frames?

You can add
MARGINWIDTH="0" MARGINHEIGHT="0"
to the Other HTML property of the frame.


Why do you need Author access to do a search on a database?


When do a search from the web, you actually author a document (though the Save Options are set to '0'); you need the access rights to create the document. This is why anonymous users can't search a database where you have set Default and Anonymous access rights to No Access. There are several workarounds:

1) Allow Default and/or Anonymous to have Author access, but change the Form Properties on each form so that users with specific Roles can create the normal forms in the database.

2) Create a Search Site database where the default access is Author. You can run searches from this database without compromising the security on other databases.

3) Give Default access the ability to create Public Access documents. Then, set your Search Form so it is available to Public Access Users.

4) Hand code the query URL as suggested by Torben Moelgaard:
What actually happens when you fill out the web-form, is that a url like this is built by the browser:

  www.corp.com/db/view?searchview&query=apple+and+pie

And Domino then accepts that request and makes the search.
a. Create a form called Search.
b. Since you'll use the ?readform parameter to open the form Domino won't produce <form>...<input type=text/submit/etc> </form> tags, so you'll have to make those yourself.
c. Create an HTML input field called Input
<input type=text name=Input>

d. Make your own submit button with this code:
<input type=button onClick="location.href='http://www.corp.com/db.nsf/$defaultview?searchview&query=' +  document.forms[0].Input.value">

When you call it with http://.../db/Search?readform it will allow searhing with only reader-access.
You can also disable a return from submitting the form by adding the following passthrough HTML (thanks for this tip go to Kevin Pauli):
  <form onSubmit="return(false)"> ... </form>


Can you make a link that lets the user jump back two web pages?

There are two methods of doing this. You can use Javascript passthru HTML:

<SCRIPT language=javaScript>
  history.go(-2)
</SCRIPT>

Or you can use hidden HTML input fields:

On the first form, place a computed for display field named http_referer in the first form. Place another formula on this form named CreateBackStash with a formula of:

"["+"<input type=hidden name=BackStash value=\""+http_referer+"\">]"

On the second form, place an editable field called BackStash, but make this field hidden in Notes. In the $$Return field of this form, use BackStash as the URL you want to return to.


Can Domino forms be mail-enabled?

Yes. Make sure all the mail fields are set (To, etc.) in the form. Then in Form Properties, select the On Close Present Mail Dialog option. When Domino saves the form, it will mail the document.


Can you have a single URL that opens the user's mailbox?

This is from Ron Heller (hellerr@msnotes.wustl.edu):

Create a database that everyone connects to (let's call it getmail.nsf). Set the ACL -Default- and Anonymous to No Access, with all your users given Reader access (you can do this with a group or wildcard OUs) - so they must be authenticated when they hit the URL.

Set the Database Properties to "launch the designated doclink", which is a blank document (the only doc in the db) created with a form as follows:

Place a hidden Computed for Display field at the top with the name $$HTMLHead and give it the following formula (note that the column names are from the R4.5 mail template and may change in other templates):

mailFile := @If(
  @IsNotMember("$$WebClient"; @UserRoles);
  "";
  @ReplaceSubstring(@DbLookup("":"NoCache"; "":"names.nsf"; "($VIMPeople)"; @Name([Abbreviate]; @UserName); "MailFile"); "\\"; "/")
);
URL := @If(
  mailFile = "";
  "";
  @IsError(@UserAccess("" : mailFile));
  "";
  "/" + mailFile +
    @If(
      @Right(@LowerCase(mailFile); 4) = ".nsf";
      "";
      ".nsf") +
    @If(
      @IsError(@DbColumn(""; "" : mailFile; "($HeadlinesView)"; 1));
      "/$Inbox/?OpenView&Count=30&ResortDescending=2";
      "?OpenDatabase"
    )
);
@If(URL = "";
  "";
  "<TITLE>Web Mail Connect</TITLE><META HTTP-EQUIV=\"Refresh\" CONTENT=\"1;URL=" + URL + " \">"
)

Place a second, visible Computed for Display field on the form with this formula:
@If(
$$HTMLHead= "";
"Sorry: Mail file not found on this server.";
"Connecting to Mail File for " + @Name([CN]; @UserName) + @Repeat("."; 25))

When a user enters the URL http://myserver.xxx/getmail.nsf, they are prompted for UserName and Password and sent directly to their Inbox, sorted descending.


Another method using a button comes from Bill_Gordon@mgic.com:

1. Create a form anywhere and place a button on a form called My Mail with @Command([FileSave]) for the formula. Set the security to force login when accessing this document's URL or use &Login.

2. Place a hidden computed field on the form called SaveOptions and set the formula to "0" (zero in quotes). When the form is submitted it will never create a document.

3. Place a hidden computed field called MailName on the form and set its formula to @V3UserName.

4. Create an agent that is called from the WebQuerySave event of the form. The agent performs a lookup to get the user's mail file name from the name and address book, formats a return URL and uses Print to send it back to the browser. See example below:
Set db = session.CurrentDatabase
Set nabdb = New NotesDatabase( db.Server, "names.nsf" )
Set doc=session.DocumentContext
strng = "Form = 'Person' & FullName = '" & doc.MailName(0) & "'"
Set collection = nabdb.Search ( strng, dateTime, 0)
If collection.count = 0 Then
address = "[" & "https://servername/database.nsf/viewname/WebError?OpenDocument" & "]"
Else
Set persondoc = collection.GetFirstDocument
address = "[[" & persondoc.MailFile(0) & ".nsf]]"
End If
Print address

5. An error form, WebError, was also created in the database. If the logged in user does not have a mail database the error form is displayed.


How do you pass variables into a form via URL's?

In a URL, you can add ampersands (&'s) followed by parameters you wish to pass into a form (e.g., "http://www.site.com/db.nsf/8A8BB01ADEB05120852564180068A3F4?OpenForm&param1=7&param2=key1")

In the receiving form, create a variable called query_string. This field will be initialized by Domino to everything following the question mark in the URL (in the example above, this would be "OpenForm&param1=7&param2=key1"). In the default value formula for the field you want to check this field in, parse the value of query_string (e.g., to get the last parameter, use "@subset(@explode(query_string;"&");-1) " to get "param2=key1"). You can then do anything you want with the parameter.


Why do I get SSL errors when using MSIE?

When using MS Internet Explorer running with High Security, it will refuse to connect to sites using self-certified SSL certificates. You have two options:
1) Purchase a Verisign server ID from Verisign.
2) Set all your MSIE clients to use Medium Security so you just get warnings.


Can you change the look of the search input form?

Create a form named $$SearchTemplateDefaultForm and Domino will use this for its search input template.


Can views be shown to web clients with the Notes V4 UI?

To make web browsers access Notes views with the Notes V4 "multi-pane look", add "&3PaneUI" to any of your ?OpenView commands. For example:

db.nsf?OpenDatabase&3PaneUI

will open the database using the multi-pane Notes V4 UI.

For Notes 4.6, you also have to add DominoEnable3PaneUI=1 to your notes.ini file.


Can you get rid of Domino's tags?

If you add "<!-- " to the end of the value of the $$HTMLHead field, and add "--></HEAD>" at the very top of the form, you'll effectively comment out the Domino-generated <BODY> tag by making it look like a comment in the <HEAD> section..

It ends up looking like this:

  <!-- </HEAD><BODY>
  --></HEAD>

thus leaving you free to add your own <BODY> tag.


How do you make a Domino site searchable by web search engines

Most web search engines like Excite or AltaVista so not follow links with "?" because they think those are CGI links; Notes 5 allows you to replace the "?" with "!". If you are not using Notes 5, you can do the following:

Create a view named "Pages" that selects all documents that you want searchable, and first sorted column of the view contain each documents key value named "Key". The "Key" value can be @Text(@DocumentUniqueID).

If this is a 4.6 view, use the "treat as HTML" feature. In one of the columns, set the formula to something like this:
"<a href=\"Pages/" + Key + "\"">Page " + Key + " from MySite.com</a>"
If it is 4.5, you'll need to create the same <a>..</a> tag in a column, and surround it with "[" and "]" to make it passthru HTML.

Submit this view (the url "http://server/path/database/Pages") to the web search engine. The engine will traverse the view, follow all the links, and index your pages. However, if the database contains 1000 documents, but the web server is only set to display 50 at a time in views, only the first 50 will get indexed.

Here's a better way to do this from Niels Harremoes (niels.u.harremoes@dk.pwcglobal.com): use an agent named e.g. GenLinks to print a page with links to all documents and then point the search engine to http://server/path/database.nsf/GenLinks

If you already have a view containing all the documents you want indexed, you could also just use that view. This also has the advantage of not cluttering your database with another view:

Sub Initialize
  Const viewname = "(CrawlIndex)" 'A view displaying all the documents to be indexed - use a view alias if needed
  Dim ses As New NotesSession
  Dim view As NotesView
  Set view = ses.CurrentDatabase.GetView(viewname)
  Dim d As NotesDocument
  Set d = view.GetFirstDocument
  While Not d Is Nothing
    Print {<A HREF="} & viewname & {/} & d.UniversalID & {">} & d.Title(0) & {</A><BR>} & Chr(13)
    Set d = view.GetNextDocument(d)
  Wend
End Sub

Of course, generating the links in this way is a lot less efficient than having Domino generate a view - however, since the agent will only be called by the search engine once upon visiting the site, this shouldn't be a problem.

Other things to be aware of: Text inside collapsed sections won't get indexed, since the search engine won't go click the "expand" link. Attachments won't get indexed either. A workaround for that would be to expand the agent above to generate links to all attachments as well as to the document itself.


How do you add context-sensitive help on Domino web pages?

You can using this bit of Javascript which you can put in your $$HTMLHead field:

<script language="javascript:>
function showHelp() {
  var helpURL = "/[dbPath]/www-help/[keyName]";
  helpWindow = windowOpener( helpURL,"helpWindow");
}
</script>

and somewhere later:

<input type=button value="Help" onClick="showHelp()" >

where [dbPath] is a computed for display field with the following:

  db1 := @Subset(@DbName; -1);
  db2 := @ReplaceSubstring(db1; " "; "+" );
  @ReplaceSubstring(db2; "\\"; "/" )

and keyName is a computed for display field that resolves to a document name in the help view.


What companies will host Domino web applications for a fee?

In the United States:
* Strategic Systems Consulting in Atlanta, GA
* Interliant in Houston, TX
* PSR Software in Orlando, FL
* Going On in Lombard, IL
* Prominic.NET in Urbana, IL
* RiverWatch in Fort Mitchell, KY

In other countries:
* Right Source in Germany
* .scon - Siekmann Consulting in Germany
* Business Interactive in Canada


How do you open the About This Database document with a URL?

Use @URLOpen("/db.nsf/$About").
Similiarly, you can use @URLOpen("/db.nsf/$Help") to open the Using This Database document from the web.


How do you select multiple documents in a view and run an action button on them?

In your view have a column which creates a check box for the document. A passthru column formula like:

<input type=\"checkbox\" name=\"noteids\" value=\"" + @Text(@NoteID) + "\">

Note that for every document in the view, the check box will have the same Name, ie., "noteids".

Now next step is a $$ViewTemplate for this view. Let it start with a <FORM> tag created using passthru HTML:

<FORM METHOD="POST" ACTION="ProcessNotes?CreateDocument">

This is followed by $$ViewBody and then (using passthru):

<INPUT TYPE="Submit" Name="Action" Value="Action1">
<INPUT TYPE="Submit" Name="Action" Value="Action2">

... etc...
</FORM>

When the view comes up on the browser, the user will see a form with multiple Submit buttons, each corresponding to a certain action to be done on the documents selected through the check boxes.

For the next part of this, you will need to have corresponding fields in a dummy Notes form called "ProcessNotes". A field named "noteids" (text, multivalue, editable) and another by name "Action" (text, editable) and of course a $$QuerySaveAgent. You will get the NoteID of all the checked documents as a multivalue list in "noteids" and the action chosen by the user as a value in "Action" when you get into your QSA. You can use this code to iterate through the document list:

Dim ContextDoc As NotesDocument
Set ContextDoc = session.DocumentContext
ForAll NoteID in ContextDoc.noteids
  set Doc = Db.GetDocumentbyID(NoteID)
  ' Process the note..according to the value of ContextDoc.Action(0)
End ForAll


How do you dynamically open a single category view?

You can use the URL:
db.nsf/CatView?OpenView&RestrictToCategory=<categoryname>


How do you jump back to the current view from the current document?


You can use @ViewTitle to get the name of the current view and re-open it.
This is only needed if the document is in multiple views.


How do you handle field inheritance when composing from the Web?

In the URL, you must have a ParentUNID argument if you are generating your own URL links which use the OpenForm or CreateDocument commands. You also have to have the Inherit Fields Form Property activated.
An easier way to do this is to put @Command([Compose]; "<formname>") in an Action Hotspot where <formname> is the name of the form with inherited fields to compose.


How do you create a document access counter?

@function Technique
A computed-for-display field with the following formula will act as a counter for Notes and web access with a timestamp and keeping the counter by document id fields within a single profile document:

  profilename := "Page Usage Count";
  docid := @Text(@DocumentUniqueID);
  timestamp := @Now;
  count := @GetProfileField(profilename; "Count"+docid);
  @If(count=""; 
    @Do(@Set("count";1); @SetProfileField(profilename; "TimeStamp"+docid; timestamp)); 
    @Do(@Set("timestamp";@GetProfileField(profilename; "TimeStamp"+docid)); @Set("count"; count+1)));
  @SetProfileField(profilename; "Count"+docid; count);
  "This page has been accessed " + @Text(count) + " times since " + @Text(timestamp; "D0T1S2")


LotusScript Technique
Create an agent using this code:

Sub Initialize
 Dim Session As New NotesSession
 Dim ProfileDoc As NotesDocument
 Dim doc As NotesDocument
 Dim num As Double
 Dim NumStr As String

  Set db = Session.CurrentDatabase

 'The following line gets a handle to the current document
  Set doc = Session.DocumentContext

 'The following line creates a Profile document called 'Domino' the first time 'it is executed and from then on modifies the existing 'Domino' document
  Set ProfileDoc = db.GetProfileDocument("Domino")

  NumStr = ProfileDoc.num(0)
  If NumStr = "" Then
    num = 1
  Else
    num = Cdbl(NumStr) + 1
  End If

  ProfileDoc.num = Cstr(num)
  Call profiledoc.save(False,False)

  doc.Number = num

End Sub

Make the agent Shared and select "Run Once (@commands may be used)" for the option "Which document(s) should it act on". Name it whatever you desire (for example: "Counter") and save. Please be aware that Notes has a limit on the number of fields you can have in a form, so you cannot have an infinite number of these counters.

On the form you wish to keep the counter on, create a number field named Number. Also add a computed text field named $$QueryOpenAgent. As the formula for this field, put in quotes whatever you named your agent (for example: "Counter").


Does Domino support Server-Side Includes (SSI)?

Yes, but only for files with an extension of .shtml. If your files are named with an extension of .html or .htm, change the following line in your http.cnf file in your Notes data directory:

  imbeds on .shtml


Can you attach files to a document using a web browser?

This works only if the user is running Netscape Navigator. It will not work with Microsoft Internet Explore 3.0. This is not a deficiency in Domino; the original MSIE 3.0 and 3.01 don't support web uploads. MSIE 3.02 has a file upload add-on that does support uploads.

Create an action hotspot (Create/Action/ActionHotspot) on the form with a formula of @Command([EditInsertFileAttachment]). No field needs to be added; a field will be automatically generated by Domino after the hotspot. Domino will create a Notes V2 style attachment (attachment shows up at bottom of page after a horizontal rule) when the user does this.

In Notes 4.6 and above, you can create a FileUpload Web Element on the form. Unfortunately, Lotus didn't make 4.6 entirely backwards compatible with Notes 4.5. If you used the old file upload method and you have JavaScript generation enabled for the database in Notes 4.6, the old method doesn't work.


How do you make a button do a document refresh?

If you call the following JavaScript from the form, Domino will do a document refresh without saving the document first:

document.forms[0].__Click.value = '$Refresh';
document.forms[0].submit()  ; 

Note that you must have "Use Javascript when generating web pages" turned on in the DB Properties.


Can you control the HTTP headers for the output of an agent?

If Domino sees print statements with the proper formatting of HTTP headers, it will output them and override the default <HTML><TITLE><BODY> tags that are emitted by Domino. E.g. you can use this to control all the HTML that is emitted by the agent:

  Print "Content-Type:text/plain"
  Print "Content-Type:text/html"
  Print 
  Print "<HTML>"


How do you re-enable WebSphere integration after a Domino upgrade?

From Bob Balaban:

Add these lines to the end of the httpd.cnf file:

Service /cgi-bin/htimage* INTERNAL:HTImage*
Service /cgi-bin/imagemap* INTERNAL:HTImage*
Service /*.jhtml d:\WebSphere\plugins\nt\go46.dll:service_exit
Service /*.shtml d:\WebSphere\plugins\nt\go46.dll:service_exit
Service /*.jsp d:\WebSphere\plugins\nt\go46.dll:service_exit
Service /servlet/* d:\WebSphere\plugins\nt\go46.dll:service_exit

pass /ibmwebas/samples/* d:\websphere\samples\*
pass /ibmwebas/doc/* d:\websphere\doc\*
pass /ibmwebas/system/admin/* d:\websphere\system\admin\*
pass /ibmwebas/* d:\websphere\web\*

ServerInit d:\WebSphere\plugins\nt\go46.dll:init_exit d:\WebSphere\properties\bootstrap.properties
ServerTerm d:\WebSphere\plugins\nt\go46.dll:term_exit


Where is the API for the View and RTFapplets?

The Waterworks site has some unofficial documentation on this.


How do you log out a user and jump to a particular URL?

This only works if you have session authentication turned on. You can ues the redirectTo argument to the ?logout command, like so:

http://www.company.com/dbname.nsf?logout&redirectTo=http://www.company.com/WelcomePage


How do you hide attachments uploaded via the web upload file field?

Add a field named "$V2AttachmentOptions" vith the value "0" (text zero instead of numerical zero) to your form.


How do you take full control of HTML generation from Domino?

From Andre Guirard:

Agents
The first thing the agent should do is:

  Print "Content-Type: text/plain
"
  Print "Content-Type: text/html"
  Print
Everything else you output from the agent is under your control.

Forms and Pages
Selecting the "Treat contents as HTML" form option will disable generation of any headers by Domino.

Views
Embed the view in a page that has "Treat contents as HTML" option selected. Select HTML display for the embedded view.


How do you change the twistie graphics?

Until Domino provides this internally, you can use JavaScript to do this. Create a $$ViewTemplate and add this code so it runs after the view page is loaded:

<SCRIPT LANGUAGE=JavaScript>
  var i, nullcount, expand, collapse; 

  expand = new Image();
  expand.src = '/icons/expview.gif'; // place URL to your image for expand (but don't name it expand.gif)
  collapse = new Image(); 
  collapse.src = '/icons/colview.gif'; // place URL to your image for collapse (but don't name it collapse.gif)

  nullcount = 1; // initialize nullcount to non-zero so it enters the while loop.

   // while loop tests if all the images are loaded by the browser
  while (nullcount) {
     // Reset nullcount which counts number of images remaining to download.
    nullcount = 0;
     // document.images is collection of all images (<IMG> tags) on the document
     // it has length attribute representing the image count.
    for (i = 0;  i < document.images.length;  i++) {
      if (document.images[i].src == null)   // src is property storing image url
        nullcount++;
      else {
        if (document.images[i].src.indexOf('expand.gif') > 0)        // checks expand
          document.images[i].src = expand.src; 
        else if (document.images[i].src.indexOf('collapse.gif') > 0) // checks collapse
          document.images[i].src = collapse.src;
      }
    }
  }
</SCRIPT> 


How do you check for the browser type using JavaScript?

The Ultimate Javascript Client Sniffer from Netscape is the answer to your wishes.


How do you remove the Domino version number from the generated HTML?

If you are running R5.02 or above, you can add this to your notes.ini file:

  DominoNoBanner=1


How do you get the current user name in LotusScript?

You can use this to get the current web user's Domino user name:

  Dim session As New NotesSession
  Dim username As String
  username = session.EffectiveUserName

You do have to have the 'Run Agent As Web User' agent property checked for this to work.


Can you have custom login forms per database?

Normal custom login forms only work on a per-server basis. Mika Heinonen came up with this interesting workaround (you have to use a unique login URL per application):

You can make a custom login form which displays different subforms for each application.
The main $$LoginUserForm needs to have only a computed for display Query_String field with the value "", and a computed subform with the formula:

  QS := @UpperCase(query_string);
  appname := @Word(QS; "&APP="; 2);
  @If
    (appname = "APP1" : "APP2" : "APP3";
     appname;
    "DefaultLoginSubForm");

Now you can have subforms APP1, APP2, APP3 and a default subform ("DefaultLoginSubForm"), if no application was defined in the login url line.

For example, if you want to launch the login form for application2, you open the url:

  http://yourserver.com/app2.nsf?Login&app=app2 


How do you create a DHTML Calendar Control?

This was covered in Lotus Notes & Domino Advisor, but the other has continued to update the code on his web site.


How do you make onSubmit work with keyword refresh?

If you turn on "Use Javascript when generating pages" option of the database properties, you'll find that the onSubmit code gets invoked every time the user changes a keyword field that is set to refresh on change.

Put all your validation code into a function in the JSHeader event like this:

function validateForm()
{
  var f = document.forms[0]
  if (f.Description.value == "") {
    alert ("Please enter a description.");
    f.Description.focus();
    return false;
  }
  return true;
}

Then, change the onClick action of the submit button on your web form to call this function before submitting the form:

  @URLOpen("javascript:if (validateForm())  document.forms[0].submit()")


Can you launch the Notes client from a web page?

You can use this syntax to launch a specific document into your Notes client when a user clicks on the link from a web page:

  notes://server/database/view/docid

If you want to use the Default view, use "0" (zero) for the view part of the URL.


How do you display a list of response docs on the parent document?

You can display a list of response documents on a parent document on the web by using this simple technique:

1) create a view of your response documents which is categorized by the $REF field
2) embed this view in the form for the parent document, but use the single category feature with the formula:
@Text(@DocumentUniqueID)

This also works for Notes applications.


Why won't views/richtext display using Java applets in MSIE?

If you installed Sun's latest Java Plug-In (Java 1.3 or Java 1.4), there's a known problem with R5. R5 sees that you're running MSIE and sends down a version of the applet that runs only on MS' JVM (not on the Sun one), but the applet is run in the Sun JVM which doesn't understand how to unpack MS' weird .zip files. To work around this, you'll have to turn off the use of the Sun Java Plug-In by doing this in MSIE:

1) Choose Tools\InternetOptions from the Menu
2) Go to the Advanced tab
3) Scroll down to "Java (Sun)"
4) Uncheck "Use Java 2 1.x for <applet> (requires restart)"

This has been fixed in R6 so that R6 will send down a .jar file to the Sun Java Plug-In.


How do you get rid of the ecblank.gif in generated HTML?
Unfortunately, there is no easy workaround for this.

For Embedded Outlines, Brad Nelson suggested using this code in your JSHeader and calling it from the onLoad event:

function fixecblank() {
  lg = document.images.length;
  for (x = 0; x < lg; x++) {
    isrc = document.images[x].src;
    i = isrc.indexOf("ecblank");
    if (i != -1) {
      if ((document.images[x].width==20) || (document.images[x].width==16)) {
        document.images[x].width=0;
      }
    }
  }
}

but it only works for browsers which support modifying the size of graphics on the fly. This includes MSIE and Mozilla, but not Netscape 4.x.

For tables, you can get rid of it by turning off fixed width tables.

For views, turning off Show Response Docs in a Hierarchy helps get rid of it. The only other way to get rid of ecblank.gif is to create the HTML for views yourself by turning on the Treat View Contents As HTML property.

How do you switch tabs in tabbed tables?

Create a programmable table (enable Switch Rows Programmatically) and name it "MyTable".
Go to each row of your table and name it in the Row Tags part of the properties; name them "Tab1", "Tab2", etc. for each of the "tabs".
Create an editable text field named "$MyTable" and hide this field from web browsers.
Enable "Generate HTML for All Fields" for the form.
You can then add clickable form actions to the form that do:
@Command([ViewRefreshFields]);
@SetField("$MyTable";"Tab2");
@Command([RefreshHideFormulas])
and Domino will generate the appropriate JavaScript for you.

You should also be able to do this using pure JavaScript by doing:
document.forms[0].$Mytable.value="tab1";
document.forms[0]._doClick('$Refresh', this, null);

If you want to jump to a specific spot on the form, add an Anchor via passthru HTML that looks like this:
<a name=MyAnchor></a>
then call the _doClick function like this:

document.forms[0]._doClick('$Refresh', this, '_self', '#MyAnchor');

Some of this is documented in Lotus Technote #173543 and there is a sample in the LDD Sandbox.


How do you handle "Allow Values Not In List" dialogs on the web?
You'll have to remove the checkbox for Allow New Values for dialog fields so the choices show up on the web.

If you don't want to use JavaScript, you can add an "Other" field (plain text input field), then in the input translation formula for the main dialog field, append this Other field if it is not empty.

If you can use JavaScript (thanks to Michael Holmstrom):
- add a "var frm;" to the forms JS Header-event
- add a "frm=window.document.forms[0];" to the forms onLoad-event
- add a button "New value" with the following JS-code:

var response= prompt("Please add your new value",'');

if (response !=null) {
var i=frm.yourField.length;
frm.yourField.length++;
frm.yourField.options[i].value=response;
frm.yourField.options[i].text=response;
frm.yourField.options[i].selected = true; }


How do you get the relative path to the database for web URLs?

You can use:
nsfpath := @ReplaceSubstring(@Subset(@DbName; -1); "\\"; "/");

In R6+, you can just use @WebDbName.


Can You Do AJAX Applications with Domino?
Richard Schwartz has created a good collections of examples of this (including a link to a nice Name Picker example):

http://www.rhs.com/poweroftheschwartz/htdocs/LotusDominoAjax.htm


Can You Run PHP Pages on a Domino Server?
You can install Apache with PHP and get them to run site by side on the same server. Mike Golding has written a nice article on how to do this.

How do you make a database open a form by default from the web?

Thanks to Carl Tyler:

Create a Navigator, call it WebNavigation it can be totally empty, as it doesn't really get used.
Create a form with the contents you want called $$NavigatorTemplateDefault
Now set the web launch options for the database to open the navigator WebNavigation and it will use the form $$NavigatorTemplateDefault


Computer Based Training

The following companies make Lotus Notes CBT courseware:
* Self Test Software (770) 971-8940
* Lotus (800)782-7876
* CBT Systems
* ReCor
* Professor 3T (end-user training)
* Headlight.com resells a few Notes courses

* TLCC's R5 Developer courses including web development, Java, LotusScript and JavaScript (free demo course available)
* TLCC's User courses for Release 5 and 6 (free demo course available)
* Get re-certified as a Release 6 CLP with TLCC's Update course
* TLCC's Notes Domino 6 courses
* TLCC's Domino Admin 6 courses cover setup of Domino and find tuning the Domino infrastructure
* Learn WebSphere and Java development with TLCC's WebSphere curriculum
* TLCC has Notes/Domino 7 courses for developers and administrators; they cover using DB2 with Domino


Virtual Classes via the Internet

Virtual Training Center
Distance learning courses for Notes users, developers, and administrators. Courses available for R4, R5, R6, R7, Notes and Domino development, LotusScript, Websphere, Java, and JavaScript. Try a free demonstration course at TLCC's web site.

Ives Development (the makers of TeamStudio) offer a web-based best practices training course for Lotus Notes and Domino developers called "TeamStudio LifeCycle".


Lotus Education Helpline
The Lotus Education Helpline has an Exam Guide for Lotus Notes; the number is (800)346-6409. Lotus Fax Support also has sample exam questions at 617-253-9150.

Notes Utilities

John Buckman's Lotus Notes Phone Dialer
[ftp] Alert! New Document Notifier
PutNote and GetNote


Notes Related Programs or Information

* [ftp] The LNOTES-L Archive
* [ftp] OS/2 Netware Requester Software
* [ftp] Lotus Internet Cookbook (.NSF format)
* [ftp] Notes Database Templates from Lotus
* [ftp] Lotus Notes Mobile Survival Kit


OfficeVision and SNADS

DGA Ltd. has a set of tools for migration and co-existence of SNADS-compatible mail systems, OfficeVision and DisplayWrite.


PalmPilot

GlobalWare has a product called Pylon Conduit which does C&S and email sync w/ the PalmPilot. They also have a product called Pylon Pro that can even convert some Notes applications to run on the PalmPilot.


Puma Technology has IntelliSync.

Lotus has EasySync but the other products have more features.

Cadenza from CommonTime can synchronize mail, calendar, and contact info between Notes and a PalmOS device.


FAX

There are quite a few FAX gateways for Notes/Domino. All of these allow a user to e-mail a document to a FAX number.

Lotus' Domino Fax Server
Extracomm's ExtraFax
GFI Communication's Faxmaker for SMTP
Biscom's Fax for Notes
OMTOOL's Fax Sr.
Softlinx' Replix dominoFAX
V-Systems' VSI-FAX
Captaris' RightFAX
Redrock's FaxNow!


POP3

POP3Fido from KEY Enterprise Solutions is an integrated server task that delivers mail to Notes users from POP3 mailboxes. A 45 day trial version is available.

Pytheas' MailGate is a standalone POP3/SMTP gateway that works w/ Notes and Exchange.


Migration Tools

cc:Mail (including DB8 support)
Creative Business Solutions has a cc:Mail migration tool that is more updated than Lotus'.
Lotus' Migration Tools now support DB8.

Outlook
Binary Tree has a tool which migrates mail, folder structure, appointments and meetings (including invitees and attendees), tasks, and notes.

Exchange
OpenOne has a tool called Direct-TO-1 that lets email be migrated between Notes, Exchange, and a few other email systems.


MS Exchange <-> Lotus Notes

Linkage's Exchange-Notes Connector synchronizes directories, databases, and lets users send mail to each other while preserving attachments and rich-text formatting. Microsoft has bought Linkage; they bundle it for free with Exchange 5.5 and above.

MESA's JumpStart currently has does MS Exchange public folder to Notes database synchronization. They plan to have application and mail synchronization in the future.

Casahl's RA.GroupwareLink synchronizes Notes databases and MS Exchange folders.

Binary Tree's MailPort for MS Exchange migrates, mail, C&S schedules, and contacts to Notes.


NetTalk (X.500, LDAP, IMAP, Exchange, cc:Mail)

NetTalk is WorldTalk's next generation messaging backbone. It has connectors for Notes, Exchange, and cc:Mail. It is also a full-blown mail server with POP3, IMAP, and X.500 LDAP support.


Windows CE/Pocket PC/Windows Mobile

Cadenza from CommonTime can synchronize mail, calendar, and contact info between Notes and a WinCE/PPC/WM device; their mForms product also lets you do custom form application on this platform.


Notes <-> Rex Organizer

A freeware utility is provided by MorphaSys to let you sync your Notes address book and contact information and your most recent email with your Rex Organizer.


Outlook to Domino
Microsoft has released a free Outlook 2002/2003 connector so it can be used with Domino 5.x/6.x servers. It also lets you access your calendar, address book, and to-do list.

Lotus' Domino Access for MS Outlook lets you also use Outlook 2000 and replicate your mail onto your system.


LNotes-L

NOTE: this mailing list is dead as of 4/15/2007


The LNotes-L mailing list is maintained by [email] Joe Ashkar. It is a Listserver based email list for Notes programmers and administrators.

Contacting the Administrator
The administrator is Joe Ashkar ([email] lnotes-l@ashcom.net). Email him w/ any administration problems.

Subscribing
Send a message to [email] listproc@ozzie.notesnic.net. In the body of the letter, enter SUBSCRIBE LNOTES-L <your-name>. You will then be automatically added to the list.
To subscribe an address other than the one you are sending from, send a message to [email] lnotes-l-mgr@ozzie.notesnic.net. In the body of the letter, enter SUBSCRIBE LNOTES-L ADDRESS. Replace ADDRESS with the address to send messages to. Approval generally occurrs within one day.

Unsubscribing
Send a message to
[email] listproc@ozzie.notesnic.net. In the body of the letter, enter SIGNOFF LNOTES-L. You will then be deleted from the list.
To unsubscribe an address other than the one you are sending from, send a message to [email] lnotes-l-mgr@ozzie.notesnic.net. In the body of the letter, enter UNSUBSCRIBE LNOTES-L ADDRESS. Replace ADDRESS with the address you wish to unsubscribe. Approval generally occurrs within one day.

Sending Mail to the Mailing List
Address your message to [email] lnotes-l@ozzie.notesnic.net. Write your message and send it. It will be automatically distributed to the members of the list.
Please make sure that you don't use Return Receipt Requested or Carbon Copy back to the list!

Temporarily Shutting Off Mail Delivery
If you don't want to receive messages for an extended period of time (e.g., you go on vacation), but want to reserve your place in the queue, send a message to [email] listproc@ozzie.notesnic.net with a body of SET LNOTES-L MAIL POSTPONE. When you return, send a message to [email] listproc@ozzie.notesnic.net with a body of SET LNOTES-L MAIL ACK.

Getting All Messages from a Specific Day
Send a message to [email] listproc@ozzie.notesnic.net with a body of GET LNOTES-L <yymmdd>.

Searching for a Specific Topic
Send a message to [email] listproc@ozzie.notesnic.net with a body of SEARCH LNOTES-L <topic>.

Threaded Archives
Turtle's Weightless Dog site archives the list.
Connectria archives the list.


DominoLinux

DominoLinux is an open, unmoderated Internet mailing list about running the Lotus Domino Server on Linux.

Domino for Linux is an important development for the Domino community. Lotus's impending release of Domino for the popular Open Source platform is sure to introduce many people to Linux, as well as give existing Linux users access to a robust and scalable application server.

DominoLinux will be very focused on using Domino on the Linux operating system, so general questions about Domino and the Lotus Notes client are probably handled on the general Notes/Domino lists such as Domino-L (see http://www.nipltd.com/domino-l.htm) and LNOTES-L.

Archives of the list and other information is available at http://www.nipltd.com/dominolinux.htm

To join DominoLinux, send an email to

  join-dominolinux@lyris.nipltd.com

The content of your email is not important.

Mail to the list itself should be sent to dominolinux@lyris.nipltd.com

For assistance etc. please mail owner-dominolinux@lyris.nipltd.com


Oklahoma State Notes-L

Note: this mailing list is dead as of 4/15/2007

This is a Listserver mailing list that is not quite as popular as LNOTES-L, but it is nearly always operational.

Subscribing
To subscribe, send a mail message to:
LISTSERV@LISTSERV.OKSTATE.EDU.
Leave the subject line blank, and include on the first line of the message:
SUB NOTES-L first_name last_name

Unsubscribing
To unsubscribe, send the command UNSUBSCRIBE NOTES-L in e-mail to LISTSERV@listserv.okstate.edu.

Posting Articles
Send all articles to NOTES-L@listserv.okstate.edu.

Digest Mode
It is possible to receive the contents of this list as a "digest", a periodic collection of articles from the list traffic. After receiving your subscription confirmation, send the command SET NOTES-L DIGEST to
LISTSERV@listserv.okstate.edu.

Administrative Requests
Send all other list-related commands to LISTSERV@listserv.okstate.edu. For assistance, send the command HELP.
The list owners are: [email] James Alexander and [email] Konrad Brandemuhl.

Archives
This list has a threaded archive accessible by web browsers.


NotesMac

NOTESMTA is an open, unmoderated Internet mailing list for users and administrators of Macs running Lotus Notes/Domino.


Archives
Archives of the list, a web interface to the list server, and other information is available at http://lists.nipltd.com/cgi-bin/lyris.pl?enter=notesmac


Subscribing
To subscribe to NOTESMTA-L, send a regular email message to:

  lyris@lyris.nipltd.com

and in the body of the note, type:
  subscribe notesmac <your name>


Unsubscribing
To unsubscribe from the list, send a message to lyris@lyris.nipltd.com with the body "unsubscribe notesmac" (without the quotes).


Posting
To post to the list, send a message to notesmac@lyris.nipltd.com.


Oklahoma State Domino-L

Subscribing
Send an email message with "subscribe DOMINO-L" in the body to LISTSERV@LISTSERV.OKSTATE.EDU.

Unsubscribing
Send an email message with "signoff DOMINO-L" in the body to LISTSERV@LISTSERV.OKSTATE.EDU.

Sending messages to the list
Send mail to DOMINO-L@LISTSERV.OKSTATE.EDU.

Archives
This list has a threaded archive for access by web browsers.


NotesMTA-L

NOTESMTA-L is an open, unmoderated Internet mailing list for administrators of MTAs and Mail gateways between Lotus Notes/Domino and other mail systems. The discussion focusses on the issues surrounding connecting Notes/Domino to other mail systems (for example the Internet, X.400, cc:Mail) using MTAs and mail gateways. Typical topics are the choice, configuration, and use of these gateways, as well as problems created by the interaction of different mailing systems.


Archives
Archives of the list, a web interface to the list server, and other information is available at http://www.nipltd.com/notesmta-l.htm


Subscribing
To subscribe to NOTESMTA-L, send a regular email message to:

  lyris@lyris.nipltd.com

and in the body of the note, type:
  subscribe notesmta-l <your name>


Unsubscribing
To unsubscribe from the list, send a message to lyris@lyris.nipltd.com with the body "UNSUBSCRIBE NOTESMTA-L" (without the quotes).


Posting
To post to the list, send a message to notesmta-l@lyris.nipltd.com.


Portugese Notes Users Mailing List
To subscribe: "lotusnotes-br-subscribe@yahoogrupos.com.br"

David Stephens' Lotus Product/Event News

This is a Lotus support person that puts out great newsletters to keep his customers in touch with various Lotus info:
Send an e-mail to david_stephens@us.ibm.com with the subject line "Subscribe Lotus Product and Event News" (remove the quotes).

LNotes-J
This mailing list for Japanese Notes users. You can subscribe here:
http://www.iij-mc.co.jp/MLOnline/IIJ/98/lnotes-j.html

And there are archives here:
http://www.uisystem.co.jp/


Yahoo LNotes-L Group
This list is a replacement for the recently deceased LNOTES-L mailing list:

http://tech.groups.yahoo.com/group/lnotes-l-new/


Domino on iSeries (AS/400)
This is the Lotus Domino on the iSeries / AS400 (Domino400) mailing list
To post a message email: Domino400@midrange.com
To subscribe, unsubscribe, or change list options,
visit: http://lists.midrange.com/mailman/listinfo/domino400
or email: Domino400-request@midrange.com
Before posting, please take a moment to review the archives at http://archive.midrange.com/domino400.

LNotes-L on Yahoo

Subscribing
You can get more info on the web site for this user group:
http://tech.groups.yahoo.com/group/lnotes-l-new/


How do you start Notes automatically on bootup in Solaris?

Here is a script you can invoke from /etc/init.d, /etc/rc3.d, or /etc/rc0.d:

#!/bin/sh
#
# agpeu * 09.04.97 * 25.07.97
#

state=$1
serverdir=${serverdir:-/data/lotus/server45}
clientdir=${clientdir:-/data/lotus/client45}
logdir=${logdir:-/data/lotus/log}
logfile=${logfile:-${logdir}/`date +%y%m%d`.log}
logfile=${logfile:-/dev/null}

domlog=${domlog:-${logdir}/domino.log}
serverlock=${serverdir}/server.lock

LOTUSDIR=${LOTUSDIR:-/vol/lotus/domino45}
export LOTUSDIR

PATH=.:/data/lotus/server45:/vol/lotus/domino45/bin:/usr/bin:/usr/openwin/bin:/usr/dt/bin:/vol/gnu/bin
export PATH

case $state in

'debug')
# Zur Anzeige der aktuellen Variablen
echo serverdir $serverdir
echo clientdir $clientdir
echo logdir $logdir
echo logfile $logfile
echo PAHT $PATH
echo LOTUSDIR $LOTUSDIR
;;

'watch')
# bringt den aktuellen LOG auf den Bildschirm
if [ -f $logfile ] ; then
echo Logfile $logfile
tail -f $logfile
else
echo Logfile $logfile ist keine Datei
fi
;;

'console')
# Starten des Notes-Clients als Server-Console im SERVER directory
cd $serverdir
echo Server Console starten `date +%y%m%d-%H%M%S`  >> $logfile
notes &
;;

'start')
# Starten des Notes Servers
cd $serverdir
if [ -f $serverlock ]
then echo Notes Server already running
else
touch $serverlock
echo "Notes Server start " `date +%y%m%d-%H%M%S`  >> $logfile
server start >> $logfile &
echo "Notes Server start " `date +%y%m%d-%H%M%S`  >> $domlog
fi
;;

'stop')
# Runterfahren des Notes Servers
cd $serverdir
if [ -f $serverlock ]
then
echo Notes Server stop   `date +%y%m%d-%H%M%S` >> $logfile
server -q >> $logfile
if [ -f *.TMP ] ; then rm *.TMP ; fi
echo Notes Server stoped `date +%y%m%d-%H%M%S` >> $logfile
echo Notes Server stoped `date +%y%m%d-%H%M%S` >> $domlog
rm $serverlock
else
echo Notes Server not running
fi
;;

'client')
# Starten des Notes-Clients im CLIENT directory
cd $clientdir
if [ -w ./notes.ini]
then notes &
else echo "Notes Client nicht installiert"
fi
;;

'kill')
# kill -9 aller Notes Server Prozesse
# Suchen und Sichern eines Core-Files
if [ -f $serverlock ] ; then rm $serverlock ; fi
ps=`ps -ef -o pid -o ppid -o stime -o args | grep /vol/lotus/domino | grep -v
grep `
if [ -n "$ps" ] ; then
echo "$ps"
echo Notes Server KILL    `date +%y%m%d-%H%M%S` >> $logfile
echo "$ps" >> $logfile
#   echo Directory >> $logfile
#   find $serverdir -name "*.n?f" -exec ls -l {} \; >> $logfile
#   echo [END] >> $logfile
if [ -f $serverdir/core ] ; then
mv $serverdir/core $logdir/core.`date +%y%m%d-%H%M%S`
fi
echo  kill -9 `ps -ef -o pid -o args | grep /vol/lotus/domino | cut -c1-6 `
kill -9 `ps -ef -o pid -o args | grep /vol/lotus/domino | cut -c1-6 `
echo Notes Server killed `date +%y%m%d-%H%M%S` >> $domlog
fi
;;

'check')
# Suchen und Sichern eines Core-Files
# Liste aller Prozesse
# Liste aller Datenbanken und -schablonen
if [ -f $serverlock ]
then echo Notes Server running
else echo Notes Server NOT running
fi

if [ -f $serverdir/core ]
then
echo "## core dump"
ls -l $serverdir/core
mv $serverdir/core $logdir/core.`date +%y%m%d-%H%M%S`
else
echo "## no core found"
fi

echo "## "
echo "## Processes"
ps -ef -o pid -o ppid -o stime -o args | grep /vol/lotus/domino | grep -v grep

echo "## "
echo "## Databasefiles and -templates"
find $serverdir -name "*.n?f" -exec ls -lL {} \;

echo "## "
;;

'clean')
# Loeschen der Log- und Corefiles die aelter als 7 Tage sind.
find $logdir  -type f -mtime +7 -exec rm {} \;
;;

*)
echo `basename $0` "[start | stop | kill | check | client | debug ]"
;;
esac

How do you set up a Digiboard under OS/2?
The following configuration setup is only a guideline. The memory address settings may very. Take the following steps if installing the Digiboard EISA board into a Compaq EISA system:
Set the 1-8 DIP Switchbox to have all switches set to OFF
Set the 1-11 Dip Switch Box to have 1, 3, and 4 set to OFF and the rest ON (This sets Memory Start Address for 0D000h and I/O Port Address for 320h)
Put the Digibaord in an empty slot on the Motherboard
Close the case on the Computer

Place the COMPAQ System Configuration Diskette in a drive
Turn on Computer
Press any key at the "Compaq System Configuration" screen
If an "Autoconfigure" screen appears, select Yes to have the computer autoconfigure the system. If the "Autoconfigure" screen does not appear and a "Welcome" screen does, press ENTER
If you selected Autoconfigure, select OK at the "Configuration" screen. If no "Autoconfigure" screen appeared, select Configure Computer from the Main Menu and press ENTER (this will process for a few minutes)
Select Step 2: Add or Remove boards and press ENTER
Select the slot number for which you entered the DigiBoard and press ENTER
Select Non-COMPAQ board and press ENTER
Place the NON-COMPAQ Option Configuration Files diskette in the drive and press ENTER
Select the Digiboard Digichannel PC/8e (or equivalent) from the Add Configuration (CFG) File screen and press ENTER
Insert the System Configuration diskette in the drive and press ENTER
Press ENTER at the "Add Confirmation" pop-up
Select the slot number for which you entered the DigiBoard and press ENTER
Confirm that the DigiBaord appears in the Slot and press ENTER (Add-Insert)
Press Esc
Press F10
Select Step 3: View or Edit Details and press ENTER
Page Down until you see the DigiBoard information
Highlight the DOS or OS/2 line and press ENTER
Select Available Resources Options and press ENTER
Press ENTER
Press F6 for Edit Resources
Press the plus (+) key until the I/O Port Address equals 320h (or whatever you set the board to in Step 2)
Press F10
Press F10 again
Select Step 5: Save & Exit and press ENTER
Select Save the configuration and restart the computer and press ENTER
If you would like to Install OS/2 at this time, insert the OS/2 Installation diskette at the "Reboot" screen and press ENTER (skip Step 1 of Installing OS/2 2.1). If you do not want to install os/2 at this time, turn the computer off at the "Reboot" screen.
Installing DigiBoard Software:
Go to the OS/2 Full Screen prompt
Type MD DIGI and press ENTER
Type CD DIGI and press ENTER
Insert the Digiboard Diskette in the drive
Type COPY A:\OS2\*.* and press ENTER
Editing the CONFIG.SYS
Go to the OS/2 Full Screen prompt
Type E CONFIG.SYS and press ENTER
Go to the end of the line that says "DEVICE=C:\OS2\MDOS\VWIN.SYS" and press ENTER
Type DEVICE=C:\DIGI\XALL.SYS /n:3 /a:q /b:57600 /d:N8 1 /p:320 /m:D0000
Save the CONFIG.SYS by pressing Alt-F4 and selecting Save

How do you change the new mail sound on a Mac?

You can use ResEdit to change the "New mail Sound" resource in the Notes executable:

1) Make a copy of your Notes program.
2) First convert your gotmail.wav (if you have a .wav file) to a gotmail.snd.
3) Open the gotmail.snd with ResEdit.
4) Open the Notes program with ResEdit.
5) Open the snd resource group of Notes.
6) Find the "New mail Sound" snd resource in Notes, make a note of its resource ID (should be 9000).
7) Delete this resource.
8) Copy the gotmail snd resource from the gotmail.snd file.
9) Paste it into Notes.
10) Rename it to "New mail Sound", renumber it to the correct ID number.
11) Save Notes.


How do you set up server proxying through MS Proxy Server?

From Mark Kaynor (mkaynor@daa.com)

I recently spent an inordinate amount of time setting up server proxying on MS Proxy Server 2.0 such that a Domino server or Notes client outside the proxy (the "external server") could replicate w/ and route Notes email to a Domino server inside the proxy (the "internal server"). Information was sparse and I had to glean bits and pieces from several, sometimes conflicting, sources of
information. I could have saved myself hours of frustration had I been able to find a document similar to this. No guarantees, but this should get one pointed in the right direction.


If problems occur, don't forget to check the event log on the internal server - the proxy client writes messages to the event log which can be very helpful during configuration and troubleshooting. BTW - both Domino servers are running v4.6.4 on NT 4.0, SP5., the proxy server in running on NT 4.0, SP3 w/ roll-up hotfix and Option Pack 4.

1) Obtain and install the Proxy Server hotfix - make sure to compare installed files date and size w/ those listed in the supplied "readme.txt" file after installation. MS tech support told me that setting up server proxying was pretty much futile w/o the hotfix. The file emailed to me was:

     prxyfixpacki.zip         308,819 bytes        6/28/99 11:45a

2) To simplify the initial setup, temporarily disable Winsock Proxy Access Control if enabled. Clear the WinSock Proxy | Properties | Permissions | "Enable access control" checkbox.

3) Also temporarily disable Packet Filtering if enabled. Clear the WinSock Proxy | Properties | Service | Security | "Enable packet
filtering on external interface" checkbox.

4) Set proxy client configuration to connect to proxy via IP address. Select WinSock Proxy | Properties | Service | Client Configuration | Clients connect to Microsoft WinSock Proxy Server by | "IP Address" checkbox.

5) On the proxy server, disable default gateway and IP forwarding on internal NIC. Clear Control Panel | Network | Protocols | TCP/IP Protocol | IP Address | "Default Gateway" entry. Clear Control Panel | Network | Protocols | TCP/IP Protocol | Routing |
"Enable IP forwarding" checkbox.

6) Add port 1352 to the SSLPortListMembers list in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3Proxy\Parameters (see Microsoft Knowledgebase article Q184028 <http://support.microsoft.com/support/kb/articles/q184/0/28.asp>. Run REGEDT32. Go to
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3Proxy\Parameters. Open "SSLPortListMembers". Add 1352 twice to the list - it should end up looking something like:

          443
          443
          563
          563
          1352
          1352

7) Reboot the proxy server

8) Reinstall proxy client on internal server. Reboot internal server

9) Test client installation on internal server by running "chkwsp /f", located in the MS Proxy Client install directory (default is "c:\mspclnt"). Success = "Client control protocol version MATCHES the server control protocol."

10) Create and save WSPCFG.INI file on internal server in same directory as NSERVER.EXE ("C:\Notes", for example) as follows:

     [Common Configuration]
     ServerBindTCPPorts=1352
     Persistent=1
     KillOldSession=0

11) Reboot internal server - proxy client on internal server should now bind to port 1352 on proxy server

12) Open CMD prompt, test proxy client connection using "ftp ftp.microsoft.com". Success = ability to logon anonymously to ftp.microsoft.com and do a directory listing

13) From the Internet <preferably from an entirely separate connection> try to telnet to port 1352 on the proxy server's external IP, eg.:
Start | Run | "telnet.exe"
Select "Connect"
Select "Remote system"
Port: 1352 <Windows Telnet defaults to Port: Telnet>
Address: <proxy server's external NIC IP address>
Success = after connecting, the "Disconnect" selection is available under main menu "Connect"
Disconnect, close Telnet

14) Create a "Local Area Network" TCPIP connection document on the external server (or client) that points to the internal Domino server name using the proxy server's external NIC IP address.

15) Remove all entries in "Web Proxy" section of current location document on external server (or client)

16) Open Notes client on external server and trace a connection to the internal server. "File | Tools | Preferences | Ports | Trace Connection", select internal server. If successful, go to step 20.

17) Stop external server Domino services <"quit" at Domino terminal> (we had an active dialup modem connection that was screwing up the ability of the internal server to bind to proxy server port 1352 - stopping the outside Domino service removed that conflict so we could test)

18) Reboot internal server - client should now successfully bind internal server to proxy server port 1352

19) Still having problems? Use "netstat -an" to review port bindings on proxy server, internal and external servers. Proxy server should show port 1352 bound to internal server's IP address. You can't continue past here until you can "see" the internal server from the external server via the proxy server's external IP address.

20) Restart external server Domino service and make sure everything works correctly.


To enable access control:

21) Create new user in "User manager for domains", as a member of the "Domain Users" group

22) Grant this user "Policy | User Rights| Log on locally", also in User Manager for Domains

23) Re-enable Access control on proxy server

24) Add new user to "Unlimited access" (for security reasons you should change this to a custom protocol later)

25) Open command prompt on internal server

26) Make "c:\mspclnt" your current directory (assumes default proxy client installation location)

27) Create credentials for nserver.exe, nrouter.exe, and nreplica.exe as follows:

     credtool -w -n nserver -c NewProxyUser Domain Password
     credtool -w -n nrouter -c NewProxyUser Domain Password
     credtool -w -n nreplica -c NewProxyUser Domain Password

where NewProxyUser is the new user created in step 21, Domain is the NT domain to which the proxy server belongs, and Password is the password assigned to NewProxyUser. The credentials for each service can be checked by replacing the "-w" <write> in the above commands with "-r" <read>

28) Add the line "ForceCredentials=1" to the WSPCFG.INI file you created in step 9. e.g.:

     [Common Configuration]
     ServerBindTCPPorts=1352
     Persistent=1
     KillOldSession=0
     ForceCredentials=1

29) Reboot the internal server

30) Test that the outside and internal servers can still communicate correctly

To enable packet filtering

31) In WinSock Proxy Security, select "Enable packet filtering"

32) Add two custom filters as follows:

     Protocol: TCP
     Direction:     In
     Local port:    Fixed, 1352
     Remote port:   Any
     Local host:    Default Proxy Server external IP addresses
     Remote host:   Single host, <external server IP>

and
     Protocol: TCP
     Direction:     Out
     Local port:    Any
     Remote port:   Fixed, 1352
     Local host:    Default Proxy Server external IP addresses
     Remote host:   Single host, <external server IP>

33) Test that the external and internal servers can still communicate correctly


Which file system should be used for the Notes server?
OS/2 has three supported file systems: FAT, HPFS, and HPFS386.

The OS/2 2.0+ implementation of FAT is 32-bit and gets slightly higher performance than the HPFS file system shipped with OS/2 2.0+. However, when a large number of files is created and destroyed on a FAT file system, it tends to get fragmented more than a similiar HPFS file system would.

The normal HPFS that is shipped with OS/2 2.0 and above is still 16-bit. There are some rumors that IBM will ship HPFS386 with OS/2 eventually, however. HPFS smart caching takes up more memory than a FAT system.

HPFS386 comes with IBMs Lan Server Advanced. It is fully 32-bit and gives a 10-15% performance gain over the regular version of HPFS. Lan Server does not have to be used on the OS/2 system, but it must be installed and a license purchased to legally use HPFS386.

How do you install R5 on Linux?

1. Install Linux (must be at least the 2.2 kernel)
1a. Login as "root"
1b. Install latest JDK from IBM AlphaWorks
2. Create a user called "notes" in the group "notes"
3. Download the R5 TAR file from Lotus
3a. If you have downloaded the multiple TAR files: cat *.tar > 5011lis.tar
4. Un-archive the TAR file: tar xvf 5011lis.tar
5. Do a cd to the directory where "install" exists (where license.txt also is)
5a. Run "./install"
6. Answer the questions, "notes" is the user, "notes" is the group
7. Change directories and start the setup program
7a. "cd /local/notesdata"
7b. Run "/opt/lotus/bin/http httpsetup"
8. You should see "the HTTP Setup is running"
9. From Netscape open "http://your.server:8081/"
10. Configure the Notes server as you would on any other Unix system
11. The server will stop
12. Change directories and start the server
12a Login as notes
12a. "cd /local/notesdata"
12b. Run "/opt/lotus/bin/server"

Another good article is available at LinuxWorld.


How do you tune the operating system for running Notes?

Add the following kernel tuning additions to /etc/system:

 set max_nprocs=4096
 set strctlsz=4096
 set shmsys:shminfo_shmmax=268435456
 set shmsys:shminfo_shmseg=600
 set shmsys:shminfo_shmmni=512
 set shmsys:shminfo_shmmin=1
 set semsys:seminfo_semmns=4096
 set semsys:seminfo_semmni=4096
 set semsys:seminfo_semmnu=4096
 set semsys:seminfo_semume=64
 set semsys:seminfo_semmap=512
 set semsys:seminfo_semmsl=128

In your notes.ini, also add:

 Disable_Locking=1

Which platform is best for a Notes server?

The best platform depends on the software you need to run and local politics :-)

Most new Notes software used to appear first on the OS/2 platform because that is what the Notes server was originally developed to run on. With Lotus support of additional platforms, more software is appearing simultaneously on other platforms with Windows NT being the second most popular in terms of software availability. However, as of Notes 4.x, Lotus has switched to developing on NT first because of the better development tools on that platform; some Notes add-ons only work on Win32 (Windows NT and Win95) and the Notes client works best on Win32.

Unix support is much better in R4, but the 3rd-party developers continue to write for NT as the main target platform.

For serious numbers of users per server, look at the AS/400 native port; there have been reports that over 10,000 users (using partitioned servers) can be placed on it without any problems.

Performance evaluations are available from:
* IBM
* NotesBench


How do you get NT to reboot automatically after the Blue Screen Of Death?

Using regedit, change the registry key:

  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\CrashControl\AutoReboot

to have a DWORD value of 1.


How do you generate a core dump for IBM to look at?
There is a program in the OS2 directory or a subdirectory of it called DDUMP.EXE. This program will create a dump disk for you to use when you get a trap like this one.
Once the server crashes with the trap insert the dump disk and hit the following keys:

Control - Alt - NUM LOCK --  NUM LOCK  (Yes, hit NumLock key twice.)

Just a word of caution. 48 MB of ram created 25 Disks. The dump Disk is needed to start the memory dump.

Where can I find info on Mac-specific (not Notes-related) problems?

Check the following WEB sites:

* The Macintosh Resource Page
* The MacInTouch Home Page
* The Cult of Macintosh
* The EvangeList Web Site

If those don't have an answer to your Mac questions, they have links to numerous other sites for software downloads and other software and hardware questions. In addition, there is the I/O MUG - Internet ONLY Macintosh User's Group mailing list. To subscribe, send
SUBscribe IO-MUG <full name>
to
LISTSERV@UTARLVM1.UTA.EDU

What is a good entry-level AS/400 system?

You should get an AS/400 with at least a 200 CPW. A good entry-level system is the 170 with the 2164 processor. Domino does not run very well on a 150 or the smaller 170 model.

IBM has an AS/400 Domino Sizing site to help you decide.


How do you back up the server automatically?
Create a batch file with the following commands:

@echo off
net stop Lotus Domino Server
ntbackup BACKUP c:\ /D Domino Server Backup /hc:on /B /T Normal
net start Lotus Domino Server

This assumes that the Notes server is running as a Windows NT service and the server Notes ID is not password-protected.

How do you get the Notes server to run in the background?

Run this as part of your system startup code in /etc/inittab:

if [ -d /usr/lotus/notes/notesr4 \
   -a -f /opt/lotus/bin/server \
   -a -f /usr/lotus/notes/autostart.server ]; then
  PATH=/opt/lotus/bin:/usr/lotus/notes/notesr4:$PATH su notes -c \
     'server </dev/null >/dev/null 2>&1' &
  sleep 10;
  echo 'Notes server started'
fi


Why can't Notes clients see my new Notes server running IPX?

The SAP Agent is not installed on NT by default when you install SPX/IPX. Install this and the Notes clients should have no problems communicating with the Notes server.


How do you set up NetBIOS?

For Notes 3.x, NetBIOS must be on port 0 under NT. Under Notes, the NetBIOS port must be on LAN Unit 0.
For Notes 4.x, you can set the LANA number. The Unit number in the Notes port setup has to match the Nbf LANA number in NT's protocol configuration (look under ControlPanel/Network/Services/NetBIOS).


How do you change the new mail tune under NT?

You can add this to a button you can send to your users (note the hardcoded .wav file path though):

Sub Click(Source As Button)
 ' by Lance I. Zakin
  Dim session As New NotesSession
  Dim fileName As String
  Dim choice As String
  Dim verify As String
  Dim Path As String

  fileName = Dir("C:\WINNT\MEDIA\*.WAV")
  Path = "C:\WINNT\MEDIA\"

  Print fileName
  choice = Inputbox$("Click OK to install, otherwise CANCEL to select another Tune.", "Install New Mail Tune", Path & FileName)

  Do While fileName <> "" And choice = ""
    fileName = Dir()
    Print Path & fileName
    choice = Inputbox$("Click OK to install, otherwise CANCEL to select another Tune.", "Install New Mail Tune", Path & FileName)
  Loop

  Call session.SetEnvironmentVar( "NewMailTune",  choice, True)

  Msgbox "Completed!  Restart Notes to initiate " & choice & " as your New Mail Tune.", , "Install New Mail Tune"
  Print "Restart Notes to initiate " & choice & " as your New Mail Tune."
End Sub

How do you determine the NT Service Pack Level using LotusScript?

Put this code into a button:

(Declarations)
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (Byval hkey As Long, Byval lpSubKey As String, Byval ulOptions As Long, Byval samDesired As Long, phkResult As Long) As Long
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (Byval hkey As Long, Byval lpValueName As String, Byval lpReserved As Long, lpType As Long, Byval lpData As String, lpcbData As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (Byval hkey As Long) As Long

Const HKEY_LOCAL_MACHINE = &H80000002
Const SYNCHRONIZE = &H100000
Const STANDARD_RIGHTS_READ = &H20000
Const KEY_QUERY_VALUE = &H1
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE))
Const ERROR_SUCCESS = 0&
Const REG_SZ = 1

Sub Click(Source As Button)
     ' from Paul Ray @ IRI_SOFTWARE
     Dim hKey As Long, result As Long, length As Long
     Dim sValue As String
     
     ' open the key
     result=RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion", 0, KEY_READ, hKey) 
     
     If result <> ERROR_SUCCESS Then
          Messagebox "Can't open key"
     Else
          sValue=Space$(128)
          length=128
          
          ' query the value of the key
          result=RegQueryValueEx(hKey, "CSDVersion", 0, REG_SZ, sValue, length)
          
          ' display it
          If result = ERROR_SUCCESS Then
               Messagebox Left$(sValue, length) 
          End If
          
          ' close the key
          Call RegCloseKey(hKey)  
     End If
     
End Sub


How do you set up the modem port for Solaris 2.x?
Notes expects the Notes port name to match the /dev name. Do not enable getty on this port. Install Sun Patch 100513-04. Set hardware handshaking in the EEPROM.

How do you run the Notes server in the background but still be able to control the console?

You can use the GNU program called Screen to run the Notes server. You can then detach and re-attach to this window as needed from any telnet session. You can find it at any GNU software archive.

For Linux and Solaris, you can modify your rc3.d script to include this line:
su - notes -c "screen -d -m -S NotesServer server"


Can you run the Notes Client on Linux?

Peter Leugner figured out to run the Notes 7.01 client on WINE:
- Get the latest wine from http://www.winehq.com (Version 0.9.19)
- Run the install: wine whatevertheinstalliscalled.exe
- Copy oleacc.dll and oleaccrc.dll from an existing windows to ~/.wine/drive_c/windows/system32 (depending on your settings this could be in windows/system, just copy them in both to be safe). Without this, you can't edit the preferences.
- Edit notes.ini and append the line
DisableUniscribe=1
This gets rid of a bunch of errormessages, I don't know, what it actually does.
- Start notes:
WINEDLLOVERRIDES="oleacc=n" ; wine "C:\Program Files\lotus\notes\nlnotes.exe"

There's a WINE wiki page on how to run the Win32 Notes client on Linux.


WINE (a windows emulator for Linux) has gotten good enough that it will run the R5 Notes client. Phil Colbourn (pcolbourn@argus.net.au) posted this helpful guide to getting it working:
* Download and install Codeweavers Wine rpm. I used codeweavers-wine-20010112-1.i386.rpm

* If you are mounting a dos partition, setup fstab and mount it. My /etc/fstab entry for my FAT32 partition is:

/dev/hda6 /mnt/e vfat noauto,owner,user 0 0

* Run winesetup Programs-System-Wine Config under gnome. I set these options:
- Look & feel to Win95
- Window Mode to Managed
- Drive E HD /mnt/e (my FAT32 partition)
- Drive H network ${HOME}
- Drive Z network / to have access to the file system
- I added E:\lotus\notes;E:\lotus\notes\data to the Path
I'm using a Compaq Armada E500. I has a lucent winmodem (for the money I expected a self-contained modem). Fortunately a linmodem driver is available. It creates a /dev/ttyLT0 device. In the ports section I set this as a Coms port, but I have not tested it yet. I also have a network HP 8000 printer set as lp and lp4 (for printing 4 pages per page, full duplex)

My ~/.wine/config file is listed here:

WINE REGISTRY Version 2
;; All keys relative to \\Machine\\Software\\Wine\\Wine\\Config

;;
;; MS-DOS drives configuration
;;
;; Each section has the following format:
;; [Drive X]
;; Path=xxx (Unix path for drive root)
;; Type=xxx (supported types are 'floppy', 'hd', 'cdrom' and 'network')
;; Label=xxx (drive label, at most 11 characters)
;; Serial=xxx (serial number, 8 characters hexadecimal number)
;; Filesystem=xxx (supported types are 'msdos'/'dos'/'fat', 'win95'/'vfat', 'unix')
;; This is the FS Wine is supposed to emulate on a certain
;; directory structure.
;; Recommended:
;; - "win95" for ext2fs, VFAT and FAT32
;; - "msdos" for FAT16 (ugly, upgrading to VFAT driver strongly recommended)
;; DON'T use "unix" unless you intend to port programs using Winelib !
;; Device=/dev/xx (only if you want to allow raw device access)
;;

[Drive A]
"Type" = "floppy"
"Path" = "/mnt/floppy"
"Label" = "/mnt/floppy"
"Device" = "/dev/fd0"

[Drive C]
"Path" = "/home/pc901725/.wine/fake_windows"
"Type" = "hd"
"Label" = "/home/pc901725/.wine/fake_windows"
"Filesystem" = "win95"

[Drive E]
"Type" = "hd"
"Path" = "/mnt/e"
"Label" = "/mnt/e"
"FS" = "win95"

[Drive Z]
"Type" = "network"
"Path" = "/"
"Label" = "Root"
"FS" = "win95"

[wine]
"Windows" = "C:\\Windows"
"System" = "C:\\Windows\\system"
"Path" = 
"C:\\Windows;C:\\Windows\\system;e:\\lotus\\notes;e:\\lotus\\notes\\data;C:\\Temp"
"Temp" = "C:\\Temp"
"GraphicsDriver" = "x11drv"
; Wine doesn't pass directory symlinks to Windows programs by default.
; Enabling this may crash some programs that do recursive lookups of a whole
; subdir tree in case of a symlink pointing back to itself.
;ShowDirSymlinks=1
"ShellLinker" = "wineshelllink"

[DllDefaults]
"DefaultLoadOrder" = "builtin, so, native"

[DllOverrides]
"kernel32" = "builtin"
"gdi32" = "builtin"
"user32" = "builtin"
"krnl386" = "builtin"
"gdi" = "builtin"
"user" = "builtin"
"toolhelp" = "builtin"
"comdlg32" = "builtin"
"commdlg" = "builtin"
"version" = "builtin"
"ver" = "builtin"
"shell32" = "builtin"
"shell" = "builtin"
"shlwapi" = "builtin"
"lz32" = "builtin"
"lzexpand" = "builtin"
"commctrl" = "builtin"
"comctl32" = "builtin"
"wsock32" = "builtin"
"winsock" = "builtin"
"advapi32" = "builtin"
"crtdll" = "builtin"
"ntdll" = "builtin"
"mpr" = "builtin"
"winspool.drv" = "builtin"
"ddraw" = "builtin"
"dinput" = "builtin"
"dsound" = "builtin"
"winmm" = "builtin"
"mmsystem" = "builtin"
"msvideo" = "builtin"
"msvfw32" = "builtin"
"mcicda.drv" = "builtin"
"mciseq.drv" = "builtin"
"mciwave.drv" = "builtin"
"mciavi.drv" = "builtin"
"mcianim.drv" = "builtin"
"msacm.drv" = "builtin"
"midimap.drv" = "builtin"
"glide2x" = "so"
"glide3x" = "so"
"opengl32" = "builtin"
"shfolder" = "builtin"
"rpcrt4" = "builtin"
"w32skrnl" = "builtin"
"wnaspi32" = "builtin"
"wow32" = "builtin"
"system" = "builtin"
"display" = "builtin"
"wprocs" = "builtin"
"wineps" = "builtin"
"icmp" = "builtin"

[x11drv]
; Number of colors to allocate from the system palette 
"AllocSystemColors" = "100"
; Use a private color map
"PrivateColorMap" = "N"
; Favor correctness over speed in some graphics operations
"PerfectGraphics" = "N"
; Color depth to use on multi-depth screens
;;ScreenDepth = 16
; Name of X11 display to use
;;Display = :0.0
; Allow the window manager to manage created windows
"Managed" = "Y"
; Run Wine windows in desktop. Contains "N" or something like "800x600".
"Desktop" = "N"
; Use XFree86 DGA extension if present
"UseDGA" = "Y"
; Use XShm extension if present
"UseXShm" = "Y"
; Enable DirectX mouse grab
"DXGrab" = "N"
; Create the desktop window with a double-buffered visual
; (useful to play OpenGL games)
"DesktopDoubleBuffered" = "N"
; Code page used for captions in managed mode
; 0 means default ANSI code page (CP_ACP == 0)
"TextCP" = "0"
; Use this if you have more than one port for video on your setup 
; (Wine uses for now the first 'input image' it finds).
;; XVideoPort = 43

[fonts]
;Read documentation/fonts before adding aliases
"Resolution" = "96"
"Default" = "-adobe-times-"

[serialports]
"Com1" = "/dev/ttyS14"
"Com2" = "/dev/ttyLT0"
"Com3" = "/dev/modem,38400"
"Com4" = "/dev/ttyS14"

[parallelports]
"Lpt1" = "/dev/lp"
"Lpt2" = "/dev/lp4"

[spooler]
"LPT1:" = "|lpr -P lp"
"LPT2:" = "|lpr -P lp4"
"LPT3:" = "/dev/lp3"
"LPT4:" = "|gs -sDEVICE=bj200 -sOutputFile=/tmp/fred -q -"

[ports]
;read=0x779,0x379,0x280-0x2a0
;write=0x779,0x379,0x280-0x2a0

[spy]
"Exclude" = "WM_SIZE;WM_TIMER;"

[registry]
; Paths must be given in /dir/dir/file.reg format.
; Wine will not understand dos file names here...

;These are all booleans. Y/y/T/t/1 are true, N/n/F/f/0 are false.
;Defaults are read all, write to Home
; Global registries (stored in /etc)
"LoadGlobalRegistryFiles" = "Y"
; Home registries (stored in ~user/.wine/)
"LoadHomeRegistryFiles" = "Y"
; Load Windows registries from the Windows directory
"LoadWindowsRegistryFiles" = "Y"
; TRY to write all changes to home registries
"WritetoHomeRegistryFiles" = "Y"
; Registry periodic save timeout in seconds
; PeriodicSave=600
; Save only modified keys
"SaveOnlyUpdatedKeys" = "Y"

[Tweak.Layout]
;; WineLook=xxx (supported styles are 'Win31'(default), 'Win95', 'Win98')
"WineLook" = "Win95"

[Console]
;Drivers=tty
;XtermProg=nxterm
;InitialRows=25
;InitialColumns=80
;TerminalType=nxterm

[Clipboard]
"ClearAllSelections" = "0"
"PersistentSelection" = "1"


[Drive D]
"Path" = "/mnt/cdrom"
"Type" = "cdrom"
"Device" = "/dev/cdrom"

[Drive H]
"Path" = "${HOME}"
"Type" = "network"
"Device" = ""

==========
Installing Lotus Notes r506 (r504 upgraded to r506 using incremental installers under MS-Windows)
==========

I already had a r506 shared install on my Windows partition. It was an r504 full shared install upgraded to r506. If I had the CD handy, I could have installed it under Wine and applied the upgrades to r506 (as I have done this in the past)

I relocated the shared Notes install to E:\lotus\notes, and my data directory to E:\lotus\notes\data. Drive E: in MS-Windows 2000 is /mnt/e under Linux and I mapped this to drive E: under Wine. This way I can share my Notes program and data files between MS-Windows and Linux/Wine.

To setup the registry, I ran wine e:\lotus\notes\setup.

I had a lot of trouble getting Notes to work. I tried all sorts of DLL combinations (builtin v's native). Eventually I discovered it was some redistributed files in the Notes program directory that were causing me grief. I removed them and ... I don't need MS-Windows 2000 anymore.

The files I removed (renamed actually) were these:
- imagehlp.dll
- msvcrt.dll
- olepro32.dll
- rpcrt4.dll (I think)

If these don't exist in your Notes program directory, don't worry.

==========
Running Notes under Wine
==========

I made this small script and placed it into ~/bin/notes

echo preparing to start notes...
if [ ! -f /mnt/e/lotus/notes/Notes.exe ]; then
  echo mounting /mnt/e...
  mount /mnt/e
fi
echo starting notes...
wine e:\\lotus\\notes\\nlnotes.exe =e:\\lotus\\notes\\data\\notes.ini

It mounts my Wine E: drive if it is not already mounted, then starts Notes using nlnotes.exe rather than notes.exe. This is because notes.exe launches the Notes splash screen first and then runs nlnotes.exe (I think). But when you close Notes (nlnotes.exe actually), the splash screen does not get closed and seems to remain running under Wine even though you can not see it. I think this happens in MS-Windows as well except that it is closed when nlnotes is closed. From my experience, nlnotes is all you need, and it does not waste memory (under wine at least). I now run nlnotes.exe instead of notes.exe under MS-Windows.

NB: chmod u+x ~/bin/notes before using it

==========
WIN.INI Modifications
==========

To control the date and time format (to dd/MM/yyyy and 24 hour time) I added these entries into C:\Windows\WIN.INI since I could not get them to work in the registry.

[intl]
iTLZero=1
iDate=1
sDate=/
sShortDate=dd/MM/yyyy
sLongDate=dddd, MMMM dd, yyyy
iTime=1
iTimePrefix=1
sTime=:
s1159=
s2359=
sTimeFormat=HH:mm:ss

NB: These work but I do not know what consistutes the minimum set.

==========
NOTES.INI
==========
This is an annotated extract of my NOTES.INI file in E:\lotus\notes\data.

[Notes]
Directory=e:\Lotus\Notes\Data - case does not seem to matter
WinNTIconPath=e:\lotus\notes\data\W32
Timezone=-10 - Australia
ClockType=24_HOUR - this did not do anything
DST=0 - our parent org does not run DST, so we can not either
TCPIP=TCP, 0, 15, 0
COM1=XPC,1,15,0,,12288,
COM2=XPC,2,15,0,,12292,115200,16,_pcgend.mdm,60,15 - my own generic modem file
COM3=XPC,3,15,0,
COM4=XPC,4,15,0,,12302,57600,16,_pcgend.mdm,60,15
COM5=XPC,5,15,0,,12292,115200,16,_PCGEND.MDM,60,15
Ports=TCPIP,COM4
DisabledPorts=COM2,COM5,COM1,COM3

MailServer=CN=amber/O=RailCom - amber is a Linux Domino server! We are running two one on RH7.0 and another on Debian. The one on Debian is not very stable yet, but it also has a stallion multi-port serial card, and is our Notes dial-in server. The RH7.0 server runs well, but currently only  as a mail/HTTP server and we are only running it on a non SMP 2.2 kernel (the box has two processors, SCSI RAID, and 1G RAM)

SPELL_DIR=e:\lotus\notes\data
DateOrder=DMY - this did not do anything either

==========

Well that is it. I'd be interested if this helps or if you find any improvements I can make.

Status for support can also be found here.


IBM has also released a Hanover native Linux Notes client.

John Smolenaers was nice enough to write up how to install it on Debian which isn't one of the supported platforms:

INSTALLING LOTUS NOTES 7.0.1 ON DEBIAN 3.1 (SARGE)

PREREQUISITES
This procedure is based on a standard Debian 3.1 (Sarge) Desktop installation using the Linux kernel 2.4 with Gnome as the default Desktop Environment.
PROCEDURE
1. Copy the directory Lotus Notes 7.0.1 for Linux Setup to /tmp or another location of your choice (if you do not know where Lotus Notes 7.0.1 for Linux Setup folder is, contact Internal IT for the location). You can either use your favourite GUI file browser or from a new terminal window by typing;
cp Source/Lotus Notes 7.0.1 for Linux Setup /tmp
2. From a terminal screen type in the following;

su root

and type in the root password when prompted.
3. Install the debian package libmotif3 by typing in;
apt-get install libmotif3

4. Create the file /etc/gre.conf by typing in;
nano )w /etc/gre.conf

or using your favourite text editor (e.g vi, emacs etc(). Insert the two following lines;
[1.7.8] <-------------- Version of Mozilla installed
GRE_PATH=/usr/lib/mozilla <-------------- Path to location of Mozilla
then save and close the file. If using nano as your text editor, you can save and close the file using the key sequence;
Ctrl+X
Y
Enter
5. Create the directory /opt/IBM by typing in;
mkdir /opt/IBM

6. Change the permission of the directory /opt/IBM to allow all users to have write permissions by typing in;
chmod 777 /opt/IBM

7. Ensure the file /tmp/Lotus Notes 7.0.1 for Linux Setup/setup_wct_platform.bin has execute permissions by typing in;
chmod 777 /tmp/Lotus\ Notes\ 7.0.1\ for\ Linux\ Setup/setup_wct_platform.bin

8. Exit as SU from the terminal screen by typing;
exit
and pressing;
Enter
9. Change to the Lotus Notes 7.0.1 for Linux Setup directory by typing in;
cd /tmp/ Lotus\ Notes\ 7.0.1\ for\ Linux\ Setup

10. Start the installation for Lotus Notes 7.0.1 by typing in;
./setup_wct_platform.bin

11. After a short while, the &Installation wizard for IBM Workplace Managed Client8 will open stating that it will install with the item &Lotus Notes Plugin8. Click Next to continue the installation.
12. Accept the terms and click Next.
13. Leave the &Directory Name:8 as the default unless you know what you are doing then click Next.
14. Click Next again and the installation wizard will begin installing files.
15. Once the files have finished installing, click Next.
16. The wizard will close and open the &Setup wizard for IBM Workplace Manage Client8 and begin installing more files. When it has almost finished, a new windows will open called &Installer8 (this is the Lotus Notes plugin installer).
17. Accept the terms again and click Next.
18. The installer will install the &Lotus Notes Application Plugin8 files. Once it has finished, it will state that you need to logout and log back in. Ignore this and click Finish.
19. The &Setup wizard for IBM Workplace Manage Client8 will finish and you can click Finish8.
20. Lotus Notes will open with two blank panes. Don,t panic, just close it as you need to set-up some environment variables and paths before it will work properly. Once closed, create the text file /home/<username>/.noteslauncher by opening a terminal window and typing in;
nano )w /home/<username>/.noteslauncher
or using your favourite text editor (e.g vi, emacs etc(). Insert the following lines;
#!/bin/sh
export NOTESBIN=/home/<username>/notes
export NOTESDATA=/home/<username>/notes/data
export NOTESDIR=/home/<username>/notes/data
export LD_LIBRARY_PATH=$NOTESBIN:$NOTESBIN/jvm/bin/classic:$NOTESBIN/jvm/bin:$LD_LIBRARY_PATH
export PATH=$NOTESBIN/jvm/bin:$NOTESBIN:$PATH
export CLASSPATH=./:$NOTESBIN/:$CLASSPATH
/opt/IBM/Workplace\ Managed\ Client/rcp/richclient -personality com.ibm.workplace.noteswc.standalone.linux.personality
then save and close the file. If using nano as your text editor, you can save and close the file using the key sequence;
Ctrl+X
Y
Enter
21. Ensure the file /home/<username>/.noteslauncher has execute permissions by opening a terminal window and typing in;
chmod 777 /home/<username>/.noteslauncher

22. On your desktop, go to the properties of the icon labelled IBM Lotus Notes by right clicking on it and selecting Properties from the context menu.
23. Select the Launcher TAB and change the Command: field to;
/home/<username>/.noteslauncher
Then click Close to close the properties window.
24. Launch Lotus Notes by double clicking on the IBM Lotus Notes icon on the desktop. Since this is the first time Lotus notes has been run, the Lotus Notes Client Configuration window will open at the Welcome screen.


Can you run the R5 client on OS/2?

Unfortunately, there is no hidden R5 client for OS/2 as there is for R4.

The only thing you can do is try running the Win32 client on top of the Odin Win32 emulation layer.

Why do I have a JVM problem running httpsetup?
This is a common problem if you install Domino 5.0x under Redhat 7.x. The problem is that the Domino JDK is not the latest. This is supposed to be fixed in Domino 5.0.9. Until then, you can download the latest JDK1.1 from IBM's site and install parts of it in the Domino directory; this procedure is described here and here.

How do you install and secure Domino on Linux?

There are several articles on this subject:
* The Lowdown on the Domino Linux Install
* Domino on Linux: Tune Up for Peak Performance
* Securing Domino on Linux

And IBM has posted information about it as well:
* Lotus Domino 6 Server for Linux


What are common useful commands for the AS/400?

WRKACTJOB
- displays all processes and their status including %cpu utilization
DSPHDWRSC TYPE(*AHW) OUTPUT(*PRINT)
- displays the current hardware configuration (#cpus, mem, etc.)
WRKDSKSTS OUTPUT(*PRINT)
- displays current disk configuration and utilization


How do you get LS:DO working on Linux?
From Ryan Hughes:

The first thing that has to be done is install UnixODBC.
Add the directory where the libodbc.so file (supplied by UnixODBC) is stored (in my case /usr/lib) to your PATH.
Then install the iSeriesODBC driver by following the iSeries ODBC Installation Guide.i.
Finally set up your data sources and you should be good to go.

On a side note, I did a test with the LS Connectors using the lctest tool (/opt/lotus/notes/linux/latest/lctest) and it seemed to work as well. The connectors would probably be the preferred way to go for data access (JDBC is a good option as well) but I needed LS:DO for some existing applications.


How big an AS/400 do I need for Domino?

Here are a few useful URLs:
http://www-912.ibm.com/supporthome.nsf/document/16533356
http://www-1.ibm.com/servers/eserver/iseries/domino/is4dprice.html
http://www-1.ibm.com/servers/eserver/iseries/domino/value.htm


Do I have to install the Notes/Domino Server on Windows Server?
No, you do not need to install the Lotus Notes/Domino server on a Microsoft Windows Server if you are installing it on Windows. You can install it on Windows 2000, XP, etc. However, IBM/Lotus has a list of supported operating systems that they've tested on and may complain if you try to report a problem that happens on an unsupported platform.

Where can I get the Notes 4.6 LotusScript Class Chart?

You can order the Developers' Documentation Kit from Lotus.
It is also available at this web site:
http://doc.notes.net/uafiles.nsf/docs/ls40/$File/lotusscript.exe


Why don't bitmaps display properly on some systems?

Notes uses a specific set of 256 colors for display; Notes will force all images to be displayed using these colors. On systems that have video drivers that have 256 colors or less, you will have to use the same 256 colors that Notes uses. On systems with more than 256 colors, this is not an issue.

The palette is available below in several formats:

Adobe Photoshop: lotus.act
MS Paint (Win95 and NT): lotus.pal
Corel Draw:


How do you write DbLookups so they work on a local database and on the server?

Use @Dbname to test for the location using these formulas::

DBName := @Text(@DbName);
Server := @Subset(DBName; 1);
Database := "KeywordLookup";
ClassCache := "Notes" : "NoCache";
View := "LookupKW";
Key := "Key Name";
@DbLookup(ClassCache; Server : Database; View; Key; "Ret_Value")

Now you can substitute the Server and Database variables everywhere in the Notes application. DbLookups or @Commands will automatically run local when the user is local and run on the server when the user is on the server.


How do you display a button next to a field only when editing?

Duplicate the line with the field and button and remove the button from the copy.
Change the field to a computed-for-display field with a formula of just the name of the original field.

Mark the original line hide when reading, and the copy hide when editing.


How do you restrict a field to contain alphanumeric characters only?

This is an example of code that uses regular expressions to limit a field's entry. Put this in a field's Input Validation formula:

  @If(
    @Matches(Serial_Num; "+{0-9}"); @Success;
    @Matches(Serial_Num; "+{a-zA-Z}"); @Success;
    @Do(@Prompt([OK]; "Invalid Serial Number"; "The Unit Serial Number contains an illegal character, probably a carriage return.  Please remove the illegal character and save again."); @Return(""))
  )


How do you validate that a field has alpha characters only?

Use the following for an Input Validation Formula:

@matches(string;"+{A-Za-z}")


Can you have views based on the current user name?

If you have a Notes client, the best thing to do is to have a shared view set to Personal On First Use; whenever you redesign the view, you will have to remove the database icon or bookmark and then re-add it to get the new design. The technique which follows keeps the view on the server, but it is very resource intensive.

In the Advanced tab of the view properties, set Refresh Index to "Automatic" and Discard Index to "After each use".

In the selection formula for the view, select only documents which the current user can read. E.g.,
@IsMember(@UserName; ReaderList)

You cannot hide the categories that are not for the reader, so you should use an uncategorized view.

Warning: This is extremely server intensive since it will cause the server to rebuild the view each time a user looks at it!


How do you categorize and count the number of documents created in a particular week?

Category field (Date format, "Created" is the field with the creation date):

        @adjust(Created;0;0;1-@weekday(Created);0;0;0)

Summation field (number format, that's right, just a number 1 to total up):

        1

This creates a view catagorized by a field that has the "previous Sunday" computed.
Set up category totals in the view for the summation field and you'll have what you want.


How do you reduce database size by deleting unused fields?

Delete the fields before the doc is saved by adding code like this to the fields' translation formula:

  @If (x="";  @DeleteField;  x)

Make sure the x's are exactly the field name, or you will loose data.
Do not delete fields if they are used in other formulas.


From a response document, how do you update a field in the parent document?

Use

  @SetDocField($Ref;"Field on Parent Doc";"Value") 

in a formula in a field on your response doc. When the response is saved, the parent will be updated. The $Ref parameter is the document number of the parent document which is always stored in the response document.


How do you delete a profile document?

You have to use LotusScript:

  Set doc = db.GetProfileDocument(profilename)
  Call doc.Remove


Can you have an agent send mail as a specific person?

In LotusScript, use the CopyToDatabase method of the NotesDocument class to copy a document into mail.box (on the server) without any Paste agents or server tasks overwriting any fields. The document must have the SendTo, Recipients, From, Subject, and Body field set properly. The Form field must be set to "Memo".

You also use the CreateDocument method of the NotesDatabase class to create a document directly in mail.box. The message will be routed as soon as you call the Save method.

This can only be done on the server. If it is done via the client, the From field will be overwritten during the replication process.

Alternatives are also described in Lotus' Agent FAQ by JulieK.


How do you get the directory the current database is in?

Public Function GetDatabasePath( db As Notesdatabase ) As String
  Dim position As Integer
  position = Instr( db.FilePath, db.FileName )
  GetDatabasePath = Left( db.FilePath , position - 1 )
End Function


How do you automatically copy an ACL from a template into a new database?

If you enclose the ACL's in bracket's, like so:


[-Default-]
No Access
[Administrator]
Manager
[LocalDomainServers]
Manager
[OtherDomainServers]
No Access

they will be copied automatically to any new databases you create using that template. However, role information is not copied automatically.


How do you display a number field with leading zeroes?

For a number with exactly 5 characters in its display:

  textnum := @Right("00000" + @Text(num);5);


How do you add more action buttons than fit on the display?

For FORM action buttons, you can give the user access to all ten buttons by adding a "scroll" action button. Divide the ten buttons into two groups of five, and for the buttons in the first set, set the Hide-When formula to ABar != "Set1". For the buttons in the second set, set the Hide-When formula to ABar != "Set2". Add a computed field to the form called ABar, and give it a value of "Set1" (this sets the default). Add another action button (this is your scroll button), leave it nameless, choose the right-arrow icon for it, and give it the following formula:

NewABar := @If(ABar = "Set1"; "Set2"; "Set1");
@SetField("ABar"; NewABar);
@Command([RefreshHideFormulas]);

For VIEW action buttons, the Hide-When formulas should be @Environment("ABar") != "Set1" for the first
set and @Environment("ABar") != "Set2" for the second set. Give the scroll button the following formula:

CurBar := @Environment("ABar");
NewBar := @If(CurBar="Set1";"Set2";"Set1");
@SetEnvironment("ABar"; NewBar);
@Command([RefreshHideFormulas]);

If you need more than two sets of buttons, just extend this logic.


How do you prevent a form from prompting to save it?

Create a hidden field with the name SaveOptions and make it computed with value "0" (zero in quotes).

Note that this will let the user close the document without prompting for a save, but it will not save the document. You have to manually save the form via script or an @Command if you really want to save the document.

How do you automatically filter mail in Notes?

1. Create a folder in your mail database to hold the mail from the mailing list.
2. Create an agent in your mail database.
3. Give it a name.
4. Specify it as "If new mail has arrived".
5. Mark that the agent will run a script, as opposed to simple action or formula.
6. In the Event field, choose Initialize and enter the following script:

Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim collection As NotesDocumentCollection
Set db = session.CurrentDatabase
Set collection = db.UnprocessedDocuments
For i=1 To collection.Count
  Set doc=collection.GetNthDocument(i)
  If doc.ReplyTo(0) = "lnotes-l@ozzie.notesnic.net" Then
    doc.PutInFolder( "Mailing List\L-Notes" )
    doc.RemoveFromFolder( "($Inbox)" )
  End If
Next
End Sub

This will place all mail from the mailing L-Notes mailing list and place it in the folder named "Mailing List\L-Notes" whenever the mail comes in. You can add additional Else statements for any other mailing lists you are subscribed to.
Your administrator will have to give you access to Run Restricted Agents in the server document for your mail server.

Another agent is from Richard Schwartz (rhs@rhs.com):

Dim folder List As String

folder("DOMINO-L@LISTSERV.OKSTATE.EDU") = "Lists\DOMINO-L"
folder("NOTES-L@LISTSERV.OKSTATE.EDU") = "Lists\NOTES-L"
folder("lnotes-l@ozzie.notesnic.net") = "Lists\LNOTES-L"
' more list entries go here...
'...
Set doc = coll.GetFirstDocument
While Not doc Is Nothing
moved = False
If doc.hasItem("SendTo")Then
For i = 0 To Ubound(doc.SendTo)
listIndex = doc.SendTo(i)
lt = Instr(listIndex,"<")
gt = Instr(listIndex,">")
If lt > 0 And gt > lt Then
listIndex = Mid(listindex,lt+1,gt-lt-1)
End If
If Iselement(folder(listIndex)) Then
doc.PutInFolder(folder(listIndex))
moved = True
End If
Next i
End If
If moved = False Then
If doc.hasItem("ReplyTo") Then
For i = 0 To Ubound(doc.ReplyTo)
listIndex = doc.ReplyTo(i)
lt = Instr(listIndex,"<")
gt = Instr(listIndex,">")
If lt > 0 And gt > lt Then
listIndex = Mid(listindex,lt+1,gt-lt-1)
End If
If Iselement(folder(listIndex)) Then
doc.PutInFolder(folder(listIndex))
moved = True
End If
Next i
End If
End If
If moved = True Then
doc.RemoveFromFolder("($Inbox)")
End If
Set doc = coll.GetNextDocument(doc)
Wend


My code deals with both the SendTo and ReplyTo fields -- I found that that was necessary in order to deal with all the various lists I get. The thing I really like about my code is that it uses the List data type, to store the destination folder names as an associative array indexed by the address string. This makes it very easy to maintain the code. I actually intended to write code that loads the List from documents in a configuration database, but it's so easy to go in and add another address to the List in code that I just never got around to it. The disadvantage of this method is that I couldn't use the Like operator, which means there has to be an exact match in the comparison. I wrote the code so that it would parse out the string between the "<"and "" signs, so at least I only have to get an exact match on that part. (I should have put in calls to lcase the addresses so that I wouldn't have to worry about matching case, but I never got around to doing that either).


How do you create a view action button that creates responses to only work on main documents?

You can check for the Form type of the document that the user is creating a response document for as follows:

@If(Form="Main";
  @Command([Compose];"Response");
  @Prompt([OK];"Select Main";
    "Select a Main document for which you wish to create a response.")
)

Can you start a different navigator depending on the user role?

Yes, put this in the Database Script's PostOpen event depending on what the user role is:

  @PostedCommand([OpenNavigator]; navigatorName);


How can you tell if a field has been changed in LotusScript?

Make a global variable named "X" which is the same data type as the field you want to monitor.
In the PostOpen event for the form, set X to the value of the field.
In the QuerySave event check to see if the current value of the field is still the same as X.


Can you inherit fields from parts of the same form?

Yes, as long as the field you wish to inherit into is below the field you want to inherit from.

This means if have field B below field A, field B can reference the contents of field A in it's computed formula.


How do you do a mail merge using info from existing documents?

A user wants to select a number of customer documents and then run a macro which composes a standard document for each of the selected customer documents.

Have the macro's Operation set to Compose new documents when run.
In the Formula add a statement:

  FIELD Form := name_of_the_standard_document_form;

The form to be composed should have inherit turned on so fields from the selected docs will appear in the new form.

If you only need to print out forms with existing data, this is even simpler. Highlight the documents in the view and then select File, Print, Form Override.

How do you generate sequential document IDs?

Computer Associates sells a sequential number generator as part of the QXCOM Tools.

A company called Sentor Communications also has a product called SequelNum. Call (+61 2 9391 0544) or
[email] email for more info. The list price is US$155 or AU$195.

I company called SISDAM also has Loyal Sequencer.

Under Version 4, you can use a triggered agent on the server which uses a locked file like so:

  Sub Initialize
  file% = Freefile
  Open "c:\\seqnum.bin" For Binary Access Read Write Lock Read As file%
   ' You can also use Open "\\NotesServer\SharedDirectory\seqnum.bin"
   ' if your client and server are Win95 or NT based.  Open can take
   ' a UNC path
  Get #file%, 1, SeqNum%
  SeqNum% = SeqNum% + 1
  Put #file%, 1, SeqNum%
  Close file%
  End Sub

This method does solve the race condition possible if you use the "field stored in a document technique", but the limitation of this is that it won't work with replicated databases (user or server). However, note that this agent cannot be run from the client because running from the client will cause the sequential numbering file to be created on the client machine.

Here is another technique that will work only if your client machines access a non-replicated database on a single server:

' MBCounter&(CounterName$, CounterToSave%)
'
' Marco Beri 
' marcob@equalis.it
'
' This functions give one or more progressive unique counters without race conflict condition
'
' Parameters:
'    CounterName$: counter name; every counter name represents a different counter; you could have as many counters as you want
'    CounterToSave%: if false MBCounter& just returns next free number, if true it gives you next free number and increment it by one
'
' Return value:
'    a long integer
'
' Usage:
'   MBCounter("EveryCounterNameYouWant", False) in QueryOpen if and only if it is a new document and just to display the next free number.
'   MBCounter("EveryCounterNameYouWant", True) in QuerySave if and only if it is a new document, just before exiting and only if you really
'   save the document (so you are sure not to waste a free number)
'
' 
Attention:
' - you cannot use form, view or folder with a name like (MBCounters); if you need this name change COUNTER_FOLDER_FORM_NAME$ constant value
' - if you want you can create a view to visualize the document with form COUNTER_FOLDER_FORM_NAME$ to check the counter values
'

Option Public
Option Explicit
Const COUNTER_FOLDER_FORM_NAME$="(MBCounters)"

Function MBCounter&(Byval CounterName$, Byval CounterToSave%)
     On Error Goto MBCounterError

     Dim CounterFieldName$
     Dim session As New NotesSession
     Dim db As NotesDatabase
     Dim view As NotesView
     Dim doc As NotesDocument
     Dim docNext As NotesDocument
     Dim CounterValue&
     Dim FieldValue As Variant
     Dim Ret%
     Dim item As NotesItem     
     Dim FolderToBeCreated%
     
     CounterFieldName$ = "MB"+CounterName$
     Set db = session.CurrentDatabase
     Set view = db.GetView(COUNTER_FOLDER_FORM_NAME$)
     If view Is Nothing Then
          FolderToBeCreated%=True
     End If
     
     Do
          If Not FolderToBeCreated% Then
               view.refresh 
               Set doc = nothing
               Set doc = view.GetFirstDocument()
          End If
          If doc Is Nothing Or FolderToBeCreated% Then               
               Set doc = New NotesDocument(db)
               doc.Form = COUNTER_FOLDER_FORM_NAME$
               Call doc.Save(True, False)
               doc.putinfolder COUNTER_FOLDER_FORM_NAME$
               If FolderToBeCreated% Then
                    Set view = db.GetView(COUNTER_FOLDER_FORM_NAME$)
                    FolderToBeCreated% = False
               End If
          Else
               Set item=doc.GetFirstItem(CounterFieldName$)
               If item Is Nothing Then
                    Call doc.ReplaceItemValue( CounterFieldName$, 0 )
                    Call doc.Save(False, False)
               Else
                    FieldValue=doc.GetItemValue(CounterFieldName$)
                    If Not CounterToSave% Then
                         CounterValue&=FieldValue(0)+1
                         Exit Do
                    Else
                         Call doc.ReplaceItemValue( CounterFieldName$, FieldValue(0)+1)
                         Ret%=doc.Save(False,False)
                         If Ret% Then
                              CounterValue&=FieldValue(0)+1
                              Exit Do
                         End If
                    End If
               End If
          End If
     Loop

     ' This deletes double docs incidentally created in the Loop (almost impossible 
     ' to happen, just first time and not a problem, but just to be clean)
     ' [For the race condition at the start if two users access an uninitialized
     ' counter at the same time.  The best workaround for this is to create
     ' the initial counter by hand..Ed.]
     Set doc = view.GetNextDocument(doc)
     Do While Not doc Is Nothing
          Set docNext = view.GetNextDocument(doc)
          Call doc.Remove(True)
          Set doc=docNext
     Loop
     
     MBCounter& = CounterValue&
     
MBCounterExit:     
     Exit Function
     
MBCounterError:     
     Msgbox "MBCounter", Err, Error$
     MBCounter& = -1
     Goto MBCounterExit
End Function


Can you show FTSearch documents with a specific view?

Add a private on first use folder named SearchResults.

In this folder, add two action buttons. One should empty out the folder and the other should prompt the user using dialog boxes to build the query.

For the query, use the Lotuscript FTSearch method on the database. This will return a NotesDocumentCollection. You can then use the PutAllInFolder method to move the documents in the collection into SearchResults. You then have to refresh the view.


How do you make a view to use for checking for replication conflicts?

Conflict documents contain the field $Conflict, so you could use

  Select @IsAvailable($Conflict)

in a view's selection formula. Also, be sure "No Response Hierarchy" is on in the view attributes since conflict documents are response documents.


How do you add an Internet-style signature at the bottom of your mail messages?

Select Actions/MailTools/CreateStationary and select Personal Stationary. Add the Internet-style "signature" in the last field and save it as "My Sig"; you can also add your own bitmap at the top. When you want to create mail with signatures, go into the Drafts View of your mail database and create a "My Sig" piece of mail. You can also do a Create/Other to select the stationary you created.


Can you access the Clipboard in Notes?

This function (thanks to Manfred_Doerwald@IDG.DE) calls Win32 functions to put a field in the clipboard:

This goes in (Options):

'dataformat ID for ANSI text with ending null (\0). CR(13)/ LF(10) are for end of line.
Public Const CF_TEXT =  &H001

This goes in (Declarations):

Declare Function OpenClipboard Lib "User32.dll" Alias "OpenClipboard" (Byval hWnd As Long) As Long
Declare Function EmptyClipboard Lib "User32.dll" Alias "EmptyClipboard" ( )  As Long
Declare Function SetClipboardData Lib "User32.dll" Alias "SetClipboardData" (Byval wFormat As Integer,Byval hAnsiText As Long ) As String
Declare Function GetClipboardData Lib "User32.dll" Alias "GetClipboardData" (Byval wFormat As Integer) As String
Declare Function CloseClipboard Lib "User32.dll" Alias "CloseClipboard" ( ) As Long

Declare Function MYlstrcpy Lib "Kernel32.dll" Alias "lstrcpyA" (Byval Buffer As Long, Byval COPYString As String) As Long
Declare Function GlobalAlloc Lib "Kernel32.dll"  Alias "GlobalAlloc" (Byval wFlags As Long, Byval dwBytes As Long) As Long
Declare Function GlobalLock Lib "Kernel32.dll" Alias "GlobalLock"(Byval hMem As Long) As Long
Declare Function GlobalUnlock Lib "Kernel32.dll"  Alias "GlobalUnlock" (Byval hMem As Long) As Long
Declare Function GlobalFree Lib "Kernel32.dll"  Alias "GlobalFree" (Byval hMem As Long) As Long

This goes in the Click event for a button:

Sub Click(Source As Button)
  Dim Status As Long
  Dim ptr As Long, ghand As Long,handle As Long
  Dim Text As String
  Dim workspace As New NotesUIWorkspace
  Dim Uidoc As NotesUIDocument
  Dim item As NotesItem
  Dim s As New notessession
  Dim db As NotesDatabase
  Dim col As NotesDocumentCollection
  Dim doc As notesDocument
     
  Set db = s.currentdatabase
  Set col = db.unprocesseddocuments
  Set doc = col.getfirstdocument
     
  ' Control of platform 16/32-Bit
  If Instr(s.platform,"16") Then
    Messagebox ("Wrong Windows-platform; this agent runs only under Windows NT/95")
    Exit Sub
  End If
  ' Select Notes field
  Set item = doc.GetFirstItem("xxxxxx")
  ' open clipboard
  Status =  OpenClipboard(handle)
  If Status <> 0 Then
    ' Example: reading clipboard contense
    'Text = GetClipboardData(CF_TEXT)
    'Delete clipboard contense
    Status = EmptyClipboard()
    ' for testing purpose make a textstring
    'Text =   "Manfred Doerwald"
    'get global storage for field contense
    ghand = GlobalAlloc(0,(Len(item.Values(0))+1))
    'or global storage for textstring contense
    'ghand = GlobalAlloc(0,(Len(Text)+1))
    'lock global storage
    ptr = GlobalLock(ghand)
    'copy contense of  field to global storage
    Status = MYlstrcpy(ptr,item.Values(0))
    'or copy of textstring contense
    'Status = MYlstrcpy(ptr,Text)
    'free global storage
    Status = GlobalUnlock(ghand)
    'write to clipboard
    Call SetClipboardData(CF_TEXT, ptr)
    'close clipboard
    Status = CloseClipboard()
    'free handle of global storage
    Status = GlobalFree(ghand)
  Else
    Messagebox ("Error opening the clipboard!")
    Exit Sub
  End If
End Sub


How do you save a document, then create a new document in its window?

@Command([FileSave]);
@Command([FileCloseWindow]);
@PostedCommand([Compose];"";"test2");

How do you force the value of a field to be unique?

This assumes you have a field named CompanyCode as an editable field. It also assumes that you have a view called "Company Code" which has CompanyCode column.

Use the following input validation formula for CompanyCode:

  @If(
       CompanyCode = "";
         @Failure("You have to fill in Company Code");
         @IsError(
             @DbLookup("" : "NoCache";  ""; "Company Code"; CompanyCode; CompanyCode));
           @Success;
         @IsNewDoc = 0;
           @Success;
       @Failure("The company code you entered has already exist, please change it")
     )

This formula first checks to make sure a value is entered. If a value is entered, it then uses that value as a key to lookup into the Company code view for a document containing that company code; if no document with that code is found, an error condition is created, which satisfies the @IsError @Function, and returns @Success. If a document with that code is found, the code is returned, which does not satisfy the @IsError function, causing the @Failure message to be returned.


How do you find the positions of a string in a text list?

Field - List = your input list
Field - FindChar = the thing you're trying to find in the list
Field - Position = multi value field with final position of the characters you're looking for.

Button code used to find the position of all of your characters:

NumInList := @Elements(List);
TargetFindList := @Explode(@Repeat(FindChar + ", " ; NumInList -1) + FindChar);
SeqList := @Text(@TextToNumber(("0":"1":"2":"3":"4":"5":"6":"7":"8":"9") *+
  ("0":"1":"2":"3":"4":"5":"6":"7":"8":"9")));
Seq := @Subset(@Subset(SeqList; -99); NumInList);
AltFindList := List + Seq;
AltTargetFindList := TargetFindList + Seq;
FindPos := @Replace(AltFindList; AltTargetFindList;Seq);
Final := @Trim(@Replace(FindPos;AltFindList;""));
@SetField("Position";@Trim(@Replace(FindPos;AltFindList;"")))


How do I get the week ending date for a specific day?
Assuming tdate contains the day you'd like to convert:

@If(tdate != ""; @Adjust(tdate; 0; 0; @Modulo(8 - @Weekday(tdate); 7); 0; 0; 0); tdate)

Why does @Explode only give me the first item?

The problem is that @Explode stops when it encounters a space or a period. You need to replace them as shown below:

 TextSrting := @ReplaceSubstring(@ReplaceSubstring(@Implode(TextList;"~");" ";"^");".";"`");
 NewTextList := @Explode(TextString;"~")

This example replaces space with "^" and periods with "`" before you do the explode. If you use elements of the new textlist, remember to convert these characters back to spaces and periods.


How do you delete Environment variables from the notes.ini file?

ENVIRONMENT environmental variable := ""; 

If you're inside a formula, use this instead:

@SetEnvironment("environmental variable";"").


How do you prevent people from viewing with a different form and saving the result?

Method 1

Create two fields, 'origForm' and 'formCheck'. 'origForm' is computed when composed with the formula: Form. The 'formCheck' field is a hidden editable field with a translation formula of:

@If(@IsNewDoc=0 & origForm <> Form; @Prompt([OK]; ""; "You cannot use the view menu to compose new documents.  Use the Compose menu or buttons.  You will not be permitted to save this document"); "");

In the validation formula, there is a copy of the above formula with @Failure instead of the @Prompt.

Method 2

1. Take all the forms out of the Compose menu. This will remove them from the View menu.

2. Add buttons for Compose. You can set up a Menu form or document with buttons on it, from which all Compose and View statements are executed. You can put this menu in a separate database or if you put in in the same database, create one instance, and then hide the form from the Compose menu.


Can you use @UserName in a view?

You can use @UserRoles and @UserName in a HideWhen of a shared view action button. This portion of the view is not stored on the server; it is only generated when a user opens a view.

For selection formulas, you cannot use @UserName because the view is generated by the server and the server is the user. Use Private On First Use if you have to make view selections dependent on the user name; private views are stored in the user's desktop.dsk file.


How do you sum number fields which have not been edited and initialized?

There are a few approaches to this problem. All of them include testing the value of each number field before summing the total:

mtotal := @If(field1 = ""; 0; field1) + @If(field2 = ""; 0; field2) +...;

Or in the input translation of each field you can do this:

@If(@IsNumber(FIELDNAME); FIELDNAME; 0)


Can you work around some of the size limitations in LotusScript programs?

The 32/64k limit applies to all events within an object. An object is anything with its own events. Buttons, forms, fields, and agents are all separate objects and have their own 32/64k limits.

For example, if you have code in the QueryOpen, PostOpen and QuerySave events within one form, all these count toward the form's 32/64k limit because they are all events for the form object.

Since there is no way to do a design synopsis which includes script, there is no easy way to figure out if you are at or near the limit. The only thing you can do is copy all the script from all the events for a particular object into a text file and then look at the size of the text file. This should APPROXIMATE the amount of compiled code (this is not a precise measure however).

There are several possible ways to work around the problem depending on where you are running into the limit.

1) Put the script in an text file and use the LotusScript %include command to load it. This script is not counted toward the memory limit. This is definitely the best workaround.

2) Put pieces of the script in entering events of hidden fields and use the LotusScript GotoField method to trigger the scripts.

3) Put additional scripts in subform events and embed the subforms in the form running into the memory limit. For example, if you're running out of space in the form object and wish to place more script in the PostOpen event, create a subform, place the desired code in its PostOpen event and insert the subform into the form.


How do you synchronize fields between documents created with two different forms?

In the 1st form, named "FORM1", create a computed field named FORM1_DocID. The FORM1_DocID field should be computed when composed and have this for the formula:

  @Text(@DocumentUniqueID)

For the documents created by the 2nd form, named "FORM2", you have to inherit and reference this FORM1_DocID value. To push information from Form2 to the document related by FORM1_DocID, you can put this your "Save & Exit" button of FOMR2:

  @SetDocField(FORM1_DocID;"form1_field1";form2_field1);
  @SetDocField(FORM1_DocID;"form1_field2";form2_field2);
  ...
  @Command([FileSave]);
  @Command([FileCloseWindow])


How do you automatically format phone numbers?

From andrew.olenick@prudential.com:

Use this code in the Exiting telephone field event. The user types in i.e. 2153339865 and the code formats the number.

Sub Exiting(Source As Field)
  On Error Goto err_SOLPhoneExit
  Dim workspace As New NotesUIWorkspace
  Set doc = workspace.CurrentDocument
  SOLPhone = doc.FieldGetText("telephone")

  ' If SOLPhone is null or like (123) 456-7890 then exit
  If SOLPhone = ""  Or SOLPhone Like  "([0-9][0-9][0-9]) [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]" Then
    Exit Sub
   ' If SOLPhone is 10 positions  and numeric, insert dashes and enter into SOLPhone field
  Elseif Len(SOLPhone) = 10  And Isnumeric(SOLPhone)  Then
    tSOLPhone = "(" + Left(SOLPhone,3) + ") " + Left(Right(SOLPhone,7),3) + "-" + Right(SOLPhone,4)
    Call doc.FieldSetText("telephone",tSOLPhone)
    Exit Sub
  Else
  ' if SOLPhone in error, give message
    Dim answer As Integer
    tSOLPhone = SOLPhone
    answer = Messagebox ("Enter the area code, exchange, and extension without the punctuation.  The  Phone # will be automatically formatted in (123) 456-7890 format.  Please try again.", MB_OK, "SOLPhone Incorrect")
    Call doc.FieldSetText("telephone",tSOLPhone)
    Call doc.GOTOFIELD("telephone")
    Exit Sub
  End If
err_SOLPhoneExit:
  Messagebox "Error " & Err() & ": " & Error()
  Exit Sub
End Sub

How do you select only Response documents in a view?

Turn off Show Response Hierarchy in your view properties and then and use this as your view selection formula:

  SELECT @IsAvailable($REF)

You can also do it using:

  Select @IsResponseDoc

or by selecting the names of the response forms like so:

  Select Form =
"the name of your response document you want to show"
& form =
"Another form that is a response document"


How do you validate a credit card number?

This LotusScript function will validate the checksum of a credit card number:

Function ValidateCC( strCC As String ) As Integer
  'Credit card validation algorithm
  'By Mark Dixon, Ives Development
  'Derived from http://prope.insa-lyon.fr/~fcoppola/credit.html
  'Parameter: strCC
  ' A string containing the card number to be validated.
  ' May contain non-numeric characters, e.g. spaces or dashes
  'Return value:
  ' True if the card number is good, False if the number is bad

  Dim nCheckSum As Integer
  Dim fDbl As Integer
  Dim nCharPos As Integer
  Dim nChar As Integer

  fDbl = False
  nCheckSum = 0


  'Read the card number from right to left
  For nCharPos = Len( strCC ) To 1 Step -1
    nChar = Asc( Mid( strCC, nCharPos, 1 ) ) - Asc( "0" )
    'Only process if the current character is a digit
    If 0 <= nChar And nChar <= 9 Then
      If ( fDbl ) Then
        nChar = nChar * 2
        If 10 <= nChar Then
          nChar = nChar - 9
        End If
      End If
      nCheckSum = nCheckSum + nChar
      fdbl = Not fdbl
    End If
  Next

  If nCheckSum Mod 10 = 0 Then ValidateCC = True Else ValidateCC = False
End Function


Can you make a popup calendar in Notes?

Put the following into text field and it will generate a calendar in your form. Make sure that the text area uses a fixed width font.

REM "Controllable spacing version from MStevenson@symantec.com";

TmpDate := @Date(@Today);

REM "HSP controls the horizontal size of the calendar.";
REM "Use 1 space for a really small calendar.";
REM "Default is 2 spaces.";
hsp := "  ";

REM "VSP controls the vertical size of the calendar";
REM "Default is 1 @Newline";
vsp := @Newline;

REM "DSP is used for the day headings";
dsp := hsp + " ";

REM "-------------------------- Calculate the days for this month";
A := @Adjust(TmpDate; 0; 0; -(@Day(TmpDate) - 1); 0; 0; 0);
A1 := @Weekday(A);
A2 := A;
A3 := @Month(A);
C1 := @Adjust(A2; 0; 0; (1 - A1); 0; 0; 0);
C2 := @Adjust(A2; 0; 0; (2 - A1); 0; 0; 0);
C3 := @Adjust(A2; 0; 0; (3 - A1); 0; 0; 0);
C4 := @Adjust(A2; 0; 0; (4 - A1); 0; 0; 0);
C5 := @Adjust(A2; 0; 0; (5 - A1); 0; 0; 0);
C6 := @Adjust(A2; 0; 0; (6 - A1); 0; 0; 0);
C7 := @Adjust(A2; 0; 0; (7 - A1); 0; 0; 0);
C8 := @Adjust(A2; 0; 0; (8 - A1); 0; 0; 0);
C9 := @Adjust(A2; 0; 0; (9 - A1); 0; 0; 0);
C10 := @Adjust(A2; 0; 0; (10 - A1); 0; 0; 0);
C11 := @Adjust(A2; 0; 0; (11 - A1); 0; 0; 0);
C12 := @Adjust(A2; 0; 0; (12 - A1); 0; 0; 0);
C13 := @Adjust(A2; 0; 0; (13 - A1); 0; 0; 0);
C14 := @Adjust(A2; 0; 0; (14 - A1); 0; 0; 0);
C15 := @Adjust(A2; 0; 0; (15 - A1); 0; 0; 0);
C16 := @Adjust(A2; 0; 0; (16 - A1); 0; 0; 0);
C17 := @Adjust(A2; 0; 0; (17 - A1); 0; 0; 0);
C18 := @Adjust(A2; 0; 0; (18 - A1); 0; 0; 0);
C19 := @Adjust(A2; 0; 0; (19 - A1); 0; 0; 0);
C20 := @Adjust(A2; 0; 0; (20 - A1); 0; 0; 0);
C21 := @Adjust(A2; 0; 0; (21 - A1); 0; 0; 0);
C22 := @Adjust(A2; 0; 0; (22 - A1); 0; 0; 0);
C23 := @Adjust(A2; 0; 0; (23 - A1); 0; 0; 0);
C24 := @Adjust(A2; 0; 0; (24 - A1); 0; 0; 0);
C25 := @Adjust(A2; 0; 0; (25 - A1); 0; 0; 0);
C26 := @Adjust(A2; 0; 0; (26 - A1); 0; 0; 0);
C27 := @Adjust(A2; 0; 0; (27 - A1); 0; 0; 0);
C28 := @Adjust(A2; 0; 0; (28 - A1); 0; 0; 0);
C29 := @Adjust(A2; 0; 0; (29 - A1); 0; 0; 0);
C30 := @Adjust(A2; 0; 0; (30 - A1); 0; 0; 0);
C31 := @Adjust(A2; 0; 0; (31 - A1); 0; 0; 0);
C32 := @Adjust(A2; 0; 0; (32 - A1); 0; 0; 0);
C33 := @Adjust(A2; 0; 0; (33 - A1); 0; 0; 0);
C34 := @Adjust(A2; 0; 0; (34 - A1); 0; 0; 0);
C35 := @Adjust(A2; 0; 0; (35 - A1); 0; 0; 0);
C36 := @Adjust(A2; 0; 0; (36 - A1); 0; 0; 0);
C37 := @Adjust(A2; 0; 0; (37 - A1); 0; 0; 0);

REM "----------------------------------- Create display values";
B1 := @If(@Month(C1) != A3; "  "; @Right("0" + @Text(@Day(C1)); 2));
B2 := @If(@Month(C2) != A3; "  "; @Right("0" + @Text(@Day(C2)); 2));
B3 := @If(@Month(C3) != A3; "  "; @Right("0" + @Text(@Day(C3)); 2));
B4 := @If(@Month(C4) != A3; "  "; @Right("0" + @Text(@Day(C4)); 2));
B5 := @If(@Month(C5) != A3; "  "; @Right("0" + @Text(@Day(C5)); 2));
B6 := @If(@Month(C6) != A3; "  "; @Right("0" + @Text(@Day(C6)); 2));

REM "These days always have values, so do not bother with the extra check";
B7 :=  @Right("0" + @Text(@Day(C7)); 2);
B8 := @Right("0" + @Text(@Day(C8)); 2);
B9 := @Right("0" + @Text(@Day(C9)); 2);
B10 := @Right("0" + @Text(@Day(C10)); 2);
B11 := @Right("0" + @Text(@Day(C11)); 2);
B12 := @Right("0" + @Text(@Day(C12)); 2);
B13 := @Right("0" + @Text(@Day(C13)); 2);
B14 := @Right("0" + @Text(@Day(C14)); 2);
B15 := @Right("0" + @Text(@Day(C15)); 2);
B16 := @Right("0" + @Text(@Day(C16)); 2);
B17 := @Right("0" + @Text(@Day(C17)); 2);
B18 := @Right("0" + @Text(@Day(C18)); 2);
B19 := @Right("0" + @Text(@Day(C19)); 2);
B20 := @Right("0" + @Text(@Day(C20)); 2);
B21 := @Right("0" + @Text(@Day(C21)); 2);
B22 := @Right("0" + @Text(@Day(C22)); 2);
B23 := @Right("0" + @Text(@Day(C23)); 2);
B24 := @Right("0" + @Text(@Day(C24)); 2);
B25 := @Right("0" + @Text(@Day(C25)); 2);
B26 := @Right("0" + @Text(@Day(C26)); 2);
B27 := @Right("0" + @Text(@Day(C27)); 2);
B28 := @Right("0" + @Text(@Day(C28)); 2);
B29 := @Right("0" + @Text(@Day(C29)); 2);

B30 := @If(@Month(C30) != A3; "  "; @Right("0" + @Text(@Day(C30)); 2));
B31 := @If(@Month(C31) != A3; "  "; @Right("0" + @Text(@Day(C31)); 2));
B32 := @If(@Month(C32) != A3; "  "; @Right("0" + @Text(@Day(C32)); 2));
B33 := @If(@Month(C33) != A3; "  "; @Right("0" + @Text(@Day(C33)); 2));
B34 := @If(@Month(C34) != A3; "  "; @Right("0" + @Text(@Day(C34)); 2));
B35 := @If(@Month(C35) != A3; "  "; @Right("0" + @Text(@Day(C35)); 2));
B36 := @If(@Month(C36) != A3; "  "; @Right("0" + @Text(@Day(C36)); 2));
B37 := @If(@Month(C37) != A3; "  "; @Right("0" + @Text(@Day(C37)); 2));

ShowMonth := @Select(@Month(A2); "January"; "February";
 "March"; "April"; "May"; "June"; "July"; "August";
 "September"; "October"; "November"; "December");

REM "---------------------- Display the calendar";
sp + sp + sp + ShowMonth + " " + @Text(@Day(TmpDate)) + ", " + @Text(@Year(A2)) + vsp +

" " + "S" + dsp + "M" + dsp + "T" + dsp + "W" + dsp + "T" + dsp + "F" + dsp + "S" + vsp +
B1 + hsp + B2 + hsp + B3 + hsp + B4 + hsp + B5 + hsp + B6 + hsp + B7 + vsp +
B8 + hsp + B9 + hsp + B10 + hsp + B11 + hsp + B12 + hsp + B13 + hsp + B14 + vsp +
B15 + hsp + B16 + hsp + B17 + hsp + B18 + hsp + B19 + hsp + B20 + hsp + B21 + vsp +
B22 + hsp + B23 + hsp + B24 + hsp + B25 + hsp + B26 + hsp + B27 + hsp + B28 + hsp + vsp +
B29 + hsp + B30 + hsp + B31 + hsp + B32 + hsp + B33 + hsp + B34 + hsp + B35 + hsp + vsp +
B36 + hsp + B37


How do you get the current access level of a user?

Dim s As New notessession
Dim db As notesdatabase

Set db = s.CurrentDatabase
Set level% = db.CurrentAccessLevel

This will return a value ranging from 0 to 6 (0 = No Access, 6 = Manager, all other levels in between).


Can you open a view in full screen mode from a Navigator?

For the navigator hotspot action, select Formula and use this:

@Command([OpenView]; "Your View here...");
@Command([ViewNavigatorsNone])


How do you generate a subset of list values based on the contents of another list?

This is useful for getting a subset of roles based on what state a document's workflow is in.

REM "From Damien Katz @ Unity Consulting"
tList1 :=tList1;
tList2 := tList2;
tItemsToKeep :=tItemsToKeep;
REM "Set variable tList1 to the list of values you want scan.";
REM "Set variable tList2 to corresponding list of values that you want to keep.";
REM "Set tItemsToKeep to the values you want ton scan list1 for.";
REM "tFilteredList returns the values from List2 that are kept";

REM "**************************Begin Function";
tListLength := @Elements( tList2);

tDigits := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";

tNumbers :=  @Subset( @If( tListLength  <= 10; tDigits; tListLength >= 100;  tDigits *+ tDigits; tDigits *+ tDigits *+ tDigits); tListLength ) + "$$";

tMixedListWithNumbers := @Replace( tNumbers *+ tItemsToKeep ; tNumbers + tList1; tNumbers + tList2);
tMixedListWithoutNumbers := @Right( tMixedListWithNumbers; "$$");
tFilteredList := @Trim( @Replace( tMixedListWithoutNumbers ; tItemsToKeep ; ""));
REM "**************************End Function";


How do you keep a running history of all entries in an RTF field?

Put this script in the QueryClose event of the document. It will append the RTF field named Details to an RTF field named Details_History:

FIELD Details:
Rich Text, Editable
Hide when previewed for reading, opened for reading, printed

FIELD Details_History:
Rich Text, Computed
never hide


Sub Queryclose(Source As Notesuidocument, Continue As Variant)
 On Error Goto Oops

 Dim session As New NotesSession
 Dim doc As NotesDocument
  Set doc = session.CurrentDatabase.GetDocumentByUNID(Source.Document.UniversalID)
 Dim vDetails As Variant
  Set vDetails = doc.GetFirstItem("Details")
 Dim vHistory As Variant
  Set vHistory = doc.GetFirstItem("Details_History")

  If (vDetails.Type = RICHTEXT And vHistory.Type = RICHTEXT) Then
    Call vHistory.AppendText(Format$(Now, "mm/dd/yy hh:nn AM/PM") & " by " & session.CommonUserName & ":  ")
    Call vHistory.AppendRTItem(vDetails)
    Call vHistory.AddNewLine(2)
    Call vDetails.Remove
    Call doc.Save(True, False)
  End If

TheEnd:
  Continue = True
Exit Sub

Oops:
  Resume TheEnd
End Sub


Can you use @Functions in LotusScript?

You can use the LotusScript Evaluate function. For example, this gets the common name portion of a hierarchical user name:

  Dim szCommonName As String
  Dim result As Variant
  result = Evaluate("@Name([CN]; @Username)")
  szCommonName = result(0)


Can you use views from the Mail template in your own databases?

There are two useful design elements from the mail template that can be used in your Notes databases:

"All Documents" is a view. Create this by creating a new view named ($All) that HAS to be a shared view. It will automatically name itself "All Documents" and you can change the design to suit your needs. If will also have an icon of several little sheets of paper and appear above the private and shared views and folders.

"Trash" is a folder. Create this by creating a new folder named ($Trash) that also HAS to be a shared folder. It will appear under the "All Documents" view and have the look of little trash can. A suggestion would be to make this folder NOT Show response documents in hierarchy so that you can drag response documents into the trash without having to drag its parent document. Now design it how ever you like. Users can drag to the trash can, exit the database without deleting the documents and have them in the trash can when they come back into the database.


How do you compare date/time fields using Lotuscript?

This example will compare two date fields in document:

mdate1V = document.DateField1(0)
mdate2V = document.DateField2(0)
If mdate1V < mdate2V Then
  MsgBox "DATE 1 LESS THEN DATE 2"
Else
  MsgBox "DATE 2 LESS THEN OR EQUAL TO DATE 1)
End If

How do you go to the last document in a category using Lotuscript?

You can also use the routine below to move onto the next category by moving to the last document in a specified category, then using view.GetNextDocument to move to the next category. Call the routine below as follows:

notesBackendDoc = GoToLastDocInCategory(StartDoc, View, FieldName, CategoryValue)

where:
StartDoc
- a NotesDocument in the category that you want to start out the search from.
View
- a NotesView that you will be searching.
FieldName
- a String containing the name of the item that holds the Category value.
CategoryValue
- a String containing what category value to check for.

Function GoToLastDocInCategory(StartDoc As NotesDocument, CheckView As
NotesView, ItemName As String, CategoryValue As String) As NotesDocument
  Dim PreviousDoc As NotesDocument
  Dim NextDoc As NotesDocument
  Dim CategoryItem As NotesItem
  Dim ThisDocCategoryValue As String
  Set NextDoc = StartDoc
  Set PreviousDoc = StartDoc
  Set CategoryItem = StartDoc.GetFirstItem(ItemName)
  ThisDocCategoryValue = CategoryItem.Text
  Do While Not(NextDoc Is Nothing) And (CategoryValue = ThisDocCategoryValue)
    Set PreviousDoc = NextDoc
    Set NextDoc = CheckView.GetNextDocument(NextDoc)
    If NextDoc Is Nothing Then Exit Do
    Set CategoryItem = NextDoc.GetFirstItem(ItemName)
    ThisDocCategoryValue = CategoryItem.Text
  Loop
  Set GoToLastDocInCategory = PreviousDoc
End Function


Why does my agent fail on the server with a "Error loading USE or USELSX module" error?

If this works fine on the client when you run the agent manually, you have probably included a references to a NotesUI class in your library. Remove all references to NotesUI classes and the agent should run.


How do you create mailing labels in Notes?

The Lotus Approach database program has built-in support for Avery labels and can read from a Notes database directly.

Intelliprint from CSS can print labels from Notes views.

NotesToPaper by SoftVision Development.

[email] Ulrich-Krause posted this example of using OLE Automation to create labels in MS Word:

Sub Click(Source As Button)
  ' This is how you call it from a view (it uses all documents in the view)
  Call CreateMailingLabels("Subject","Subject","Subject","Subject","Subject")
End Sub


Sub CreateMailingLabels(Line1Fields As Variant,Line2Fields As Variant,Line3Fields As Variant,Line4Fields As Variant,Line5Fields As Variant)
  ' requires MS Word 97 or Word 2000
  ' works on any Notes view or folder
  ' supply list of Notes field names to be concatenated onto each line of the
  ' label in the parameters Line1Fields etc
  '
  ' e.g.
  '
  ' Call CreateMailingLabels_
  '   ("FirstName,LastName","HouseNo,Street","Town","County","PostCode")

  On Error Goto ErrorProc

  ' declare Notes back-end objects
  Dim session As New notessession
  Dim db As notesdatabase
  Dim doc As NotesDocument
  Dim dc As NotesDocumentCollection 
  Set db = session.currentdatabase
  Dim settings As NotesDocument
  Set settings=db.GetProfileDocument("Settings","")

  ' declare Notes front-end objects 
  Dim ws As New NotesUIWorkspace 

  Set dc=db.UnProcessedDocuments

  ' Find out active Notes view
  Dim view As NotesView
  Dim UIView As NotesUIView
  Set view = UIView.View

  ' declare constants

  wdCell = 12 'Microsoft Word VBA constant. Designates unit for table cell.
  wdLine=5
  wdCustomLabelA4 = 2
  cr = Chr(13) & Chr(10) ' Carriage return.

  SelectedDocuments=Messagebox("Do you want to use all the addresses in this view?",35,db.Title) 
  If SelectedDocuments=2 Then
    Messagebox "Merge Cancelled",16,db.Title
    Exit Sub
  End If

  If SelectedDocuments=7 Then
    TickBased%=True
  End If
  ' get required template from user

  LabelTemplate = Inputbox("Please enter the MS Word mailing label name to use. ", db.Title,"Avery L7413")

  ' trim off "Avery" prefix if used 
  LabelTemplateName=LabelTemplate 
  If Left$(LabelTemplate,5)="Avery" Then
    LabelTemplate=Trim$(Right$(LabelTemplate,Len(LabelTemplate)-5))
  End If
  If Instr(LabelTemplate,"-")>1 Then
     LabelTemplate=Trim$(Left$(LabelTemplate,Instr(LabelTemplate,"-")-1))
  End If
  If LabelTemplate="" Then
    Messagebox "Merge Cancelled",16,db.Title
    Exit Sub
  End If

  ' Create an instance of Excel
  Dim wrd As Variant 
  Set wrd = CreateObject("word.application")

  ' create a new Word document only if required 
  If wrd.documents.count=0 Then
    Call wrd.documents.add 
  End If

  ' create new mailing lanels 
  Print "Generating ";LabelTemplateName;" mailing labels in MS Word"
  On Error Goto TrapTemplateName 
  Call wrd.MailingLabel.CreateNewDocument(LabelTemplate)
  On Error Goto ErrorProc

  ' create each mailing label from line of Notes view

  LabelCount!=0
  If TickBased% Then
    Set doc=dc.GetFirstDocument
  Else
    Set doc=view.GetFirstDocument 
  End If
  While Not doc Is Nothing

    ' build label text

    LabelAddress = GetListFieldValues(doc,Line1Fields) & cr
    LabelAddress = LabelAddress & GetListFieldValues(doc,Line2Fields) & cr
    LabelAddress = LabelAddress & GetListFieldValues(doc,Line3Fields) & cr
    LabelAddress = LabelAddress & GetListFieldValues(doc,Line4Fields) & cr
    LabelAddress = LabelAddress & GetListFieldValues(doc,Line5Fields)

    If Not SingleColumn% Then
      Call wrd.Selection.TypeText(LabelAddress) ' Insert full address into Word.
      On Error Goto TrapSingleColumn
      Call wrd.Selection.MoveRight(wdCell) ' Move one cell to the right. 
      On Error Goto ErrorProc
      If SingleColumn% Then
        Call wrd.MailingLabel.CreateNewDocument(LabelTemplate,LabelAddress) 
      End If
    Else
      Call wrd.MailingLabel.CreateNewDocument(LabelTemplate,LabelAddress) 
    End If
    LabelCount!=LabelCount!+1
    If TickBased% Then
      Set doc = dc.GetNextDocument(doc)
    Else
      Set doc = view.GetNextDocument(doc)
    End If
  Wend
  If TickBased% Then
    Print LabelCount!;" labels created from selected addresses" 
  Else
    Print LabelCount!;" labels created from this Notes view (";SheetTitle$;")"
  End If
  REM Make the instance visible to the user
  wrd.visible = True

Exit Sub

TrapTemplateName:
  Messagebox "Incorrect Template Name",16,db.Title
  Exit Sub

TrapSingleColumn:
  Print "Detected non-table labels and changing behaviour accordingly" 
  SingleColumn%=True
  Resume Next
  ErrorProc:
  Print "(";Erl;") ";Error$
  Resume Next

End Sub


Function GetListFieldValues(doc As NotesDocument, FieldList As Variant) As String
  Dim TempList As String
  Dim TempOutput As String
  Dim TempArray As Variant
  Dim ThisField As String

  TempList=FieldList
  TempOutput=""
  If TempList<>"" Then
    ' parse list of fields 
    While Len(TempList)>0
      If Instr(TempList,",")>0 Then
        ThisField=Trim(Left$(TempList,Instr(TempList,",")-1))
        TempList=Right$(TempList,Len(TempList)-Instr(TempList,","))
      Else
        ThisField=Trim(TempList)
        TempList=""
      End If
      ' retrieve notes field 
      If Instr(ThisField,"(")>0 And Instr(ThisField,")")>0 Then
        ThisFieldTemp$=Right$(ThisField,Len(ThisField)-Instr(ThisField,"("))
        ThisFieldIndex%=Val(Left$(ThisFieldTemp$,Len(ThisFieldTemp$)-1))
        ThisField=Left$(ThisField,Instr(ThisField,"(")-1)
      Else
        ThisFieldIndex%=-1
      End If
      TempArray=doc.GetItemValue(ThisField)
      If ThisFieldIndex%>=0 Then
        If Ubound(TempArray)>=ThisFieldIndex% Then 
          TempOutput=TempOutput+" "+TempArray(ThisFieldIndex%)
        End If
      Else
        TempOutput=TempOutput+" "+TempArray(0) 
      End If
    Wend
  End If
  GetListFieldValues=TempOutput
End Function


What are the Unix syntaxes for calling DLLs from LotusScript?

For AIX: Declare Function name Lib "libnotes_r.a" Alias "alias" (arglist)
For Solaris: Declare Function name Lib "libnotes.so" Alias "alias" (arglist)
For HP/UX: Declare Function name Lib "libnotes.sl" Alias "alias" (arglist)


Can you dynamically select documents per user without using a ReaderNames field?

Use this for a view selection formula:
username := @Name([CN]; @UserName);
Select (Form = "OrderForm") & (@Contains(OrderOwner;username) = 1)
and set the View Properties so that the view index is discarded every time. This will select documents that use the form "OrderForm" and were created by the current user.


How do you display documents created with a form using a layout region?

Use LotusScript in the QueryOpenDocument Event of the view to do this:

Set the variable CONTINUE = FALSE to prevent the document from being displayed using the form.
Get the collection from the NotesUIView.Documents property (the NotesUIView is the SOURCE variable for the event). The first document in the collection is the one the user is trying to open.
Use the NotesUIWorkspace.DialogBox method to open this document.


Why doesn't NotesUIDocument.GetFieldText return the alias for a keyword selection?

The NotesUIDocument class is designed to work with the interface elements (what is being displayed) of a document. Because of this, the value you retrieve from a field is what is being displayed to a user.
If you are designing a new form, a workaround you can use is to make the aliases numerical such as:

  Draft | 1
  Submitted | 2
  Comment | 3

Then, you can handle each case using this code (if the keyword field is named "Status"):
  Dim ws As New NotesUIWorkspace
  Dim doc As NotesDocument
  Dim uidoc As NotesUIDocument
  Dim desiredresult As Variant
  Set uidoc = ws.CurrentDocument
  Set doc = uidoc.Document
  desiredresult = doc.GetItemValue("Status")
  Select Case desiredresult(0)
  Case "1"
       ...
  Case "2"
       ...
  Case Else
       ...
  End Select

Why doesn't my "If documents have been created or modified" agent execute immediately?

The agent manager task only executes every 30 minutes by default. This is why the "when created/modified" agent runs every 30 minutes. You can change these two lines in your notes.ini file to decrease the delay between runs of the agent manager:


  AMgr_DocUpdateAgentMinInterval=value in minutes(default is 30)
  Amgr_DocUpdateEventDelay= value in minutes (default is 5)

The lowest values recommended for these two values are 5 and 2, respectively.


How do you insert a new line in a Rich Text field using a macro?

@Command([EditInsertText]; @Char(0))

Instead of using @Char(0), you can also use @Newline.


How do you set the server to run agents on with a hidden database design?

Add a configure button to the application's UI. You can use these @Commands in the button

  @Command([AgentSetServerName];"AgentName");  // prompts for server name
  @Command([AgentEnableDisable];"AgentName"; "1");

Alternately, you could have a second unhidden database which just has agents which call agents within the hidden database. This technique lets the user set the schedule for the other agents. Here is code that will run an agent in another database:

Sub Initialize
  Dim db As New NotesDatabase("", "")
  Dim Agent As NotesAgent
  If (db.open("", "hiddenDB.nsf")) Then
    Set Agent = db.GetAgent("(Check Servers For Response)")
    If Not(Agent Is Nothing) Then
      Call Agent.Run
    Else
      Msgbox "Agent not found..."
    End If
  End If
End Sub


How do you sort a document collection?

Here is an example that does a bubble sort on the document collection using two fields as the sort key:

Sub Swap(doc1 As NotesDocument ,doc2 As NotesDocument)
 Dim d As NotesDocument
 Set d = doc1
 Set doc1 = doc2
 Set doc2 = d     
End Sub

Sub CollectionToSortedArray(col As NotesDocumentCollection, docs() As NotesDocument)     
 Dim i As Integer
 Dim doc As NotesDocument
 Dim n As Integer, sorted As Integer
 Dim s1, s2 As String
     
 n = col.count
 Redim docs(n) As NotesDocument
 ' push to array
 For i = 0 To n - 1
   Set docs(i) = col.GetNthDocument(i+1)
 Next
     
 'bubble sort array
 sorted = False
 Do While (sorted = False)          
   sorted = True
   For i = 0 To n - 2
     ' sort based on QuoteNo and ItemNo fields
     s1 = docs(i).QuoteNo(0) & docs(i).ItemNo(0)
     s2 = docs(i+1).QuoteNo(0) & docs(i+1).ItemNo(0)
     If(Strcomp(s1, s2, 5) = 1) Then  'No Pitch, No Case Compare
       Call Swap(docs(i),docs(i+1))
       sorted = False
     End If
   Next
 Loop
End Sub


Here's another sample using a Merge Sort which is faster for large collections:

Sub qsort(element, sortkeys)
  ' To be called by REFERENCE
  Call mergesort(element, sortkeys, Lbound(sortkeys), Ubound(sortkeys), False)
End Sub

Sub mergesort(element, sortkeys, l As Integer, r As Integer, isclass As Variant)
  ' From Paul Hudson (paulh@harlequin.co.uk)
  ' Called by qsort, but can be used on its own
  Dim i As Integer, j As Integer, k As Integer, m As Integer
  Dim b1(), b2()

  If (r - l > 0) Then
    m = (r + l) \ 2
    Call mergesort(element, sortkeys, l, m, isclass)
    Call mergesort(element, sortkeys, m + 1, r, isclass)
    Redim b1(l To r)
    Redim b2(l To r)
    For i = l To m
      If isclass Then
        Set b1(i) = element(i)
      Else
        b1(i) = element(i)
      End If
      b2(i) = sortkeys(i)
    Next
    For j = m+1 To r
      If isclass Then
        Set b1(r + m + 1 - j) = element(j)
      Else
        b1(r + m + 1 - j) = element(j)
      End If
        b2(r + m + 1 - j) = sortkeys(j)
    Next
    i = l
    j = r
    For k = l To r
      If b2(i) < b2(j) Then
        If isclass Then
          Set element(k) = b1(i)
        Else
          element(k) = b1(i)
        End If
        sortkeys(k) = b2(i)
        i = i + 1
      Else
        If isclass Then
          Set element(k) = b1(j)
        Else
          element(k) = b1(j)
        End If
        sortkeys(k) = b2(j)
        j = j - 1
      End If
    Next
  End If
End Sub


What HelpDesk products are available for Notes?

Thanks to Charles Ross for compiling this list :-)


Company

Product name
Alvea
Help Desk
Arcidea (bought out Synergistics) helpdesk
Ardexus service CARE
Automation Centre Tracker Suite
DataWatch Visual Help Desk
Cadence Solutions Extended Reach�Helpdesk
CogniCase Maximizer Enterprise Helpdesk
Eden ProjectTrak Helpdesk
GeoCom GmbH HelpDesk
groupapps.com dispatch
Groupsoft Systems Groupsoft Help Desk for Domino
GSX GSX Help Desk
GWI c.support
Mayflower HelpDesk
Relavis (sells OverQuota) eService for Domino
Techflow, Inc. Enhanced Help Desk R4
Velocity Integration Software VI Service Desk

It's interesting that the PLATO Notes was originally created for a help desk application (see the History of Notes)

How do you force a user to use a button to save a form?

This code satisfies three requirements:
1) prevent users from saving a form via menu or keyboard controls; only the action button can be used for saves
2) only use ONE field to accomplish to keep application overhead down
3) depending on a value in a status field, action button should issue file save or file save new version

AllowSave field: Editable, Text, Hidden

  Default value = "0"
  Validation = @If(AllowSave = "0"; @Failure(...); @Success)

Action Button:

  FIELD AllowSave := "1";
  @If(Status = "Open";
    @PostedCommand([FileSaveNewVersion]);
    @PostedCommand([FileSave])
  )

Form PostOpen Event:

  FIELD AllowSave := "0";
  @SetField("AllowSave"; "0")


Can you start a program in Notes and wait for it to finish?

Yes, you can use this LotusScript code to do so:

Public Type STARTUPINFO
  cb As Long
  lpReserved As String
  lpDesktop As String
  lpTitle As String
  dwX As Long
  dwY As Long
  dwXSize As Long
  dwYSize As Long
  dwXCountChars As Long
  dwYCountChars As Long
  dwFillAttribute As Long
  dwFlags As Long
  wShowWindow As Integer
  cbReserved2 As Integer
  lpReserved2 As Long
  hStdInput As Long
  hStdOutput As Long
  hStdError As Long
End Type

Public Type PROCESS_INFORMATION
  hProcess As Long
  hThread As Long
  dwProcessID As Long
  dwThreadID As Long
End Type

Declare Function WaitForSingleObject Lib "kernel32" (Byval _
  hHandle As Long, Byval dwMilliseconds As Long) As Long

Declare Function CreateProcessA Lib "kernel32" (Byval _
  lpApplicationName As Long, Byval lpCommandLine As String, Byval _
  lpProcessAttributes As Long, Byval lpThreadAttributes As Long, _
  Byval bInheritHandles As Long, Byval dwCreationFlags As Long, _
  Byval lpEnvironment As Long, Byval lpCurrentDirectory As Long, _
  lpStartupInfo As STARTUPINFO, lpProcessInformation As _
  PROCESS_INFORMATION) As Long

Declare Function CloseHandle Lib "kernel32" (Byval _
  hObject As Long) As Long

Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const INFINITE = -1&

Public Sub ShellAndWait(Byval RunProg As String)
  ' From Kevin Pauli (kcpauli@usa.net)
  Dim RetVal As Long
  Dim proc As PROCESS_INFORMATION
  Dim StartInf As STARTUPINFO
  StartInf.cb = Len(StartInf)
  'Execute the given path
  RetVal = CreateProcessA(0&, RunProg, 0&, 0&, 1&, _
  NORMAL_PRIORITY_CLASS, 0&, 0&, StartInf, proc)

  'Disable this app until the shelled one is done
  RetVal = WaitForSingleObject(proc.hProcess, INFINITE)
  RetVal = CloseHandle(proc.hProcess)
End Sub

However, note that it is only for Win32 systems. The concept would be the same for other operating systems.


How do you access a specific element of a multi-value field?

To extract element 'n' from the field "Choices":

  @Subset(@Subset(Choices;n); -1)


How do you prevent orphan response documents?

The Database Script in Notes 4.5 has a PostDocumentDelete event which is called when documents are deleted. You can use it to automatically delete response documents associated with a document:

Sub PostDocumentDelete(Source As NotesUIDatabase)
  Dim db As NotesDatabase
  Dim doc As NotesDocument
  Dim coll As NotesDocumentCollection
  Dim coll2 As NotesDocumentCollection
  Dim curdoc As NotesDocument
  Set db = source.Database
  Set coll2 = source.Documents
  For x = 1 To coll2.Count
    Set curdoc = coll2.GetNthDocument(x)
    Set coll = curdoc.Responses
    If coll.count > 0 Then Call coll.RemoveAll(True)
  Next
End Sub


How do you find out what Roles a user has in the current database?

This function will give you a list of all roles a user is in, even if the user is in nested groups which are listed in the ACL.

' Roles(UserName$) :
'        It checks for roles of UserName$ (could be both abbreviate or hierarchical) in current database
'        If no exact ACL found it checks all group recursively
'        If UserName$ is member of two or more groups it returns the roles of all of them
'        If nothing found, it returns "-Default-" roles
'        It doesn't check duplicate roles (but this is not a problem)
'    Return value: array of roles (strings)
'
' IsaMemberOf(UserName$, GroupName$) :
'        Check if UserName$ is a member of GroupName$
'        It searchs group recursively
'        It searchs first public address book
'    Return value: boolean
'
' NameSimple$(Name$) :
'        Just acts like formula @Name( [Abbreviate]  ; Name )
'    Return value: string
'
' Marco Beri
' marcob@equalis.it

Function Roles(UserName$) As Variant
  Dim AllRoles As Variant
  Dim session As New NotesSession
  Dim db As NotesDatabase
  Dim acl As NotesACL
  Dim entry As NotesACLEntry
  Dim FirstGroupFound%

  Set db = session.CurrentDatabase
  Set acl = db.ACL
  Set entry = acl.GetEntry( UserName$ )
  If entry Is Nothing Then
    Set entry = acl.GetEntry( NameSimple$(UserName$) )
  End If
  If Not entry Is Nothing Then
    AllRoles = entry.Roles
  Else
    Set entry = acl.GetFirstEntry
    Do While Not entry Is Nothing
      'Default roles (survives only if no other found)
      If Trim$(Ucase$(entry.name)) = Ucase$("-Default-") Then
        AllRoles = entry.roles
      Else
        If IsaMemberOf(UserName$, entry.name) Then
          If FirstGroupFound% Then
            Redim Preserve AllRoles(Ubound(AllRoles)+Ubound(entry.roles)+1)
            For Cont%=0 To Ubound(entry.roles)
              AllRoles(Ubound(AllRoles)-Cont%) = entry.roles(Cont%)
            Next
          Else
            FirstGroupFound% =True
            AllRoles=entry.roles
          End If
        End If
      End If
    Set entry = acl.GetNextEntry( entry )
    Loop
  End If
  Roles = AllRoles
End Function

Function IsaMemberOf(UserName$, GroupName$)
  On Error Goto IsaMemberOfError

  Dim doc As NotesDocument
  Static ViewGroup As NotesView

  If (ViewGroup Is Nothing) Then
    Dim PublicBook As Variant
    Dim session As New NotesSession

    Set PublicBook=Nothing
    Forall Book In session.AddressBooks
      If (Book.IsPublicAddressBook) Then
        Set PublicBook=Book
        Exit Forall
      End If
    End Forall
    If PublicBook Is Nothing Then
      Forall Book In session.AddressBooks
        Set PublicBook=Book
        Exit Forall
      End Forall
    End If
    If Not (PublicBook Is Nothing) Then
      Call PublicBook.Open("", "")
      Set ViewGroup=PublicBook.GetView("Groups")
      If ViewGroup Is Nothing Then
        Messagebox "No group view found"
      End If
    Else
      Messagebox "No address book found"
      Exit Function
    End If
  End If

  Set doc=ViewGroup.GetDocumentByKey(GroupName$)
  If doc Is Nothing Then
    IsaMemberOf = False
  Else
    If Not (doc Is Nothing) Then
      Forall Member In doc.Members
        If Trim$(Ucase$(Member)) = Trim$(Ucase$(UserName$)) Or Trim$(Ucase$(Member)) = Trim$(Ucase$(NameSimple(UserName$))) Then
          IsaMemberOf = True
          Exit Forall
        Else
          If IsaMemberOf(UserName$, Cstr(Member)) Then
            IsaMemberOf = True
            Exit Forall
          End If
        End If
      End Forall
    End If
  End If

Exit Function

IsaMemberOfError:
  Messagebox "IsaMemberOf"+Str$(Err)+": "+Error$
  Exit Function
End Function

Function NameSimple$(Byval NameToConvert$)
  Dim InstrUguale%,Cont%,NameResto$
  Do
    InstrUguale%=Instr(NameToConvert$,"=")
    If InstrUguale%=0 Then
      Exit Do
    End If
    NameResto$=Mid$(NameToConvert$,InstrUguale%+1)
    For Cont%=InstrUguale%-1 To 0 Step -1
      If Cont%=0 Then
        NameToConvert$=""
      Elseif Mid$(NameToConvert$,Cont%,1)="/" Then
        NameToConvert$=Left$(NameToConvert$,Cont%)
        Exit For
      End If
    Next
    NameToConvert$=NameToConvert$+NameResto$
  Loop
  NameSimple$=NameToConvert$
End Function


An alternative way of doing this is to use the Evaluate of doing this (providing you aren't using early versions of Notes 4.x which leaked memory each time Evaluate was called):

  Dim UserRoles As Variant
  UserRoles = Evaluate("@UserRoles")

How do your control an OLE-VBA object in LotusScript?

This code uses the OLE support in the Win32 version of Notes to set the A1 cell in the Sheet1 worksheet to a value from Field1 in the current document:

  Set obj = CreateObject("Excel.Application")
  obj.Workbooks.Open "c:\test.xls"
  value = doc.GetItemValue("Field")(0)
  obj.Worksheets("Sheet1").Range("A1").value = value


How do you add a response's doclink to a parent document?

Assuming you have a way to get to the response document, or you are already in the response document, try this:

Sub PlaceDocLinkInParent (rdoc As NotesDocument)
REM "NOTE" rdoc is supposed to be the response document that you want the 
REM Doc Link for.  As I said above, I will assume you have obtained this and
REM just need to call this procedure to place the doc link.

Dim sess As New NotesSession
Dim db As NotesDatabase
Dim pdoc As NotesDocument
Dim m_unid As String
Dim rt As NotesRichTextItem
Set db = sess.CurrentDatabase
m_unid$ = rdoc.ParentDocumentUNID
Set pdoc = db.GetDocumentByUNID(m_unid$)
Set rt = New NotesRichTextItem(pdoc, "DocLinkField")
Call rt.AppendDocLink(rdoc, "Link To Reponse Document")
Call pdoc.Save(True, False)
End Sub

Basically, this script gets the parent unid from the response, then gets the parent document with the GetDocumentByUNID method, creates a new rich text field in the parent and places a doc link in it.


How do you track who changed a critical field?

If you want to add an audit trail of the modifications made to a critical field named "PRIORITY" it can be done with the following:
1) Create a duplicate "PRIORITY" field (named "PRIORITYDelta") that is hidden and has in its input translation formula a statement to store the contents of the PRIORITY field. This will retain the contents of the priority field before editing in a current session.
2) Create a multi-value field ("Updatesto") with a formula that compares PRIORITY to PRIORITYDelta when the document is being saved and when the condition is right (i.e. PRIORITY != PRIORITYDelta) attaches the new save information to the field:

feed := @Name([CN]; @V3UserName) + " on " + @Text(@Date(@Now)) + " at " + @Text(@Time(@Now)) + "     " +  PRIORITY;
FIELD Updatesto := @If(@IsDocBeingSaved & PRIORITY != PRIORITYDelta ; Updatesto : feed; Updatesto);
Updatesto

Note that the Revision field (Updatesto) needs to before PRIORITYDelta Field on the form for the revision field to keep a change history.


How do you find the exact version number of Notes?

The following converts the version number, including lettered versions, to dotted decimal format for easy comparison. 4.6.3b becomes version 4.6302, which is definitely a "higher" version than 4.6.2a (4.6201 in this code).

' from Scott Purl (spurl@acm.org)
' Yep.  Only one Dim.  Session.NotesVersion holds the key.
Dim Session As New NotesSession

' Find out the version information
MyVersion = Session.NotesVersion
' Get the length of the version string
Length = Len(MyVersion)
' Discover What the "half" delimiter is, meaning what's the first non-alphanumeric character starting from the left
' Note that in the North American and International versions, this first non-alpha character differs
If MyVersion Like "*Intl*" Then
   SepChar = "("
   OffSet = 0
Else
   SepChar = "|"
   OffSet = 2
End If
' Now find this first non-alphanumeric character
SepLoc = Instr(MyVersion, SepChar)
' Get everything to the left of that character
LeftHalf = Left(MyVersion, SepLoc - 1)
' Find the only space in the left half
SpacLoc = Instr(LeftHalf, " ")
' The version number is what's left to the right of the space in the left half, minus extra spaces
' code (with bug) was VerNum = Trim(Right(LeftHalf, SpacLoc - OffSet))
VerNum = Trim(Right(LeftHalf,  Len(LeftHalf) - SpacLoc - OffSet))
' Get the length of the version number
VerLen = Len(VerNum)
' Loop through the version number to remove periods
For x =1 To VerLen
CurChar = Mid(VerNum, x, 1)
If CurChar = "." Then
   ' Do Nothing
Else
   ' Keep appending non-period characters until finished
   SubVer = SubVer & CurChar
End If
Next

' If the right-most character is a letter, convert it to a number
' "a" becomes "01", and "z" becomes "26"
RSubVer = Right(SubVer, 1)
If Instr("abcdefghijklmnopqrstuvwxyz", RSubVer) Then
   ' The first 9 letters of the alphabet need a padding leading zero
   If Instr("abcdefghi", RSubVer) Then
      AlphaNum = Instr("abcdefghi", RSubVer)
      NewSubVer = Left(SubVer, Len(SubVer) -1) & "0" & AlphaNum
   End If
   ' The rest of the alphabet are all greater than 10 in position
   If Instr("jklmnopqrstuvwxyz", RSubVer) Then
      AlphaNum = Instr("abcdefghijklmnopqrstuvwxyz", RSubVer)
      NewSubVer = Left(SubVer, Len(SubVer) -1) & AlphaNum
   End If
' And the numeric equivalent of the position is
SubVer = NewSubVer
End If

' Assemble it into leftmost character, plus a period, plus the rest of the iterated sub version
TextVer = Left(SubVer, 1) & "." & Right(SubVer, Len(Subver) -1)

' Convert it to a normal number so we can do quick comparisons.
FinVer = Cstr(TextVer)

You can then do:

If FinVer >= 4.6302 Then
  'Action if True
Else
  'Action if False
End If

Where can I get more information on Java access to Notes?

Go to Lotus Developer Central and search for "Java".


How do you put a popup calendar in a layout region?

In Notes 4.x, if you have a date field in a layout region, it will automatically have a calendar popup. In other fields or in Notes 3.x, you can also use a linkpopup text field.


How do you operate on the currently selected documents in LotusScript?

To determine the currently selected document in a view you have to traverse it with the NotesDatabase property:UnprocessedDocuments which returns a document collection that you can examine.
The ... part in the code below marks where you can put code to access the active document. This may look strange, but it actually only returns the document that you currently stand on (unless you have marked a set of documents).

  Dim workspace As New notesuiworkspace
  Dim uidoc As notesuidocument
  Dim session As New NotesSession
  Dim db As NotesDatabase
  Dim collection As NotesDocumentCollection
  Dim doc As NotesDocument
  Set db = session.CurrentDatabase
  Set collection = db.UnprocessedDocuments
  If (Not collection Is Nothing) Then
    For i = 1 To collection.Count
      Set doc = collection.GetNthDocument(i)
      ...
    Next
  End If


In what order do @Commands execute?

1) Declare variables.
2) Everything else except a few @Commands.
3) The few @Commands that are not in (2).

These are the commands in category (3):
FileCloseWindow
FileDatabaseDelete
FileExit
NavigateNext
NavigateNextMain
NavigateNextSelected
NavigateNextUnread
NavigatePrev
NavigatePrevMain
NavigatePrevSelected
NavigatePrevUnread
ToolsRunBackgroundMacros
ToolsRunMacro
ViewChange
ViewSwitchForm


How do you display progress to the user via LotusScript?

If you are processing a large collection of documents, it would be useful to show the user progress so they don't think your application has hung. Here is a method to show a progress meter in the status bar in Notes:

REM "From Erden Eruc @ NGSINC"

Dim i As Integer
Dim s As String
     
'Is this 100 meter dash? no it is a meter with 100 dashes :)
     
s = "--------------------------------------------"&_
"--------------------------------------------------------"
For i = 1 To 100
  s = Left( Chr(1) & s, 100 )
  If i<10 Then
    Print "Percent Done:    "& Str(i)&"% - " & s 
  Elseif i = 100 Then
    Print "Percent Done:"& Str(i)&"% - " & s
  Else
    Print "Percent Done:  "& Str(i)&"% - " & s
  End If
Next
Print "Percent Done: 100% - Job complete..."


For a Win32-specific version that uses Notes functions (note: this is a dangerous thing to do, but if you really must have a graphical progress bar..):
Const NPB_TWOLINE% = 1
Const NPB_STATUSBAR% = 32

Declare Function NEMProgressBegin Lib "nnotesws.dll" ( Byval wFlags As Integer ) As Long
Declare Sub NEMProgressDeltaPos Lib "nnotesws.dll" ( Byval hwnd As Long, Byval dwIncrement As Long )
Declare Sub NEMProgressEnd Lib "nnotesws.dll" ( Byval hwnd As Long )
Declare Sub NEMProgressSetBarPos Lib "nnotesws.dll" ( Byval hwnd As Long, Byval dwPos As Long)
Declare Sub NEMProgressSetBarRange Lib "nnotesws.dll" ( Byval hwnd As Long, Byval dwMax As Long )
Declare Sub NEMProgressSetText Lib "nnotesws.dll" ( Byval hwnd As Long, Byval pcszLine1 As String, Byval pcszLine2 As String )

' From Mark Dixon @ Ives

Dim hwnd As Long
Dim i As Long
Dim j As Long
     
'Create the progress bar
hwnd = NEMProgressBegin( NPB_TWOLINE )
     
'Set the bar range - the default is 100
NEMProgressSetBarRange hwnd, 200
     
'Display some text on the dialog.  The second line is ignored if NPB_TWOLINE not specified
NemProgressSetText hwnd, "Calculating..", "Start"
     
For i = 0 To 200
  'Simple delay loop to give us time to see whats going on
  For j = 0 To 5000     
  Next
  'Update the bar position - we could also use NEMProgressDeltaPos hwnd, 1 here
  NEMProgressSetBarPos hwnd, i 
  'Update the text at halfway
  If i = 100 Then
    NEMProgressSetText hwnd, "Calculating", "Half way"
  End If
Next

'Destroy the dialog when we're done
NEMProgressEnd hwnd


How do you convert a numerical value to the equivalent written text?
To convert a field called origNumber to written text, use the field formula:

REM "Thanks to MStevenson@symantec.com for updating this for negative numbers";

REM "Get numerical value";
origNumber := <numerical value>;

REM "Convert negative to positive";
NUMBER := @If(origNumber = "";0;origNumber < 0;-origNumber;origNumber);

REM "Test range";
@If(NUMBER > 999999999999999; @Return("Quadrillion Plus!"); "");

REM "Parse, format fixed with 2 decimals";
money := @Text(NUMBER; "F2");
c := @Word(money; "."; 2);
d := @Right("000000000000000" + @Word(money; "."; 1); 15);

REM "Constants";
n := "" : "One" : "Two" : "Three" : "Four" : "Five" : "Six" : "Seven" :
 "Eight" : "Nine" : "Ten" : "Eleven" : "Twelve" : "Thirteen" :
 "Fourteen" : "Fifteen" : "Sixteen" : "Seventeen" : "Eighteen" :
 "Nineteen";
m := "" : "" : "Twenty " : "Thirty " : "Forty " : "Fifty " : "Sixty " :
 "Seventy " : "Eighty " : "Ninety ";

REM "Ones";
d0 := @Middle(d; 12; 3);
h0 := @Subset(@Subset(n; @TextToNumber(@Left(d0; 1)) + 1); -1);
v0 := @TextToNumber(@Right(d0; 2));

t0 := @Trim(@If(h0 = ""; ""; h0 + " Hundred ") + @Subset(@Subset(m; 
 1 + @TextToNumber(@Middle(d0; 1; 1))); -1) + @Subset(@Subset(n;
 @If(v0 > 19; @TextToNumber(@Right(d0; 1)); v0) + 1); -1));

REM "Thousands";
d1 := @Middle(d; 9; 3);
h1 := @Subset(@Subset(n; @TextToNumber(@Left(d1; 1)) + 1); -1);
v1 := @TextToNumber(@Right(d1; 2));

t1 := @Trim(@If(h1 = ""; ""; h1 + " Hundred ") + @Subset(@Subset(m; 
 1 + @TextToNumber(@Middle(d1; 1; 1))); -1) + @Subset(@Subset(n; 
 @If(v1 > 19; @TextToNumber(@Right(d1; 1)); v1) + 1); -1));

REM "Millions";
d2 := @Middle(d; 6; 3);
h2 := @Subset(@Subset(n; @TextToNumber(@Left(d2; 1)) + 1); -1);
v2 := @TextToNumber(@Right(d2; 2));

 t2 := @Trim(@If(h2 = ""; ""; h2 + " Hundred ") + @Subset(@Subset(m; 
 1 + @TextToNumber(@Middle(d2; 1; 1))); -1) + @Subset(@Subset(n; 
 @If(v2 > 19; @TextToNumber(@Right(d2; 1)); v2) + 1); -1));

REM "Billions";
d3 := @Middle(d; 3; 3);
h3 := @Subset(@Subset(n; @TextToNumber(@Left(d3; 1)) + 1); -1);
v3 := @TextToNumber(@Right(d3; 2));

t3 := @Trim(@If(h3 = ""; ""; h3 + " Hundred ") + @Subset(@Subset(m;
 1 + @TextToNumber(@Middle(d3; 1; 1))); -1) + @Subset(@Subset(n; 
 @If(v3 > 19; @TextToNumber(@Right(d3; 1)); v3) + 1); -1));

REM "Trillions";
d4 := @Left(d; 3);
h4 := @Subset(@Subset(n; @TextToNumber(@Left(d4; 1)) + 1); -1);
v4 := @TextToNumber(@Right(d4; 2));

t4 := @Trim(@If(h4 = ""; ""; h4 + " Hundred ") + @Subset(@Subset(m;
 1 + @TextToNumber(@Middle(d4; 1; 1))); -1) + @Subset(@Subset(n;
 @If(v4 > 19; @TextToNumber(@Right(d4; 1)); v4) + 1); -1));

REM "Put it together";
@If(origNumber = "";"";origNumber < 0;"Negative ";"") + @Trim(@If(t0 +
 t1 + t2 + t3 + t4 = ""; "Zero"; @If(t4 = ""; ""; t4 + " Trillion ") +
 @If(t3 = ""; ""; t3 + " Billion ") + @If(t2 = ""; ""; t2 + " Million") +
 @If(t1 = ""; ""; t1 + " Thousand ") + t0)) + " Dollars and " +
 @If(c = ""; "00"; c) + " Cents"

How do you open a document in edit mode if it exists?

The following code will open in edit mode the first doc in the "UPref" view if it exists or create a new document if it doesn't exist. Use GetDocumentByKey on the view if you need to select a specific document first.

Dim session As New Notessession
Dim ws As New NotesUIWorkspace
Dim db As NotesDatabase
Dim view As NotesView
Dim doc As NotesDocument

Set db = session.CurrentDatabase
Set view = db.GetView("UPref")

On Error Resume Next
Set doc = view.GetFirstDocument
If doc Is Nothing Then
 Set uidoc = ws.ComposeDocument("","","UPref")
Else
 ws.OpenDatabase "","","UPref"
 ws.EditDocument
End If

How do I remove erased fields that keep appearing in the Add Fields window?

From Ron Heller (hellerr@msnotes.wustl.edu):

1. Run an agent to remove the erased field's data from all documents.
  FIELD oldField1 := @Unavailable; 
  Select @ALL

2. Then, you need to remove the field from ALL forms and views; do a Design Synopsis to make sure.

3. When you delete a field on a form, always do it by clicking to the right of it and doing a backspace. You should get a prompt asking if you want to delete the field. If you just select the field and press delete, you get no prompt, and the field is not deleted from the UNK.

My experience has been that there are other ways to get the field "properly" deleted, but the key is that, if you are prompted if it's OK to delete the field, you did it properly; if you are NOT prompted, you did
not.

4. Then (and this is the one most often forgotten), if the db has a Full Text Index, you MUST DELETE IT BEFORE compacting the db.

5. Then compact the db.

The above steps work about 80-90% of the time.

If not, here's an optional step we'll call 2a, which you can do before steps 3 and 4 (also from Mark Darrah):
"You must now get rid of the field's "ghosts" from all the forms. There are several ways to do this.
a. Create a new blank form. Now take each of your existing forms in design mode, and copy and paste the entire form contents into the new blank form. Repeat for each form in the database. Then delete the old forms and rename the new ones. Be aware that although this is the easiest method, I have had problems with tab settings and hide-whens getting screwed up. Double check all settings when done. Also, you'll need to copy all your form events if any, and the window title formulas.
b. Another method is to add the field back into the form temporarily (just create a new field and name it as the old field). Now do the delete as described above by clicking to the right and doing a backspace. You should get the prompt."


How do you retrieve a column total for a category?

As it turns out, @DbColumn will return totals and subtotals on categories, and not the individual line items. Therefore, if you have the correct hidden view, you don't have to resort to that lengthy @Sum at all!!

Case in point:

Apples 16
Red 10
Green 6
Oranges 22
Juicy 11
Firm 10
Mandarin 1
Bananas
13
Big 13
51

If you want to retrieve that 22 from the hidden view, don't use @DbLookup (which will return 11;10;1), instead use 2 @DbColumns:

x:="Oranges"; Usually you use an @Prompt to get this value
a:=@DbColumn("";"";"(Fruit Count)";1); Returns "Apples":"Oranges":"Bananas"
b:=@DbColumn("";"";"(Fruit Count)";3); Returns 16:22:13:51
c:=@Member(x;a); Look for where Oranges appears in the list (2nd)
d:=@If(c=0;-1;c); @Subset crashes if it sees a 0, so this is to prevent that
e:=@Subset(@Subset(b;d);-1); Select the proper (2nd) item out of the number list (22)
@If(c=0;"No Match";e) Other half of @Subset workaround, returns 22, the proper answer.


How do you sort and categorize a view based on months in chronological order?

Insert a hidden column just to the left of your month column. Make this column contain a numeric @month type value that will sort correctly, then sort it. The hidden column should be sorted but not categorized, while the visible month column should be categorized.
If you have multiple years to cope with, use

  (@Year(TheDate)-1900)*12 + @Month(TheDate)

in the hidden column so that everything sorts nicely, or you can use an extra categorization on @Year to the left of the month.


Can you dynamically change the color of the form?

You can put a number in the $papercolor field when the form is first opened.
This is from the code in the Color Swatches database. You can set the field "ColorName" to the name you see in the InfoBox's Color Chooser and put this formula in the $papercolor field:

@Member(ColorName;
"black":
"white":
"red":
"green":
"blue":
"magenta":
"yellow":
"cyan":
"dark red":
"dark green":
"dark blue":
"dark magenta":
"dark yellow":
"dark cyan":
"gray":
"light gray":
"white":
"vanilla":
"parchment":
"ivory":
"pale green":
"sea mist":
"ice blue":
"powder blue":
"arctic blue":
"lilac mist":
"purple wash":
"violet frost":
"seashell":
"rose pearl":
"pale cherry":
"white":
"blush":
"sand":
"light yellow":
"honeydew":
"celery":
"pale aqua":
"pale blue":
"crystal blue":
"light cornflower":
"pale lavender":
"grape fizz":
"pale plum":
"pale pink":
"pale rose":
"rose quartz":
"5% gray":
"red sand":
"buff":
"lemon":
"pale lemon lime":
"mint green":
"pastel green":
"pastel blue":
"sapphire":
"cornflower":
"light lavender":
"pale purple":
"light orchid":
"pink orchid":
"apple blossom":
"pink coral":
"10% gray":
"light salmon":
"light peach":
"yellow":
"avocado":
"leaf green":
"light aqua":
"light turquoise":
"light cerulean":
"azure":
"lavender":
"light purple":
"dusty violet":
"pink":
"pastel pink":
"pastel red":
"15% gray":
"salmon":
"peach":
"mustard":
"lemon lime":
"neon green":
"aqua":
"turquoise":
"cerulean":
"wedgewood":
"heather":
"purple haze":
"orchid":
"flamingo":
"cherry pink":
"red coral":
"20% gray":
"dark salmon":
"dark peach":
"gold":
"yellow green":
"light green":
"caribbean":
"dark pastel blue":
"dark cerulean":
"manganese blue":
"lilac":
"purple":
"light red violet":
"light magenta":
"rose":
"carnation pink":
"25% gray":
"watermelon":
"tangerine":
"orange":
"chartreuse":
"green":
"teal":
"dark turquoise":
"light slate blue":
"medium blue":
"dark lilac":
"royal purple":
"fuchsia":
"confetti pink":
"pale burgundy":
"strawberry":
"30% gray":
"rouge":
"burnt orange":
"dark orange":
"light olive":
"kelly green":
"sea green":
"aztec blue":
"dusty blue":
"blueberry":
"violet":
"deep purple":
"red violet":
"hot pink":
"dark rose":
"poppy red":
"35% gray":
"crimson":
"red":
"light brown":
"olive":
"dark green":
"dark teal":
"spruce":
"slate blue":
"navy blue":
"blue violet":
"amethyst":
"dark red violet":
"magenta":

"light burgundy":
"cherry red":
"40% gray":
"dark crimson":
"dark red":
"hazelnut":
"dark olive":
"emerald":
"malachite":
"dark spruce":
"steel blue":
"blue":
"iris":
"grape":
"plum":
"dark magenta":
"burgundy":
"cranberry":
"50% gray":
"mahogany":
"brick":
"dark brown":
"deep olive":
"dark emerald":
"evergreen":
"baltic blue":
"blue denim":
"cobalt blue":
"dark iris":
"midnight":
"dark plum":
"plum red":
"dark burgundy":
"scarlet":
"60% gray":
"chestnut":
"terra cotta":
"umber":
"amazon":
"peacock green":
"pine":
"seal blue":
"dark slate blue":
"royal blue":
"lapis":
"dark grape":
"aubergine":
"dark plum red":
"raspberry":
"deep scarlet":
"70% gray":
"red gray":
"tan":
"khaki":
"putty":
"bamboo green":
"green gray":
"baltic gray":
"blue gray":
"rain cloud":
"lilac gray":
"light purple gray":
"light mauve":
"light plum gray":
"light burgundy gray":
"rose gray":
"80% gray":
"dark red gray":
"dark tan":
"safari":
"olive gray":
"jade":
"dark green gray":
"spruce gray":
"dark blue gray":
"atlantic gray":
"dark lilac gray":
"purple gray":
"mauve":
"plum gray":
"burgundy gray":
"dark rose gray":
"black") - 1


Can you open a Navigator using Lotuscript?

Use this to open the navigator named "NAV1":

  Call ws.OpenDatabase("",DbName,"NAV1","1")

where "1" opens the navigator in it's own window and a "0" causes it to open in the navigation pane.


How do you do @Explode in LotusScript?

There is no equivalent but here is a function that performs an @Explode:

Function AtExplode(strList As String, varSeparators As Variant) As Variant

'********************************************************************************************************************
' From ebrehault@amadeus.net
' Breaks a delimited string into its component elements and returns the elements in a string array
'
' Parameters:
' Input:
' strList: The delimited string to parse
' varSeparators: An array of strings representing the delimiters used in strList
'
' Return Value: An array of strings with each element representing an element in strList
'
'********************************************************************************************************************

Dim intI, intDone As Integer 'Loop control variables
Dim intPosTemp, intPosLastDelimiter, intPosNextDelimiter As Integer 'For determining position of last found and next delimiter in strList
Dim strLastDelimiter, strNextDelimiter As String 'The actual delimiters
last found and next occuring in strList
Dim strElement As String 'An element of strList
Dim astrElement() As String 'The array to return containing the elements of strList
Dim iastrElement As Integer 'Count of elements found in strList

'If an empty string is passed in, return an empty string
If Trim$(strList) = "" Then
  Explode = ""
  Goto Explode_Exit
End If

'Initialize the variables
iastrElement = 0
strLastDelimiter = ""
strNextDelimiter = ""
intPosLastDelimiter = 1 'The combination of intPosLastDelimiter and the Length of strLastDelimiter determine where the next element should start
intDone = False
intPosNextDelimiter = Len(strList) 'The default position for the next delimiter is the end of strList

'Loop through the potential delimiters to identify the next one. Store its position in strLIst and its text
For intI = Lbound(varSeparators) To Ubound(varSeparators)
  intPosTemp = Instr(1, strList, varSeparators(intI))
  If intPosTemp <> 0 And intPosTemp <= intPosNextDelimiter Then
    intPosNextDelimiter = intPosTemp
    strNextDelimiter = varSeparators(intI)
  End If
Next

'Process strList until intDone is true
Do
  'Get the next element in strList with extraneous spaces removed. The starting position of the next element is equal to the starting position
  'of the last delimiter found + the length of the last delimiter found (intPosLastDelimiter + Len(strLastDelimiter))
  If strNextDelimiter = "" Then
    'If strNextDelimiter is blank then we're getting the rest of the string.
    strElement = Trim(Mid(strList, intPosLastDelimiter + Len(strLastDelimiter)))
  Else
    'The length of the next element equals the position of the last character of the element (intPosNextDelimiter - 1) minus the position
    'one character before the first character of the element (intPosLastDelimiter + Len(strLastDelimiter) - 1)
    strElement = Trim(Mid(strList, intPosLastDelimiter + Len(strLastDelimiter),_
     intPosNextDelimiter - 1 - (intPosLastDelimiter + Len(strLastDelimiter) - 1)))
  End If
  If strElement <> "" Then
    'If the current element isn't blank, increment the count of elements and add it to the array of elements
    iastrElement = iastrElement + 1
    Redim Preserve astrElement(1 To iastrElement)
    astrElement(iastrElement) = strElement
  End If
  If strNextDelimiter <> "" And (intPosNextDelimiter + Len(strNextDelimiter) - 1) < Len(strList) Then
    'If you haven't reached the end of strList (strNextDelimiter <> "") and the last character of the last delimiter found
    'is not at the end of strList (intPosNextDelimiter + Len(strNextDelimiter) - 1 < Len(strList)

    'Set the Last delimiter variables equal to reflect the last delimiter found
    intPosLastDelimiter = intPosNextDelimiter
    strLastDelimiter = strNextDelimiter

    'Set the Next delimiter variables to assume there isn't one
    intPosNextDelimiter = Len(strList)
    strNextDelimiter = ""

    'Loop through the potential delimiters to identify the next one. Store its position in strLIst and its text
    For intI = Lbound(varSeparators) To Ubound(varSeparators)
      'Start looking with the character in strList after the end of the last delimiter found
      intPosTemp = Instr(intPosLastDelimiter + Len(strLastDelimiter), strList, varSeparators(intI))
      If intPosTemp <> 0 And intPosTemp <= intPosNextDelimiter Then
        'If the position of the current delimiter is less than the nearest one found so far, store it's position and text
        intPosNextDelimiter = intPosTemp
        strNextDelimiter = varSeparators(intI)
      End If
    Next
  Else
    'If you've reached the end of strList (strNextDelimiter = "") or the last character of the last delimiter found
    'is at the end of strList (intPosNextDelimiter + Len(strNextDelimiter) - 1 >= Len(strList) then you're done.
    intDone = True
  End If
Loop Until intDone

'Return the array containing the elements
Explode = astrElement

Explode_Exit:

End Function


Can you launch a URL without using InterNotes?

Put this formula in a button:

@Command([Execute]; "C:\\netscape\\netscape.exe"; "HTTP://www.xyzcorp.com")

Note that this will invoke netscape, so this form will not be cross-platform if you have other Notes clients.


How do you get the date for Easter of a given year?

REM "This formula returns the date of Easter day for the year of a given date D";
REM "Is valid until year 4000";
REM "Contributed from billingt@online.no";

D:=[30.07.97];
Year:=@Year(D);

c:=@Integer(Year/100);
n:=Year-19*@Integer(Year/19);
k:=@Integer((c-17)/25);
i:=c-@Integer(c/4)- @Integer((c-k)/3)+19*n+15;
ii:=i-30*@Integer(i/30);
iii:=ii-@Integer(ii/28)*(1-@Integer(ii/28)*@Integer(29/(ii+1))*@Integer((21-n)/11));

j:=Year+@Integer(Year/4)+iii+2-c+@Integer(c/4);
jj:=j-7*@Integer(j/7);
l:=iii-jj;
EasterMonth:=3+@Integer((l+40)/44);
EasterDay:=l+28-31*@Integer(EasterMonth/4);
@Date(Year;EasterMonth;EasterDay);

How do you hide the design of a database, but let the user see field names?

1) Hide the database design in the NSF.
2) Rename the hidden design database to an NTF template.
3) Put the NTF template in the Notes Data directory.
4) Create a new database from the NTF template.
5) Replace its design using NTF template.


Can you access Win32 registry keys in LotusScript?

This code goes in the declarations section

Declare Function RegOpenKey Lib "advapi32.dll" Alias "RegOpenKeyA" (Byval hKey As Long, Byval lpSubKey As String, phkResult As Long) As Long
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (Byval hKey As Long, Byval lpSubKey As String, Byval ulOptions As Long, Byval samDesired As Long, phkResult As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (Byval hKey As Long) As Long
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (Byval hKey As Long, Byval lpValueName As String, Byval lpReserved As Long, lpType As Long, Byval lpData As String, lpcbData As Integer) As Long
Declare Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" (Byval hKey As Long, Byval dwIndex As Long, Byval lpname As String, Byval cbName As Long) As Long
Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (Byval hKey As Long, Byval dwIndex As Long, Byval lpname As String, lpcbName As Long, Byval lpReserved As Long, Byval lpClass As String, lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long

Declare Function RegSetValueEx& Lib "advapi32.dll" Alias "RegSetValueExA" _
(Byval hKey As Long, Byval lpValueName As String, Byval Reserved As Long, Byval _
dwType As Long, Byval lpData As String, Byval cbData As Long)

Declare Function RegCreateKey& Lib "advapi32.dll" Alias "RegCreateKeyA" (Byval _
hKey As Long, Byval lpSubKey As String, phkResult As Long)

Type FILETIME
     dwLowDateTime As Long
     dwHighDateTime As Long
End Type

'-- Constant Definitions for WIN32API
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_PERFORMANCE_DATA = &H80000004

Const SYNCHRONIZE = &H100000
Const STANDARD_RIGHTS_READ = &H20000
Const STANDARD_RIGHTS_WRITE = &H20000
Const STANDARD_RIGHTS_EXECUTE = &H20000
Const STANDARD_RIGHTS_REQUIRED = &HF0000
Const STANDARD_RIGHTS_ALL = &H1F0000
Const KEY_QUERY_VALUE = &H1
Const KEY_SET_VALUE = &H2
Const KEY_CREATE_SUB_KEY = &H4
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_CREATE_LINK = &H20
Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE))
Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
Const KEY_EXECUTE = (KEY_READ)
Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))
Const ERROR_SUCCESS = 0&

' Reg Data Types...
Const REG_NONE = 0                       ' No value type
Const REG_SZ = 1                         ' Unicode nul terminated string
Const REG_EXPAND_SZ = 2                  ' Unicode nul terminated string
Const REG_BINARY = 3                     ' Free form binary
Const REG_DWORD = 4                      ' 32-bit number
Const REG_DWORD_LITTLE_ENDIAN = 4        ' 32-bit number (same as REG_DWORD)
Const REG_DWORD_BIG_ENDIAN = 5           ' 32-bit number
Const REG_LINK = 6                       ' Symbolic Link (unicode)
Const REG_MULTI_SZ = 7                   ' Multiple Unicode strings


This sample code sets some registry keys associated with IBM's Global Network dialer:

KeyName$ = "Software\IBM Global Network\Dialer\Calling\From\"+ActiveLocation.LocName
res1& = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName$ , 0, KEY_ALL_ACCESS, keyhandle)
If res1& <>ERROR_SUCCESS Then
  res1& = RegCreateKey( HKEY_CURRENT_USER, Keyname$, KeyHandle )
End If
If res1& = ERROR_SUCCESS Then
  res = setDialerReg( keyHandle, ThisPhone )
End If
Call RegCloseKey(keyhandle)
          
KeyName$ = "Software\IBM Global Network\Dialer\Settings"
res2& = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName$ , 0, KEY_ALL_ACCESS, keyhandle)
res = setDialerReg( keyHandle, ThisPhone )
Call RegCloseKey(keyhandle)
          
If res1& <> ERROR_SUCCESS And res2& <> ERROR_SUCCESS Then
  Msgbox "The Advantis Dialer is not installed correctly - please contact support"
  Exit Sub
Else
  DialerProgram$ = "c:\Program Files\IBM Global Network\IDialer.EXE"
  KeyName$ = "Software\IBM Global Network\Dialer\Install"
  res3& = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName$ , 0, KEY_ALL_ACCESS, keyhandle)
  If res3& = ERROR_SUCCESS Then
    Dim Ret_Type As Long
    Dim lpFileName As String
    Dim lpReturnedString As String*127
    Dim retSize%
    retSize% = 126
    res4& = RegQueryValueEx( keyhandle, "Directory", 0, Ret_Type, lpReturnedString, retSize% )
    If res4& = ERROR_SUCCESS Then
      DialerProgram$ = Left$(lpReturnedString, retSize%-1) +"\IDialer.exe"
    End If
  End If
  Call RegCloseKey(keyhandle)
  Print "Launching the Advantis Dialer " & DialerProgram$
  rc& = Shell( DialerProgram$ , 2) ' run the dialer and bring the focus to the foreground
End If


How do you launch an embedded object using LotusScript?

First use the extract file method of EmbeddedObjects class to save the file to a temporary directory then use the LotusScript "Shell" command to run the program with the temporary file.


How do you debug into the QueryModeChange and QuerySave events in LotusScript?

Create an Initialize event script for your document and put x=1 in it.
The bebugger will stop there when the document is opened.
Scroll to the appropriate event or events and put a break point into them.
Go back to the initialize event and click the continue or step into button.


How do I refresh a document's fields after one field is changed?

Place this in the exiting event of the field that will trigger changes in other fields:

Sub Exiting(Source As Field)
  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
  Call uidoc.Refresh
End Sub

The "Refresh fields on keyword change" option does not work on anything other than static keyword lists.
One bad side-effect is that the uidoc.Refresh will cause all the field input validation formulas to be run; you may want to cause the refresh to happen only if all the fields with input validation have been filled in.

How do you change the title of a dialog box?

With the @DialogBox macro, the dialog box Window Title defaults to "Lotus Notes". Placing a form title in the Window Title property of the form has no effect. However, there is this undocumented version of @DialogBox:

@DialogBox(<Form>; [AutoHorizFit] : [AutoVertFit]; "Title Here")

Here are several other undocumented options:

[NoCancel] No Cancel button
[NoFieldUpdate] Does not return anything to calling form/document
[ReadOnly] Makes the dialogbox readonly


How do you remove an element from a Textlist field?

You can use the LotusScript Evaluate function to run the Notes V3 version of this FAQ. If you prefer a pure LotusScript version, this example takes a text list (tl1) with the values "Elem1":"Elem2":"Elem3" and turns it into "Elem1":"Elem3". You will have to do some fiddling with the Redim Statement to make sure that you have enough space (eg, the element you wish to remove is not in the list).

Dim db As NotesDatabase
Dim ws As New NotesUIWorkspace
Dim session As New NotesSession
Dim i As Integer
Dim j As Integer
Dim textList As Variant
Dim outList As Variant

Set uidoc = ws.CurrentDocument        ' get the current uidoc
Set doc = uidoc.Document              ' get the backend doc

textList = doc.GetItemValue("tl1")    ' Get the list and put it in a variant
Redim outList(Ubound(textList)-1) As Variant    ' Dim the outlist
j = 0
For i = 0 To Ubound(textlist)         ' Loop through and check for the element to be removed
  If textList(i) <> "elem2" Then      ' Check for the value to replace (could be a variable)
    outList(j) = textList(i)          ' Populate the outlist
    j = j + 1
  End If
Next
Call doc.ReplaceItemValue("tl1",outList)        ' replace the original
list with the outlist
Call uidoc.Reload                     ' reload the UIDoc with the new backend values


Is there any way to prevent the user from deleting parent documents with child documents?

Put one of these techniques in the QueryDocumentDelete Event:

Stop all deletions if any documents have children

Sub Querydocumentdelete(Source As Notesuidatabase, Continue As Variant)
  Dim docsDelete As NotesDocumentCollection
  Dim docsChildren As NotesDocumentCollection
  Dim doc As NotesDocument
  Dim i%
  Set docsDelete = Source.Documents  ' Selected documents
  ' Check selected documents for children
  For i = 1 To docsDelete.Count
    Set doc = docsDelete.GetNthDocument( i )
    Set docsChildren = doc.Responses
    ' If children then cannot continue
    If docsChildren.Count > 0 Then
      Msgbox "Some of the selected documents have responses." + Chr(13)  "No deletions were made."
      Continue = False
      Exit Sub
    End If
  Next
  ' No children found - OK to delete
  Continue = True
End Sub


Prompt for user to continue if some documents have children

Sub Querydocumentdelete(Source As Notesuidatabase, Continue As Variant)
  Continue = False  ' deletions will be done by this script, so don't let Notes do it
  Set docsDelete = Source.Documents  ' Selected documents
  ' Check documents first
  For i = 1 To docsDelete.Count
    Set doc = docsDelete.GetNthDocument( i )
    Set docsChildren = doc.Responses
    If docsChildren.Count > 0 Then
      cHaveChildren = cHaveChildren + 1
    Else
      cDelete = cDelete + 1
    End If
  Next
  If cHaveChildren > 0 Then
    msg = msg & Chr(13) & Cstr( cHaveChildren) & " document(s) have responses and will not be deleted." & Chr(13)
  End If
  msg = msg & Chr(13) & "Do you wish to continue?"
  If Msgbox( msg, MB_YESNO+MB_ICONQUESTION, "Confirm Deletion" ) = IDYES Then
    For i = 1 To docsDelete.Count
      Set doc = docsDelete.GetNthDocument( i )
      Set docsChildren = doc.Responses
      If docsChildren.Count = 0 Then
        Call doc.Remove( True )
      End If
    Next
    ' No way to refresh view here, so prompt user to press [F9] to refresh view
    MsgBox("Press F9 to refresh view")
  End If
End Sub


How do you avoid error messages with DBLookup?

TempVar := @DbLookup();
@If(@IsError(TempVar); 'perform desired error response'; 'perform desired
action on TempVar')

TempVar is used to hold the results and will also hold the special "error" value for comparison.


How do you find the elements which are unique to a list?

This will get the elements which are unique to List B:

UniqueToB:= @Trim( @Replace( ListB, ListA, ""))


Why don't my changes to a Rich Text field show up immediately?

Unlike text or time fields, you need to close and reopen the uidoc to see changes that are made on rich text fields. You have to do something like:

  Dim ws As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument

  Set uidoc = ws.CurrentDocument

  Dim doc As NotesDocument
  Set doc = uidoc.Document
  ' make changes to back-end document's Rich Text fields here
  Call uidoc.Close
  Set uidoc = ws.EditDocument(True, doc)

Here is this code applied to place a graphical logo from a central database onto a form:

Sub Postopen(Source As Notesuidocument)
'From Rich_Collette@siemon.com
'This routine copies a logo from a ListMaintenance form to this newly created LetterHead.
  Dim ListDoc As NotesDocument
  Dim doc As notesDocument
  Dim db As NotesDatabase
  Dim ListItem As notesRichTextItem
  Dim docItem As NotesRichTextItem
  Dim view As notesview
  Dim workspace As New NotesUIWorkspace
  'get the doc behind the new UIDoc
  Set doc=source.document
  'get the handle to the item that holds the Logo
  Set docItem=doc.GetFirstItem("LogoRTF")
  'check if Logo is already there from previous call to this routine
  If (docItem.ValueLength > 10) Then ' For some reason the value is > 0 even if the item is empty.
    Exit Sub
  End If
  'get handle to this database
  Set db=doc.ParentDatabase
  'form name isn't set on newly created docs, must set the value
  doc.form="LetterWithLogo"
  'get the "List" document that contains a logo in the RTFItems field
  Set view = db.GetView( "List Maintainence" )
  Set ListDoc = view.GetDocumentByKey( "LetterLogo", True )
  'if you don't remove the item prior to seting its value, you get an error message about the types not being equivalent
  Call doc.RemoveItem ("LogoRTF")
  Set ListItem = ListDoc.GetFirstItem("RTFItems")
  Set docItem = doc.CopyItem( ListItem, "LogoRTF" )
  'close the uidocument that is currently open on the user's screen
  Call source.close
  'reopen the document
  Set source = workspace.EditDocument( True, doc )
End Sub

When composing a UIDocument, RTF fields are stored on the backend NotesDocument as NotesItems rather than as NotesRichText items.�� They are not converted to a true RichTextItem on the backend Notesdocument until the Notesdocument is saved.�� Therefore, if you try to do:
Set rtfItem2 = doc.CopyItem( RTFItem1, "LogoRTF" )
after getting rtfItem1 from an existing RTF field prior to saving the notesdocument it is on, you are actually trying to copy an RTF field into a regular NotesItem and it gives you a data type error.


Rich sent in another example that creates and displays a rich text field without requiring that the back-end doc get saved first:

Sub Initialize
  'How to populate a new rich text field via backend classes and display without having to save the backend document
  '
  'If this is an existing saved document, then most of this is not needed, you just append to the existing RTF, close the UIDOC and edit the document
  'similar to what is done at the very end of this code
  '
  'Created by Richard Collette, CLP Principal Developer
  'rcollette@yahoo.com
  '
  'This demo needs a single form called "DemoDoc" - on the form place a field called "myRTF"
  '
  'Please note an actual application would test for "is Nothing" prior to using objects to insure they were instantiated.
  'This was eliminated here to provide a more concise example
  '
  'Caveat:   When closing and reopening a uidoc via its backend NotesDocument, the uidoc loses all notion that it is a response document and will save
  'as a regular document.  If you need a response document, you must capture the parent documentUNID (the selected document) and make the new
  'document a response using the MakeResponse method.
  Dim session As New NotesSession
  Dim doc As NotesDocument
  Dim tmpRTItem As NotesRichTextItem
  Dim RTItem As NotesRichTextItem
  Dim uiws As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Dim styleHeadline As NotesRichTextStyle
  Dim styleNormal  As NotesRichTextStyle
  Dim item As NotesItem

  'compose the new document, or you can grab the current document that is on the workspace
  Set uidoc=uiws.composeDocument("", "" , "DemoDoc")
  If uidoc Is Nothing Then Exit Sub
  Set uidoc=uiws.currentDocument
  'get the backend document
  Set doc=uidoc.document
  'Create a temporary Rich Text Field that won't be saved
  Set tmpRTItem= New NotesRichTextItem( doc , "RTFTemp")
  tmpRTItem.saveToDisk=False
  'create the style objects for setting text properties
  Set styleHeadline=session.createRichTextStyle()
  Set styleHeadline=session.createRichTextStyle()
  Set styleNormal=session.createRichTextStyle()
  styleHeadline.bold=True
  styleHeadline.fontsize=14
  styleNormal.bold=False
  styleNormal.fontsize=10
  'Add text, images, etc to the temporary rich text field
  Call tmpRTItem.appendStyle(styleHeadline)
  Call tmpRTItem.AppendText( "Headline" )
  Call tmpRTItem.appendStyle(styleNormal)
  Call tmpRTItem.AddNewLine(1)
  Call tmpRTItem.AppendText( "Normal Text")
  '**********************************The meat and potatoes of this demo starts here**********************************************************
  'form name isn't stored on the composed NotesDocument until it is saved.  Set the form name so we can re-edit the document and it will know what form to use.
  doc.form="DemoDoc"
  'don't prompt the user to save the document, set this back to "1" at the end of the code if you want the user to be able to save the document
  doc.saveOptions="0"
  'this causes two RTItems of the same name (RTFTemp) to be created on the back end document, they get combined into one only when the NotesDocument is saved, this makes RTF's difficult to work with
  Call uidoc.close
  'remove the RTF field to be displayed becuase it really isn't an RTF until the document is saved, another reason why RTFs are so difficult to work with
  If (doc.isNewNote) Then
    Call doc.removeItem("myRTF")
  End If
  'get the first of the two RTItems that were created during the close event, the second one is empty
  Set tmpRTItem=doc.getFirstItem("RTFTemp")
  'because the temporary RTF item was formated during UIDOC close process, we can now copy it to our actual document field.
  'this code will not work without using a temporary RTFItem
  Set RTItem = doc.CopyItem( tmpRTItem, "myRTF" )
  'remove the temporary RTF Item from the backend document
  Call doc.removeItem("RTFTemp")
  'open the document in read mode and don't allow change to edit mode   (any other mode possible)
  Set uidoc=uiws.editDocument(True,doc)
  Set doc=uidoc.document
  'allow the user to save the document
  doc.saveOptions="1"
  'prevent SaveOptions from saving on the document
  Set item=doc.getFirstItem("saveOptions")
  item.saveToDisk=False
End Sub

How do signatures and sections in a form work?
A section will be signed if a field within it is edited, and that field is marked for signing when saved within a section. Sections aren't really signed at all - the field is - but the client notices the field's inside a section and makes it look like the section was signed. Different sections can be signed by different people.

When saving, Notes will sign all sections to which the user has access - even if signed before by that user or someone else. So, if you want a section signed and that signature to last, you must change the section's access control list to deny permssion to anyone else that might edit the document. Even then, if the signatory edits some other part of the document, the section will get re-signed. To avoid this, it's probably possible to make the section editable by no-one.

Note that the section ACL appears to be just a textlist field in the actual document (i.e. all the section handling is part of the form handling), so permissions can be re-widened (and re-signature etc. re-enabled) from a macro.

What does the @Db NoCache parameter do?
NoCache means that every time the @Db Function is executed, it will look at the current state of the Lookup View. If you do not use NoCache, the @Db Function will default to Caching the Lookup View the first time it executes. For Lookup Views which do not change rapidly (minute by minute), you should allow Notes to Cache the Lookup View. The difference in performance is tremendous.

How do you generate unique document numbers?
Because of the nature of Notes database replication, there is no way to generate unique document numbers. What is normally done to give each document a unique key is to concatenate the users name or initials with the date, and the time of document creation. An example can be found in Lotus Notes Call Tracking Database example. Another example is below:

TimeNow := @Now;
YearString := @Right(@Text(@Year(TimeNow)); 2);
MonthString := @Select(@Month(TimeNow); "01"; "02"; "03"; "04"; "05"; "06"; "07"; "08"; "09"; "10"; "11"; "12");
DayNumber := @Day(TimeNow);
DayString := @Select(DayNumber; "01"; "02"; "03"; "04"; "05"; "06"; "07"; "08"; "09";  @Text(DayNumber));
HourNumber := @Hour(TimeNow);
HourString := @If(HourNumber = 0; "00"; @Select(HourNumber; "01"; "02"; "03"; "04"; "05"; "06"; "07"; "08"; "09"; @Text(HourNumber)));
MinuteNumber := @Minute(TimeNow);
MinuteString := @If(MinuteNumber = 0; "00"; @Select(MinuteNumber; "01"; "02"; "03"; "04"; "05"; "06"; "07"; "08"; "09"; @Text(MinuteNumber)));
SecondNumber := @Second(TimeNow);
SecondString := @If(SecondNumber = 0; "00"; @Select(SecondNumber; "01"; "02"; "03"; "04"; "05"; "06"; "07"; "08"; "09"; @Text(SecondNumber)));
UniqueID := YearString + MonthString + DayString + HourString + MinuteString + SecondString + @Left(@Name([CN];@UserName); 1) + @MiddleBack(@Name([CN];@UserName); " "; 1)

Note that if this code is called twice, it doesn't take into account that there may be duplicate values. To prevent this case, you have to have a hidden view with all the unique numbers sorted. Then, do a @DbLookup to see if the value is already used.

For Notes 4 and above, you can use the @Unique function to get Notes' unique document ID.


What happened to Tools\RefreshFields that was in Notes 3.x?

There is now no menu option for refreshing fields; you have to make a SmartIcon or Action that uses either:

@Command([ToolsRefreshAllDocs])

or
@Command([ToolsRefreshSelectedDocs])


How do you do a data import/merge using Lotuscript?

This function can be placed in an action button in a database:

Sub Initialize
 On Error Goto _Error

 Dim bFileOpen As Integer
 Dim session As New NotesSession
 Dim db As NotesDatabase
 Set db = session.CurrentDatabase

 Dim view As NotesView
 Dim doc As NotesDocument
 Set view = db.GetView( "(CleanAddress)" )
 Set doc = view.GetFirstDocument

  ' The Fields we are going to import
 Dim Field1 As String
 Dim Field2 As String

  ' The unique field for each record
 Dim UniqueID As String

  ' Name of file to get from user
 Dim FileName As String

 If Not (doc Is Nothing) Then
   FileName  = Inputbox$("Enter your import file name: ", "Data Import", "C:\")
   If (FileName = "") Then
     Messagebox ("Import has been cancelled")
     Exit Sub
   End If
 Else
    ' There is nothing to merge into
   Messagebox ("Nothing to merge into! Are you sure you have already imported this file?")
   Exit Sub
 End If

  ' Open the file
 Dim fileNum As Integer
 fileNum = Freefile()
 Open FileName For Input As fileNum
 bFileOpen = 1

 Dim item As NotesItem
  ' Until we eof
 Do Until Eof (fileNum)
   Input #fileNum, UniqueID, Field1, Field2
   If (UniqueID > "") Then
     Set doc = view.GetDocumentByKey(URN)
     If Not (doc Is Nothing) Then
       doc.Field1 = Field1
       doc.Field2 = Field2
       Call doc.Save(True, False)
     Else
       Messagebox("File contains a record that is not in the database " + UniqueID)
     End If
   Else
     Messagebox("File contains an record without a unique id")
   End If
 Loop

  ' Close file
 Close fileNum
 bFileOpen = 0

 Messagebox("File imported successfully")
 Exit Sub

_Error:
 If (bFileOpen = 1) Then
   Close fileNum
 End If
 Messagebox("Error importing data: " + Error$)
End Sub


How do you lock a document that is being edited?

From Martin Zuercher (martin.zuercher@web.de):

Sub Queryopen (Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
Dim ws As New notesuiworkspace
Dim s As New notessession
Dim Uidoc As Notesuidocument
Dim doc As NotesDocument
Dim boxType As Long
Dim answer As Integer

Set uidoc = source
Set doc = source.Document
Continue = True

If Mode = 1 And Not source.IsNewDoc Then
If uidoc.document.LockedBy (0) = "" Then
uidoc.document.lockedby = s.COMMONUSERNAME
If Not uidoc.Document.Save (False, False, True) Then ' If problem mit save => wird konflikt geben -> kein Edit Status
Messagebox("Das Dokument ist schon in Bearbeitung" )
Call uidoc.Close
End If
uidoc.document.save True, False
Else
If uidoc.document.lockedby(0) = s.COMMONUSERNAME Then
boxType = MB_YESNO + MB_ICONQUESTION
Answer = Messagebox ("Die letzte Bearbeitung wurde nicht ordnungsgemäss beendet. Wollen Sie das Dokument freigeben?", MB_YESNO, "Unlock?")
If answer = 7 Then
Continue = False
Else
uidoc.document.lockedby = s.COMMONUSERNAME
uidoc.document.save True, False
End If
Else
continue = False
Messagebox("Das Dokument ist schon in Bearbeitung bei " + uidoc.document.lockedby(0))
End If
End If
End If

End Sub


Sub Querymodechange(Source As Notesuidocument, Continue As Variant)
Dim s As New notessession
Dim doc As NotesDocument
Dim boxType As Long
Dim answer As Integer

Set doc = source.Document
Continue = True

If source.EditMode = False Then ' READ -> EDIT
If doc.LockedBy (0) = s.COMMONUSERNAME Then
boxType = MB_YESNO + MB_ICONQUESTION
Answer = Messagebox ("Die letzte Bearbeitung wurde nicht ordnungsgemäss beendet. Wollen Sie das Dokument freigeben?", MB_YESNO, "Unlock?")
If answer = 7 Then
Continue = False
End If

Elseif doc.LockedBy (0) <> "" Then
Messagebox ("Das Dokument ist schon in Bearbeitung bei " + doc.lockedby(0))
continue = False

End If


Else ' EDIT -> READ
Call source.reload ()
If Not doc.lockedby = "" Then
doc.lockedby = ""
doc.save True, False
End If

End If

End Sub


Sub Postmodechange(Source As Notesuidocument)
Dim s As New notessession
Dim doc As NotesDocument

If source.EditMode = True Then ' READ -> EDIT
Call Source.reload ()
Set doc = source.Document

If doc.LockedBy (0) = "" Then
doc.lockedby = s.COMMONUSERNAME
If (False = doc.Save (False, False, True) ) Then
Messagebox ("Das Dokument ist schon in Bearbeitung" )
doc.SaveOptions = "0"
Call Source.close ()
Exit Sub
End If
doc.save True, False

End If

End If

End Sub


Sub Queryclose(Source As Notesuidocument, Continue As Variant)
Dim uiw As New notesuiworkspace
Dim doc As notesDocument

Set doc = Source.Document

If ( Source.EditMode ) And Not (Source.Isnewdoc)Then
doc.LockedBy = ""
doc.SaveOptions = "1"
If (False = doc.Save (False, False, True)) Then
Print "Fehler beim Speichern, da Dokuments bereits von anderem Benutzer gespeichert wurde."
End If
' Source.refreshhideformulas
End If

Call uiw.ViewRefresh

End Sub

The Lotuscript code works by adding a lock field to the document the user wishes to edit and then immediately saving the document; when other users try to open the document, they will see that there is a lock field so they cannot edit the document.

This technique will only work if the database is on a single server (no replication). There is also a small chance for a race condition if two users manage to read a document simultaneously without the lock. Finally, if the user's system crashes while editing this locked document, the document will remain locked unless you implement some sort of emergency unlock mechanism.

Domino 6 has a supported way of locking a document for editing, even in replicated environments.


How can I see if the directory exists using LotusScript?

You can use the DIR$ command like so:

If Dir$(dirName, ATTR_DIRECTORY) = "" Then
     'Directory does not exist
Else
     'Directory does exist
End If


How can you tell if a view is Private?

There is no property in the view thtat lets you do this, but you can look at the design note for the view via this function:

Function IsPrivateView(v As Variant) As Integer

     ' From Hugh Pyle@NIP
     ' Since LotusScript doesn't give us a view property for this, we have to do the work ourselves.
     ' This is determined by opening the view note and checking for $Flags item containing the character "V".
     '
     Dim vdoc As NotesDocument
     Dim db As NotesDatabase
     '
     IsPrivateView=False
     If Not (v Is Nothing) Then
          Set db=v.Parent
          Set vdoc = db.GetDocumentByUnid( v.UniversalID )
          If Not (vdoc Is Nothing) Then
               If vdoc.HasItem("$Flags") Then
                    If Instr(vdoc.GetItemValue("$Flags")(0), "V") Then
                         IsPrivateView=privateview%
                    Elseif Instr(vdoc.GetItemValue("$Flags")(0), "p") Then
                         IsPrivateView=privateonfirstuse%
                    End If
               End If
          End If
     End If
End Function


How do you check if an RTF field is empty?

This interesting workaround comes from [email] Alan Keele. It does cause a little bit of UI flicker, but works on the front-end and back-end:

Function IsRTFNull(rtfield As String) As Integer
     
  'This function tests a Rich Text field to see whether or not it is null.  It returns TRUE if the field is null, and
  'returns FALSE if the field is not null.  It works even if the rich text field contains a file attachment, 
  'doclink, or OLE object but does not contain any text.
     
  On Error Goto Errhandle
     
  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
     
  'Store the name of the field that currently has focus.  Note:  if this function is being called from a form button,
  'currentfield will be null (because the button has the focus, and not a field).  If this function is called
  'from an action button, and if the cursor is in a field, then currentfield will correctly store the name 
  'of the field that has focus.
  currentfield = uidoc.CurrentField
     
  Call uidoc.GotoField(rtfield)
  Call uidoc.SelectAll
  'The next line will generate a 4407 error message if the Rich Text Field is null
  Call uidoc.DeselectAll
     
  'Return the cursor the the field that had focus before this function ran.  If the currentfield variable is null (because a button
  'or hotspot had focus, then the cursor will actually wind up getting left in the rich text field.
  If currentfield <> "" Then
    Call uidoc.GotoField(currentfield)
  End If
     
  IsRTFNull = False
     
  Exit Function
     
     
Errhandle:
  Select Case Err
  Case 4407
    'the DeselectAll line generated an error message, indicating that the rich text field does not contain anything
    If currentfield <> "" Then
      Call uidoc.GotoField(currentfield)
    End If
    IsRTFNull = True
  Exit Function
  Case Else
    'For any other error, force the same error to cause LotusScript to do the error handling
    Error Err
  End Select 
End Function

From Eric Wilson:
In LotusScript, you can also check by looking for: NotesItem.Text = "" and ValueLength < 200.


How do you log off a Notes user so you can force user revalidation?

You can use the following @Command:

  @Command([ToolsUserLogoff])


Can you get MSIE to understand "notes://" URLs?

Did you know that you could get Internet Explorer to understand the "notes:" protocol? Clicking on a link (containing an HREF='notes://...') in the IE browser will actually launch you back into your Notes Client.

Here is how to set up the registry:

(info derived from: http://www.microsoft.com/workshop/networking/pluggable/appendix_a.htm)

To enable an application to handle a particular URL protocol, you must add a new key, with the appropriate keys and values, to the registry in HKEY_CLASSES_ROOT. The new registry key must match the protocol scheme that is being added. For instance, to add the protocol notes:, the key added to HKEY_CLASSES_ROOT should be notes. Under this new key, the Default string value should be the name of the new protocol, and the URL Protocol string value should contain either protocol-specific information or an empty string. Also under the new key, a DefaultIcon key and a shell key should be added. The Default string value under the DefaultIcon key must be the file name to use as an icon for this new URL protocol. Under the shell key, a key using a verb (such as open) should be added. A command key can be added under the key using a verb. The values under the command key is used to call the application.

You should end up with this sort of structure in your registry (using your own paths, where appropriate):

[HKEY_CLASSES_ROOT]
[notes] (Default) = "URL:Notes Protocol"
URL Protocol = ""
[DefaultIcon] (Default) = "c:\Notes\notes.exe"
[shell]
[open]
[command] (Default) = "c:\notes\notes.exe %1"


The parameters following notes:// are Server/ReplicaID/ViewID/NoteID. The last two parameters can be omitted.


How do you calculate the week number in a year of a given date?

This formula satisfies ISO 8601:1988:

REM "This formula satisfies ISO 8601:1988";
REM "Formulae updated : 08.28.1997 ";
REM "by Stephen P.R. Renton (sprenton@mcmail.com)";
REM "Version: 1.01";
REM "Tested on : Lotus Notes Release 4.5";

REM "D is the date of interest.";
D := @TextToTime(@Prompt([OKCANCELEDIT]; "Enter Date"; "Please enter a
date to convert to a week number:"; ""));
REM "D := [31/12/95]";

FirstOfYear := @Date(@Year(D); 1; 1);
LastOfYear := @Date(@Year(D); 12; 31);
FirstDayNum := @Weekday(FirstOfYear);
LastDayNum := @Weekday(LastOfYear);

REM "ISO weeks start on Monday and ends on Sunday.";
ISOFirstDayNum := @If(FirstDayNum = 1; 7; FirstDayNum - 1);
ISOLastDayNum := @If(LastDayNum = 1; 7; LastDayNum - 1);

REM "The first and last ISO week is the first";
REM "and last ISO week to include Thursday";
IsFirstWeek := 7 - ISOFirstDayNum > 2;
IsLastWeek := 7 - ISOLastDayNum < 4;
REM "The date of the first day of the first ISO week";
ISOFirstDay := @If(IsFirstWeek;
            @Adjust(FirstOfYear; 0; 0; 1 - ISOFirstDayNum; 0; 0; 0);
            @Adjust(FirstOfYear; 0; 0; 8 - ISOFirstDayNum; 0; 0; 0));
REM "The date of the last day of the last ISO week";
ISOLastDay := @If(IsLastWeek;
            @Adjust(LastOfYear; 0; 0; 7 - ISOLastDayNum; 0; 0; 0);
            @Adjust(LastOfYear; 0; 0; -ISOLastDayNum; 0; 0; 0));

REM "Date outside ISOFirstDay and ISOlastDay";
REM "are from the previous or next year";
REM "Return the ISO week number and exit";

FirstWeekNextYear := @If(D > ISOLastDay; @Return(@Prompt([OK]; "FWNY";
@Text(@Year(D)+1) + "W01")); NULL);

REM "I suspect this is where Julian dates would be useful";
REM "A recursive call could be used in a real language";
LastWeekLastYear := (D - @Adjust(FirstOfYear; -1; 0; 0; 0; 0; 0))/60/60/24/7;
AdjustLastWeek := 1 - (LastWeekLastYear - @Integer(LastWeekLastYear));
@Set("LastWeekLastYear"; LastWeekLastYear + AdjustLastWeek);
@If(D < ISOFirstDay;
@Return(@Prompt([OK]; "LWLY"; @Text(@Year(D) - 1) + "W" +
@Text(LastWeekLastYear))); NULL);

REM "If you get this far, the date falls into an ISO week this year";
REM "Convert the difference in seconds to weeks";
NumWeeks := (D - ISOFirstDay)/60/60/24/7;

REM "Fractions indicate that the date falls";
REM "in the middle of the ISO week";
WeekAdjust := 1 - (NumWeeks - @Integer(NumWeeks));
ISOWeekNum := NumWeeks + WeekAdjust;

REM "Conform to ISO 8601 format";
Pad:=@If(ISOWeekNum<10;"0";"");
Result := @Text(@Year(D))+"W"+Pad+@Text(ISOWeekNum);

@Prompt([OK];"Week number"; Result)

Here is another version that gives you a week number in another ISO format:

REM "Formulae Calculate the Week Number(01-53) for any Date. ";
REM "The output follows the ISO 8601:1988 standard: ex 1997-W31-4 for 1997.07.31 ";
REM "Formulae writer : Nikolai Aasen (nsaa@pvv.org), UNI Storebrand, Norway";
REM "Formulae written : 1997.07.30";
REM "Formulae updated : 1997.08.04";
REM "Version  : 1.03";
REM "Tested on   :Lotus Notes 4.6PreRelease2";
REM "This formulae is available in the";
REM "Lotus Notes FAQ: http://www.keysolutions.com/NotesFAQ/";
REM "More Calendar information in http://www.pip.dknet.dk/~pip10160/calendar.html"; 
REM "ISO 8601:1988 summary at: http://quake.stanford.edu/~lyle/ISOdate/Date.html";
REM "--------------------------------------------------------------------------------------------------";             
REM "Replace D with the date of interest.";
D := [1997.31.07];

REM "**************************";
REM"Calculate some data for this Year";
REM "**************************";
FirstOfYear := @Date(@Year(D); 1; 1);
LastOfYear := @Date(@Year(D); 12; 31);
FirstDayNum := @Weekday(FirstOfYear);
REM "ISO weeks start on Monday and ends on Sunday.";
ISOFirstDayNum := @If(FirstDayNum = 1; 7; FirstDayNum - 1);

REM " Week 1 of any year is the week that contains the first Thursday in January.";
REM "=1 if 1. jan = man - thu. WeekNumber is then 1, else 0";
IsFirstWeek := 7- ISOFirstDayNum >2;

REM "The first Monday after 1. jan this Year";
FirstMonday := 9 - ISOFirstDayNum;

REM "Number of Days from 1. jan to D";
DaysToDateD:=(D-FirstOfYear)/60/60/24+1;

REM "Number of days in Year(either 365 or 366)";
DaysInYear:=(LastOfYear-FirstOfYear)/60/60/24;

REM "Number of Weeks in Year. Most years have 52 weeks, but years that start on a 
Thursday and leapyears that start on a Wednesday have 53 weeks.";
NumberOfWeeksThisYear:=@If( (ISOFirstDayNum=4 | (ISOFirstDayNum=3 & 
DaysInYear=366));53;52 );

REM "***************************";
REM"Calculate some data for last Year  ";
REM "***************************";
FirstOfLastYear := @Date(@Year(D)-1; 1; 1);
LastOfLastYear := @Date(@Year(D)-1; 12; 31);
FirstDayNumLast := @Weekday(FirstOfLastYear);
REM "ISO weeks start on Monday and ends on Sunday.";
ISOFirstDayNumLast := @If(FirstDayNumLast = 1; 7; FirstDayNumLast - 1);


REM "Number of days in Year(either 365 or 366)";
DaysInYearLast:=(LastOfLastYear-FirstOfLastYear)/60/60/24;

REM "Number of Weeks Last Year. Most years have 52 weeks, but years that start on a 
Thursday and leapyears that start on a Wednesday have 53 weeks.";
NumberOfWeeksLastYear:=@If( (ISOFirstDayNumLast=4 | (ISOFirstDayNumLast =3 & 
DaysInYearLast=366));53;52 );

REM "************************";
REM"Calculates the Week Number  ";
REM "************************";


DDayNum := @Weekday(D);
ISODDayNum := @If(DDayNum = 1; 7; DDayNum - 1);

REM"Is D in the last Week of the last Year?";
DayInLastWeek := @If((DaysToDateD<FirstMonday & IsFirstWeek = 0);
                                @Return( @Text(@Year(D)-1)+"-W"+@Text(NumberOfWeeksLastYear)+"-"+@Text(ISODDayNum));
                                 NULL);

REM "Calculate number of Complete Weeks Between D and 1.jan";
ComplNumWeeks:=@Integer((DaysToDateD-FirstMonday)/7);

REM "Are there remaining days?";
RemainingDays:=@If( (DaysToDateD+1-(FirstMonday+7*ComplNumWeeks))>0);

NumWeeks:= IsFirstWeek+ComplNumWeeks+1;

Out :=
@If(RemainingDays;
@If( (NumWeeks>52 & NumWeeks>NumberOfWeeksThisYear );
         @Return(@Text(@Year(D)+1)+"-W01-"+ @Text(ISODDayNum));
          @Return(@Text(@Year(D))+"-W"+@Right("0"+@Text(NumWeeks);2)+"-"+@Text(ISODDayNum))); 
@Return(@Text(@Year(D))+"-W"+@Right("0"+@Text(NumWeeks-1);2) +"-"+@Text(ISODDayNum)));
Out

This LotusScript version comes from Christian Meis:

'CalculateWeekNumbers: 

Option Declare


Sub Initialize
     
%REM
This short agent shows how the calendar week
function can be implemented.

Christian Meis, 09.04.1999
E-Mail: Christian.Meis@mlc.de
%END REM
     
  Dim dateval As Variant
  Dim test As String
     
  dateval = Cdat( Inputbox( "Please enter date: " ) )
  test = GetCalendarWeek( dateval )
     
  Messagebox( Cstr( dateval ) & " --> " & test )
     
End Sub


Function GetCalendarWeek( Byval inputdate As Variant ) As String
     
%REM
This function calculates the calendar week number 
(ISO standard) for a given date value. The format
function of LotusScript (parameter "ww") does not solve 
this problem. 

Monday is the first day of the week. Week #1 is the week 
that contains the 4th of January (ISO 8601). The week at the
end/beginning of the year belongs to the next/previous year,
if there are 3 days or less of that week in the year in question.

Christian Meis, 4.2.2000
%END REM
     
  Dim InputDateOffset As Integer
  Dim YearInQuestion As Integer
  Dim January4 As Variant
  Dim January4Offset As Integer
  Dim FirstMondayOfYear As Variant
  Dim January1Offset As Integer
  Dim December31Offset As Integer
  Dim weeknum As Integer
     
  ' The year value is preset with that of the input date
  YearInQuestion = Year( inputdate )
     
  ' Calculate offset to monday from the input date
  InputDateOffset = CalculateIsoWeekday( inputdate )
     
  ' Calculate offsets for the first/last day of the year
  January1Offset = CalculateIsoWeekday( Cdat( "01.01." & Cstr( YearInQuestion ) ) )
  December31Offset = CalculateIsoWeekday( Cdat( "31.12." & Cstr( YearInQuestion  ) ) ) 
     
  ' If the input date is before the 4th of January and the year starts with
  ' a friday, saturday or sunday, the week belongs to the previous year
  ' if the entered date is not a monday or tuesday
  If Month( inputdate ) = 1 And Day( inputdate ) < 4 And January1Offset> 3 And InputDateOffset > 1 Then
    YearInQuestion = YearInQuestion - 1
  End If

  ' If the input date is after the 28th of December and the year ends with
  ' a monday, tuesday or wednesday, then the week belongs to the following year
  ' if the entered date is not a saturday or sunday
  If Month( inputdate ) = 12 And Day( inputdate ) > 28 And December31Offset < 3 And InputDateOffset < 5 Then
    YearInQuestion = YearInQuestion + 1
  End If
     
  ' The 4th of January defines week #1
  January4 = Cdat( "04.01." & Cstr( YearInQuestion ) )
     
  ' Offset to the monday of week #1 
  FirstMondayOfYear = Cdat( January4 - CalculateIsoWeekday( January4 ) )
     
  ' The time range between the monday of week #1 and the monday
  ' of the week in question is divided by 7, plus 1 for the first week
  weeknum = ( inputdate - InputDateOffset - FirstMondayOfYear ) \ 7 + 1
     
  ' The return value is a string with the week number and the year
  GetCalendarWeek = Cstr( weeknum ) & " " & Cstr( YearInQuestion )
     
End Function


Function CalculateIsoWeekday( tmpdate As Variant ) As Integer
     
%REM
This function converts the weekday-numbers from the
standard function to an offset acc. to the ISO version
monday -> 0, ... , sunday -> 6
%END REM
     
  Dim n As Integer
     
  n = Weekday( tmpdate )
   
  If n = 1 Then ' sunday to end of week
    n = n + 7
  End If
     
  CalculateIsoWeekday = n - 2
     
End Function

This version for 53 week years comes from Bill Westaway (Bill.Westaway@fmr.com):

The following "week of the year" formula was adopted from the set that shows on your web site.
It is for applications that require 53 week years because the last few days of the year still occur in a calendar year (government reporting or payroll support would be good examples). 2002 is a good example of a 53 week year.
(you can actually get a 54th week by this definition - this formula will also work then)

The formula works in views, fields, and agents and should work for every Notes/Domino release 3.0 and greater.

As was your formula -- it's "Monday" based.

I've used your padding concept for text conversion at the end. I normally leave it in numeric form and convert to text only when I need to combine with other text, as is done in the example below.

I've tried to keep this fairly efficient (I think I might be able to improve on the WeekOfYear_Gross variable formula)

REM { routine to figure out the week of the year a given date falls into --- for those applications that require a 53 week year};
REM { (i.e. those applications where the last week of the year is the first few days of the week that};
REM {    splits across a year end boundary};
REM { it's also true that the first week of the year is defined as the last days of the week that splits across a year end boundary.};
REM {   Its actually possible to get 54 week years by this definition!};

REM { Set the below Variable (SourceDate) to the value of the field that contains the date we will work on};

SourceDate := CompletedOnDist;

REM {First fugure out what the first day of the year in question is.};

FirstOfYear := @Date(@Year(SourceDate); 1; 1);
FirstDayNum_SundayBased := @Weekday(FirstOfYear);
FirstDayNum_MondayBased := @If(FirstDayNum_SundayBased = 1; 7;
FirstDayNum_SundayBased -1);

REM {Then get the week of the year for this date};
WeekOfYear_Gross := @Integer(@Integer(((@Date(SourceDate) - FirstOfYear) / (86400)) + 1) / 7) +1;

REM {Then adjust the week because the first day of they year may not fall on Monday};

DayOfWeek := @Weekday(SourceDate);
WeekOfYearNum := @If(DayOfWeek >= FirstDayNum_MondayBased;
WeekOfYear_Gross; WeekOfYear_Gross + 1);

Pad := @If(WeekOfYearNum < 10; "0"; ""); "Week " + Pad + @Text(WeekOfYearNum)

Stewart Clow (SCLOW@uk.ibm.com) modified Bill Westerly's formula to start weeks on Monday (the UK standard) instead of Sunday (the US standard):

REM { Returns the days at the start of the year up to Sunday as Week 1. Weeks start on Monday. FYI: 1984 is 54 weeks!
i.e. always returns week number for current year, rather than ISO week which can be week number for previous/next year. }

SourceDate := @Date(2009; 1; 11);

Jan1 := @Date(@Year(SourceDate); 1; 1);

DaysSinceJan1 := 1 + (SourceDate - Jan1) / 86400;
Jan1Weekday_SundayIs1 := @Weekday(Jan1);

Jan1Weekday_MondayIs1 := @If(Jan1Weekday_SundayIs1 = 1; 7; Jan1Weekday_SundayIs1 - 1);

WeekNum := @Integer((5 + DaysSinceJan1 + Jan1Weekday_MondayIs1) / 7);

PadChar := @If(WeekNum < 10; "0"; "");

"Week " + PadChar + @Text(WeekNum)

How do you create response documents from a navigator hotspot?

Add this before your response document compose:

  @Command([OpenView]);

This will give focus back to the view so that the proper document is selected.


How do you provide context-sensitive help for your database?

There are two ways of supporting this in Notes:

1) Use text pop-ups. You can also use @DBLookup's in pop-ups to look up code in a "help" database; this allows you to let someone edit the help in a centralized place instead of on the documents or forms.

2) Use a button that opens a help document in a help view.

@Command([FileOpenDatabase]; @DbName; "helpview";Key;"1";"");
@Command([OpenDocument]; "0");
@Command([FileOpenDatabase]; @DbName; "helpview";"";"0";"");
@Command([FileCloseWindow]);


Can you get a filename from the user in Lotuscript?

This example illustrates how Lotuscript can be used to call a Win32 API Common File Dialog to get a file. Similiar techniques can be used on other operating systems:

Option Public
Option Declare


Dim Filter As String
Dim FileName As String
Dim FileTitle As String
Dim TruncName As String

Dim VaultWIPRoot As String
Dim VaultWIPUserPath As String

Type tagOPENFILENAME
     lStructSize As Long
     hwndOwner As Long
     hInstance As Long
     lpstrFilter As String
     lpstrCustomFilter As Long
     nMaxCustFilter As Long
     nFilterIndex As Long
     lpstrFile As String
     nMaxFile As Long
     lpstrFileTitle As String
     nMaxFileTitle As Long
     lpstrInitialDir As String
     lpstrTitle As String
     Flags As Long
     nFileOffset As Integer
     nFileExtension As Integer
     lpstrDefExt As String
     lCustData As Long
     lpfnHook As Long
     lpTemplateName As Long     
End Type

Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (OPENFILENAME As tagOPENFILENAME)  As Long

Dim OPENFILENAME As tagOPENFILENAME

Public Const OFN_ALLOWMULTISELECT = &H200
Public Const OFN_CREATEPROMPT = &H2000
Public Const OFN_ENABLEHOOK = &H20
Public Const OFN_ENABLETEMPLATE = &H40
Public Const OFN_ENABLETEMPLATEHANDLE = &H80
Public Const OFN_EXPLORER = &H80000                         
Public Const OFN_EXTENSIONDIFFERENT = &H400
Public Const OFN_FILEMUSTEXIST = &H1000
Public Const OFN_HIDEREADONLY = &H4
Public Const OFN_LONGNAMES = &H200000                       
Public Const OFN_NOCHANGEDIR = &H8
Public Const OFN_NODEREFERENCELINKS = &H100000
Public Const OFN_NOLONGNAMES = &H40000                      
Public Const OFN_NONETWORKBUTTON = &H20000
Public Const OFN_NOREADONLYRETURN = &H8000
Public Const OFN_NOTESTFILECREATE = &H10000
Public Const OFN_NOVALIDATE = &H100
Public Const OFN_OVERWRITEPROMPT = &H2
Public Const OFN_PATHMUSTEXIST = &H800
Public Const OFN_READONLY = &H1
Public Const OFN_SHAREAWARE = &H4000
Public Const OFN_SHAREFALLTHROUGH = 2
Public Const OFN_SHARENOWARN = 1
Public Const OFN_SHAREWARN = 0
Public Const OFN_SHOWHELP = &H10

Function OpenCommDlg ()
     Dim Title As String    
     Dim DefExt As String
     Dim szCurDir As String 
     Dim APIResults%
     
     SetFileFilter
     
     'Give the dialog a caption title.
     Title = "Add supporting document" & Chr$(0)
     
     'Allocate string space for returned strings
     FileName = Chr$(0) & Space$(255) & Chr$(0)
     FileTitle = Space$(255) & Chr$(0)
     
     'If the user does not specify an extension, append TXT.
     DefExt = "BMP" & Chr$(0)
     
     'Set up the default directory
     szCurDir = Curdir$ & Chr$(0)
     
     'Set up the data structure before you call the GetOpenFileName
     
     OPENFILENAME.lStructSize = Len(OPENFILENAME)
     
     'If the OpenFile Dialog box is not linked to any form use this line.
     'It will pass a null pointer.
     
     OPENFILENAME.hwndOwner = 0&
     
     OPENFILENAME.lpstrFilter =  Filter
     OPENFILENAME.nFilterIndex = 1
     OPENFILENAME.lpstrFile = FileName
     OPENFILENAME.nMaxFile = Len(FileName)
     OPENFILENAME.lpstrFileTitle = FileTitle
     OPENFILENAME.nMaxFileTitle = Len(FileTitle)
     OPENFILENAME.lpstrTitle = Title
     OPENFILENAME.Flags = OFN_FILEMUSTEXIST
     OPENFILENAME.lpstrDefExt = DefExt
     OPENFILENAME.hInstance = 0
     OPENFILENAME.lpstrCustomFilter = 0
     OPENFILENAME.nMaxCustFilter = 0
     OPENFILENAME.lpstrInitialDir = szCurDir
     OPENFILENAME.nFileOffset = 0
     OPENFILENAME.nFileExtension = 0
     OPENFILENAME.lCustData = 0
     OPENFILENAME.lpfnHook = 0
     OPENFILENAME.lpTemplateName = 0
     
     'This will pass the desired data structure to the Windows API,
     'which will in turn it uses to display the Open Dialog form.   
     APIResults% = GetOpenFileName(OPENFILENAME)
     
     If APIResults% <> 0 Then          
          FileName = Cstr( OPENFILENAME.lpstrFile )
          FileTitle = Cstr( OPENFILENAME.lpstrFileTitle )        
          OpenCommDlg = 1
     Else 
          OpenCommDlg = 0
     End If
End Function

Sub Initialize
     If (OpenCommDlg = 1) Then
          Print " "
' use FileExt and other globals that contain the filename and extension, etc.
     Else
          Print "No documents were attached."
     End If
     
End Sub


Another way to do this is to use Notes' undocument API to do this. This is available on more platforms, but the usual caveats apply about using an undocumented API:

Declare Function NEMGetFile Lib "nnotesws" ( wUnk As Integer, Byval szFileName As String, Byval szFilter As String, Byval szTitle As String ) As Integer          'use nnotesws for Win95 and WinNT, inotesws for OS/2, and _nem for Win16

Sub Click(Source As Button)
  Dim szFileName As String*256
  Dim szTitle As String
  Dim szFilter As String
     
  szFilename = Chr(0)
  szTitle = "Select a database"
  szFilter = "Notes Databases|*.NSF|Notes Templates|*.NTF|Programs|*.EXE|All Files|*.*|"          'Use this format for ANY file type
  If NEMGetFile( 0, szFileName, szFilter, szTitle) <> 0 Then
    Messagebox szFileName
  End If
End Sub


How do you make a Computed For Display Rich Text Field?

Computed when composed formulas don't just execute when the document is composed. They execute when the form is edited and the field doesn't exist in the document. So they're really computed-when-field-doesn't exist formulas, which is also true for a new document.

Create another field whose formula deletes the rich text field on saving the document. For the rich text field, use a DbLookup to initialize the field. This gives you, in effect, a computed-for-display rich text field.


How do you look up all the people in a Group?

You can use this formula to get the Common Name list of persons in YourGroupName

  @Name([CN];@DbLookup("";@Subset(@MailDbName;1) : "Names.nsf";"Groups";"YourGroupName";
"Members"))


How do you make dynamically sized tables?

Starting with the second row, and for each subsequent row, select the entire row, and then in the Text Properties window, check the box "Hide paragraph if formula is true" with the formula:

( ! @IsDocBeingEdited & ThisRowKeyField = "" ) | LastRowKeyField = ""

Substitute the name of a required field in the current row for ThisRowKeyField, and the name of a required field in the previous row for LastRowKeyField. What this will do is suppress the display of any blank rows for read and print, and suppress all blank rows except one in edit mode.

Then you will need a way to "add" rows as they are entered by "un-hiding" them as needed, so put a button or hotspot in the form or on the action bar entitled "Add a row" with the formula:

@if(BottomRowKeyField="";
     @Command([RefreshHideFormulas]);
     @Prompt([OK];"Error";"Thirty rows is maximum"))


How do you send mail based on whether a field is modified?

Use a hidden computed field to monitor the target field for changes. For example, if the field you want to monitor is named "Dept", make a hidden computed field of the same type named "Monitor" with a formula similar to:

@If((Monitor=Dept)|(!@IsDocBeingSaved); @Return(Monitor); @Success);
@MailSend(Dept; ""; ""; ""; "Dept changed to yours"; "Doclink:"; [IncludeDoclink]);
Dept


How do you execute DOS commands from a formula?
Use the macro (note the use of the /C in the DOS command):

@Command([Execute]; "C:\\COMMAND.COM"; "/C DEL C:\\TEST.TXT")

How do I calculate the number of weekdays between two date fields?
The following formula counts the number of weekdays (but not weekend days):

diffDays := (EndDate - StartDate) / 86400 + 1;
strtDay := @Modulo(@Weekday(StartDate); 7);
endDay := @Modulo(@Weekday(EndDate); 7);
result := (diffDays - endDay + strtDay - 8) * 5 / 7 - @Max(-2; -strtDay) - @Min(1; endDay) + 5 - strtDay + endDay

Can I look up Multiple Keywords using @Db functions?
Yes, by separating the multiple keywords by a colon. Also, you can reference a field which contains multiple keywords, as long as that field has "Allow Multi-Values" checked ON.

How do you prevent LotusScript errors when handling deletion stubs in document collections?

Sometimes when you get a document collection for a LotusScript agent to handle, Notes passes you a deletion stub (this is probably a bug). In case you have to handle it, you can use this code:

' From Mike Woolsey
' IsErr_NOTES_ERROR is 4000
On Error lsErr_NOTES_ERROR Goto HandleDocError
dim failureCode as Integer    ' may be global
dim test as Variant
.....
set doc = dc.GetFirstDocument    ' say this is a FTS result collection, the doc may have been deleted since it was indexed
while not(doc is Nothing)
  failureCode% = 0
  test = doc.Items
  if failureCode% = 0 then   ' no error
    ....   ' handle document
  end if
  set doc = dc.GetNextDocument(doc)
wend
.....

HandleDocError:
  failureCode% = Err
  Resume next


How do you monitor your own threads in a discussion database?

Create a private folder. Add the discussion topics of interest to this folder. When responses are made to the topics, they will also show up in your private folder.


How do you notify users when documents are modified or added to a database?

Set up an agent for the database that will:
1) Run "If documents have been created or modified"
2) Act on "Newly modified documents"
3) Simple Action:"Send Newsletter Summary"

The result of this will be that anyone that you have entered in the To field will receive an e-mail message that includes a doc-link to the new or modified document. No scheduling is required.


How do you force the user to click an Edit action button to edit a document?

These scripts collectively force the user to use an action to place an existing document in edit mode. The action script places the current document in edit mode. The Postopen and Querychangemo de event scripts prevent the user from changing to edit mode through other means such as Actions - Edit Document (Ctrl+E).


'(Globals) object, (Declarations) event
Dim allowEdit As Integer

'(Form) object, Postopen event
Sub Postopen(Source As Notesuidocument)
  ' From Carl_Burgess@inter-tel.com
  'Let document pass if new or not in EditMode
  'Otherwise if existing document is in EditMode
  ' Set allowEdit so Querymodechange doesn't reprocess
  ' Turn EditMode off so document opens in read mode
  ' Tell the user to use the action
  If source.EditMode And Not source.IsNewDoc Then
    allowEdit = True
    source.EditMode = False
    Messagebox "Use Edit Mode action to edit document"
  Else
    allowEdit = False
  End If
End Sub

'(Form) object, Querymodechange event
Sub Querymodechange(Source As Notesuidocument, Continue As Variant)
  'Allow user to proceed, and turn off allowEdit if
  ' user clicked the action (allowEdit on)
  ' already processed by Postopen (allowEdit on)
  ' trying to get out of edit mode
  '  (allowEdit off but EditMode on)
  'Tell user to click action if changing existing document
  ' to edit mode and not already processed by Postopen
  '  (allowEdit and EditMode off)
  If allowEdit Or (source.EditMode And Not allowEdit) Then
    allowEdit = False
  Else
    Messagebox "Use Edit Mode action to edit document"
    continue = False
  End If
End Sub

'(Action) object, Click event
Sub Click(Source As Button)
  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
  'Turn on allowEdit so Querymodechange will let it pass
  'Turn on EditMode
  allowEdit = True
  uidoc.EditMode = True
End Sub


How do you copy all parts of a script from one form to another?

1) Open the form in design mode.
2) Choose File/Export and select a file name.
3) Choose Current Object.
4) Go to the other form or the Script Library.
5) Choose File/Import.


Can I lookup Rich Text using @Db functions?
Yes, but if the value you are trying to look up is in a view column, it will fail.

How do you change fields based on the state of a separate keyword field?

There is an item in the field Properties box tab to the right of the "Basics" tab (the tab only appears for Keyword types and has a picture of a field). It's a little checkbox called "Refresh fields on keyword change".

In your "key" field, turn that little property on and your other fields will be recomputed upon an entry being made in the key field, just as if F9 had been pressed. The "key" field must be placed "before" (to the left and above) the fields to update within the form.

How do you keep doc.send() from crashing a background agent?

You can add these error handlers before you do the doc.send():


On Error Goto HandleError
On Error 4294 Resume Next
On Error 4295 Resume Next
On Error 4000 Resume Next
On Error 4160 Resume Next


How do you notify the author of a document when a response has been composed?

1) In the Main Document, Response and Response to Response forms, create a field called 'From', defaulting to

@Name([CN];@Username);

2) In the Response Form, create a hidden field called 'ParentFrom', defaulting (with inheriting on) to:
From

3) In the Reponse to Response, create a field called 'ParentFrom' defaulting to (with inheriting on):
ParentFrom:From

4) In Response and Response to Response forms, add another hidden computed for display field called 'Announce' with this formula:
@If(@IsDocBeingSaved;@Success;@Return(0));
List := @Trim(@Replace(ParentFrom;From;""));
@If(@Elements(List) > 0; @Success;@Return(0));
SendList := @Prompt([OKCANCELLIST]; "Mail Notify?"; "Send mail notification about your reply to ";"";List);
@If(@Elements(SendList) > 0; @Success;@Return(0));
@MailSend(SendList; ""; ""; "Ref:" + OriginalSubject; ""; "My reply: "; [IncludeDoclink])

[<a name="NT00001052">How do I make the "loser" of a replication/save conflict be the
"winner"?
</a>]

When you select the "loser" document and run this agent, it will be promoted to be a replication "winner". You can then delete the original "winner" document. Put this code into an agent that runs on selected documents:

Sub Initialize
  ' From Kevin Pauli (kcpauli@usa.net)
  Dim session As New NotesSession
  Set db = session.CurrentDatabase
  Set collection = db.UnprocessedDocuments
  Set doc = collection.GetFirstDocument
  Call doc.RemoveItem( "$Conflict" )
  If doc.IsResponse Then
    Set parent = db.GetDocumentByUNID( doc.ParentDocumentUNID )
    If parent.IsResponse Then
      Dim grandParent As NotesDocument
      Set grandParent = db.GetDocumentByUNID(parent.ParentDocumentUNID )
      Call doc.MakeResponse( grandParent )
    Else
      Call doc.RemoveItem( "$REF" )
    End If
  End If
  Call doc.Save( True, True )
End Sub

Glenn Thibert (glenn.thibert@thehartford.com) suggested something much simpler:

1) Highlight the 'winner' doc and press the delete key so that the trashcan icon appears.
2) Highlight the 'loser' doc and use the menu command or smarticon for Tools - Refresh Selected Docs

When you hit F9, the deleted doc will disappear and the 'loser' conflict will be in the view as the winner.


How can I control whether the @Db Function returns a list?
If the @DbLookup key is valid for multiple documents, it will return a list. All @DbColumns will return a list (unless there is only one value in the Lookup View). If you want to return only one value, use

@Subset(@DB Function; 1)
or
@Subset(@DB Function; -1)
.
If you want the @Db Function to return a list to a field, make sure that "Allow Multi-Values" is checked ON.

How do you return custom data types in LotusScript?

From Greg_Prickril@lotus.com:


Declarations
Class ReturnObj
  Private m_stName As String
  Private m_stType As String
     
  Property Get NameVal As String
    NameVal = m_stName$
  End Property
     
  Property Get TypeVal As String
    TypeVal = m_stType$
  End Property
     
  Sub new( arg_stName$, arg_stType$ )
    m_stName = arg_stName$
    m_stType = arg_stType
  End Sub   
End Class

Function Test() As ReturnObj
  Set Test = New ReturnObj( "Name", "Type" )
End Function

Initialize
  Dim var
  Set var = Test()
  Msgbox( var.NameVal )


Can you create Shared Actions?

You can create a blank subform that has actions, then use that subform in other forms.


Can you use regular expressions for string matching?

Look for the following formula to return true in each case.

To check if a character is alphanumeric...

@Matches(Char; "{A-z}")

To check for "a" or "b" or "c" ...

@Matches(Char; "{a-c}")

To check for all upper case alphanumeric plus the letters "a" or "b" or "c" and "d" ...

@Matches(Char; "{A-Za-d}")

To check for $321,000 or $321.000 but not $321.00 or $321,000- ...

@Matches(Dollar; "{$}{0-9}{0-9}{0-9}{,.}{0-9}{0-9}{0-9}")

To check for US long distance phone number...

@If(
  @Matches(PhoneNum; "{0-9}{0-9}{0-9}-{0-9}{0-9}{0-9}-{0-9}{0-9}{0-9}{0-9}");
  @Success;
  @Failure("The Phone number must be in the format \"xxx-xxx-xxxx\"")
)

To check for social security ...
@Matches(SSNum;"{0-9}{0-9}{0-9}-{0-9}{0-9}-{0-9}{0-9}{0-9}{0-9}")


Can you run an Agent from LotusScript?

Using LotusScript under Windows, you can use OLE Automation of Notes to run an agent:

  Set s = CreateObject("Notes.NotesSession")
  Set db = s.GETDATABASE("", "db.nsf")
  Set a = db.GETAGENT("SomeAgent")
  Call s.SETENVIRONMENTVAR("AgentDocID", "ABCD")
  Call a.RUN

Parameters are passed to the agent using environment variables.

You can also run the agent directly:

  ' from Gary_VanCott@nvhin.com
  Dim db As New NotesDatabase("", "")
  Dim Agent As NotesAgent
  If (db.open("ServerName/Domain", "path\filename.nsf")) Then
    Set Agent = db.GetAgent("Agent Name")
    If Not(Agent Is Nothing) Then
      Call Agent.Run
    Else
      Msgbox "Agent not found..."
    End If
  Call db.close
  End If

How do you create Names, Readers, Authors fields in LotusScript?

You can use the "specialType" field of the NotesItem type:

  Dim variableName As New NotesItem( notesDocument, name$, value [,specialType%])

For example,
Dim TAuthor As New NotesItem(doc, "Author",  Auths, AUTHORS)
 TAuthor.IsSummary = True

will create a TAuthor field which is an author names field.


Can you implement exclusive locking across replicated databases?

This is impossible using Notes. Think about what would be required. You have a database replicated to multiple servers and clients. If a user opens a document, how would all the other unconnected databases know about this?
The only way to do this using Notes is to have a single un-replicated database. Normally, a redesign of the application is enough to work around this requirement.
Note that similiar problems occur if you try this with relational databases.


How do you do @MailSend in Lotuscript?

Use the function below as follows:

  flag% = SendMailMemo("SendTo", "cc", "bcc", "Subject", "Body Text", NotesDocument)

If you pass a valid NotesDocument in as the last parameter, the mail memo is sent with a doclink to it. If you don't want to send a doclink, just pass the Nothing constant as the last parameter.

If the function encountered problems, it returns False, otherwise it returns True.

Function SendMailMemo(sendTo As String, _
                      cc As String, _
                      bcc As String, _
                      subject As String, _
                      body As String, _
                      linkTo As NotesDocument) As Integer
  On Error Goto ErrorHandler

  Dim mailDb As New NotesDatabase("", "")
  Dim mailDoc As NotesDocument
  Dim rtItem As NotesRichTextItem

  Call mailDb.OpenMail
  If (mailDb.IsOpen = False) Then Call mailDb.Open("", "")
  Set mailDoc = mailDb.CreateDocument
  mailDoc.Form = "Memo"
  mailDoc.SendTo = sendTo
  mailDoc.CC = cc
  mailDoc.BCC = bcc
  mailDoc.Subject = subject
  Set rtItem = mailDoc.CreateRichTextItem("Body")
  Call rtItem.AppendText(body)
  If Not(linkTo Is Nothing) Then
    Call rtItem.AddNewLine(2)
    Call rtItem.AppendDocLink(linkTo, "Double-click to open document")
  End If
  Call mailDoc.Send(False)
  SendMailMemo = True
  Exit Function

 ErrorHandler:
  Print "Error " & Str$(Err) & ": " & Error$
  Resume TheEnd

 TheEnd:
  SendMailMemo = False
End Function


Can you dynamically hide/unhide sections of a form?

Put a keyword field on the form and use the 'refresh fields on keyword change' property of the keyword field. If the user changes the keyword field, the hide when formulas are re-evaluated and the form is redisplayed.


How do you update a field in response documents after it is changed in the parent document?

Assumptions:
1) The name (or synonym) of the Response form is "TASK" and it inherits field values.
2) The name of the field to synchronize is "STATUS" in both Project and Task forms. This is computed when composed to inherit from the parent form.

Include the following Script code in the QuerySave event of the parent form:

Sub QuerySave(Source As Notesuidocument, Continue As Variant)
  Dim Collection As NotesDocumentCollection
  Dim Doc As NotesDocument
  Dim Form, ParentStatus, Status As String
     
  Set Doc = Source.Document
  Set Collection = Doc.Responses
  Set Doc = Collection.GetFirstDocument
  ParentStatus = Source.FieldGetText("STATUS")
  While Not ( Doc Is Nothing )
    Form = Doc.GetItemValue("Form")(0)
    Status = Doc.GetItemValue("Status")(0)
    If (Form = "TASK")And (Status <> ParentStatus) Then
      Call Doc.ReplaceItemValue( "STATUS", ParentStatus )
      Call Doc.Save (True, False)
    End If
    Set Doc = Collection.GetNextDocument(Doc)
  Wend
End Sub

Gerard O'Sullivan posted this more general-purpose solution to place in the QuerySave event (the above solution only goes down one level):

%REM
***************************************************************

Function: UpdateResponseDocs

Input(s): Nothing

Output(s): Nothing

Purpose: 

Updates the appropriate fields in the response docs
by replacing the appropriate item values from the the parent 

*****************************************************************
Authored By: Ger O'Sullivan
Authored Date: 07-Mar-2002

Last Modified By: Ger O'Sullivan
Last Modified Date: 07-Mar-2002

%END REM
Sub UpdateResponseDocs()

  RoutineName="UpdateResponseDocs"

  Dim arr_fieldsToSet(6) As String

  arr_fieldsToSet(0) = "<fieldName_0>"
  arr_fieldsToSet(1) = "<fieldName_1>"
  arr_fieldsToSet(2) = "<fieldName_2>"
  arr_fieldsToSet(3) = "<fieldName_3>"
  arr_fieldsToSet(4) = "<fieldName_4>"
  arr_fieldsToSet(5) = "<fieldName_5>"
  arr_fieldsToSet(6) = "<fieldName_6>"

  Call RecursiveSetResponseDocs(note, arr_fieldsToSet)
End Sub


%REM
***************************************************************

Function: RecursiveSetResponseDocs

Input(s): NotesDocument
Variant

Output(s): None

Purpose: 

Loop through the list of response documents, 
checking to see if these responses have responses
themselves. We do this by recursively calling the same 
function but passing the response object instead.

*****************************************************************
Authored By: Ger O'Sullivan
Authored Date: 07-Mar-2002

Last Modified By: Ger O'Sullivan
Last Modified On: 07-Mar-2002

%END REM
Sub RecursiveSetResponseDocs(_
    doc As NotesDocument, _
    v_fieldsToSetList As Variant)

  RoutineName = "RecursiveSetResponseDocs #1"

  Dim item As NotesItem
  Dim threadIndent As String
  Dim collection As NotesDocumentCollection
  Dim currentResponse As NotesDocument
  Dim s_fieldToSet As String


  'With the current doc object, get a collection of it's responses
  Set collection = doc.Responses

  'Ensure that we have a vald collection otherwise, skip on to the next document.
  'If (collection.Count > 0) Then

  RoutineName = "RecursiveSetResponseDocs #2"
  'Get a handle on the first document
  Set currentResponse = collection.GetFirstDocument

  'Loop through the list of response documents, checking to see if these responses have responses
  'themselves. We do this by recursively calling the same function but passing the response object instead.
  While Not ( currentResponse Is Nothing )
    Forall v_fieldToSet In v_fieldsToSetList
      s_fieldToSet = Cstr(v_fieldToSet)
      'Get a handle on the item
      Set item = doc.GetFirstItem(s_fieldToSet)
      If (currentResponse.HasItem(s_fieldToSet)) Then 
        If (item.Type = RICHTEXT) Then
          Call currentResponse.ReplaceItemValue(s_fieldToSet, item.text)
        Else
          Call currentResponse.ReplaceItemValue(s_fieldToSet, item.values)
        End If
      Else
        Call currentResponse.CopyItem(item, "")
      End If
    End Forall

    Call currentResponse.Save(True, False)

    ' Recursive call. Check for response to responses
    Call RecursiveSetResponseDocs(_
         currentResponse, _
         v_fieldsToSetList)

    RoutineName = "RecursiveSetResponseDocs #3"
    Set currentResponse = collection.GetNextDocument( currentResponse )
  Wend

End Sub 


How do you refresh RichText fields in a UI document?

Other field types get refreshed when you call the uidoc.Refresh method. However, with RichText fields, you have to close the document and reopen it (the user will see a flicker as you do this). Here is an example of how to do this:

Dim s As New NotesSession 'reference to the current session
Dim db As NotesDatabase 'reference to the current database
Dim ws As New NotesUIWorkspace 'reference to the current orkspace
Dim uidoc As NotesUIDocument 'reference to the current UI document
Dim doc As NotesDocument 'the back-end document open in the UI

  'set db equal to the currently open database found in the current session
  Set db = s.currentdatabase
  'set the uidoc equal to the currently open uidoc
  Set uidoc = ws.currentdocument
  'set doc equal to the document open in the uidoc
  Set doc = uidoc.document

' *** make your changes to the doc object here ***

  'Save the document
  Call doc.Save(False,False)
  ' grab the document's UNID
  Dim DocUNID As String
  DocUNID = doc.UniversalID

  'Set the saveoptions so that the user is not prompted when the document closes.
  doc.SaveOptions = "0"

  'Close the document in the UI
  Call uidoc.Close

  ' get the document back
  Set doc = db.GetDocumentByUNID(DocUNID)

  'Re-open the document in the UI
  Set uidoc = ws.EditDocument(True, doc)


How do you create a preview pane in a framed UI?

You have to name one of the panes "NotesPreview". When you click on a document in a view, it will then be previewed in this pane.


How do you install COM?

This only works in Notes 5.02b and above on Win32 platforms. Go to the Notes directory using a command prompt and type in:

  regsvr32 nlsxbe.dll

This will register Notes' COM objects and you can then use the .tlb files in that directory in Visual Basic or some other COM aware program.


How do you convert a time field to display as military time?

REM "From Jason Collier (jcollier@sd.synetics.com)";
time := @Time(StartTime);
t:= @Text(@Time(time));
t2:=@Left(@Text(@ReplaceSubstring(@Text(@Time(time)); ":" ;"")); 6);
h:=@Left(t2;2);
s:=@Right(t2;2);
m:=@Middle(t2;2;2);
AM:=@ReplaceSubstring(@Left(t2;2) ; "12" ;"00");
PM:=@ReplaceSubstring(@Left(t2;2) ; "12" ;"12");
PM2:=@Text(@TextToNumber(h)+12);
timecheck:= @If(@Contains(t;"AM"); AM ; @If(@Contains(t;"PM") & @Contains(@Left(t2;2);"12") ; PM ; PM2));
timecheck + m

How do you display a custom image in a Notes view?

Editor's note: this sounds like an unsupported "feature" in R5. Use at your own risk, as it may change in future versions.

This is from Sushanta Kumar Manna's Notes Advisor Tip:

1. In the PostSave Event of the form, get the RichText Filed and set the IsSummary Value to true (normally this is set to false for all RichText Items). This example uses an RTF named "Picture":

Sub Postsave(Source As Notesuidocument)
  Dim doc As NotesDocument
  Dim item As NotesItem
  Set doc = Source.Document
  Set item = doc.GetFirstItem("Picture")
  item.IsSummary = True
  Call doc.Save(False,True)
End Sub

2. Compose the form.

3. Insert a image resource in a rich text field using the Create|Image Image Resource menu option. Do not just import an picture as this will give you the Red Box of Death (RBOD).

4. Save the document.

5. Now Create a view for the specific form and map the Rich text field "Picture" to the first column and don't forget to tick the column property "Display value as Icons".

6. Now you can view your image in the first column of the view.

Here's a tip from Stephen Knight on how to use image resources to do something similar:

You add an image resource then put the name of that resource in the column -- this can be a literal, i.e. "logo.gif" or a fieldname to make the image variable.� Then just tick the box "display as icon" and it appears....� the image needs to be the correct size and you have to extend the row height to a suitable number... shrink to fit doesn't work. You can also include multiple images using a multi-value field or list in the column, tick the box saying "display multiple values as separate entries", sort the column, and they all appear.


Can you stop the refresh view indicator for displaying if you use @Today in a view?

This is an undocumented workaround. If you use this formula code:

  Today := @TextToTime("Today");

The view will still get updated daily, but the refresh view indicator will not be displayed.


How do you load different LSX/DLLs based on which platform you are on?

From Gary Devendorf and Ben Langhinrichs:

You can use an Execute statement to handle this, or any other variable loading of libraries, LSXs, etc.

Dim cmdstring As String
If platform Then
  cmdstring = |Uselsx "*mylsx"|
Else
  cmdstring = |Uselsx "*yourlsx"|
End if
Execute cmdstring


How do you check if a document is a deletion stub?

When you write an agent that works on New or Modified Documents, the UnprocessedDocuments collection will sometimes return a deletion stub instead of only returning documents (this is probably a bug, but it's been around since R4). If you do not program for this case, your agent may suddenly stop working because it tried to access an item on a document that has been deleted.

You can check if a document is a deletion stub with this:
If (doc.Size = 0) then
'doc is a deletion stub
Else
'doc is not a deletion stub
End If

You can also use a comparision of:
If (doc.NoteID = "")


Why do I get an "Object Variable Is Not Set" or "Variant is Not Set" error?

There is a bug in Lotuscript code which should be fixed by the developer (if you're a user, complain; if you're a developer, read on). The quickest way to find the problem is do a File/Tools/DebugLotuscript, then run the code again. You will be brought to the exact line you are having the problem on. Look at all the variables for that line; one of the variables is not an object. The object you are accessing is probably not an object because you forgot to set it or had an error setting it.


Is there a @Replace equivalent function in LotusScript?

Unfortunately, LotusScript doesn't have this function that can be found in MS VBScript (the Replace function). Andy Armstrong (andy@tagish.com) coded this up:


' Return strArg with instances of strSrc replaced by strDst. After each
' replace searching recommences after
' the last character of the replacement
'
' History:
'
' 23-12-1998 AA Initial release

Function StringStuffReplaceString(Byval strArg As String,
Byval strSrc As String,
Byval strDst As String) As String
Dim iPos As Integer
iPos = Instr(strArg, strSrc)
While iPos > 0
strArg = Left$(strArg, iPos - 1) + strDst + Mid$(strArg, iPos +
Len(strSrc))
iPos = Instr(iPos + Len(strDst), strArg, strSrc)
Wend
StringStuffReplaceString = strArg
End Function


How do you prevent jagged right table edges when merging/splitting cells?
From Pam Clinton:

When you create a new table, add a column that is as small as possible (0.125") and hide all cell edges and make it transparent. Never use this column. Don't merge it in, etc. Notes just screwed up the last column when displaying these jagged tables.

Unfortunately, there's no fix for corrupted tables except recreating them.


How do you synchronize custom databases with PDAs?

Lotus' Domino Everyplace Enterprise Services (DEES) was the latest attempt by Lotus to do this. It was supposed to ship when R6 was released, but has disappeared from any mention. The Websphere Everyplace Access (WEA) PDA client is the next attempt at it, but it won't be available until the R7 timeframe (late 2004 at the earliest :-)

In the meantime, you should look at these tried and true applications:
Pylon Pro
mForms
OneBridge Mobile Data Suite


What Are Domino's Field Size Limits?
You can find the answers in Mike Woolsey and Ben Langhinrichs' Domino Size Limits page.

How do you output a PDF from Notes?

There are a few solutions for this:
- DominoPDF (uses the HTTP task to help convert documents to PDF)
- N2PDF (an export-filter add-in for Notes)
- PDF995 (a PDF driver with COM automation under Windows)


How do you create a NotesDocumentCollection?
A NotesDocumentCollection is useful for storing a set of documents that you later want to put into a folder or delete; it's a lot faster to make one call than to delete or folder documents one at a time. Unfortunately, there's no simple way to do this such as "Dim dc As NotesDocumentCollection" which is what most people would try. Instead, you have to get it by using one of the search collection APIs with a key that can't be found such as this:

Dim session As New NotesSession
Dim dc As NotesDocumentCollection
Set dc = session.CurrentDatabase.GetProfileDocCollection("nonexistingform") 

or by doing this:

Dim session As New NotesSession
Dim db As NotesDatabase
Set db = session.CurrentDatabase
Dim view As NotesView
Set view = db.GetView("($All)")
Dim dc As NotesDocumentCollection
Set dc = view.GetAllDocumentsByKey("12345") ' non-existent key


What causes "Entry Not Found In Index" Errors?
This is generally caused by @DbLookup/@DbColumn on a view and the lookup key is not found.
However, it can also be caused by a save/replication conflict in the same view used for the lookups. If this is true, you can add "(!@IsAvailable($Conflict))" to the view selection formula to exclude conflicts.
It can also be caused by the view setting "Generate Unique Keys in Index for ODBC" if there's a replication conflict in the same view. In this case, you'll get an error if you try to open the view but you don't see any formulas or events that are doing any lookups.


Can LotusScript operate on Private Views?

From Phanikumar <gundimedap@yahoo.com>"

LotusScript can access private views by opening the desktop.dsk file.

Being able to do this is useful if there is design change on the private view. Users won't get the new design unless they remove the database icon/bookmark from their desktop and re-add it. However, you can send them a button with LotusScript to open their desktop.dsk and delete the private views so they will get the new view design the next time they use the view.

Can you add a database to the replicator page?

From Jason Collier:

Sub Click(Source As Button)

Dim workspace As New NotesUIWorkspace
Dim session As New NotesSession
Dim db As NotesUIDatabase
Dim replica As NotesDatabase
Dim dbLookUp As NotesDatabase

Const LookupServer = "servername"
Const LookupDb = "databasename.nsf"
Set dbLookUp = session.GetDatabase( LookUpServer, LookUpDb )
'set the values of the replication

Set replica = dbLookUp.CreateReplica( "", "databasename.nsf" )

'Add Local and Server Icons to Workspace and Replicator Tab
Call workspace.OpenDatabase( LookUpDb.Server , LookUpDb.filename)
Set db = workspace.CurrentDatabase
Call workspace.OpenDatabase( "" , LookUpDb.filename)
Set db = workspace.CurrentDatabase


Msgbox("Replica database created for: "+ LookUpDb)

End Sub


Can you refresh a document in the Notes client after changing a rich text field?

What you have to do is:
1) set the SaveOptions field to "0"
2) save using uidoc.save (doesn't really save because of the SaveOptions field)
3) get a reference to the original document using uidoc.Document
4) close the document in the Notes client using uidoc.Close
5) use ws.EditDocument to re-open the document in edit mode

Here's a code snippet for doing this:

  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
  Dim doc As NotesDocument
  Set doc = uidoc.Document
  Call doc.ReplaceItemValue("SaveOptions", "0")
  uidoc.Save
  Set doc = uidoc.Document
  uidoc.Close
  Call workspace.EditDocument(True, doc)
  Call doc.RemoveItem("SaveOptions")


How do you track who deleted a document?
From Dirk Hamilton:
This is used to log each document deletion. It creates a log document which is displayed in a "deletion log" view so users can clearly see who deleted each document instead of pointing fingers :-)

Sub Postdocumentdelete(Source As Notesuidatabase)
  Dim s As New notessession
  Dim db As notesdatabase
  Dim d3 As notesdocument
  Dim c As notesdocumentcollection
  Dim d As notesdocument
  Set db = s.currentdatabase
  Set c = source.documents
  Set d = c.getfirstdocument
  Do 
    f = d.form
    If f(0) = "log" Then Goto skipper
    i = d.issue
    p = d.pkey
    'create log entry
    Set d3 = db.createdocument
    d3.form = "log"
    d3.when = Now
    d3.who = Evaluate("@Name([CN];@V3UserName)")
    d3.what = "Deleted a document. Form = " + f(0) + " :: issue = " + i(0) + " :: pkey = " + p(0)
    Call d3.save(True,False)
   skipper:
    Set d = c.getnextdocument(d)
  Loop Until d Is Nothing
End Sub


How Do You Run Console Commands From Lotuscript?

Here's an example of code (from Daniel Nashed) on how you can issue Remote Console commands from Lotuscript:

Dim rcmd_server As String
Dim rcmd_command As String     
Dim rcmd_result as String

rcmd_server = "notes.nashcom.de"
rcmd_command = "show server"

Dim remoteConsole As New RemoteConsole (rcmd_server)

recmd_result = remoteConsole.Execute (rcmd_command)

REM W32 De
clares
Declare Function W32_NSFRemoteConsole Lib "NNOTES.DLL" Alias "NSFRemoteConsole" (Byval server As Lmbcs String, Byval cmd As Lmbcs String, ret As Long) As Integer
Declare Function W32_OSLockObject  Lib "NNOTES.DLL" Alias "OSLockObject" (Byval handle As Long) As Lmbcs String
Declare Sub W32_OSUnlockObject  Lib "NNOTES.DLL" Alias "OSUnlockObject" (Byval handle As Long) 
Declare Sub W32_OSMemFree  Lib "NNOTES.DLL" Alias "OSMemFree" (Byval handle As Long)
Declare Function W32_OSLoadString Lib "NNOTES.DLL" Alias "OSLoadString" (Byval hModule As Long, Byval stringCode As Integer, _
Byval retBuffer As Lmbcs String, Byval bufferLen As Integer) As Integer

REM MAC Declares
Declare Function MAC_NSFRemoteConsole Lib "NotesLib" Alias "NSFRemoteConsole" (Byval server As Lmbcs String, Byval cmd As Lmbcs String, ret As Integer) As Integer
Declare Function MAC_OSLockObject  Lib "NotesLib" Alias "OSLockObject" (Byval handle As Integer) As Lmbcs String
Declare Sub MAC_OSUnlockObject  Lib "NotesLib" Alias "OSUnlockObject" (Byval handle As Integer) 
Declare Sub MAC_OSMemFree  Lib "NotesLib" Alias "OSMemFree" (Byval handle As Integer)
Declare Function MAC_OSLoadString Lib "NotesLib" Alias "OSLoadString" (Byval hModule As Integer, Byval stringCode As Integer, _
Byval retBuffer As Lmbcs String, Byval bufferLen As Integer) As Integer

REM Linux Declares
Declare Function LINUX_NSFRemoteConsole Lib "libnotes.so" Alias "NSFRemoteConsole" (Byval server As Lmbcs String, Byval cmd As Lmbcs String, ret As Long) As Integer
Declare Function LINUX_OSLockObject  Lib "libnotes.so" Alias "OSLockObject" (Byval handle As Long) As Lmbcs String
Declare Sub LINUX_OSUnlockObject  Lib "libnotes.so" Alias "OSUnlockObject" (Byval handle As Long) 
Declare Sub LINUX_OSMemFree  Lib "libnotes.so" Alias "OSMemFree" (Byval handle As Long)
Declare Function LINUX_OSLoadString Lib "libnotes.so" Alias "OSLoadString" (Byval hModule As Long, Byval stringCode As Integer, _
Byval retBuffer As Lmbcs String, Byval bufferLen As Integer) As Integer

REM AIX Declares
Declare Function AIX_NSFRemoteConsole Lib "libnotes_r.a" Alias "NSFRemoteConsole" (Byval server As Lmbcs String, Byval cmd As Lmbcs String, ret As Integer) As Integer
Declare Function AIX_OSLockObject  Lib "libnotes_r.a" Alias "OSLockObject" (Byval handle As Integer) As Lmbcs String
Declare Sub AIX_OSUnlockObject  Lib "libnotes_r.a" Alias "OSUnlockObject" (Byval handle As Integer) 
Declare Sub AIX_OSMemFree  Lib "libnotes_r.a" Alias "OSMemFree" (Byval handle As Integer)
Declare Function AIX_OSLoadString Lib "libnotes_r.a" Alias "OSLoadString" (Byval hModule As Integer, Byval stringCode As Integer, _
Byval retBuffer As Lmbcs String, Byval bufferLen As Integer) As Integer

REM Solaris Declares
Declare Function SOLARIS_NSFRemoteConsole Lib "libnotes.so" Alias "NSFRemoteConsole" (Byval server As Lmbcs String, Byval cmd As Lmbcs String, ret As Integer) As Integer
Declare Function SOLARIS_OSLockObject  Lib "libnotes.so" Alias "OSLockObject" (Byval handle As Integer) As Lmbcs String
Declare Sub SOLARIS_OSUnlockObject  Lib "libnotes.so" Alias "OSUnlockObject" (Byval handle As Integer) 
Declare Sub SOLARIS_OSMemFree  Lib "libnotes.so" Alias "OSMemFree" (Byval handle As Integer)
Declare Function SOLARIS_OSLoadString Lib "libnotes.so" Alias "OSLoadString" (Byval hModule As Integer, Byval stringCode As Integer, _
Byval retBuffer As Lmbcs String, Byval bufferLen As Integer) As Integer

Class RemoteConsole
Public IsError As Variant
Private rc As Integer
Private Long_hBuffer As Long
Private Integer_hBuffer As Integer
Private Server As String
Private Command As String
Private Result As String
Sub New (InputServer As String)
If InputServer = "" Then
Me.IsError = True
Else
Me.Server = InputServer
Me.IsError = False
End If
End Sub
Function get_platform () As String
Dim tmp_platform As Variant
Dim lower_platform As String
tmp_platform = Evaluate(|@Implode(@Platform([Specific]);" ")|)
lower_platform = Lcase(Cstr(tmp_platform(0)))
REM Messagebox Cstr(tmp_platform(0))
If (Instr (lower_platform, "aix")) Then
get_platform = "aix"
Elseif (Instr (lower_platform, "solaris")) Then
get_platform = "solaris"
Elseif (Instr (lower_platform, "linux")) Then
get_platform = "linux"
Elseif (Instr (lower_platform, "win")) Then
get_platform = "w32"
Elseif (Instr (lower_platform, "mac")) Then
get_platform = "mac"
Else
Messagebox "Unsupported Platform : >" + lower_platform + "<"
get_platform = ""
End If
End Function
Function Execute (InputCommand As String) As String
Dim StrLen As Integer
Dim errorStr As String * 1024
Dim Session As New NotesSession
Dim platform As String
platform = get_platform
REM Messagebox session.platform
REM Messagebox platform
If Me.Server = "" Then
Me.Execute = "No server specified"
Me.IsError = True
Exit Function
End If
If InputCommand = "" Then
Me.Execute = "No command specified"
Me.IsError = True
Exit Function
End If
Me.command = InputCommand + Chr (0)
Me.server = Me.server + Chr(0)
Select Case platform
Case "w32"
Me.rc = W32_NSFRemoteConsole (Me.Server, Me.Command, Long_hBuffer)
Case "mac"
Me.rc = MAC_NSFRemoteConsole (Me.Server, Me.Command, Integer_hBuffer)
Case "linux"
Me.rc = LINUX_NSFRemoteConsole (Me.Server, Me.Command, Long_hBuffer)
Case "aix"
Me.rc = AIX_NSFRemoteConsole (Me.Server, Me.Command, Integer_hBuffer)
Case "solaris"
Me.rc = SOLARIS_NSFRemoteConsole (Me.Server, Me.Command, Integer_hBuffer)
Case Else
Exit Function
End Select
If (Me.rc <> 0) Then
Me.IsError = True
Select Case platform
Case "w32"
StrLen = w32_OSLoadString(0&, Me.rc, errorStr , Len(errorStr) - 1)
Case "mac"
StrLen = MAC_OSLoadString(0&, Me.rc, errorStr , Len(errorStr) - 1)
Case "linux"
StrLen = LINUX_OSLoadString(0&, Me.rc, errorStr , Len(errorStr) - 1)
Case "aix"
StrLen = AIX_OSLoadString(0&, Me.rc, errorStr , Len(errorStr) - 1)
Case "solaris"
StrLen = SOLARIS_OSLoadString(0&, Me.rc, errorStr , Len(errorStr) - 1)
Case Else
Exit Function
End Select
If (StrLen = 0 ) Then
errorStr = ""
End If
Me.Result= "Error: [" & Cstr (rc) & "] " + errorStr
Else
Select Case platform
Case "w32"
Me.Result = W32_OsLockObject (Long_hBuffer) + Chr (0)
Call W32_OSUnlockObject (Long_hBuffer)
Call W32_OsMemFree (Long_hBuffer)
Case "mac"
Me.Result = MAC_OsLockObject (Integer_hBuffer) + Chr (0)
Call MAC_OSUnlockObject (Integer_hBuffer)
Call MAC_OsMemFree (Integer_hBuffer)
Case "linux"
Me.Result = LINUX_OsLockObject (Long_hBuffer) + Chr (0)
Call LINUX_OSUnlockObject (Long_hBuffer)
Call LINUX_OsMemFree (Long_hBuffer)
Case "aix"
Me.Result = AIX_OsLockObject (Integer_hBuffer) + Chr (0)
Call AIX_OSUnlockObject (Integer_hBuffer)
Call AIX_OsMemFree (Integer_hBuffer)
Case "solaris"
Me.Result = SOLARIS_OsLockObject (Integer_hBuffer) + Chr (0)
Call SOLARIS_OSUnlockObject (Integer_hBuffer)
Call SOLARIS_OsMemFree (Integer_hBuffer)
Case Else
Exit Function
End Select
Me.IsError = False
End If
Me.Execute = Me.Result
End Function End Class


How do you find out if a document has been foldered?
You can use the FolderReferences property of a document to do this. This code will put all documents that have not been foldered into a new "Lost Documents" folder:

Sub Initialize
  Dim sess As New NotesSession
  Dim db As NotesDatabase
  Set db = sess.CurrentDatabase
  db.FolderReferencesEnabled = True
  Dim coll As NotesDocumentCollection
  Set coll = db.AllDocuments
 
  Set doc = coll.GetFirstDocument
 
  While Not(doc Is Nothing)
    i = 0
    If doc.FolderReferences(0) <> "" Then 'doc is in at least one folder, so pull it from this collection
      Call coll.DeleteDocument(doc) 'just pulls it from the folder)
    End If
    Set doc = coll.GetNextDocument(doc)
  Wend
  Call coll.PutAllInFolder("Lost Documents") 'all those that remain in the collection
End Sub


How do you open a File Dialog in LotusScript?
Use the OpenFileDialog and SaveFileDialog methods in the NotesUIWorkspace class.

How do you edit a rich text field in an open document and redisplay it?
From Andre Guirard:

Dim wksp As New NotesUIWorkspace
Dim session As New NotesSession
Dim uidoc As NotesUIDocument, uidocNew As NotesUIDocument
Dim doc As NotesDocument
Dim rti As NotesRichTextItem
Dim strFieldname As String

Set uidoc = wksp.CurrentDocument
uidoc.Refresh True ' do this if the rich text field is editable, to get the current contents in case user has modified them.
Set doc = uidoc.Document �

' get the back-end document for the document open on screen.
strFieldname = uidoc.CurrentField ' remember the current field if any
Set rti = doc.GetFirstItem("fieldname") ' insert your fieldname here, generally "Body"

' Make your rich text changes here, for instance:
Call rti.AddNewLine(1, True)
Call rti.AppendText(Now & ": log entry.")
If session.NotesBuildVersion >= 190 Then
rti.Update ' ND6 only
Else
Call doc.ComputeWithForm(True, False) ' caution, as this may erase some field values if you have @Db functions in formulas.
End If

doc.SaveOptions = "0" ' make it possible to close the document without a "do you want to save" prompt. If this is a mail-in doc you may need to set MailOptions="0" also to avoid being prompted.
Call uidoc.Close
Set uidocNew = wksp.EditDocument(True, doc, , , , True)
Delete uidoc
uidocNew.Document.RemoveItem("SaveOptions")
If strFieldname <> "" Then uidocNew.GotoField(strFieldname) ' return focus to field that was current before.

Note: this will cause Queryclose, Queryopen, Postopen (and so on) form events to trigger. Also, uidoc.Refresh will execute computed field formulas and input validations, so you should write the validation formulas to not return @Failure unless @IsDocBeingSaved | @IsDocBeingSent is true.


How do you find out if there is default view?
From Jens Winkelmann:

Dim docDefault As NotesDocument
Set docDefault = db.GetDocumentByID("FFFF0008")
If docDefault is Nothing then
'// default view does not exist else
'// default view exist end if


How do you scan an image into a rich text field using a button?

From Jamie Magee at www.MartinScott.com:

In a button next to the RT field named Body:
@Command([EditGotoField]; "Body");
@Command([EditInsertObject]; "Image Document")

In place of "Image Document" (which is installed with my Win2k by default) you could use the OLE object type name of any installed scanner program on your workstation. If it's TWAIN compliant, and if your scanner is, too, then the software will work with the scanner. To see OLE object type names, just do Create -> Object... from the Notes menu while the cursor is in a RT field. They appear in the list.

Most imaging programs launched in New object mode automatically prompt you to scan whatever's on the bed/feeder, so then the user is just one click away. You can sometimes default production-grade imaging programs to automatically scan without prompting upon startup.


Companies with Notes Applications

* GroupQuest Software
* Application Partners
* Groupware Technologies
* Cambridge Software Group
* Ives Development (makers of the TeamStudio Design System, application development tools for Lotus Notes and Domino; they also make Caio!, a revision control system for Notes; they also make Delta, Configurator, Design Manager, Profiler, Snapper, and Validator)
* Candle/Cleversoft
* Mayflower Software
* Casahl Technology
* Brainstorm Technologies
* QDM
* Binary Tree (makers of BT Migration Suite and eTeam)
* Solutions By Design (makers of NavMaker, a Notes Navigator editor)
* Business Evolution (makers of InteractPro, a Java chat environment for Domino)
* AppJunction
* Technovations International (makers of Notes capacity planning tools)
* Extracomm Technologies (makers of ExtraFax)
* G2 Associates (makes of ProActive Assistant, a Notes stress-testing tool)
* Genii Software (makers of the Midas Rich Text LSX)
* Metaware bv
* Surety Technologies (makers of Digital Notary Accelerator for Lotus Domino)
* GSX Groupware Solutions (makers of the GSX Administration Suite)
* GlobalServe (makers of PageMe! pager gateway and Sales Accelerator: Contact
Management System)
* xorTech Systems (makers of InfoSync, a client replication admin tool)
* ErgoTech (makers of Act! For Notes)
* Greyfriars Consulting Group, Inc. (makers of Domino business components)
* DPI Services (makers of QMX, quality management software)
* DataMirror (makes of a Notes/ODBC replication tool)
* GFI (makers of FAX gateway, Workflow and Internet gateway software)
* CommVault (makers of CommVault for Notes, a doc-level backup program)
* ProjectEdge (makers of a construction project management application)
* Jagre (makers of version control and change management software)
* DominoAnalyzer (makers of web security analysis software)
* Cassetica (makers of GroupManager, CCI, NotesMedic)
* Sorites (makers of mailing list and survey software)
* SoftVision makers of NotesToPaper (a reporting tool) and n2pdf (a tool to convert Notes content - including links - to the pdf format)
*
Colligo (makers of Lotus Notes Workgroup Edition, that lets Notes clients replicate with each other directly)


Sources of Notes API Information

* Rick Gansler's Mac API Presentation
* Lotus' Developers' Page


Local User Groups

* WALNUT
* WALNUT Canberra, Australia
* Wisconsin Notes User Group
* Charlotte Lotus Notes User Group (in North Carolina)
* St. Louis Domino Notes User Group (in Missouri, USA)
* Washington, DC Notes User Group
* GRANITE (Notes User Group in Chicago)
* NUTS (Notes User Group for Ohio, Kentucky, Indiana)
* Atlanta Lotus Notes Users Group
* New England Domino and Notes Special Interest Group
* IBM's Notes User Group List


Notes Related Web Sites

Lotus' Notes Knowledgebase can be queried here. This is a great place to search for known bugs and workarounds that Lotus recommends.

These are all Domino Discussions:
* Lotus Domino Discussion
* Lotus Japan Notes Dicussion

Other Sites from Lotus and Iris Associates:
* Lotus Domino Wiki
* Lotus Development Corp. Home Page
* Lotus' Developer Central
* Notes.ini Settings

Other good Notes web sites:
* XPages Info site
* Frank Cseh's Notes Page
* European Notes Page
* Rose Kelleher's (a great book author) Notes Stuff
* Notes Design & Development
* Lotus Notes Job Listings
* E-Pro Mag
* Notesnet.Com
* Domino Security news
* Domilock (Domino web security checking tool)
* Breaking Par (Domino web hosting and free code snippets)
* Emie's Notes Links
* DRCC Notes.ini Reference
* SearchDomino
* Notes WikiFAQ
* Domiclipse (lets you write/debug Java agents using Eclipse)
* Open Source Quickplace Templates from SNAPPS
*
Domingo (Java Notes API w/o needing recycle calls)
* Ext.ND (Web 2.0 extensions for Domino)
* Lotus Notes is Cool
* XPage Sample Apps and Controls


Mirrors Sites for the Notes FAQ

The following sites mirror this FAQ site:
* KEY Enterprise Solutions


Non-Notes Web Sites

* Notes Icon Catalog
* Icon Bazaar


What Notes Related RSS Feeds Are There?

* Lotus has a Support RSS feed that updates you on any technote changes.
* Ed Brill has a Blog that has an RSS feed.
* InsideLotus weblog