With the specifications in place, we can focus on the implementation. We'll document first and code later, and we'll use OOP techniques for much of the application. One advantage of this approach is that the specifications dictate how the coding takes place. For example, we have specified that we will have users and scripts; these elements can be implemented as objects. This makes the coding process more applicable to real-world situations. We know the different properties of a user and the different properties of a script (outlined in the specifications), so these will be the properties of our objects. Even though we are using some OOP techniques and some objects, the application is not strictly an object-oriented application.
We will build the overall structure before we set out to code the functionality of the application. I've found that this is often the best way to approach a problem. You can think of it like drawing a picture: if you draw the outline first, it is a lot easier to color in, rather than color the picture and then try to draw the outline around it after the fact. In this way, comments and function skeletons make up your outline, and the actual code is used to "color in" the program. This has the added benefit that the comments are finished when your code is finished, rather than requiring you to add comments at the end of the project.
The skeleton code should be fully working code. Even placeholder functions should include return statements so that the program works as you code.
The database is the first physical structure to create. The database needs to be in place and functional before the application can be built. The database structure is shown in Tables Table 14-1 through Table 14-5. Table 14-1 shows the Users table, which is used to manage user login.
Column name |
Datatype |
Length |
Notes |
---|---|---|---|
UserID |
integer |
4 |
Auto-numbering, primary key |
Username |
text |
16 | |
Password |
text |
12 | |
FirstName |
text |
60 | |
LastName |
text |
60 | |
EmailAddress |
text |
255 | |
HintQuestion |
text |
255 |
Prompt the user if password is forgotten |
HintAnswer |
text |
20 |
Verify user response if password is forgotten |
Table 14-2 shows the Categories table, which is used to group scripts into categories for easier searching and sorting once the repository grows larger.
Column name |
Datatype |
Length |
Notes |
---|---|---|---|
CategoryID |
integer |
4 |
Autonumbering, primary key |
CategoryDesc |
text |
60 |
Table 14-3 shows the Scripts table, which is used to manage the contributed scripts.
Column name |
Datatype |
Length |
Notes |
---|---|---|---|
ScriptID |
integer |
4 |
Autonumbering, primary key |
ScriptName |
text |
60 | |
ScriptDescription |
text |
255 | |
ScriptCode |
text |
4095 | |
LanguageID |
integer |
4 |
Foreign key to Languages table |
CategoryID |
integer |
4 |
Foreign key to Categories table |
UserID |
integer |
4 |
Foreign key to Users table |
DateUploaded |
date/time |
8 |
Defaults to current date |
DateModified |
date/time |
8 |
Defaults to current date |
VersionMajor |
integer |
4 |
Defaults to 1 |
VersionMinor |
integer |
4 |
Defaults to 0 |
VersionMicro |
integer |
4 |
Defaults to 0 |
ScriptUniqueID |
Unique identifier (UUID) |
36 |
Table 14-4 shows the Languages table, which is used to track the programming languages in which scripts are written.
Column name |
Datatype |
Length |
Notes |
---|---|---|---|
LanguageID |
integer |
4 |
Autonumbering, primary key |
LanguageName |
text |
50 |
Table 14-5 shows the CompanyInfo table, which is used to provide contact information for contributors.
Column name |
Datatype |
Length |
---|---|---|
CompanyName |
text |
60 |
Address |
text |
127 |
City |
text |
60 |
State |
text |
2 |
Zip |
text |
9 |
Phone |
text |
50 |
Fax |
text |
50 |
ContactFirstName |
text |
50 |
ContactLastName |
text |
50 |
ContactEmail |
text |
127 |
PrivacyPolicy |
text |
1000 |
Description |
text |
1000 |
The database table specs have been shown in a generic fashion, to allow you to implement them in your own particular database. For example, the text datatypes are implemented as varchar or nvarchar fields in SQL Server or MySQL. Similarly, the DateUploaded field in the Scripts table is implemented as a datetime field, with a default value of getdate( ) in SQL Server or current_date( ) in MySQL. Other database implementations will vary.
The completed database diagram of table relationships is shown in Figure 14-1.
The server-side services are implemented with ColdFusion Components. The required services are shown in Tables Table 14-6 through Table 14-8.
Table 14-6 lists the service methods of the UserService service.
Service method |
Description |
Arguments |
Returns |
---|---|---|---|
loginUser( ) |
Validates username and password against the database. Sets the session if the login is successful, and sets the user's access level. |
Username (string), Password (string) |
Userid (numeric) |
addUser( ) |
Adds a new user to the database. If the registration is successful, the user is also automatically logged in. |
UserObject |
UserObject |
emailPassword( ) |
Emails a password to a user if he forgets his password. |
EmailAddress |
True |
createUserObj( ) |
Package method that creates an object of type UserObject for passing back to ActionScript. |
FirstName, LastName, EmailAddress, Username, Userpassword, HintQuestion, HintAnswer |
UserObject |
getEmail( ) |
Gets the user's hint question for retrieving a password. |
EmailAddress |
Hint question (string) |
getScriptsForUser( ) |
Gets all scripts submitted by logged-in user. |
UserID (numeric) |
Recordset |
Table 14-7 lists the service methods of the ScriptService service.
Service method |
Description |
Arguments |
Returns |
---|---|---|---|
addScript( ) |
Adds a script to the database. |
ScriptObject |
Script id (numeric) |
updateScript( ) |
Updates an existing script in the database. |
ScriptObject |
ScriptObject |
displayScript( ) |
Displays the script on the screen. |
ScriptID (numeric) |
ScriptObject |
displayList( ) |
Displays a list of available scripts, with clickable links. |
Search word (optional) |
Recordset |
getScript( ) |
Gets all information about a script to display. |
ScriptID (numeric) |
ScriptObject |
createScriptObj( ) |
Package method that creates an object of type ScriptObject for passing back to ActionScript. |
ScriptID, ScriptName, ScriptDescription, ScriptCode, LanguageID, CategoryID, UserID, DateUploaded, DateModified, VersionMajor, VersionMinor, VersionMicro, ScriptUniqueId |
ScriptObject |
DateTimeString( ) |
Package method that converts a Date object from ActionScript into a human-readable date/time string |
Date object or string |
Formatted date string |
Table 14-8 lists the service methods of the SiteService service.
Service method |
Description |
Arguments |
Returns |
---|---|---|---|
about( ) |
Returns a short paragraph about the company from the database. |
None |
RecordSet |
contactForm( ) |
Contacts the site administrator by email through a standard form. |
UserID (numeric), Comment (string) |
true |
sendPage( ) |
Sends the page information to a friend. |
UserID (numeric), Email address (string), Script ID (numeric) |
true |
getCategories( ) |
Retrieves a list of all categories for drop-down list. |
None |
RecordSet |
getLanguages( ) |
Retrieves a list of all languages for drop-down list. |
None |
RecordSet |
getUsers( ) |
Retrieves a list of all users for drop-down list. |
None |
RecordSet |
Using Dreamweaver MX, you can create skeletons for all of the services. Dreamweaver MX allows you to create CFCs using an interface (shown in Figure 14-2), with function skeletons in place.
Example 14-1 lists the skeleton code for the UserService service.
<!--- Generated by Dreamweaver MX 6.0.1722 [en] (Win32) - Wed Jan 29 19:07:39 GMT-0800 (Pacific Standard Time) 2003 ---> <cfcomponent displayName="UserService"> <cffunction name="loginUser" displayName="loginUser" hint="Logs a user into the script repository" access="remote" returnType="string" output="false"> <cfargument name="username" type="string" required="true"> <cfargument name="password" type="string" required="true"> <!--- loginUser body ---> <cfreturn > </cffunction> <cffunction name="addUser" displayName="addUser" hint="Add a user to the database" access="remote" returnType="string" output="false"> <cfargument name="Username" type="string" required="true"> <cfargument name="FirstName" type="string" required="true"> <cfargument name="LastName" type="string" required="true"> <cfargument name="EmailAddress" type="string" required="true"> <cfargument name="Password" type="string" required="true"> <cfargument name="HintQuestion" type="string" required="false"> <cfargument name="HintAnswer" type="string" required="false"> <!--- addUser body ---> <cfreturn > </cffunction> <cffunction name="emailPassword" displayName="emailPassword" hint="Email a password to a user, given the email address" access="remote" returnType="string" output="false"> <cfargument name="EmailAddress" type="string" required="true"> <cfargument name="HintQuestion" type="string" required="true"> <!--- emailPassword body ---> <cfreturn > </cffunction> <cffunction name="createUserObj" displayName="createUserObj" hint="Create ActionScript object to hold user information" access="package" returnType="struct" output="false"> <cfargument name="Username" type="string" required="true"> <cfargument name="FirstName" type="string" required="true"> <cfargument name="LastName" type="string" required="true"> <cfargument name="EmailAddress" type="string" required="true"> <cfargument name="Password" type="string" required="true"> <cfargument name="HintQuestion" type="string" required="false"> <cfargument name="HintAnswer" type="string" required="false"> <!--- createUserObj body ---> <cfreturn > </cffunction> <cffunction name="getEmail" displayName="getEmail" hint="Retrieve the user's hint question given an email address" access="remote" returnType="string" output="false"> <cfargument name="EmailAddress" type="string" required="true"> <!--- getEmail body ---> <cfreturn > </cffunction> <cffunction name="getScriptsForUser" displayName="getScriptsForUser" hint="Retrieve the user's scripts to feed a combo box" access="remote" returnType="recordset" output="false"> <cfargument name="UserID" type="string" required="true"> <!--- getEmail body ---> <cfreturn > </cffunction> </cfcomponent>
The methods of the CFC are each defined with all arguments and an empty return value. As you can see, the method bodies are empty, except for a comment. The code body will go there, but not yet. We'll fill in comments for each method, explaining what the method does, what is required, and what is returned. This will make it that much easier to write the methods afterwards, and the code will be fully commented. An example of a fully commented function skeleton is shown in Example 14-2. The component skeletons can be downloaded from the online Code Depot.
<cffunction name="displayList" access="remote" returnType="query" output="false"> <!--- Method: displayList Version: 1.0.0 Author: Tom Muck Arguments: search Optional search criteria Return: query object of all script information. Properties are ScriptID ID number of the script (primary key) Category The category name CategoryID The categoryID ScriptName The name of the script Description: This service returns a complete list of scripts available or a list that meets the search criteria ---> <!--- displayList body ---> <!--- End displayList body ---> <cfreturn /> </cffunction>
Test mechanisms (also known as test harnesses) can be set up as plain ColdFusion pages to test that each service and each method is working. Inside of the Dreamweaver MX environment, simply drag the CFC from the Components panel onto a .cfm page and insert a form and conditional logic to test the form, as in the test page shown in Example 14-3.
<cfparam name="form.test" default="" /> <html> <head> <title>User Service Test Page</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body> <form name="form1" method="post" action=""> <select name="test" id="test"> <option value="addUser">addUser</option> <option value="emailPassword">emailPassword</option> <option value="loginUser">loginUser</option> </select> <input type="submit" name="Submit" value="Submit"> </form> <cfif form.test EQ "addUser"> <cfinvoke component="com.oreilly.frdg.ScriptRepository.UserService" method="addUser" returnvariable="addUserRet"> <cfinvokeargument name="Username" value="enter_value_here"/> <cfinvokeargument name="FirstName" value="enter_value_here"/> <cfinvokeargument name="LastName" value="enter_value_here"/> <cfinvokeargument name="EmailAddress" value="enter_value_here"/> <cfinvokeargument name="Password" value="enter_value_here"/> <cfinvokeargument name="HintQuestion" value="enter_value_here"/> <cfinvokeargument name="HintAnswer" value="enter_value_here"/> </cfinvoke> <cfoutput>#addUserRet#</cfoutput> </cfif> <cfif form.test eq "emailPassword"> <cfinvoke component="com.oreilly.frdg.ScriptRepository.UserService" method="emailPassword" returnvariable="emailPasswordRet"> <cfinvokeargument name="EmailAddress" value="enter_value_here"/> <cfinvokeargument name="HintAnswer" value="enter_value_here"/> </cfinvoke> <cfoutput>#emailPasswordRet#</cfoutput> </cfif> <cfif form.test eq "loginUser"><cfinvoke component="com.oreilly.frdg.ScriptRepository.UserService" method="loginUser" returnvariable="loginUserRet"> <cfinvokeargument name="username" value="enter_value_here"/> <cfinvokeargument name="password" value="enter_value_here"/> </cfinvoke> <cfoutput>#loginUserRet#</cfoutput> </cfif> </body> </html>
If you build pages like these to test each server-side service, they will be invaluable in determining where problems might occur before you begin to bring Flash into the equation. Using a page like this in ColdFusion gives you full access to ColdFusion debugging and also allows you to easily manipulate the parameters and return values to test different situations.
With the server-side service skeletons in place, you can begin to flesh out the services. If you have built ColdFusion test pages as recommended, you can test the services one by one as you build them.
|
The UserService service implements all methods that relate to users, such as logging in and retrieving passwords. You can easily add more methods to the service as the application becomes more advanced. In addition to the remote methods available to the Flash movie, there is a package method called createUserObj( ) that is used internally by some of the methods to create an object of type UserObject to pass back to ActionScript.
The completed code for the UserService remote service is shown in Example 14-4. Refer to Table 14-6 for a summary of the service methods for this service.
<!--- Generated by Dreamweaver MX 6.0.1722 [en] (Win32) - Wed Jan 29 19:07:39 GMT-0800 (Pacific Standard Time) 2003 ---> <cfcomponent displayName="UserService"> <!--- Service: UserService Package: com/oreilly/frdg/ScriptRepository Description: Services to interact with Users from the ScriptRepository application ---> <cffunction name="createUserObj" displayName="createUserObj" hint="Create ActionScript object to hold user information" returnType="struct" access="package" output="false"> <!--- Create the ActionScript object ---> <cfobject type="java" class="flashgateway.io.ASObject" name="UserObject" action="create" /> <!--- Create an instance of the object ---> <cfset o = UserObject.init( )> <!--- Set the type to our custom UserObjectClass for deserialization ---> <cfset o.setType("UserObject")> <cfset o.put("UserID", arguments[1]) /> <cfset o.put("Username", arguments[2]) /> <cfset o.put("Userpassword", arguments[3]) /> <cfset o.put("FirstName", arguments[4]) /> <cfset o.put("LastName", arguments[5]) /> <cfset o.put("Emailaddress", arguments[6]) /> <cfset o.put("HintQuestion", arguments[7]) /> <cfset o.put("isUserLogged", 1) /> <cfset o.put("inited", 1) /> <cfreturn o /> </cffunction> <cffunction name="loginUser" displayName="loginUser" hint="Logs a user into the script repository" access="remote" returnType="any" output="false"> <!--- Method: loginUser Version: 1.0.0 Author: Tom Muck Arguments: username string of up to 16 characters password string of up to 12 characters Return: user object Description: This service allows the user to log in to the application by verifying the username/password in the database and returning all of the properties of the user to the Flash movie. ---> <cfargument name="username" type="string" required="true"> <cfargument name="userpassword" type="string" required="true"> <!--- loginUser body ---> <cftry> <cfquery datasource="ScriptRepository" name="rsUserLogin"> SELECT * FROM Users WHERE Username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#username#"> AND Password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#userpassword#"> </cfquery> <cfcatch type="Any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfif rsUserLogin.RecordCount GT 0 > <cfset UserObj = createUserObj(rsUserLogin.UserID, rsUserLogin.Username, rsUserLogin.Password, rsUserLogin.FirstName, rsUserLogin.LastName, rsUserLogin.Emailaddress, rsUserLogin.HintQuestion ) /> <cfelse> <cfthrow message="Not a valid user" /> </cfif> <!--- end loginUser body ---> <cfreturn UserObj /> </cffunction> <cffunction name="addUser" displayName="addUser" hint="Add a user to the database" access="remote" returnType="any" output="false"> <!--- Method: addUser Version: 1.0.0 Author: Tom Muck Arguments: FirstName string of up to 60 characters LastName string of up to 60 characters EmailAddress string of up to 127 characters Username string of up to 16 characters Userpassword string of up to 12 characters HintQuestion string of up to 255 characters HintAnswer string of up to 20 characters Return: user object Description: This service allows a new user to be added to the database, and automatically to log in to the application by verifying the username/password in the database and returning all of the properties of the user to the Flash movie. ---> <cfargument name="Username" type="string" required="true"> <cfargument name="FirstName" type="string" required="true"> <cfargument name="LastName" type="string" required="true"> <cfargument name="EmailAddress" type="string" required="true"> <cfargument name="Userpassword" type="string" required="true"> <cfargument name="HintQuestion" type="string" required="false"> <cfargument name="HintAnswer" type="string" required="false"> <!--- addUser body ---> <cftry> <cfquery datasource="ScriptRepository" name="rsDoesUserExist"> SELECT * FROM Users WHERE Username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#username#"> </cfquery> <cfcatch type="Any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfif rsDoesUserExist.RecordCount EQ 0> <cftry> <cfquery datasource="ScriptRepository" name="rsAddUser"> INSERT INTO Users (Username, Password, FirstName, LastName, EmailAddress, HintQuestion, HintAnswer) VALUES ( <cfqueryparam cfsqltype="cf_sql_varchar" value="#Username#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#Userpassword#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#FirstName#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#LastName#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#EmailAddress#">, <cfqueryparam cfsqltype="cf_sql_varchar" null="#HintQuestion EQ ''#" value="#HintQuestion#">, <cfqueryparam cfsqltype="cf_sql_varchar" null="#HintAnswer EQ ''#" value="#HintAnswer#"> ) </cfquery> <cfcatch type="Any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfelse> <cfthrow message="Already a user with that username" /> </cfif> <!--- End addUser body ---> <cfreturn this.loginUser('#username#','#userpassword#') /> </cffunction> <cffunction name="getEmail" displayName="getEmail" hint="Retrieve the user's hint question given an email address" access="remote" returnType="string" output="false"> <!--- Method: getEmail Version: 1.0.0 Author: Tom Muck Arguments: EmailAddress string of up to 127 characters Return: Hint question (string) Description: This service retrieves the user's hint question given an email address ---> <cfargument name="EmailAddress" type="string" required="true"> <!--- getEmail body ---> <cftry> <cfquery datasource="ScriptRepository" name="rsGetQuestion"> SELECT HintQuestion FROM Users WHERE Emailaddress = <cfqueryparam cfsqltype="cf_sql_varchar" value="#EmailAddress#"> </cfquery> <cfcatch type="Any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfif rsGetQuestion.RecordCount NEQ 1> <cfthrow message="No match found in database" /> </cfif> <!--- End getEmail body ---> <cfreturn rsGetQuestion.HintQuestion /> </cffunction> <cffunction name="emailPassword" displayName="emailPassword" hint="Email a password to a user, given the email address" access="remote" returnType="boolean" output="false"> <!--- Method: emailPassword Version: 1.0.0 Author: Tom Muck Arguments: EmailAddress string of up to 127 characters HintAnswer string of up to 20 characters Return: boolean of successful email of the password Description: This service allows a user to have his password emailed to him, if the hint answer matches the user's hint in the database. ---> <cfargument name="EmailAddress" type="string" required="true"> <cfargument name="HintAnswer" type="string" required="true"> <!--- emailPassword body ---> <cftry> <cfquery datasource="ScriptRepository" name="rsGetUser"> SELECT Username, Password FROM Users WHERE Emailaddress = <cfqueryparam cfsqltype="cf_sql_varchar" value="#EmailAddress#"> AND HintAnswer = <cfqueryparam cfsqltype="cf_sql_varchar" value="#HintAnswer#"> </cfquery> <cfcatch type="Any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfif rsGetUser.RecordCount EQ 1> <!---Only send the email if there is a matching record in the database ---> <cfmail from="admin@flash-remoting.com" to="#Emailaddress#" subject="Requested information"> Your username is: #rsGetUser.username# Your password is: #rsGetUser.password# Please respond to admin@flash-remoting.com if you have received this message in error. Administrator </cfmail> <cfelse> <cfreturn 0 /> </cfif> <!--- End emailPassword body ---> <cfreturn 1 /> </cffunction> <cffunction name="getScriptsForUser" access="remote" returnType="query" output="false"> <!--- Method: getScriptsForUser Version: 1.0.0 Author: Tom Muck Arguments: Username username for currently logged-in user Userpassword password for currently logged-in user Return: query object of scriptid and scriptname information. Description: This service returns a complete list of scripts available for the currently logged in user. ---> <cfargument name="username" hint="Username of current user" type="string" default="" /> <cfargument name="userpassword" hint="Password of current user" type="string" default="" /> <!--- getScriptsForUser body ---> <cftry> <cfquery name="rsScripts" datasource="ScriptRepository"> SELECT s.ScriptID, s.ScriptName FROM Users u, Scripts s WHERE u.Username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#username#"> AND u.Password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#userpassword#"> AND s.UserID = u.UserID ORDER BY s.ScriptName </cfquery> <cfcatch type="any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <!--- End getScriptsForUser body ---> <cfreturn rsScripts /> </cffunction> </cfcomponent>
The UserService service interacts with the Flash movie using a UserObject, a custom object that we set up in ActionScript using Object.registerClass. As discussed in Chapter 4, this method of transferring objects allows for seamless passing of data between client and server. ColdFusion supports a <cfobject> tag, which allows you to set up the object as a Java object of type flashgateway.io.ASObject.
There are a few things of note in the code. All queries that contain user-supplied parameters are set up with a <cfqueryparam> tag. This tag guards the application against SQL injection attacks, in which a malicious user sends SQL statements in a URL to attempt to damage your data or even gain control of your database.
|
The following query, from the getEmail( ) method, demonstrates the use of the <cfqueryparam> tag:
<cfquery datasource="ScriptRepository" name="rsGetQuestion"> SELECT HintQuestion FROM Users WHERE Emailaddress = <cfqueryparam cfsqltype="cf_sql_varchar" value="#EmailAddress#"> </cfquery>
The <cfqueryparam> tag takes the place of the parameter within the query and throws an error if the datatype is not right. Therefore, the parameter is usable only as a proper parameter; crackers cannot inject SQL statements into the query.
|
Also, the queries in the page are all wrapped in try/catch blocks, in order to trap errors and simply throw them back to the Flash movie. We could put some other form of error handling within the block, such as writing to a log file or sending an email to a site administrator, but this method is the simplest.
The <cfmail> tag contains the body of the email message to be sent to the end user. For that reason, the text is aligned flush left, even though the code is nicely indented otherwise. The <cfmail> tag translates any spaces or line breaks within the body of the message literally, so the text format should be preserved inside the tag.
One last item deserves a mention: when a user is added to the database, the user is also automatically logged in, as shown in the following line from the addUser( ) method:
<cfreturn this.loginUser('#username#','#userpassword#') />
Rather than simply return a Boolean value indicating whether the user was successfully added to the database, we log the user in using the UserService.loginUser( ) method and return the UserObject to the Flash movie. This improves the end user's experience because she doesn't need to log in as a separate step after registering.
The ScriptService service includes methods that relate to the storing of the scripts. The service contains many of the same types of features that the UserService.cfc file had, such as the package method that creates a ScriptObject object type, and the use of the <cfqueryparam> tags to guard against malicious user input.
The completed ScriptService service is shown in Example 14-5. Refer to Table 14-7 for a summary of the service methods for this service.
<!--- Generated by Dreamweaver MX 6.0.1722 [en] (Win32) - Wed Jan 29 19:32:00 GMT-0800 (Pacific Standard Time) 2003 ---> <cfcomponent displayName="ScriptService"> <!--- Service: ScriptService Package: com/oreilly/frdg/ScriptRepository Description: Utilizes a Script object to pass information back and forth from the Flash movie. ---> <cffunction name="createScriptObj" hint="Create ActionScript object to hold script information" returnType="struct" access="package" output="false"> <!--- Create the ActionScript object ---> <cfobject type="java" class="flashgateway.io.ASObject" name="ScriptObject" action="create" /> <!--- Create an instance of the object ---> <cfset o = ScriptObject.init( )> <!--- Set the type to our custom UserObjectClass for deserialization ---> <cfset o.setType("ScriptObject")> <cfset o.put("ScriptID", arguments[1]) /> <cfset o.put("ScriptName", arguments[2]) /> <cfset o.put("ScriptDescription", arguments[3]) /> <cfset o.put("ScriptCode", arguments[4]) /> <cfset o.put("LanguageID", arguments[5]) /> <cfset o.put("CategoryID", arguments[6]) /> <cfset o.put("UserID", arguments[7]) /> <cfset o.put("DateUploaded", this.DateTimeString(arguments[8])) /> <cfset o.put("DateModified", this.DateTimeString(arguments[9])) /> <cfset o.put("VersionMajor", arguments[10]) /> <cfset o.put("VersionMinor", arguments[11]) /> <cfset o.put("VersionMicro", arguments[12]) /> <cfset o.put("ScriptUniqueID", arguments[13]) /> <cfset o.put("inited",1) /> <cfreturn o /> </cffunction> <cffunction name="addScript" access="remote" returnType="any" output="false"> <!--- Method: addScript Version: 1.0.0 Author: Tom Muck Arguments: ScriptObj a script object with all properties needed to add the script to the database. Properties are: ScriptName ScriptDescription ScriptCode LanguageID CategoryID UserID DateUploaded DateModified VersionMajor VersionMinor VersionMicro Return: scriptid Description: This service allows a registered user to upload a script to the database. ---> <!--- <cfargument name="ScriptObj" type="struct" required="true"> ---> <!--- AddScript body ---> <!--- Create a unique ID to aid in retrieving the primary key ---> <cfset scriptuniqueid = CreateUUID( ) /> <cfset ScriptObj = this.createScriptObj( 0, arguments.ScriptName, arguments.ScriptDescription, arguments.ScriptCode, arguments.LanguageID, arguments.CategoryID, arguments.UserID, arguments.DateUploaded, arguments.DateModified, arguments.VersionMajor, arguments.VersionMinor, arguments.VersionMicro, scriptuniqueid ) /> <!--- Insert the script into the database ---> <cftry> <cfquery name="insertScript" datasource="ScriptRepository"> INSERT INTO Scripts ( ScriptName, ScriptDescription, ScriptCode, LanguageID, CategoryID, UserID, DateUploaded, DateModified, VersionMajor, VersionMinor, VersionMicro, scriptuniqueid ) VALUES ( <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptName EQ ''#" value="#ScriptObj.ScriptName#">, <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptDescription EQ ''#" value="#ScriptObj.ScriptDescription#">, <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptCode EQ ''#" value="#ScriptObj.ScriptCode#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.LanguageID EQ ''#" value="#ScriptObj.LanguageID#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.CategoryID EQ ''#" value="#ScriptObj.CategoryID#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.UserID EQ ''#" value="#ScriptObj.UserID#">, <cfqueryparam cfsqltype="cf_sql_timestamp" null="#ScriptObj.DateUploaded EQ ''#" value="#ScriptObj.DateUploaded#">, <cfqueryparam cfsqltype="cf_sql_timestamp" null="#ScriptObj.DateModified EQ ''#" value="#ScriptObj.DateModified#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.VersionMajor EQ ''#" value="#ScriptObj.VersionMajor#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.VersionMinor EQ ''#" value="#ScriptObj.VersionMinor#">, <cfqueryparam cfsqltype="cf_sql_numeric" null="#(ScriptObj.VersionMicro EQ '')#" value="#ScriptObj.VersionMicro#">, <cfqueryparam cfsqltype="cf_sql_varchar" null="no" value="#scriptuniqueid#"> ) </cfquery> <cfquery name="rsScript" datasource="ScriptRepository"> SELECT ScriptID FROM Scripts WHERE ScriptUniqueID = '#scriptuniqueid#' </cfquery> <cfcatch type="any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <!--- End AddScript body ---> <cffile action="append" file="c:\log.txt" output=#this.objToString(ScriptObj)#> <cfreturn ScriptObj /> </cffunction> <cffunction name="updateScript" access="remote" returnType="any" output="false"> <!--- Method: updateScript Version: 1.0.0 Author: Tom Muck Arguments: ScriptObj a script object with all properties needed to add the script to the database. Properties are: ScriptID ScriptDescription ScriptCode LanguageID CategoryID UserID DateUploaded DateModified VersionMajor VersionMinor VersionMicro Return: Updated ScriptObject Description: This service allows a registered user to change a script that exists in the database. ---> <!--- UpdateScript body ---> <cfset ScriptObj = this.createScriptObj( arguments.ScriptID, arguments.ScriptName, arguments.ScriptDescription, arguments.ScriptCode, arguments.LanguageID, arguments.CategoryID, arguments.UserID, arguments.DateUploaded, arguments.DateModified, arguments.VersionMajor, arguments.VersionMinor, arguments.VersionMicro, 0 ) /> <cftry> <cfquery name="updateScript" datasource="ScriptRepository"> UPDATE Scripts SET ScriptName = <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptName EQ ''#" value="#ScriptObj.ScriptName#">, ScriptDescription = <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptDescription EQ ''#" value="#ScriptObj.ScriptDescription#">, ScriptCode = <cfqueryparam cfsqltype="cf_sql_varchar" null="#ScriptObj.ScriptCode EQ ''#" value="#ScriptObj.ScriptCode#">, LanguageID = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.LanguageID EQ ''#" value="#ScriptObj.LanguageID#">, CategoryID = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.CategoryID EQ ''#" value="#ScriptObj.CategoryID#">, UserID = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.UserID EQ ''#" value="#ScriptObj.UserID#">, DateModified = <cfqueryparam cfsqltype="cf_sql_timestamp" null="#ScriptObj.DateModified EQ ''#" value="#ScriptObj.DateModified#">, VersionMajor = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.VersionMajor EQ ''#" value="#ScriptObj.VersionMajor#">, VersionMinor = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.VersionMinor EQ ''#" value="#ScriptObj.VersionMinor#">, VersionMicro = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.VersionMicro EQ ''#" value="#ScriptObj.VersionMicro#"> WHERE ScriptID = <cfqueryparam cfsqltype="cf_sql_numeric" null="#ScriptObj.ScriptID EQ ''#" value="#ScriptObj.ScriptID#"> </cfquery> <cfcatch type="any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <!--- End UpdateScript body ---> <cfreturn ScriptObj /> </cffunction> <cffunction name="displayList" access="remote" returnType="query" output="false"> <!--- Method: displayList Version: 1.0.0 Author: Tom Muck Arguments: search Optional search criteria Return: query object of all script information. Properties are ScriptID ID number of the script (primary key) Category The category name CategoryID The categoryID ScriptName The name of the script Description: This service returns a complete list of scripts available or a list that meets the search criteria ---> <cfargument name="search" hint="Search criteria for script listing" type="string" default="" /> <!--- DisplayList body ---> <cftry> <cfquery name="rsScripts" datasource="ScriptRepository"> SELECT c.CategoryDesc , c.CategoryID , s.ScriptID , s.ScriptName FROM Categories c INNER JOIN Scripts s ON c.CategoryID = s.CategoryID <cfif search neq ""> WHERE s.ScriptDescription + s.ScriptName + c.CategoryDesc LIKE <cfqueryparam cfsqltype="cf_sql_varchar" value="%#search#%"> </cfif> ORDER BY c.CategoryDesc, s.ScriptID </cfquery> <cfcatch type="any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <!--- End DisplayList body ---> <cfreturn rsScripts /> </cffunction> <cffunction name="getScript" access="remote" returnType="struct" output="false"> <!--- Method: getScript Version: 1.0.0 Author: Tom Muck Arguments: ScriptID ID number of the script to display Return: ScriptObj Description: This service returns a script object to be displayed in the Flash movie ---> <cfargument name="ScriptID" type="any" required="true"> <!--- DisplayScript body ---> <cftry> <cfquery name="rsScripts" datasource="ScriptRepository"> SELECT * FROM Scripts WHERE ScriptID = <cfqueryparam cfsqltype="cf_sql_numeric" value="#ScriptID#"> </cfquery> <cfcatch type="any"> <cfthrow message="There was a database error" /> </cfcatch> </cftry> <cfif rsScripts.RecordCount EQ 1> <cfset ScriptObj = createScriptObj(rsScripts.ScriptID, rsScripts.ScriptName , rsScripts.ScriptDescription , rsScripts.ScriptCode, rsScripts.LanguageID, rsScripts.CategoryID, rsScripts.UserID, rsScripts.DateUploaded, rsScripts.DateModified, rsScripts.VersionMajor, rsScripts.VersionMinor, rsScripts.VersionMicro, rsScripts.ScriptUniqueID ) /> <cfelse> <cfthrow message="No script with that ID" /> </cfif> <!--- End getScript body ---> <cfreturn ScriptObj /> </cffunction> <cffunction name="DateTimeString" access="package" hint="Convert a date/time data to a string for display" returntype="string" > <cfargument name="dateObj" type="any" required="false" /> <cfif isdate(dateObj)> <cfset returnstring = "#DateFormat(dateObj,'mm/dd/yyyy')# #TimeFormat(dateObj, 'hh:mm:ss tt')#" /> <cfelse> <cfset returnstring = dateObj /> </cfif> <cfreturn returnstring /> </cffunction> </cfcomponent>
The SiteService service contains methods to populate UI components, contact the site administrator, send messages to other users, and populate the About screen, as summarized in Table 14-8.
The complete server-side code for the service is shown in Example 14-6.
<!--- Generated by Dreamweaver MX 6.0.1722 [en] (Win32) - Thu Jan 30 21:49:32 GMT-0800 (Pacific Standard Time) 2003 ---> <cfcomponent displayName="SiteService" hint="General service for site methods"> <!--- Service: SiteService Package: com/oreilly/frdg/ScriptRepository Description: General utility methods for the site ---> <cffunction name="about" displayName="About" hint="Short paragraph and info about the company" access="remote" returnType="query" output="false"> <!--- Method: about Version: 1.0.0 Author: Tom Muck Arguments: none Return: a query object with the information about the site Description: This service sends the information about the site back to the caller ---> <!--- about bo