Tag Archives: FileMaker 17

  • 0

FileMaker 17: Data API Multi-File Upload

Tags : 

The FileMaker 17 Data API, also known as fmrest, includes a new capability to upload data to a container field. Prior to 17 the Data API only allowed container data to be downloaded. Uploading data from a website to FileMaker had to be done using plugins, Base64 encoding, or by saving a temporary copy of the file in a spot where a FileMaker script could find it. Now we can upload to FileMaker container fields using only the Data API. This blog post describes how to use the new upload capability with HTML and JavaScript.

Before uploading to FileMaker, we need to obtain an access token. We’ll use this token to authenticate our requests to FileMaker. The 17 Data API allows you to obtain an access token using either a basic authentication header or an OAuth identity provider like Google. In this blog I’ll use basic authentication.

To simplify the code I’m using jQuery. Google makes jQuery easily available through their content delivery network. Load the library by including the following script tag at the top of your HTML:

<html>
   <head>
       <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   </head>

 

To configure basic authentication we prompt the web user for a username and password. The user needs to enter credentials for an account that has been assigned the “fmrest” extended privilege. Once the user submits this form, we concatenate their username and password, separated by a colon. Then we Base64 encode this concatenated string, which can be done in a web browser using the JavaScript method btoa. Here’s what this looks like:

   <form id="login">
       <label for="username">Username</label>
       <input name="username" type="text" />
       <label for="password">Password</label>
       <input name="password" type="password" />

       <input type="submit" value="Login" />
   </form>
  
   <script>
   let request;

   $("#login").submit(function(event){
       event.preventDefault();
       if(request) {
           request.abort();
       }

       let $form = $(this);

       let $inputs = $form.find("input");

       const auth = btoa($form.find('input[name="username"]').val() + ':' +  
$form.find('input[name="password"]').val());

       $inputs.prop('disabled', true);

 

With our newly encoded string, we’re ready to make our login request. If you used the Data API in FileMaker 16, the format of 17 API calls will be familiar to you. Note however that many of the URIs have changed. The URI for logging in has changed to “/fmi/data/v1/databases/filename/sessions”, for example.

Our login request looks like this:

       request = $.ajax({
           url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/sessions',
           type: 'post',
           headers: {
               'Content-Type': 'application/json',
               'Authorization': 'Basic ' + auth
           },
           data: {}
       });

 

If our login request is successful we store the access token in localStorage so we can use it in subsequent requests, then load the upload page. Otherwise we log an error message.

       request.done((response) => {
           localStorage.setItem('fmrest_token', response.response.token)
           $inputs.prop('disabled', false);
           location = 'blog-demo-upload.html'
       });

       request.fail((jqXHR, textStatus, errorThrown) => {
           console.log(errorThrown)
           $inputs.prop('disabled', false);
       })
   });
   </script>
</html>

 

Now that we have an access token saved in localStorage, we can begin making other requests to FileMaker, including uploading files to container fields.

My FileMaker demo file contains a simple table and layout called “Image”. The Image table contains 17’s default fields and two fields of my own: a container field named File and a text field named Name. We’ll upload a file by creating a new record in the Image table, obtaining the recordId in the response, then using the recordId in a call to insert the file into the container field.

To get started, let’s load jQuery and set up our form:

<html>
   <head>
       <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   </head>

   <form id="uploadForm">
       <label for="upload">Upload</label>
       <input name="upload" type="file" />

       <label for="name">Name</label>
       <input name="name" type="text" />

       <input type="submit" value="Upload" />
   </form>

 

Next let’s get our access token from localStorage. If we find that the token does not exist, we redirect the user back to the login page.

   <script>
       const token = localStorage.getItem('fmrest_token');
       if (token === null) {
           location = 'blog-demo-login.html'
       }

 

When the user submits the form, we need to create a new record in the Image table and obtain it’s recordId from FileMaker’s response. First let’s grab what the user typed in the “Name” field:

       let request;
       $("#uploadForm").submit(function(event) {
           event.preventDefault();
           if(request) {
               request.abort();
           }

           let $form = $(this);

           let $inputs = $form.find("input");

           $inputs.prop('disabled', true);

           let name = $('#name').prop('value');

 

To create a record using the 17 Data API, we need to send a JSON object containing the fields and values we want to set. Let’s create our JSON object:

            const fieldData = {"fieldData": {"Name": name } }
            const data = JSON.stringify(fieldData)

 

With our JSON object created, we’re ready to make our request. The URI for creating records is in the format “/fmi/data/v1/databases/database-name/layouts/layout-name/records”. Notice that in our request, we include an Authorization header set to our token, with the prefix Bearer:

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records',
               type: 'post',
               headers: {
                   'Content-Type': 'application/json',
                   'Authorization': 'Bearer ' + token
               },
               data: data
           });

 

