Cascading Delete Self-join or Same-Table relationships are very useful (two table occurrences from the source table related to each other), but be careful! Don't ever turn on the option to allow deletion of related records! If you do, whenever you delete a parent record you may delete more than you want. What happens is that since your related records are also your parent records, deleting records can cascade out of control. I have seen the deletion of a single record cause the deletion of all records in all tables.
Claris Commercial Few people know that Claris produced a commercial and it aired on national television. It was one of those favor things so it only played once. I'm not saying it's anywhere close to the impact of the Apple Super Bowl commercial but it's fun to have a look.
Download the Claris commercial
Scripts that run on the opening of a FileMaker file allow you to accomplish so many different tasks. Open scripts can initialize global fields, setup variables, determine which layout to display based on security or platform, perform sub-scripts, install menu sets and so much more. This article will cover some of the most important uses of Open scripts but each FileMaker solution will likely have unique open script steps.
For Starters FileMaker 12 introduced scripts that run upon opening of any window. Don't get the OnWindowOpen Script Trigger confused with the OnFirstWindowOpen Script Trigger. OnFirstWindowOpen is the traditional script that runs when you open a file. OnWindowOpen will trigger every time a window is spawned. These Script Triggers, as well as OnWindowClose and OnLastWindowClose, can be found in File Options under their own tab. The focus of this article will be on the OnFirstWindowOpen script trigger.
BTW: Script Triggers go as far back as FileMaker Pro 2.0 but weren't named till FileMaker Pro 12.0. Back then there was just an open and close script popup menu.
Caveat It's important to remember that open scripts will fail to run when a file is opened via a relationship. The reason is simple... performance. If an Open Script runs whenever a related field opens another FileMaker file, the performance could be greatly degraded and slow the rendering of a layout. Imagine a Replace Field Contents that runs on all the records in the database. Even a minute of waiting will seem like an eternity to a user who just tried to navigate to a different layout. In an even worse scenario, imagine the open script in the other file pulling focus from the current file.
The same is true for the performing of an external script. If the file isn't open, the called script will run but the file will remain hidden without the open script firing. Even when the hidden file is selected, the open script won't run. Only when the file is opened manually or via an Open script step will the startup script run. Interestingly enough, the Script Trigger behavior changes when the file is opened through a related field on a layout. In this case, selecting the hidden window from the Window menu activates the Open script trigger. Go figure!?! Glad I tested it!
Therefore, it is important to anticipate when a file is needed, especially if it has an OnFirstWindowOpen Script Trigger. For instance, if you are switching to a layout that contains a related field from another file, make sure to run the Open script step for the related file since it will allow the startup script to run. If you can wait for the Open Script, it will run as soon as the window of the hidden file displays. Most FileMaker solutions are created as a single file with multiple tables these days but it's possible for a corporate wide deployment of FileMaker to communicate between solutions used by different departments.
Open scripts can also be interrupted using two other methods. The first method uses the Script Debugger. Just turn it on and then open a file. You can either walk through the Open script to see how it functions or simply cancel it using the Halt Script button (the button with the square icon). A lesser known method is to use the Advanced Options in the Recover feature. Here you can choose to recover a file without actually doing anything to the file but bypassing the Open script.
Most solutions are produced in a single file with multiple tables today so this information isn't as useful anymore. However, company wide FileMaker deployment often means means solutions for each department. Often, these FileMaker solutions need to share information so they are linked. So, keep this information in your back pocket in case you ever need to work with multi-file solutions.
Close Scripts Close Scripts aren't as important as Open Scripts, especially in a multi-user scenario. For instance, if you set a global field in an Open Script, it's available to the user throughout the session. In the case of a Close Script using OnLastWindowClose, the global value is immediately reset to the value last stored in single-user mode. While there may be important scenarios where Close Scripts can be beneficial, this exercise will focus on Open Scripts that perform on first window open.
Let's Go! Be prepared in this exercise to learn a variety of different techniques that can be used in an Open Script. However, the possibilities are virtually endless. This article just gives you an idea of some of the most common uses. Let's start with Allow User Abort. I run this script step on any script longer than a couple lines. It's important to run it on startup as well so the user doesn't interrupt vital file setup routines.
My Allow User Abort script isn't just a single step. I like to modularize my code like so:
If [Get(AccountPrivilegeSetName) = "[Full Access]"] Allow User Abort [On] Else Allow User Abort [Off] End If
This allows the developer to cancel scripts but prevents all others. The same should be done with Set Error Capture, allowing the developer to see errors he might not have considered:
If [Get(AccountPrivilegeSetName) = "[Full Access]"] Set Error Capture [Off] Else Set Error Capture [On] End If
The same Modular Scripting can be applied to installing custom menus. While many folks set the menu in layout mode via Layout Options, this creates an annoying issue while developing. Every time the layout is selected, the custom menu reverts from the "Standard FileMaker Menus" to the specified menu, requiring the developer to constantly change the menus manually. It's much better to set menus via a script so the developer can navigate manually without menu changes. And, if possible, a single adaptive Custom Menu is employed so the default menu only needs to be set once on open.
If [Get(AccountPrivilegeSetName) = "[Full Access]"] Install Menu Set ["Standard FileMaker Menus"; Use as file default] Else Install Menu Set ["Users"; Use as file default] End If
Pay close attention to the "use as file default" option in the script above. This refers to the "[Default]" option in Layout Setup that is automatically applied to every new layout. Hint, hint... FMI is trying to tell you to use scripts to install a Custom Menu. Layout specified custom menus are really just for folks who aren't familiar with scripting.
If you aren't familiar with Modular Scripting, look at the article on this site with the straightforward title of Modular Scripting.
Layout by Platform There are three different FileMaker platforms these days: Desktop (Macintosh and Windows), iOS and Web Browser. The way I see it, there's really five platforms. I divide iOS into iPhone and iPad and Web Browser into Web Direct and CWP (Custom Web Publishing). Good design dictates specialized layouts for each of these five platforms. I'm not going to cover layout design for the various platforms, just how to navigate to the appropriate layout on open.
There are three functions that can determine the platform, each with it's ups and downs.
Get(SystemPlatform) returns the following values:
Macintosh = 1 Windows = -2 iOS = 3 WebDirect = 4
TIP: Get(SystemPlatform) returns a negative value on Windows due to legacy code to distinguish between different versions of Windows. At one point, Macintosh also had positive and negative values to differentiate different operating systems. It's best to get the absolute value (ABS) of this function in case things change again in the future.
This function works great but doesn't distinguish between iPhone and iPad. On the other hand, Get(ApplicationVersion) does a better job at determining the piece of hardware where FileMaker Go is running:
FileMaker Pro = Pro *version* FileMaker Pro Advanced = ProAdvanced *version* FileMaker Runtime = Runtime *version* FileMaker Server Web Client = Web Publishing Engine *version* xDBC Client = xDBC *version* FileMaker Server or FileMaker Cloud = Server *version* FileMaker Go on iPhone or iPod touch = Go *version* FileMaker Go on iPad = Go_iPad *version*
Unfortunately, Get(ApplicationVersion) also returns the version of FileMaker, making it more difficult to determine the platform (e.g. Pro 17.0.2). It's nothing a little calculation wizardry can't solve:
The above formula will return true if the platform is FileMaker Pro or FileMaker Advanced. However, you might find Get(Device) a little more straightforward:
Unknown device = 0 Macintosh = 1 Windows = 2 iPad = 3 iPhone or iPod touch = 4 Android = 5
The only problem is Get(Device) doesn't identify WebDirect. Seems like there's no single function to identify every platform so I change what I'm doing depending on the deployment of a project. I end up using Get(Device) most of the time since it's easy to implement and identifies all but two of my defined platforms. CWP doesn't need to be identified in an open script since layouts are specified in the PHP, XML or JSON code. That just leaves WebDirect so if all other platform tests fail, it must be a WebDirect client.
The zoom level is also set in this script in order to adhere to FileMaker, Inc. guidelines. In order for a solution to be accepted to the filemaker.com web site, it cannot allow zooming on an iOS device. Everything should be visible without zooming, much like the requirements Google places on mobile device web sites. The zoom level is also set on desktop machines but is not locked to allow the user to adjust it when necessary.
Window Settings If you want your FileMaker solution to be professional, it has too look nice. One of the easiest ways to pretty-up your solution is to frame your windows. This can easily be accomplished using the Adjust Window script with the Resize to Fit option. Resize to Fit sets the window to the size of your layout using the last part on the layout as the height and the separator between the usable and unusable space as the width.
Adjust Window [Resize to Fit]
NOTE: If a layout is viewing as list or table, the Adjust Window script step with the Resize to Fit option will resize the height to the size of the current screen. Therefore, it's best to use this script step on form view only.
I also like to move my solution to the upper left hand corner of the screen using the Move/Resize Window script step with the top and left options set to zero. Otherwise, the location of the window will be wherever the developer left the window just before he uploaded it to the server.
Before framing your solution, it's a good idea to hide the FileMaker window elements that aren't needed in a professionally designed FileMaker solution. The most important are the Show/Hide Toolbars and Show/Hide Menu Bar script steps. The Toolbars are the portion of the screen at the top that display the rolodex, for example. I hide it for users but show it for developers using the Get(AccountPrivilegeSetName) function. While developers can make use of the Toolbar, it just wastes valuable screen real estate for features a user is unlikely to have access like Share.
The Toolbar displays on FileMaker Go and should also be hidden. However, there is an additional bar called the Menubar on iOS devices only that should be hidden as well. While there is no Menubar on the Macintosh operating system, it does return an error message that can be ignored if Set Error Capture is turned on.
I also like to turn off the text ruler and formatting bar, just in case. These features can be toggled with the Show/Hide Text Ruler and Allow Formatting Bar script steps. With all these items removed from the interface, you are left with a nice, clean interface.
File Path I've been creating FileMaker solutions for over two decades now and one of the most common things for clients to do is change the name of the file. In most cases, this is fine and FileMaker adjusts. However, one place FileMaker can have problems is when a file name is specified in an Import Records script step. This is most common when importing from one table to another in the same file. Therefore, I prefer to declare the file name on open and use a global variable to avoid any location issues.
Set Variable [$$FileName; Value: Get(FileName)]
NOTE: Global variables should be avoided whenever possible since they clutter up the Data Viewer, making it difficult to combine the Data Viewer with the Script Debugger. There are very few scenarios where a local variable can't be used. In this particular article, the variable needs to exist beyond the Open Script so a global type is required.
System Formats It's always a good idea to be careful and set your system format settings on open. These can be found in File Options under the Text tab and can be set to one of three features.
You don't need to worry about this option in a multi-user scenario since it can be set before uploading it to the server. It's also the default settings for a new file but I still like to be safe rather than sorry. And, if it's a single-user solution, there's always a chance a user might fiddle with File Options so setting it on open is a good idea. Besides, it's only a single script step.
Set Use System Formats [On]
Initialize Globals Global fields are local to each user in a multi-user scenario. Most developers know this important tidbit of information because it helps them design interfaces that update independently for each user. What's less commonly known is that global fields default to the last value the developer placed in them before uploading the solution to the server. Rather than trying to remember how the global should default before uploading, it's best to include global initialization in an open script. This can be a simple clearing of any value or the setting of a default value. The values could even come from a preference set by the user.
Set Field [MyTable::xtext; ""]
Set Field [MyTable::xdate; Get(CurrentDate)]
The Open Script Here's the complete script from the example file that is set to run OnFirstWindowOpen in File Options and brings everything together.
Conclusion Remember... the possibilities for an Open script are endless. This article just covers some of the most common uses. Feel free to comment below about any common scripts you run on open. I'd love to hear!