Posted by on April 6, 2014

Ever since FileMaker 7 was released developers have had the ability to create solutions within a single file. While I tend to create single-file systems, there are times when it’s useful to break a solution into multiple files. Alternatively, even a decade after the release of FileMaker 7, I still occassionally come across systems that were originally created in FileMaker 6 or earlier and were never consolidated into a single file.

This latter case is something I came across recently with a system that I’d been hired to make a few changes to. One of the requested changes was the ability to allow the user to change their password across the system’s multiple files. Without some custom functionality, changing the password for a user would mean manually navigating to each of a dozen files and performing the same operation for each one, which is quite a pain, especially for a security-conscious organization that requires the periodic chaning of passwords.

Fortunately, as with most other needs, FileMaker gives us all the tools we need to provide this to our users, and this article will demonstrate how I accomplished this goal while pointing out numerous techniques to make the code more readable and maintainable. The solution makes use of custom menus and (optionally) custom functions, so you’ll need FileMaker Pro Advanced to implement it.

I built this in FileMaker Pro 13 Advanced, but it can be built with FileMaker 8 or above (which included the introduction of custom menus). Theoretically you could go as far back as FileMaker 7, but then you wouldn’t be able to override FileMaker’s built-in Change Password menu item, at least not without a plugin. Feel feel to download the sample I built and tear it appart.

The basics of this solution are to create a single controlling script in one file and identical subscripts in all files. But these scripts use a few standard custom functions and require a global repeating field be in place, so let’s take care of that first.

Out of your multi-file solution, pick one to contain the controlling script. Which file doesn’t matter, but if you have a Globals or Main Menu file, that might be an obvious choice. We’re going to use a custom dialog box in the controlling script, so we need a place to store the user’s responses, so create a global text field with three repetitions. I’ve called mine g_dialog_result.

Each subscript will perform an error check to see if the user entered their old password correctly. To make this error check more readable, create a custom function in all of your files (one of the reasons I dislike working with multi-file solutions in FileMaker) called errn.kErrAccountPasswordError and set its result to simply 213 (“User account and/or password does not exist”).

Reference is also made throughout the scripts to an empty string using the devp.Null custom function that only returns an empty string.

The final piece of infrastructure that I use is for sending parameters to scripts, and was covered in a previous article on multiple script parameters. Use that or some other similar technique, as our subscripts will need to be sent two parameters, the user’s old password and the new password.

Subscripts

The subscripts are all very short and straightforward. Create a script called Change Password( OriginalPassword; NewPassword ) in each of your files, including the master file. Reference the article on multiple script parameters to understand what’s going on with the script’s name, the scpm.AssignParams custom function and how the local variables are assigned.

If[scpm.AssignParams]
  Set Error Capture[On]
  Change Password[Old Password: $OldPassword; New Password: $NewPassword; No dialog]
  Exit Script[Result: Get( LastError ) ≠ errn.kErrAccountPasswordError]
End If

Note that if your custom function is in place in all your files, you can copy and paste or import this script from one file to the others after creating it the first time.

All these subscripts are doing is accepting the old and new passwords as named parameters, capturing the possible error that the user didn’t enter the old password correctly, attempting to change the password and returning to the calling script whether the change was successful or not.

Master Script

The master script handles prompting the user for the old and new passwords, ensuring that the new passwords match and looping to ensure that the user is re-prompted if a mistake is made, exiting the loop when the user’s actions are successful or the user cancels the operation.

 One slight downside to this solution, as far as code reuse is concerned, is that there’s no way to dynamically specify which script subscript to execute (at least without a plugin), so each file that needs to have a password changed is going to require two lines of code, one to execute the subscript and one to update whether the subscript was successful or not.

A few minor techniques to take note of in the following script:

  • The use of Let() functions to set multiple script variables in a single script step.
  • The loop that exits when either the operation was successful or the user cancels.
  • The use of script variables to provide names to buttons in custom dialog boxes.

Create a script called Change Password Across Files with the following code.

Set Variable[$?; Value:
  Let(
    [
      $valid_password = False;
      $user_cancelled = False;
      $base_dialog_message = "Please enter your current password followed by your new password with a confirmation of the new password.";
      $dialog_message = $base_dialog_message
    ];

    devp.Null
  )
]
Loop
  Exit Loop If[$valid_password or $user_cancelled]
  Set Field[Master::g_dialog_result; devp.Null]
  Set Field[Master::g_dialog_result[2]; devp.Null]
  Set Field[Master::g_dialog_result[3]; devp.Null]
  Show Custom Dialog["Change Password";
    Let(
      [
        $cancel_button = 1;
        $change_button = 2
      ];

      $dialog_message
    )
    Button 1: "Cancel";
    Button 2: "Change"; Commit Data;
    Show input field #1; Master::g_dialog_result; Use password character; Label: "Old Password";
    Show input field #2; Master::g_dialog_result; Use password character; Label: "New Password";
    Show input field #3; Master::g_dialog_result; Use password character; Label: "Confirm New Password"
  ]
  If[Get( LastMessageChoice ) = $change_button]
    If[
      Let(
        [
          $original_password = Master::g_dialog_result;
          $new_password = Master::g_dialog_result[2];
          _confirm_password = Master::g_dialog_result[3];

          _is_empty = IsEmpty( $new_password );
          _is_equal = Exact( $new_password; _confirm_password )
        ];

        ( not _is_empty ) and _is_equal
      )
    ]
      Set Error Capture[On]
      Perform Script["Change Password( OriginalPassword; NewPassword )"; Parameter:
        scpm.Param( "OriginalPassword"; $original_password ) &
        scpm.Param( "NewPassword"; $new_password )
      ]
      Set Variable[$valid_password; Value: Get( ScriptResult )]
      Perform Script["Change Password( OriginalPassword; NewPassword )" from file: "File1"; Parameter:
        scpm.Param( "OriginalPassword"; $original_password ) &
        scpm.Param( "NewPassword"; $new_password )
      ]
      Set Variable[$valid_password; Value: $valid_password and Get( ScriptResult )]
      Perform Script["Change Password( OriginalPassword; NewPassword )" from file: "File2"; Parameter:
        scpm.Param( "OriginalPassword"; $original_password ) &
        scpm.Param( "NewPassword"; $new_password )
      ]
    End If
  Else
    Set Variable[$user_cancelled; Value: True]
  End If
End Loop

Assigning Custom Menus

Once you have the scripts in your files the last step is to use custom menus to replace the functionality of the user’s default Change Password menu with your implementation. If your solution doesn’t already have custom menus you’ll need to create a new set if you’re using FileMaker 12 or 13. FileMaker 11 creates files with a basic menu set that’s a duplicate of the standard FileMaker menus, so that portion is actually easier in pre-12 versions.

 

For the non-master files, just point the Change Password menu item to the master script in the master file.

Posted in: Articles

Comments

Be the first to comment.

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*