The other day, I discussed how we extended the dmProfile FarCry object so that we could add more properties. Today, I am going to discuss how we used this object’s properties to be able to create a form that will easily allow users to edit the extra information we needed to save. This is a slightly different approach than you might take for other custom content types as most custom content types would be editable like any other content.
First, let me throw out a disclaimer. What we did may not necessarily be considered ‘best practices’, but it did solve the problem we were having. Also, this form is only being used in the admin and allows administrators to update their profile. That being said, let’s get to it.
In my last post, I showed two of the properties that we added, ‘bio’ and ‘image’. The default profile form doesn’t lend itself to easy editting, so we decided to use a brand new form, and use formtools. In order for formtools to work as we wanted for every property, we neded to copy over ALL the <cfproperty> tags from the native dmProfile object. Here is what the property definitions looked like when we were done.
<cfproperty name="firstName" type="nstring" hint="Profile object first name" required="yes" fttype="string" ftseq="5" ftfieldset="User" ftlabel="First Name" bLabel="1" /> <cfproperty name="lastName" type="nstring" hint="Profile object last name" required="yes" fttype="string" ftseq="10" ftfieldset="User" ftlabel="Last Name" bLabel="1" /> <cfproperty name="emailAddress" type="nstring" hint="Profile object email address" required="no" fttype="string" ftseq="20" ftfieldset="User" ftlabel="Email address" ftvalidation="validate-email " /> <cfproperty name="company" type="string" hint="I am the User's company" required="no" fttype="string" ftseq="30" ftfieldset="User" ftlabel="Company" /> <cfproperty name="position" type="string" hint="I am the User's position" required="no" fttype="string" ftseq="32" ftfieldset="User" ftlabel="Job Title" /> <cfproperty name="department" type="string" hint="I am the User's department" required="no" fttype="string" ftseq="32" ftfieldset="User" ftlabel="Department" /> <cfproperty name="phone" type="string" hint="I am the User's phone" required="no" fttype="string" ftseq="40" ftfieldset="User" ftlabel="Phone" /> <cfproperty name="fax" type="string" hint="I am the User's fax" required="no" fttype="string" ftseq="50" ftfieldset="User" ftlabel="Fax" /> <cfproperty name="bio" type="longchar" hint="User's Bio" required="no" ftseq="60" ftfieldset="User" ftlabel="Bio" /> <cfproperty name="locale" type="string" hint="Profile object locale" required="yes" default="en_US" ftLabel="Language" ftType="list" ftSeq ="70" ftrendertype="dropdown" ftListData="getLocalesAndNames" ftListDataTypeName="dmProfile" /> <cfproperty name="bReceiveEmail" type="boolean" hint="Does user receive workflow and system email notices" required="yes" default="0" ftLabel="Receive E-mail notification" ftType="boolean" ftSeq="80" /> <cfproperty name="image" type="uuid" required="false" default="" hint="Image" ftFieldSet="Picture" ftJoin="dmImage" ftLabel="Picture" ftType="UUID" ftSeq="90" />
I’ll go over some of the attributes we see here that were not discusses previously, specifically, ‘bLabel’, ‘ftRenderType’, ‘ftListData’ and ‘ftListDataTypeName’.
One of the things FarCry can do automagically is display labels for items throughout the system. One way it determines what the label will be is by the use of ‘bLabel’ in the properties of an object. Any property you specify with bLabel=”1″ will be used in the label. Make sure you use this sparingly as it can become ugly real quick, and it could also cause database errors that are very difficult to debug. You can see in this example, only the first name and last name properties are used as part of the ‘label’.
If you look at the property named ‘locale’ you will see three new attributes, ‘ftRenderType’, ‘ftListdata’ and ‘ftListDataTypeName’. These tell FarCry how we want to display the form item, ftRenderType, and where and how to get the data used to populate it, ftListDataTypeName and ftListData respectively. By default, whenever you have a property that is the ID or property of another object, FarCry will give you what is called the ‘library’ view. If you edit any content item in FarCry, you will recognize the format:
For the ‘locale’ property, we wanted it to be a drop down, rather than the default library UI. That is what ‘ftRender’ does, it tells FarCry how to render to form field. The other attributes tell FarCry where to find the data. In this case, FarCry will call the ‘getLocalesAndNames’ method (the value of ftListData) on the dmProfile object (the value of ftListDataTypeName). This method simply takes values that are stored in in the applcication scope, reorders them and puts it in a format that FarCry can use to populate the dropdown.
Now that we discussed some of the new attributes, let’s talk about how we created the form. Not knowing how we could use this form, I reverse engineered the dmProfile ‘edit’ method (OK, I just copied it into my dmProfile.cfc file) and changed the ColdFusion page that gets called to disply the form. You can see that I changed it so that it points to my project directory, rather than the FarCry core.
Before:
<cffunction name="edit" access="PUBLIC" hint="dmProifle edit handler"> <cfargument name="objectID" type="UUID" required="yes"> <cfscript> // getData for object edit stObj = this.getData(arguments.objectID); </cfscript> <cfinclude template="_dmProfile/edit.cfm" /> </cffunction>
After:
<cffunction name="edit" access="PUBLIC" hint="dmProifle edit handler"> <cfargument name="objectID" type="UUID" required="yes"> <cfscript> // getData for object edit stObj = this.getData(arguments.objectID); </cfscript> <cfinclude template="/farcry/projects/alagad/packages/types/_dmProfile/edit.cfm" /> </cffunction>
Before anyone gets all worked up, I know it is not good practice to output code inside a CFC, but this is the way it was being done and I did not want to risk breaking anything, or taking longer to create the form, had I tried to find a way around this.
Next, we need to create the form. The first thing we do is import the formtools custom tag library.
<cfimport taglib="/farcry/core/tags/formtools/" prefix="ft" >
Next we layout our form:
<ft:form> <ft:object stObject="#stObj#" lFields="FirstName,LastName,emailAddress,company,bio,position,department,phone,fax,image,locale" Legend="General" format="edit" /> <ft:farcryButtonPanel> <ft:farcrybutton value="Save" /> <ft:farcrybutton value="Cancel" /> </ft:farcryButtonPanel> </ft:form>
That’s it. That’s all we needed to do to create the form. Let’s break down what exactly this is doing. The ‘stObject’ attribute tell FarCry what object we will be using to create the form, and populate the values if they exist. The ColdFuson varialble named #stObj# is created in the edit() method we copied.
The ‘lFields’ attribute tells FarCry what form fields to display. The value here is a comma delimited list of the property names you wish to include in the form.
The ‘legend’ attribute is used just like a <legend> tag. The ‘format’ attribute tells farCry we are going to display form fields that can be edited. There are other attributes that can be used but we will discuss those in the upcoming formtools entry.
If you want to break your form up into smaller fieldsets, you simply add more <ft:object> tags with the properties you wish to edit and a value for the legend.
The last section simply adds submit and cancel buttons ot the page.
One issue we had was that we did not want some users to be able to uplaod or choose an image. This was easy enough to accomplish by adding a <cfif> block.
<cfif listFind(session.dmSec.Authentication.lPolicyGroupIDs,1)> <ft:object stObject="#stObj#" lFields="FirstName,LastName,emailAddress,company,bio,position,department,phone,fax,image,locale" Legend="General" format="edit" header="Editing #stObj.Label#" /> <cfelse> <ft:object stObject="#stObj#" lFields="FirstName,LastName,emailAddress,company,bio,position,department,phone,fax,locale" Legend="General" format="edit" header="Editing #stObj.Label#" /> </cfif>
The only difference between the two <ft:object> tags is that one shows the image library, and one does not.
Now that we have created our form, now we need to process it. To process the form we simply add more formtool code to the top of the page.
<ft:processForm Action="Save"> <ft:processFormObjects typename="dmProfile" /> </ft:processForm>
Note that the value of the action attribute in <ft:processform> matches the value of one of our buttons, ‘Save’. At this point, when the page is loaded, if the form field ‘Save’ exists, FarCry will process everything in the <ft:processform> code block.
The next line of code tells farCry to process the dmProfile object. If you had a form that consisted of information from more than one object, you would add other <ft:processFormObjects> tags to handle the processing.
The last thing we needed to do was add some code that would redirect the user when the processing was complete.
<ft:processForm Action="Save"> <ft:processFormObjects typename="dmProfile" /> <cfoutput> <script type="text/javascript"> parent.sidebar.location = parent.sidebar.location; window.location = '#application.url.farcry#/overview/home.cfm?sec=home'; </script> </cfoutput> </ft:processForm>
This is merely JavaScript which first reloads the sidebar to show any new information, and then loads the deafult page into the main content area.
This process seems a bit more complicated than others we will discuss becaus we are using a ‘special’ content type, dmProfile. In future posts, we will show how you create custom content types that would be used like the native content types and are typically used for display, in one way or another, on your site.
The last piece of this puzzle is how we changes the menu system so that we could actually use this form in the admin. That will be discussed in my next entry in this series.
Comments on: "FarCry – Creating Our Custom Form" (1)
Many thanks for this article. I am able to extend dmProfile for my project. I have got a problem though, I get an error saying ‘getLocalesAndNames’ is not defined. Where is this method defined?
LikeLike