If our request succeeded, we obtain the recordId from FileMaker’s response and pass it to our insertFile function (see below). Otherwise, we display an error.

           request.done((response) => {
               insertFile(response.response.recordId)
               $inputs.prop('disabled', false);
           });

           request.fail((jqXHR, textStatus, errorThrown) => {
               console.log(errorThrown)
               $inputs.prop('disabled', false);
           })
       })

 

Our insertFile function builds a FormData object from the file upload input box:

        const insertFile = (recordId) => {
           let formData = new FormData();
           formData.append('upload', ($("#upload"))[0].files[0]);
           console.log(formData)
           event.preventDefault();
           if(request) {
               request.abort();
           }

 

Then it sends the object to FileMaker and uses recordId to identify which record it’s trying to modify:

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records/9/containers/File/1',
               type: 'post',
               headers: {
                   'Authorization': 'Bearer ' + token
               },
               data: formData,
               cache : false,
               contentType: false,
               processData: false
           });

       }

 

Knowing what we know now about uploading a single file to FileMaker, how can we change our code to allow us to upload multiple files at once? The answer lies in the HTML Drag and Drop API. This API is built into modern browsers and allows us to assign drag and drop event listeners to our DOM elements. A property of these events is dataTransfer, which is an object containing whatever files the user dropped on our DOM element.

The following chunk of code is borrowed from Mozilla’s documentation.

First let’s set up a div on the page where users can drop their files:

<div id="dropbox" style="height:200px; width:200px; background:rgba(0,0,0,0.2)">Drop files here.</div>

 

When “dropbox” hears a “drop” event, it assigns a dataTransfer object to dt. Then we select the files from dt and pass them to a handler function, handleFiles. Our handler function simply needs to iterate through our list of files and pass them off one by one to functions that call the Data API:

       const handleFiles = (files) => {
           for(i = 0; i < files.length; i++){
               createRecord(files[i], multiInsert)
           }
       }

 

Like our process when uploading a single file, we first create a record in the Image table using createRecord then call multiInsert to upload the file:

       const createRecord = (file, callback) => {

           const fieldData = {"fieldData": {"Name": file.name } }
           const data = JSON.stringify(fieldData)

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records',
               type: 'post',
               headers: {
                   'Content-Type': 'application/json',
                   'Authorization': 'Bearer ' + token
               },
               data: data
           });

           request.done((response) => {
               callback(file, response.response.recordId)
           });

           request.fail((jqXHR, textStatus, errorThrown) => {
               console.log(errorThrown)
           });

       }


       const multiInsert = (file, recordId) => {
           const formData = new FormData();
           formData.append('upload', file);
           event.preventDefault();

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records/' + recordId + '/containers/File/1',
               type: 'post',
               headers: {
                   'Authorization': 'Bearer ' + token
               },
               data: formData,
               cache : false,
               contentType: false,
               processData: false
           });
       }

 

These code snippets should hopefully get you started working with the Data API’s container features. They can be refined and built upon to do things like validation (only accept certain file types), relate an uploaded file to a record in another table, and even traverse a folder structure. If you’re interested in learning more about 17’s features, check out our other recent blog posts.


  • 1

FileMaker 17: Default Field Customization

Tags : 

With the release of FileMaker 17 comes Default Fields, and thankfully they are customizable! This will be a time saver for all of us who copy and paste the same five (or more) fields into every table in every file, all the time. It may seem like a small savings at first, but will quickly amount to a lot of time and clicks. So let’s get started!

The 5 Default Fields from FileMaker

You just created your first new table in FileMaker 17 and (bam!) there are five new fields in the table already. “That’s awesome!” you thought, which was probably followed by “But how do I customize them?”  Don’t worry we’ll cover that next, but first let’s take a look at the fields that FileMaker has already provided:

  1. PrimaryKey – Unique identifier of each record in this table
  2. CreationTimestamp – Date and time each record was created
  3. CreatedBy – Account name of the user who created each record
  4. ModificationTimestamp – Date and time each record was last modified
  5. ModifiedBy – Account name of the user who last modified each record

Although naming conventions for them vary, the fields above are standard to most developers and should be for you too. They are incredibly valuable in maintaining the integrity of your data. If you are new to FileMaker or are unsure of their use, I recommend you keep them as they will come in handy in the future.

Customization

The fields that FileMaker have included by default are a great starting point, but this new feature becomes truly powerful when we customize them. Modifying the field names to conform to your specific naming conventions, and adding your additional ‘standard’ utility fields are just a couple reasons you should take the time to customize them.

Step 1:

Download the template file called DefaultFields.xml included in this article. You can also dig into the FileMaker Advanced 17 package folder to find it. If you choose the latter, be sure to exit the application before doing so and make a separate copy of the file in a different location where you can then modify it.

