Normally Panorama allows the data in any field to be editing at any time. However, it is possible to “lock” individual fields so that they cannot be edited (they can still be modified by a formula or by program code). Individual fields can be disabled in the data sheet, in forms, or both. Fields can be disabled/enabled manually, or this process can be automated with program code.

Disable a Field in the Data Sheet

To disable editing of a field in the data sheet, uncheck the Editable option in the Field Properties panel.

When this option is unchecked, Panorama will just beep when you double click on this field in the data sheet.

Disable Fields in a Form

The Editable option normally applies only to the data sheet, not to forms. But if you want, you can extend the effect of this option to forms on a case-by-case basis. If you want the Editable option to apply to a form, check the Disabled Fields mirror Data Sheet option in the Form Properties panel.

With this option enabled, any fields disabled in the data sheet will also be disabled for editing in this form as well. Not only will text editing be disabled, but also checkboxes, radio buttons, pop-up menus, steppers and sliders. When tabbing through the form, disabled fields will be skipped. (Note: If your form includes Text List or Matrix objects attached to a disabled field, these types of objects will remain enabled.)

The .BlockedEdit Procedure

Usually Panorama simply beeps when you click on a field that is disabled (whether you click on a data sheet cell, or on a Text Editor, Data Button, or other item in a form.) However, if the database contains a .BlockedEdit procedure, that procedure will run when a disabled field is clicked on (it does not run, however, when a disabled field is skipped while tabbing). For example, this procedure could be used to notify the user why their editing is being blocked, or even to ask the user for a password to unlock the field.

If there is more than one disabled field, the .BlockedEdit procedure will be triggered no matter which field was clicked on. However, the procedure can find out which field was blocked – the field name is passed in the first parameter to the procedure. Here’s an example of how a .BlockedEdit procedure could be written to notify the user that the field is disabled, instead of simply beeping.

let blockedField = parameter(1)
nsnotify "Cannot edit.","TEXT","Editing of the "+blockedField+" field is disabled."

Note: The .BlockedEdit code can enable the field (see below), but if it does so the user will have to click again to edit the field. There is no way that Panorama can be told to allow the original editing request. By the time the .BlockedEdit code runs, the original request has already been blocked.

Using a Procedure to Enable or Disable Fields

In addition to using the Field Properties panel to manually enable or disabled fields, this can also be done on-the-fly with code, either with the setfieldproperties statement (for one field at a time) or the disablefieldediting statement (for all fields).

The disablefieldediting statement has one parameter, a carriage return separated list of fields for which editing is to be disabled. This example disables editing for the Price and Discount fields, while enabling editing for all other fields.

disablefieldediting commatocr("Price,Discount")

The Price and Discount fields will be disabled in the data sheet, and also in any forms where the Disabled Fields mirror Data Sheet option has been checked.

A quick way to disable all fields is to use the info(“fields”) function.

disablefieldediting info("fields")

Conversly, to enable all fields, just use "" (an empty list of fields).

disablefieldediting ""

Note: Panorama 6 had a similar statement, called lockfields. In fact, Panorama X treats lockfields as a synonym for disablefieldediting, so if you have old code with this statement, it will still work. For example, you can lock all fields with this line of code.

lockfields info("fields")

However, there are some important differences from Panorama 6 that you need to keep in mind. The old lockfields statement allowed a second parameter that specified whether the effects were permanent. This parameter is no longer allowed, instead, the disabled field settings are always permanent. Also, the old lockfield statement would have to be re-run if any fields were inserted into or removed from the database, otherwise the wrong fields might be disabled. This is no longer the case.

Which Fields are Disabled?

Procedure code can use either the getfieldproperties( function or the info(“disabledfields”) function to find out which fields are currently disabled. The info(“disabledfields”) function returns a carriage return delimited list of currently disabled fields (use the info(“enabledfields”) function if you want a list of enabled fields).

This example uses this function to toggle whether editing is allowed. If no fields are disabled, then this code will disable all fields. If any fields are disabled, it will enable all fields. In other words, each time this code runs it will toggle back and forth between enabling and disabling field editing.

if info("disabledfields")=""
    disablefieldediting info("fields")
    disablefieldediting ""

Note, the code above can also be written on a singe line by using the ?( function.

disablefieldediting ?(info("disabledfields")="",info("fields"),"")

Note: If you want to find out what fields are disabled in some other database, you can use the dbinfo( function with the “disabledfields” option (or the “enabledfields” option if you want to know enabled fields).

Requiring a Password to Edit Data

Finally, let’s look at an example putting all of the features described above to practical use. In this example, five procedures will be added to a database to lock down all data entry into the database. When someone clicks on a field to edit it, they will be asked for a password. If the password is entered correctly, they will be able to edit any field in that record – but as soon as they switch to a different record, everything will be locked down again. The one exception is a new record – if someone creates a new record, they will be able to fill it in. But if they click to a new record and come back to the new record, they’ll have to enter the password to edit it further.

The first component of this system is the .CurrentRecord procedure. This procedure is triggered whenever Panorama switches to a different record (see Implicitly Triggered Procedures). In this case, there is just one line of code that will disable editing of all fields the database. Since the .CurrentRecord procedure is also triggered when the database first opens, this code will immediately disable all fields when the database is opened.

The next component is the Unlock Record procedure. This will appear in the Action menu. This code asks the user to type in a password. If they type in the correct password (in this case presto), all the fields will be unlocked, and they can edit the current record. You’ll want to change the password to one of your choice.

Next, we have the .BlockedEdit procedure. This isn’t absolutely required, but it makes the system more convenient. When the user clicks on a field to edit it, this code automatically calls the Unlock Record procedure to ask for a password. If you leave this procedure out, Panorama will just beep, and the user would have to go to the Action menu to unlock the record.

The .NewRecord procedure makes sure that when new records are added to the database, they are automatically enabled and ready to fill in the fields. There’s a bit of a trick to this code. When Panorama adds a new record, it first triggers the .NewRecord procedure, but then triggers the .CurrentRecord procedure. That’s a problem, because the .CurrentRecord procedure is written to disable editing. The wait 0 code tells Panorama to wait a beat before running the next line. In this case Panorama will run the .CurrentRecord procedure, then the .NewRecord procedure can get in the last word by enabling the editing.

The .DeleteRecord procedure completes the system. If a record is completely empty, this code will allow it to be deleted. But if the record is not empty, then it will ask for a password. To find out if the password was entered correctly, the code checks to see if the fields are enabled. If they are, it goes ahead and deletes the record, but first it disables the fields again so that editing is locked going forward.

Here is the source code for these procedures in case you want to cut and paste it into your own databases.

// .CurrentRecord
disablefieldediting info("fields")

// Unlock Record
let userPassword = ""
supergetpassword userPassword,{remember=no}
if info("dialogtrigger") contains "cancel" stop endif
if userPassword = "presto" /* <-- YOUR PASSWORD HERE */
    disablefieldediting ""
    nsnotify "Record unlocked."

// .BlockedEdit
nsnotify parameter(1)+" is locked."
call "Unlock Record"

// .NewRecord
if info("trigger") = "New.Add"
elseif info("trigger") = "New.Insert"
elseif info("trigger") = "New.Return"
wait 0 /* let .CurrentRecord run before finishing */
disablefieldediting ""

// .DeleteRecord
if strip(exportline())<>""
    call "Unlock Record"
    if dbinfo("disabledfields","")<>""
    disablefieldediting info("fields")

That’s it! Less than 30 lines of code to add password protected editing to your database. Of course you can customize this code to your exact needs.

See Also


10.2NewNew in this version.