Mac:
In Finder, navigate to the FileMaker Advanced 17 folder and right-click on the application, then click ‘Show Package Contents’. From there you can navigate to the following file:
Contents/Resources/en.lproj/DefaultFields.xml

Windows:
C:\Program Files\FileMaker\FileMaker Pro 17 Advanced\Extensions\English

Step 2:

Open the XML file in a text editor and start customizing!
Side note: although I couldn’t find documentation from FileMaker on the grammar required, it’s fairly easy to gauge what options are available for each field and how to modify them.

Step 3:

Move your customized file into the following directory:
Mac: Users/Shared/FileMaker/Shared/
Windows: C:\ProgramData\FileMaker\Shared\

Step 4:

Open your FileMaker solution and go to File>Manage>Database to create a new table. Your new default fields should be created automatically. That’s it!

Turn Default Fields Off

If for some reason having FileMaker’s default fields added to every new table just doesn’t work for you, you can easily turn this functionality off (and back on again when you realize how valuable they really are!).

Step 1:

Create and save a blank file called DefaultFields.xml. Any basic text editor such as TextEdit on Mac or Notepad on Windows will work fine for this task.

Step 2:

Move this file into the following directory:
Mac: Users/Shared/FileMaker/Shared/
Windows: C:\ProgramData\FileMaker\Shared\   

 

What default fields are you excited to create?

 


  • 0

FileMaker 17: Edit Grouped Objects

Tags : 

How many times have I been annoyed and delayed by having to ungroup something just to make a simple edit? Too many to count.

I often group many things together to aid in layout setup and positioning. Say you have a set of fields and objects that together make up a ‘widget’ or function (e.g. a custom calendar picker, or custom labeling for a radio button set, a set of fields for an address, etc), and you want to keep them all together in a particular arrangement. But then you might have to make a change to a font, or a hide condition, or a small tweak to the position of one item within the group. Now you can do all those adjustments without having to break the group and then re-group the objects when you are done!

There is a double whammy when there is a Hide Condition applied to a group, or a button action has been applied to non-button objects, e.g. a Text object. (To make it a button FileMaker makes it into a group, first. It’s a group of 1 object, but still a group.) In FileMaker Pro Advanced 16 you could apply a hide condition to a group, but then if you needed to edit one of the objects in the group, you had to un-group those items in order to make those edits. Breaking the group apart causes you to lose the hide condition or button definition on the group – at least FM was nice enough about it and would warn you that you were going to lose something when breaking the group. These types of groups will cause you to lose your calculation definitions as well as being slower to work with the sub-objects.

Even simple groups that don’t have an additional button or hide definitions will incur delays just because you’ll have to un-group and then re-group the items. (Minor, yes, but still delays – the keyboard shortcuts here help a lot, but even then it’s a delay.) Now in FileMaker Pro Advanced 17 you don’t have to break the group in order to edit items in that group, thus you won’t lose the hide condition. To be clear, in 17 if you break the group you WILL still lose the hide condition, you just won’t have to break the group nearly as often.

The user experience for this new feature is pretty darn intuitive and straightforward. In FileMaker Pro Advanced 17 when you first click on a group, it will highlight the group in the normal blue outline with drag handles – the same as it does now in FileMaker Pro Advanced 16.

When you click a second time on an object in that same group a dashed box shows up around the group as a whole, and the individual object you clicked on will have the typical blue highlight border with the drag handles:

This combination of borders gives you clear indication that you are still working in a group, but that you are editing a single object of that group. Once you have a single object of the group as your in-focus object, you can edit any of the definitions about that object – font, padding, size, position, etc. This can be done by dragging an object handle, using arrow keys (for movements), or making changes in the Inspector.

Another nice touch is that the dynamic guides are specific to whichever level of editing you are doing. If you drag an entire group the dynamic guides will display relative to the entire group.

With this new feature when you move a single object within a group with the mouse or arrow keys, the dynamic guides will show up with respect to that single object you are editing.

Here I moved the “Country” field down to create empty space and moved “Postal Code” over into the empty space. Notice that the group-bounding box increased in size – handily, the outer/group dashed-box dynamically changes size if you happen to move an object that defines one of the outer bounds of the group (the “Country” field in this case).

It’s really just like you are normally editing the single object on the layout, with the addition of the dashed box around the group. And FileMaker has made this single-object editing very simple to access – it doesn’t get in the way of attempting to edit that single object: If you double click an item that’s part of a group, you will still immediately start editing that item – the same as if it hadn’t been a part of a group. Thus, you can double click a text object and it will insert the cursor into that text object and allow you to start editing, or if you double click a field it will display the field picker dialog. You don’t have an extra click just because it’s in the group.

So make your work more efficient and less copy/paste happy by utilizing the new edit-within-group feature of FileMaker Pro Advanced 17. It will save you time and aggravation.