“Heaven is under our feet
as well as over our heads.”

The making of the IWE Guestbook (part 2)

Posted by Alessandro on Aug 1, 2011 2:03:57 PM
Filed under PHP | Comments (0)

Average: 0/100 (0 ratings)

This tutorial is the prosecution on how I made my IWE Guestbook application

You can find the previous part here.

Remember: this is an old tutorial, most of Flash AS written here is obsolete

Remember2: this is only a lesson about the interaction of PHP and Flash, it's not intended to show security best practices (taht is to say, you cannot refer to this tutorial to secure your applications)

3. BUILDING THE CLIENT-SIDE

3.1 Introduction.

This chapter will be really quite long, so I will talk of what we are going to see together starting from the same part I started before, the installation UI. If someone is not interested in creating an installation UI and Administration tools can jump directly to "Building the Guestbook UI". As I said in the beginning, this application is built by two main parts: the Installation/Settings administration and the Guestbook/Guestbook administration user interfaces. If we wanted to build a PHP guestbook now we would be near the end of our development work. Using Flash it's a bit more complicated, but not so much. Remember that with Flash you can obtain a good result, you have not to think about HTML limits, so you can structure the guestbook as you prefer. I opted for a quite professional style, that with the customization features that I included can be adapted to many situations. The first thing to think about is how these movies can be structured: all elements that will be common to the two movies should be put inside a common library whose filename is sharedlibrary.fla: this is another way to optimize Flash movies, like functions, includes and PHP require and include functions. It's normal that we cannot know before all elements that we should put inside this library, because of many factors, but if you make a good project of an application you can know before most of what you will include in a shared library. The first thing I inserted is the preloader component, because this is a component that will be used in both the two movies. The second thing is the Fading Cover Textbox component, that will be used both in the Installing/Settings UI and in the Guestbook UI. Another thing that you can insert are the fonts that you will use, so that you can use them with ActionScript. Other elements that we will insert in this shared library movie will be listed down later. Well, let's proceed and show what to do in this first Flash movie.

3.2 The shared library.

First of all, install all the components that I included in the sources file, that is to say the ones that you find inside the FadingCoverTxt1_2.zip and the IWEDataManager1_1_0.zip files. The other components used in the movie can be found in the Macromedia Flash Exchange section (they are the Flash UI components), while the IWE_preloader component is not avaiable for downloading, but it's only a simple preloader, I used it to speed up my development work, so you relly don't need it. Remember that for installing the components you will need the Macromedia Extension Manager, that you can also download from the Macromedia website. Ok, after installing al what you need, start a new document in Flash MX. Save it as sharedlibrary.fla. As I said before, in this movie I inserted all common elements. So I inserted the preloader component (you can make a preloader movieclip that you can use in both the other two movies, or you can develop your own component), the Fading Cover Textbox component, the IWE Data Manager component, the Flash UI PushButton and CheckBox components, the two used fonts (in my movies I used Miniml pixel fonts, you can buy them on Craig Kroeger website at http://www.miniml.com), the login movieclip and a graphic symbol used several times in the other two movies. Well, remember to export each symbol for runtime sharing and give the URL of the shared library swf file (I used the sharedlibrary.swf file). Well, maybe let's see together how to structure the login movieclip, as we need to export it too. You are myabe asking yourselves why you need to use it in both the other movie. The answer is really simple: you need to login the administrator in both the Installation/Settings interface movie and the Guestbook movie as he/she must be able to administrate the posted messages. Insert a new Movieclip symbol (CTRL+F8) and give it the name "login". Edit this symbol in the following way: drag two instances of the FadingCoverTextbox component and position the second one below the first one: give them the instance names "userID" and "passwd" respectively. Position also two labels (static textboxes) on the left side of the two FadinCover textboxes, writing "AdminID" and "Password" inside. After that, drag an instance of the CheckBox and one of the PushButton. Give them "Save password" and "Submit" labels respectively, as shown in picture 3.1.

Picture 3.1

Now, select the Submit button and write in the Click Handler field "login_submit": this is the name that I gave to the button click handler function, that is to say the AS function that manages the onRelease event of the PushButton component instance.

Then give the CheckBox instance the name "checkBox". And now write the code relative to this login form interface: as you can notice, I used two layers in my movieclip, the first is for actions and the second contains all the interface elements. You will find this style everywhere in my movies where there is some ActionScript, it's a workflow that I have learnt and now usually use when developing in Flash. Look at the code:

Code:

  1. function setCookie(theCookie, value){
  2. myCookie = SharedObject.getLocal(theCookie);
  3. myCookie.data.pwd = value;
  4. myCookie.flush();
  5. }
  6. function login_submit(){
  7. var usr = userID.myText.text;
  8. var pwd = passwd.myText.text;
  9. if((usr != undefined && usr != "" && usr != "Write here...") &&
  10. (pwd != undefined && pwd != "" && pwd != "Write here...")){
  11. if(checkBox.getValue() == true){
  12. setCookie("logCookie", pwd);
  13. }else{
  14. setCookie("logCookie", null);
  15. }
  16. logAdmin = new LoadVars();
  17. logAdmin.administrator = usr;
  18. logAdmin.password = pwd;
  19. logAdmin.sendAndLoad("admin_login.php", logAdmin, "POST");
  20. _root.setError("Authenticating...");
  21. logAdmin.onLoad = function(success){
  22. if(success){
  23. if(this.login == 'true'){
  24. _root.setError("Login successfull, accessing Administration Panel");
  25. _root.intervalID = setInterval(_parent.loginSuccessfull, 1000);
  26. }else{
  27. if(this.error != undefined && this.error != ""){
  28. _root.setError(this.error);
  29. }
  30. }
  31. }else{
  32. _root.setError("There was a problem during the connection to the server");
  33. }
  34. };
  35. }else{
  36. _root.setError("Please, insert userID and password");
  37. }
  38. }
  39. this.onKeyDown = function(){
  40. if(Key.isDown(Key.ENTER)){
  41. login_submit();
  42. }
  43. };
  44. Key.addListener(this);

The first function that you can see is the one with which you can set a Flash cookie (Shared Object) in which to save the password if the user checks the checkbox. The second one is instead the Submit button Click Handler: it first sets the name of two local variables using the values entered in the userID and passwd fields then checks for their value. If they are empty or with the component default value it returns an error (calling another function defined in the main movie) else it goes on. If all is right the checkbox value is scanned: if it is checked it writes the cookie (called logCookie as it contains the login password info). Two arguments are passed to the function: the cookie name and the cookie value. In this case the value is the pwd variable, that is to say the variables which contains the passwd textfield value. If the checkbox is not checked, the value will be null and the cookie will reset. After setting the cookie, a LoadVars object is built, as we need to send the login values to the PHP script that will do the login work for you. You need to set also this object variables to send before using the sendAndLoad method, so you will write:

Code:

  1. logAdmin = new LoadVars();
  2. logAdmin.administrator = usr;
  3. logAdmin.password = pwd;

in this way you will send to the PHP script the administrator and the password values.

Then you send and load data to the PHP script using

logAdmin.sendAndLoad("admin_login.php", logAdmin, "POST");

In this way you use the LoadVars object itself as the container for the received values. I set the error value with the word "Authenticating...", in this way the main movie will display it before receiving an answer from the server. Now it's time to set a function that will handle the data loading event. It is defined using the event handler

logAdmin.onLoad = function(success){};

Inside this handler you can find the code shown before. It looks for the value of the success argument. This argument can be true or false as many of you know. It assumes a true value if the server answers, false if the LoadVars object does not receive any answers. In this last case it returns an error. If instead success is true the script looks for the login parameter (sent by the server). If it is defined and it is true a message is shown to the user to notice him/her that the login is successful and the function that manages the actions to be executed in case of a successful login is called using setInterval to give time the user to read the status message ("Login successfull, accessing Administration Panel"). A handler function is called, as the administration panel can be in a different location depending on the movie in which this login movieclip will be inserted.

In case the login parameter is not set or it is not true, the script will search for the error parameter sent from the PHP script (you remember how many of these parameters I set at the beginning of our lesson! See chapter 2). If it has been found it will be shown in the error textfield, else you could set another alternative message, maybe more generic: in fact, using the error parameter sent from the server-side script, lets us create a more dynamic and targeted error control.

When all this is complete, you can now look to the last lines of this code:

Code:

  1. this.onKeyDown = function(){
  2. if(Key.isDown(Key.ENTER)){
  3. login_submit();
  4. }
  5. };
  6. Key.addListener(this);

With this code, you can easily handle the pressure of the Enter key, as the user could press this key on the keyboard in order to submit the data to the server rather than clicking with the mouse on the Submit PushButton component instance.

Ok, this part is complete, now you know how to structure a shared library movie to ease up your development work.

[i]3.3 The Installation/Settings administration movie.[/i]

This movie needs several frames to work. Before starting to build this movie and coding it you'd better load all shared symbols that you need from the shared library. So you will import the preloader movieclip or component, the FadingCoverTextbox component symbols, the two fonts, the Strip_bg graphic symbol, the login movieclip and the Flash UI components (the PushButton and the CheckBox). Now that all what you need to start is in the library save the movie as "admin.fla".

Well, in the first frame place the preloader, if you want it. You will need five keyframes without counting the preloader one to place all the structure of the movie, as several situations must be managed: the installation checking script, that will check if the guestbook has been yet installed, the installing user interface, the installation confirmation and navigation interface, the administrator login interface and at the end, the settings administration interface.

I structured the movie using five layers. The first one is dedicated to the actions, so this what I am going to show you in a moment. In picture 3.2 you can see how is structured my movie's timeline.

Picture 3.2

In the preloader layer I put the preloader, as you could imagine. In the errorTxt layer there is dynamic textbox that will show the error messages received from the server-side scripts or set by the developer. It starts only from the second frame, as during the preloader it is not required. The interface layer contains all interface elements. It's composed by four keyframes starting from frame 3, each one with different elements, as you will see.

The last layer contains the movie background. Starting from keyframe 3, each keyframe is labeled with an appropriate name: install, success, main and admin.

3.3.1. Defining all needed functions in first frame.

In the second keyframe in the action layer you must define all the functions that we are going to use in the together with the installation checking script. As this script and the error manager function will be used also in the guestbook movie, I put them in two separate .as files that I included in both the movies. All the included .as files are stored in the "include" folder, so remember this detail. The first included file is saved with the name "install_check.as"; it's coded in the following way:

Code:

  1. installCheck = new LoadVars();
  2. installCheck.load("detect.php");
  3. installCheck.onLoad = function(success){
  4. if(success){
  5. if(this.require_install == 'true'){
  6. _root.gotoAndStop("install");
  7. }else{
  8. _root.gotoAndStop("main");
  9. }
  10. }else{
  11. _root.setError("error: data load failure");
  12. }
  13. };

As you can notice it's a simple LoadVars object with its own onLoad event handler function. With this object I loaded only the output of the "detect.php" page that returns a require_install parameter. If the success argument is false an error message is set, else the require_install parameter is checked: if it is true the movie playhead will stop on the "install" labeled interface keyframe else it will stop on the keyframe labeled "main".

The next included .as file is a function, that is to say the "error_manager.as". The function has been put in this included file as it is needed again in the next movie:

Code:

  1. // show errors
  2. function setError(message){
  3. _root.error.autoSize = true;
  4. _root.error.text = message;
  5. var l = int(_root.error._width);
  6. _root.error._x = int((550/2)-(l/2));
  7. };

It needs only one argument, that is the message to show in the error textbox placed in the errorTxt layer. The function also performs a dynamic positioning of the textbox (that is autosized).

Let's see now all the frame 2 code step to step:

first of all the two external .as files are included

Code:

  1. #include "include/install_check.as"
  2. #include "include/error_manager.as"

Then the useCodepage command is set to allow the movie to use the operating system traditional code page.

Code:

  1. System.useCodepage = true;

A prototype is set to allow the exadecimal code being formatted correctly for using with the preview movieclip.

Code:

  1. MovieClip.prototype.setColor = function(theClip, theColor){
  2. newColor = new Color(theClip);
  3. newColor.setRGB("0x"+theColor);
  4. }

With the following function the FadingCover textboxes are reset to the original status when the user presses the Cancel button.

Code:

  1. function reset(){
  2. for(var i = 1; i <= 10; i++){
  3. _root.theClip["text"+i].myText.text = "Write here...";
  4. }
  5. }

The next function is used to validate the form before it is submitted to the PHP script that will save the settings into the database and will delete the install.php page.

The sendForm function sends the form formatting the needed variables before sending them to the server. The validate function returns an error if a color is not 6 characters long or returns false if one of the first seven fields has not being completed. In this case the checkFields variable saves the returned value and the function outputs the appropriate error message. The onLoad function handles the returned data as usual: it returns an error if something's wrong or it proceeds with the next step if all is right.

Code:

  1. function validate(){
  2. for(var i = 1; i <= 10; i++){
  3. var textToCheck = _root.theClip["text"+i].myText.text;
  4. if(i < 8){
  5. if(textToCheck == "Write here..." || textToCheck == ""){
  6. return false;
  7. }else{
  8. if(i > 2 && i < 8){
  9. if(textToCheck.length <> 6){
  10. colorError = "color error";
  11. return colorError;
  12. }
  13. }
  14. }
  15. }else{
  16. if(i == 8){
  17. if(textToCheck == "Write here..." || textToCheck == ""){
  18. textToCheck = "Welcome on IWE Guestbook V. 1.0.0 beta";
  19. }
  20. }else if(i == 9){
  21. if(textToCheck == "Write here..." || textToCheck == ""){
  22. textToCheck = "Please, sign my guestbook-in";
  23. }
  24. }else if(i == 10){
  25. if(textToCheck == "Write here..." || textToCheck == ""){
  26. textToCheck = "images/various/logo.jpg";
  27. }
  28. //check this one and check the textToCheck values
  29. return true;
  30. }
  31. }
  32. }
  33. }

The confirm function handles the success of the install process and moves the playhead to the "success" keyframe.

Code:

  1. function confirm(){
  2. _root.gotoAndStop("success");
  3. _root.setError("");
  4. clearInterval(intervalID);
  5. }

3.3.2. The installation UI

Picture 3.3

As you can see in picture 3.3 in the third frame you can find all this elements.

They compose together the guestbook installation user interface. In the interface layer there are a fieldsClip movieclip instance and a previewer movieclip instance together with al textboxes and PushButton instances. Only the error textbox is located in the errorTxt layer and the two instances of the strip_bg graphic symbol are located in the BG layer.

The fieldsClip is a movieclip that contains all the needed textfields. I inserted them in a movieclip because they are needed again at frame 6, the one labeled "admin". The instance name for this clip at frame 3 is the same of the movieclip symbol itself, that is to say "fieldsClip". The Fading Cover Textbox instances are named with a progressive name, starting from "text1" and ending with "text10". Each instance skin color and font face color have been changed with appropriates tints, according to the developer preferences.

The previewer movieclip instance is named "preview". This clip shows how the guestbook main interface will look after submitting the settings so that the user can see if he/she needs to use some different settings.

The other two simbols that compose the Interface layer are the two instances of the PushButton component: their instance names are instalBtn and cancelBtn and they are labeled respectively "Install" and "Cancel". The first executes the guestbook installation process and the second resets all fields. The click handlers are not assigned in the parameters panel, but by code.

The BG layer contains two instances of the graphic symbol strip_bg.

Go now into the third keyframe of the actions layer and open the Actions Panel. Let's look together through the code:

stop();

The following line let you set which script to use in the sendForm function that was defined in the previous frame.

_root.serverSideScript = "install.php";

This line instead specifies which clip needs to be check during the validation of the form (as there is also the settings administration frame that will use the validation function).

_root.theClip = _root.fieldsClip;

With the action variable is defined the current action that is performed (you can see it working in the sendForm function).

_root.action = "Installing";

Identical to the above variable, but this time is defined the performed process.

_root.process = "Installation";

The previewer movieclip instance needs some instructions to perform its tasks, so I wrote them in the onKeyDown event handler function:

preview.onKeyDown = function(){

All neede variables are set

Code:

  1. var headColor = fieldsClip.text3.myText.text;
  2. var row1Color = fieldsClip.text4.myText.text;
  3. var row2Color = fieldsClip.text5.myText.text;
  4. var bodyFontColor = fieldsClip.text6.myText.text;
  5. var headFontColor = fieldsClip.text7.myText.text;
  6. var welcomeText = fieldsClip.text8.myText.text;
  7. var headText = fieldsClip.text9.myText.text;
  8. var logo = fieldsClip.text10.myText.text;

When the TAB key is pressed a number of controls is executed: these controls check if the variable value is correct and if it is correct it is formatted in the right way using the setColor function previously defined. This allows the preview movieclip ssume the right tints.

Code:

  1. if(Key.isDown(9)){
  2. if(headColor != "" && headColor != "Write here..."){
  3. this.setColor(this.head, headColor);
  4. }
  5. if(row1Color != "" && row1Color != "Write here..."){
  6. this.setColor(this.row1, row1Color);
  7. }
  8. if(row2Color != "" && row2Color != "Write here..."){
  9. this.setColor(this.row2, row2Color);
  10. }
  11. if(bodyFontColor != "" && bodyFontColor != "Write here..."){
  12. this.setColor(this.bodyFont1, bodyFontColor);
  13. this.setColor(this.bodyFont2, bodyFontColor);
  14. }
  15. if(headFontColor != "" && headFontColor != "Write here..."){
  16. this.setColor(this.headFont, headFontColor);
  17. }
  18. if(welcomeText != "" && welcomeText != "Write here..."){
  19. this.setColor(this.welcome, "0xFFFF00");
  20. }
  21. if(headText != "" && headText != "Write here..."){
  22. this.setColor(this.headText, "0xFFFF00");
  23. }
  24. if(logo != "" && logo != "Write here..."){
  25. this.setColor(this.logo, "0xFFFF00");
  26. }
  27. }

The preview movieclip listener is added to the key object

Key.addListener(preview);

The needed click handlers are assigned to the two PushButton instances (installBtn and cancelBtn).

Code:

  1. installBtn.setClickHandler("sendForm");
  2. cancelBtn.setClickHandler("reset");

The preview _alpha is decreased as it looks a little smarter.

preview._alpha = 60;

Well, the Installation UI is ready, now I'm going to explain what I did next.

3.3.3. Confirming a succesfull installation

If the sendForm function is executed succesfully the confirm function is executed after 1.5 seconds. This last function simply moves the movie playhead to the next frame, the one labeled "success". Here there are only three elements: two buttons and a dynamic textbox named "output". The two buttons are labeled "Run IWE GB>>" and "Goto Admin Panel" and have no instance names. But each one has the Click Handler specified in the component paramenters panel this time: respectively "run" and "admin". What is the functionality of this frame? It simply confirms that the installation or the update processes were succesful and let the user choose if running the guestbook or going back to the administration panel to make some new changes.

A few lines of code are needed this time, let's look to them:

stop();

The following is the Run IWE GB button click handler

Code:

  1. function run(){
  2. getURL("index.html", "_self");
  3. }

And the next is the Goto Admin Panel button click handler

Code:

  1. function admin(){
  2. gotoAndStop("login_panel");
  3. }

The following lines simply set the output textfield text depending on the actual process executed (here you can see also what the process variable is used for).

Code:

  1. if(_root.process == "Installation"){
  2. output.text = "INSTALLATION WAS SUCCESSFULL!\nNow you can run IWE Guestbook V. 1.0.1 beta on your website";
  3. }else if(_root.process == "Update"){
  4. output.text = "UPDATE WAS SUCCESSFULL!\nNow you can run IWE Guestbook V. 1.0.1 beta to see applied changes";
  5. }

3.3.4. The administrator login

In the fifth frame (labeled "main") you can see that the interface layer is composed by an instance of the login movieclip that comes from the shared library movie. You must give it an instance name because you need to refer to this movieclip in the loginSuccessfull() function defined in the actions current keyframe. This function, if you remember, is called in the the login_submit function inside the login movieclip, so you have got to use the same name to make it work correctly. Let's jump into the code:

stop();

As I've just said above, this function allows you to control the movie if the login was succesful. In this case it sets two variables and moves the playhead to the "admin" labeled keyframe.

Code:

  1. function loginSuccessfull(){
  2. _root.administrator = logClip.logAdmin.administrator;
  3. _root.password = logClip.logAdmin.password;
  4. _root.gotoAndPlay("admin");
  5. clearInterval(intervalID);
  6. }

Here two .as file are included, let's see more in the detail how they work just below the next two lines of code that end the current frame actions.

Code:

  1. #include "include/set_password.as"
  2. #include "include/read_cookie.as"

The first included file contains a function needed to fill the password textbox with the saved password if the user checked the checkbox in the login form.

Code:

  1. // set the password in the textbox if cookie exists
  2. function setPassword(){
  3. logClip.passwd.fadeOut = true;
  4. logClip.passwd.myText.text = checkCookie;
  5. clearInterval(intervalID);
  6. }

The second one sets up a chunk of code containing a function that is needed to read a saved sharedObject and to check if the password has been saved: if the password exists it calls the above function (setPassword) to make it appear in the right textbox.

Code:

  1. // read a cookie
  2. function readCookie(theCookie, value){
  3. myCookie = SharedObject.getLocal(theCookie);
  4. var valueToRead = myCookie.data[value];
  5. if(valueToRead != undefined) var myValue = valueToRead;
  6. else var myValue = false;
  7. return myValue;
  8. }
  9. checkCookie = readCookie("logCookie", "pwd");
  10. if(checkCookie == false){
  11. logClip.passwd.myText.text = "";
  12. }else{
  13. i ntervalID = setInterval(setPassword, 500);
  14. logClip.checkBox.setValue(true);
  15. }

3.3.5. Updating the saved settings

If the guestbook administrator one day would need to change the guestbook colors to match the new website's tints or maybe would simply want to change his/her username and/or password will certainly use the last keyframe interface.

It contains an instance of the fieldsClip movieclip named this time changeForm and two PushButton instances again: this time instead of Install the submit button has got "Submit changes" as label and submitBtn as instance name. The second button has got "Reset" as label and resetBtn as instance name. Also the upper label has been changed in "Welcome to the IWE Guestbook Administration Panel". The structure is the same of keyframe three (the one labeled "install"), so let's look at the code:

Code:

  1. stop();
  2. // Also this time all variables needed by the functions are setup:
  3. _root.theClip = _root.changeForm;
  4. _root.serverSideScript = "admin_settings.php";
  5. _root.action = "Updating";
  6. _root.process = "Update";
  7. // Then Click handlers are assigned to the two PushButton instances:
  8. resetBtn.setClickHandler("setData");
  9. submitBtn.setClickHandler("sendForm");

And at the end the two click handler functions are setup.

The first one is needed at the beginning, when the playhead comes into the last keyframe: you need to show to the user the current settings, so the movie first extracts saved data from the database table with the setDatabaseText() functions and then calls the setData() function to show the appropriate data in the respective textbox. As you can notice, the setDatabaseText() function is executed with a little delay using a setInterval function. This is because of a timing problem: the FadingCoverTextboxes need to initialize and fade in their covers, in this way you can wait that they are ready to be controlled by the setData() function.

Code:

  1. function setData(){
  2. var i = 10;
  3. for(var a in seekData){
  4. if(a != "onLoad"){
  5. var field = _root.changeForm["text"+i];
  6. var currentText = field.myText.text = seekData[a];
  7. if(currentText.substr(0,1) == "#"){
  8. field.myText.text = currentText.substr(1, currentText.length);
  9. }
  10. if(currentText != "Write here..." || currentText != ""){
  11. field.fadeOut = true;
  12. }
  13. i--;
  14. }
  15. }
  16. with(_root.changeForm.text1){
  17. myText.text = administrator;
  18. fadeOut = true;
  19. }
  20. with(_root.changeForm.text2){
  21. myText.text = password;
  22. fadeOut = true;
  23. }
  24. }
  25. function setDatabaseText(){
  26. _root.setError("Loading saved data...");
  27. seekData = new LoadVars();
  28. // if true gets data, else saves them
  29. seekData.getInfo = true;
  30. seekData.sendAndLoad(_root.serverSideScript, seekData, 'POST');
  31. seekData.onLoad = function(success){
  32. if(success){
  33. if(this.error == undefined){
  34. delete this.getInfo;
  35. setData();
  36. _root.setError("Data loaded successfully.");
  37. }else{
  38. _root.setError(this.error);
  39. }
  40. }else{
  41. _root.setError("Error: data loading failure");
  42. }
  43. };
  44. clearInterval(intervalID);
  45. }
  46. intervalID = setInterval(setDatabaseText, 500);

And this is all. The only thing to add is that the submit button, as you can see in the Click Handlers assignment at the beginning of the code above, uses the same submit function as the installBtn symbol used at keyframe three ("install" label).

The Installation/Settings User Interface is now ready to work. You can do some tests in your local database (if you use a local webserver with PHP and MySQL).

3.4 The Guestbook interface and administration movie.

3.4.1 Introduction: setting up the movie.

Here we are, the lesson is near its end, the guestbook is going to be completed within the next steps.

Open a new file or save the admin.fla one with a new name: guestbook.fla. This is the last Flash movie that you are going to analyze and/or develop. I've just said that you could save the admin.fla file with the new name. This is because this new file is based on the previous one: their structures are quite similar, both in the timeline and in the library. The library is the same, it has only some more elements, while the timeline is a little different, because the needed elements are a bit different starting from the third keyframe. You can see both the timeline and the library in pictures 3.4 and 3.5.

Picture 3.4

As you can notice, the first two keyframes are the same of the admin.fla movie. There is the preloader in the first one (and I used again my preloader component, so you can find it in the library again) and the installation check in the second one (in the actions layer). Look to the code: this time it is a little different and looks like it was shorter.

Code:

  1. #include "include/install_check.as"
  2. #include "include/error_manager.as"
  3. _global.enterTimes = 0;
  4. System.useCodepage = true;
  5. stop();

Picture 3.5

I included the same .as files but no functions are set this time, because all what we need will be in the next frame. You can see that a _global variable has been set: it sets the times the user enters the guestbook keyframe. I will show you more in the detail this concept later. Again, the System.useCodepage command is used.

The remaining frames are 5 (the "credits" one is not important for our lesson): they are labeled in sequence (jumping the "credits" one) as "main", "insertEntry", "login", "admin" and "install" . There are also some new layers: the data layer, the strokes layer and the navigation layer.

3.4.2 The "main" frame.

This is the "heart" of this movie: in this frame there is the engine and the graphic interface that makes the guestbook live! Let's look first at the interface structure: there are two dynamic texboxes, welcomeTxt and headTxt. In the top-right corner you can see the logo movieclip. Under these elements there is the header movieclip that contains the guestbook table header and the "Add entry" and "credits" buttons. I won't describe how to build these elements, as this is an advanced tutorial and I need to show you how to code. If you don't understand something you can study the source file.

Another important movieclip that you need to build is the record movie clip that contains all the elements needed to show an entry. Look at picture 3.6 to see how it is structured.

Picture 3.6

You can see in the picture that there are five blank dynamic textfields and two dynamic textfields that work as labels: these two last textfields need to be dynamic for one reason, that is the must be able to change their font face color (I'll show it in a moment). Their instance names are URL_tip and date_tip. All the textfield of this movieclip are in the "text" layer: you can see the structure in picture 3.7. The other five fields have got their own instance name as you need to fill and format them as specified in the settings table. Their names are respectively: name, URL, date, message and ip. In this layer also an instance of the ScrollBar component has been placed.

Picture 3.7

The URL button layer contains a transparent button with instance name urlBtn: it is needed when an entry contains a homepage with the respective location. If this is the case the button is enabled and the location is assigned.

The "buttons" layer contains the edit and the showIPBtn buttons while the "bg" layer contains the bg movieclip that will be set to change the row color as set in the settings table in the database.

Let's look now at the actions in frame ten of the "init" layer.

With the first two variables I set the starting _x and the _y positions where buttons must be positioned. To understand this you have to think to what you need to do. For a more dynamic movie I wanted to show the email and the homepage buttons only if the current entry contains these two values. So I created the buttons symbols and exported them with an identifier in the library, using the linkage option (picture 3.8).

Code:

  1. startX = 210;
  2. buttonY = 79;

With the next function you can check if an URL is correct with the correct syntax before assigning it to the urlBtn instance.

Code:

  1. function checkURL(theURL){
  2. var url = theURL.substring(0, 7) == "http://" ? theURL : "http://" + theURL;
  3. return url;
  4. }

On the enterFrame event you have got to check if the email and the url values are defined: in this case the script will attach the respective movieclips from the library, position them and assign the needed actions

Code:

  1. this.onEnterFrame = function(){
  2. if(!ok){
  3. if(email != undefined && email != ""){
  4. this.attachMovie("email", "emailBtn", 1);
  5. this.emailBtn._x = startX;
  6. this.emailBtn._y = buttonY;
  7. this.emailBtn.onPress = function(){
  8. getURL("mailto:" + email);
  9. };
  10. ok = true;
  11. ok2 = true;
  12. }else{
  13. removeMovieClip("emailBtn");
  14. ok = true;
  15. ok2 = true;
  16. }
  17. }
  18. if(!ok2) return;
  19. if(ok2){
  20. if(URL.text != null && URL.text != ""){
  21. myURL = checkURL(URL.text);
  22. if(email != undefined){
  23. this.attachMovie("home", "homeBtn", 2);
  24. this.homeBtn._x = startX + (this.emailBtn._width + 10);
  25. this.homeBtn._y = buttonY;
  26. this.homeBtn.onPress = function(){
  27. getURL(myURL, "_blank");
  28. };
  29. ok2 = false;
  30. }else{
  31. this.attachMovie("home", "homeBtn", 2);
  32. this.homeBtn._x = startX;
  33. this.homeBtn._y = buttonY;
  34. this.homeBtn.onPress = function(){
  35. getURL(myURL, "_blank");
  36. };
  37. ok2 = false;
  38. }
  39. }else{
  40. removeMovieClip("homeBtn");
  41. ok2 = false;
  42. }
  43. }
  44. };

The logAdmin() function is called when the showIPBtn or the edit button are pressed: it simply moves the _root playhead to the login frame. It sets also the needed _global variables and removes the two instances of the record movieclip attached on the main timeline (as you will see after, only two records per page are showed).

Code:

  1. function logAdmin(){
  2. _global.lastRecordShown = _parent.currentRecord;
  3. _global.lastPageShown = _parent.currentPage;
  4. _root.gotoAndStop("login");
  5. removeMovieClip(_root.record1);
  6. removeMovieClip(_root.record2);
  7. }
  8. edit.onPress = function(){
  9. _global._currentRowID = rowID;
  10. logAdmin();
  11. };
  12. showIPBtn.onPress = function(){
  13. if(!showIP){
  14. _global.showIP = true;
  15. logAdmin();
  16. }
  17. };
  18. stop();

Picture 3.8

Now that you should have understood how I structured the record movieclip (and you should be able to build your own), I go back to the main timeline.

Before starting to code look again on the Stage: what do you see? In the movie footer a movieclip is left: it is located in the "navigation" layer and its name is navBar. This clip is a real navigation bar: it shows the navigation buttons and all the needed navigation info to display to the final user, such as the number of pages, the total records and the current displayed records. As you can see editing the movieclip, there are three dynamic texfields: totMessages, curRecord and curPage. The prevPage and the nextPage buttons are movieclips composed by a dynamic textbox and a button called button, this because also the button text needs to be formatted conforming the settings saved in the database.

- A look at the IWE Data Manager Flash MX component

I want to introduce the IWE Data Manager component that you must drag in the "data" layer at keyframe three, before going on with the code.

If you haven't already done it, drag two instances of the component from the Components panel: you need two instances because the guestbook needs to load both the settings and the entries. As you can see, the component is nothing but an empty movieclip. Anyway, you can set its parameters from the Component Parameters panel. Select the first instance of the IWE Data Manager: enter entryDataLoader as instance name and switch to the Parameters form. Now enter the path of your get_entries.php file that returns the entries inserted in the database. It can be a relative path. Select true from the AutoLoad field: this option, if true (false is the default value), let the component start loading data when the playhead reaches the frame where the component is placed. Now make the same with the other instance, but this time give it settingsLoader as instance name and write the path of the get_settings.php file. If you test your movie now (if it is all correct), you can see in the Debug menu --> List variables that each component created a externalData property (array) containing a sub-array for each index and each sub-array containing all the respective record's data, each field per sub-array index. In this way it will be really easy to get data from the database and use them in a momento referring to the component instance name and its externalData property.

You will see in a moment how to check for a successful data loading (you can do this in the enterFrame), but you can also find it in the documentation .txt file that is attached to the component .zip archive.

- Coding the guestbook

Now that it's all clear let's code it!

In this frame there are really tons of code: here all needed functions are concentrated. I will proceed step by step. This kind of workflow is useful to optimize the redundant code: this does not mean that all the code in this tutorial is optimized in the best possible way, but I tried to do my best in the optimization work.

First, you need to increment the enterTimes _global variable: it specifies how many time the user accesses this frame (main) to enable the calculation about the current page and the current record to show when a user changes to another section (the insert new entry one for example).

enterTimes++;

The following function is called by the "back" buttons located in the other frames of the movie.

Code:

  1. _global.goBack = function(){
  2. backBtn.onRelease = function(){
  3. gotoAndPlay("main");
  4. };
  5. };

The init function initialize the guestbook interface each time that the playhead enters the current frame. I go inside it to explain step by step the actions it executes.

function init(){

You need to set some positioning variables: the _x and _y starting points where to set the attached 'record' movieclip position, the attached 'error' movieclip position (see the no_data symbol in the library - MovieClips folder -) that will be attached in case no entries are saved in the database, while the buttonX and buttonY variables need to be removed as they are only test values that I forgot to leave before.

Code:

  1. startX = 1.3;
  2. startY = 147;
  3. errorY = startY;
  4. //buttonX = 2;
  5. //buttonY = 332;

Some elements are initialized, such as the preloaders in the logo movieclip and in the welcomeTxt textbox.

Code:

  1. welcomeTxt.text = headTxt.text = "Loading...";
  2. navBar._visible = 0;
  3. with(logo){
  4. loadingClip._visible = 1;
  5. loadingClip.play();
  6. }

This function simply attach a new row to the guestbook

Code:

  1. function attachClip(level, x, y){
  2. _root.attachMovie("record", "record"+level, level);
  3. var clip = "record"+level;
  4. with(_root[clip]){
  5. _x = x;
  6. _y = y;
  7. }
  8. return clip;
  9. }

This function makes the record clip specified in the argument plays the init movie (that one that looks like a flash).

Code:

  1. function playClip(theClip){
  2. if(intervalID1 != undefined){
  3. theClip._visible = 1;
  4. theClip.gotoAndPlay(2);
  5. clearInterval(intervalID1);
  6. intervalID2 = setInterval(playClip2, 300, _root[clip2]);
  7. }
  8. }

The playClip2() function makes another clip to play. It's called from the playClip() function.

Code:

  1. function playClip2(theClip){
  2. if(intervalID2 != undefined){
  3. theClip._visible = 1;
  4. theClip.gotoAndPlay(2);
  5. clearInterval(intervalID2);
  6. }
  7. }

The next function returns a color value formatted for using it with the setRGB Color Object method.

Code:

  1. function formatColor(theColor){
  2. if(theColor.substr(0,1) == "#"){
  3. var newColor = "0x" + theColor.substr(1, 6);
  4. return newColor;
  5. }else{
  6. var newColor = "0x003366";
  7. return newColor;
  8. }
  9. }

formatDate() returns a formatted date for the record movieclip's date textfield.

Code:

  1. function formatDate(theDate){
  2. var date = theDate.split(" ");
  3. var dateArray = date[0].split("-");
  4. var newDate = dateArray[2] + "-" + dateArray[1] + "-" + dateArray[0] + " " + date[1];
  5. return newDate;
  6. }

The following function checks the lenght of a message for showing or not the scrollbar (if a message is shorter than the message textfield's capacity it's not graphically good to show the scrollbar).

Code:

  1. function showScrollBar(message){
  2. if(message.length > 192) return true;
  3. else return false;
  4. }

selectDataSource() sets the entryData variable value used in the enterFrame event handler (it refers to the entryDataLoader's 'externalData' property).

Code:

  1. function selectDataSource(){
  2. entryData = entryDataLoader.externalData;
  3. }

The following function makes the invisible button over the homepage field "appear" (if it exists) and sets the relative url in its onPress event handler.

Code:

  1. function setUrl(clip, currentRecord, dataSource){
  2. if(clip.URL.text != "" && clip.URL.text != undefined){
  3. clip.urlBtn.enabled = true;
  4. clip.urlBtn.onPress = function(){
  5. getURL(dataSource[currentRecord-1][4], "_blank");
  6. };
  7. }else{
  8. clip.urlBtn.enabled = false;
  9. }
  10. }

With the following function each record is printed inside the attached record movieclip: each field value will appear in the respective textfield. Four arguments are required: the clip identifiers, the number of the current record and the data source (the entryDataLoader component instance).

Code:

  1. function setText(clip1, clip2, currentRecord, dataSource){
  2. with(clip1){
  3. clip1.rowID = currentRecord-1;
  4. name.text = dataSource[currentRecord-1][1];
  5. clip1.email = dataSource[currentRecord-1][2];
  6. URL.text = dataSource[currentRecord-1][3];
  7. setUrl(clip1, currentRecord, dataSource);
  8. date.text = formatDate(dataSource[currentRecord-1][8]);
  9. message.text = dataSource[currentRecord-1][5];
  10. if(showScrollBar(message.text)) myScrollBar._visible = 1;
  11. else myScrollBar._visible = 0;
  12. if(IWantIP) ip.text = dataSource[currentRecord-1][6];
  13. else ip.text = "";
  14. }
  15. if(clip2 != "" && clip2 != undefined){
  16. if(currentRecord+1 <= _root.l){
  17. with(clip2){
  18. clip2.rowID = currentRecord;
  19. name.text = dataSource[currentRecord][1];
  20. clip2.email = dataSource[currentRecord][2];
  21. URL.text = dataSource[currentRecord][3];
  22. setUrl(clip2, currentRecord, dataSource);
  23. date.text = formatDate(dataSource[currentRecord][8]);
  24. message.text = dataSource[currentRecord][5];
  25. if(showScrollBar(message.text)) myScrollBar._visible = 1;
  26. else myScrollBar._visible = 0;
  27. if(IWantIP) ip.text = dataSource[currentRecord][6];
  28. else ip.text = "";
  29. }
  30. }else{
  31. // removes 2nd record box if last record is odd
  32. removeMovieClip(clip2);
  33. }
  34. }
  35. setTextColor();
  36. }

As its name shows, the next function simply calls another function for each instance of the attached record movieclip.

Code:

  1. function setTextColor(){
  2. setRowTextColor(_root[clip1]);
  3. if(_root[clip2] != undefined){
  4. setRowTextColor(_root[clip2]);
  5. }
  6. }

Called from the previous one, this function applies the fontFormat textFormat object properties to the specified textboxes.

Code:

  1. function setRowTextColor(theClip){
  2. with(theClip){
  3. name.setTextFormat(fontFormat);
  4. URL_tip.setTextFormat(fontFormat);
  5. URL.setTextFormat(fontFormat);
  6. date_tip.setTextFormat(fontFormat);
  7. date.setTextFormat(fontFormat);
  8. message.setTextFormat(fontFormat);
  9. ip.setTextFormat(fontFormat);
  10. }
  11. }

The following function sets the information to display in the navigation bar (navBar).

Code:

  1. function setNavInfo(){
  2. with(navBar){
  3. totMessages.text = l;
  4. var record = currentRecord < l ? currentRecord + "-" + (currentRecord+1) : currentRecord;
  5. curRecord.text = record + "/" + l;
  6. curPage.text = currentPage;
  7. }
  8. }

When the last record is odd you don't need to show two record in the last page, so the second instance will be removed. The following function checks for the second instance, if it does not exist it means that it was removed as records were odd: in this case, coming back from the last page it attaches the second instance again.

Code:

  1. function setOdd(){
  2. if(currentRecord < l-1){
  3. // attach the 2nd clip if it was removed in case last record was odd
  4. if(_root[clip2] == undefined){
  5. clip2 = attachClip(2, startX, (startY + .5) + _root[clip1]._height);
  6. if(returned){
  7. row2Color = new Color(_root[clip2].bg);
  8. row2Color.setRGB(formatColor(settings[0][2]));
  9. returned = false;
  10. }else{
  11. row2Color.setRGB(formatColor(settings[0][2]));
  12. }
  13. intervalID2 = setInterval(playClip2, 100, _root[clip2]);
  14. }
  15. navBar.nextPage._visible = 1;
  16. }else navBar.nextPage._visible = 0;
  17. }

The following function checks for the current record value to set the navigation bar appearance and displayed information.

Code:

  1. function checkRecord(){
  2. if(currentRecord > 1){
  3. navBar.prevPage._visible = 1;
  4. setOdd();
  5. setNavInfo();
  6. }else{
  7. setOdd();
  8. navBar.prevPage._visible = 0;
  9. setNavInfo();
  10. }
  11. }

With the next function are managed the records and pages counters: as for each page two records are displayed the increment or decrement factor of the counter is of two units. The direction argument is passed so that the function itself knows what to do.

Code:

  1. setRecords = function(direction){
  2. if(direction == "forward"){
  3. if(currentRecord < l-1) currentRecord += 2;
  4. if(currentPage < totalPages) currentPage++;
  5. checkRecord();
  6. }else if(direction == "backward"){
  7. if(currentRecord > 1) currentRecord -= 2;
  8. if(currentPage > 1) currentPage--;
  9. checkRecord();
  10. }
  11. setText(_root[clip1], _root[clip2], currentRecord, entryData);
  12. if(_root[clip1] != undefined && _root[clip2] != undefined){
  13. _root[clip1].ok = false;
  14. _root[clip2].ok = false;
  15. }
  16. }

This function is called by the 'nextPage' button in the 'navBar' instance and simply calls the 'setRecords()' function passing it the direction argument.

Code:

  1. function nextRecords(){
  2. setRecords("forward");
  3. }

This function is really similar to the nextRecords() one but is called by the prevPage button.

Code:

  1. function prevRecords(){
  2. setRecords("backward");
  3. }

The following function is called by the 'addEntryBtn' and the 'creditsBtn' placed inside the 'header' symbol in the 'interface' layer. It simply cleans the Stage, removing the record instances or the no_data instance when one of those two buttons is pressed.

Code:

  1. function cleanStage(){
  2. if(currentRecord == undefined || currentRecord == 0){
  3. currentRecord = 1;
  4. currentPage = 1;
  5. }
  6. _global.lastRecordShown = currentRecord;
  7. _global.lastPageShown = currentPage;
  8. if(_root[clip1] != undefined) removeMovieClip(_root[clip1]);
  9. if(_root[clip2] != undefined) removeMovieClip(_root[clip2]);
  10. if(_root.no_data != undefined) removeMovieClip(_root.no_data);
  11. }

The following functions, as specifiend in the header below, are needed to manage the forms that are placed at frame 4 and 6 (labels: 'insertEntry' and 'admin').

The next function checks for the inserted text and returns the default Fading Cover Textbox one if the field is empty.

Code:

  1. function checkText(msg){
  2. var checkedText = msg == "Write here..." ? "" : msg;
  3. return checkedText;
  4. }

The next one initializes the fading of the cover of the Fading Cover component if the text inserted is not null or the default one.

Code:

  1. function setFadeOut(textBox, value){
  2. if(value != "" && value != "Write here..."){
  3. textBox.fadeOut = true;
  4. }
  5. }

The next function formats an URL before it will be sended to the PHP script for database insert.

Code:

  1. function checkURL(theURL){
  2. var toCheck = "http://";
  3. if(theURL.substr(0, 1) == "\\" || theURL.substr(0, 1) == " " || theURL.substr(0, 1) == ""){
  4. var newURL = "";
  5. }else if(theURL.substr(0, 7) != toCheck && theURL.substr(toCheck.length, theURL.length) != "\r\n\t\t\t\t"){
  6. var newURL = toCheck + theURL;
  7. }else if(theURL.substr(0, theURL.length) == toCheck || theURL.substr(toCheck.length-1, theURL.length) == "\r\n\t\t\t\t"){
  8. var newURL = "";
  9. }
  10. return newURL;
  11. }
  12. /*
  13. */

Here following, the setGlobalStyles.as file is included. This file contains a setGlobalStyles function written by the great Colin Mook (http://www.moock.org) that allows you to change the FStyleFormat compliance components to change their colors.

[i]#include "include/setGlobalStyles.as"[/i]

Now the "real" engine of the guestbook interface comes out: it is situated in the enterFrame eventHandler and in the code that follows it. I go inside it to comment you the basilar steps.

_root.onEnterFrame = function(){

The next control simply checks for the success in loading data with the two instances of the IWE Data Manager component: each instance returns a success variable if the data were loaded. If data are not loaded it returns an error and if you check the "else" statement you can see that the script checks for this error and prints it. If data are loaded the header texts and logo are setup.

Code:

  1. if(entryDataLoader.success && settingsLoader.success){
  2. // set settings
  3. settings = settingsLoader.externalData;
  4. welcomeTxt.text = settings[0][5];//.toUpperCase();
  5. headTxt.text = settings[0][6];
  6. with(logo){
  7. logoBG.loadMovie(settings[0][7]);
  8. play();
  9. loadingClip._visible = 0;
  10. }
  11. }

The setGlobalStyles function included above ([i]#include "include/setGlobalStyles.as"[/i]) is called and globalStyles are applied to the compliant components.

setGlobalStyles();

All settings are applied and/or created

Code:

  1. headerColor = new Color(header.header);
  2. headerColor.setRGB(formatColor(settings[0][0]));
  3. headFontFormat = new TextFormat();
  4. headFontFormat.color = formatColor(settings[0][4]);
  5. fontFormat = new TextFormat();
  6. fontFormat.color = formatColor(settings[0][3]);
  7. with(header){
  8. name.setTextFormat(headFontFormat);
  9. message.setTextFormat(headFontFormat);
  10. addEntryBtn.myText.setTextFormat(headFontFormat);
  11. creditsBtn.myText.setTextFormat(headFontFormat);
  12. }

The l variable is set: its value is the number of records loaded from the database iwe_gb_entries table

l = entryDataLoader.lenght;

If there are some records the "navigation" variables are setup: currentPage, currentRecord and the number of records per page (that in our case will be 2).

Code:

  1. if(l > 0){
  2. currentPage = enterTimes == 1 ? 1 : lastPageShown;
  3. totalPages = Math.ceil(l/2);
  4. currentRecord = enterTimes == 1 ? 1 : lastRecordShown;
  5. recordsPerPage = 2;

The navigation bar is showed and information are displyed within it. The prevPage button is hidden as in the first page you don't need it.

Code:

  1. with(navBar){
  2. _visible = 1;
  3. setNavInfo();
  4. }
  5. navBar.prevPage._visible = 0;

If there is only one record (yes, you must think also about this situation!) only one record movieclip is attached. Then the data source is selected and the checkRecord function is called to set the navigation information. The text is set using the setText function and the color of the row is set according to the settings specified by the administrator. At last the clip is played with an interval to let all be set as needed.

Code:

  1. if(l == 1){
  2. clip1 = attachClip(1, startX, startY);
  3. clip1._visible = 0;
  4. selectDataSource();
  5. checkRecord();
  6. setText(_root[clip1], "", currentRecord, entryData);
  7. rowColor = new Color(_root[clip1].bg);
  8. rowColor.setRGB(formatColor(settings[0][1]));
  9. intervalID1 = setInterval(playClip, 100, _root[clip1]);

if the records are more than one, two instances of the record movieclip are attached and a new color is set as the first row and the second row can have different colors.

Code:

  1. }else if(l > 1){
  2. clip1 = attachClip(1, startX, startY);
  3. clip2 = attachClip(2, startX, (startY + .5) + _root[clip1]._height);
  4. selectDataSource();
  5. checkRecord();
  6. setText(_root[clip1], _root[clip2], currentRecord, entryData);
  7. // set row colors
  8. row1Color = new Color(_root[clip1].bg);
  9. row2Color = new Color(_root[clip2].bg);
  10. row1Color.setRGB(formatColor(settings[0][1]));
  11. row2Color.setRGB(formatColor(settings[0][2]));
  12. _root[clip1]._visible = _root[clip2]._visible = 0;
  13. intervalID1 = setInterval(playClip, 100, _root[clip1]);
  14. }

If there are more than two entries, the nextPage button is showed.

navBar.nextPage._visible = l > 2 ? 1 : 0;

In case the number of entries is less than one (zero), the no_data movieclip is attached and showed: this movieclip simply tells to the user that there are no entries to show.

Code:

  1. }else{
  2. currentPage = 0;
  3. // show no data movie clip
  4. _root.attachMovie("no_data", "no_data", 1);
  5. with(_root.no_data){
  6. _x = startX;
  7. _y = errorY;
  8. }
  9. }

When all is done, the eventHandler is erased, so that it won't continue monitoring and executing the scripts.

delete this.onEnterFrame;

As I told before, this else statement searches for the error property if data were not loaded succesfully from the server.

Code:

  1. }else{
  2. if(entryDataLoader.error != undefined && settingsLoader.error != undefined){
  3. welcomeTxt.text = entryDataLoader.error + " " + settingsDataLoader.error;
  4. delete this.onEnterFrame;
  5. }
  6. }

After that all has been set, the init() function is called to set the needed parameters

init();

The actions for the creditsBtn and the addEntryBtn instances are created.

Code:

  1. header.creditsBtn.button.onRelease = function(){
  2. _root.gotoAndStop("credits");
  3. cleanStage();
  4. };
  5. header.addEntryBtn.button.onPress = function(){
  6. _root.gotoAndStop("insertEntry");
  7. cleanStage();
  8. };

The nextPage and the previousPage buttons need to call their respective functions to work properly.

Code:

  1. navBar.nextPage.button.onPress = nextRecords;
  2. navBar.prevPage.button.onPress = prevRecords;
  3. stop();

Well and this ends our guestbook coding. The only two things left are the insert entry form and the administration form. Let's go on and see them in the detail.

3.4.3 The "insertEntry" form.

A guestbook without an interface that allows users to insert their own entries is not a good guestbook. So let's add another keyframe or move to the next keyframe labeled "insertEntry" if you've already made it before. This frame should look like picture 3.9.

Picture 3.9

You can notice that it is a really simple form with the well known structure that I used in all the other forms I made before for this project. There is an instance of the insert_edit_message movieclip named 'insertMessage'. Within this clip there are four Fading Cover Textbox instances and a simple multiline input textbox with a ScrollBar component instance attached and the respective labels on their left. the textboxes instance names are (from top to base): text1, text2, text3, text4 and text5. Two PushButtons instances, the error textbox and a back button complete the interface together with a label on the top of the movie that explains the user what the form is intended to. You can find all the code in the respective keyframe in the actions layer.

Let's look at it:

The first function makes a call to the previously defined checkText() function that I set in the "main" frame (do you remember? There were some function within an header that said "FORM CONTROL FUNCTIONS"): it checks if the inserted text is valid and returns it if it is, if it is not it returns an empty string.

Code:

  1. function setValue(msg){
  2. var value = checkText(msg);
  3. return value;
  4. }

The postMessage() function (called by the postBtn button) validates the form and with a LoadVars() object sends data to the PHP script that will insert the new record into the 'iwe_gb_entries' table. The onLoad event handler of the LoadVars() object is structured as usual, that is checking the succes argument and printing out the success sentence or an error. In case of a successful data loading, the script calls the showNewEntry() function.

Code:

  1. function postMessage(){
  2. for(var i = 1; i <= 5; i++){
  3. var textBox = insertMessage["text"+i];
  4. if(i < 5 && i <> 4){
  5. var value = setValue(textBox.myText.text);
  6. textBox.myText.text = value;
  7. setFadeOut(textBox, value);
  8. }else if(i == 4){
  9. var value = setValue(textBox.myText.text);
  10. textBox.myText.text = checkURL(value);
  11. setFadeOut(textBox, value);
  12. }else textBox.text = checkText(textBox.text);
  13. }
  14. var insertNewMessage = new LoadVars();
  15. insertNewMessage.name = insertMessage.text1.myText.text;
  16. insertNewMessage.email = insertMessage.text2.myText.text;
  17. insertNewMessage.homepage = insertMessage.text3.myText.text;
  18. insertNewMessage.url = insertMessage.text4.myText.text;
  19. insertNewMessage.entry = insertMessage.text5.text;
  20. insertNewMessage.sendAndLoad("save_entry.php", insertNewMessage, 'POST');
  21. insertNewMessage.onLoad = function(success){
  22. if(success){
  23. if(this.action != undefined){
  24. setError(this.action);
  25. intervalID = setInterval(showNewEntry, 1000);
  26. }else if(this.error != undefined) setError(this.error);
  27. }else{
  28. setError("An error occured while sending or loading data");
  29. }
  30. };
  31. }

The next function is called by the previous one if the data insert process was successful: in this case the program will go back to the "main" frame and will restart from the beginning (first page) as the last entry inserted is normally the first one to be displayed.

Code:

  1. function showNewEntry(){
  2. _global.lastRecordShown = 1;
  3. _global.lastPageShown = 1;
  4. _root.gotoAndStop("main");
  5. clearInterval(intervalID);
  6. }

The little backBtn button calls the goBack() function set in the main frame actions layer.

backBtn.onPress = goBack;

The click handler of the postBtn button is set.

postBtn.setClickHandler("postMessage");

As it won't be complete, I insert here a correction that I made in version 1.0.3 beta: in the current version (the one on which this tutorial is based on, the 1.0.1 beta) has not any cancelBtn button click handler defined.

This function simply reset the form's textfields. It is placed in the "main" frame in the "FORM CONTROL FUNCTIONS". Its following line assigns the click handler to the button.

Code:

  1. function resetMessage(){
  2. for(var i = 1; i <= 5; i++){
  3. var textBox = insertMessage["text"+i];
  4. if(i < 5){
  5. textBox.myText.text = "";
  6. setFadeOut(textBox, "");
  7. }else textBox.text = "";
  8. }
  9. }

Note: the following line is to add to the current frame!

cancelBtn.setClickHandler("resetMessage");

The returned variable is needed to reset the row2Color in the "main" frame.

[i]

returned = true;

stop();

[/i]

Well, congratulations! Also this part has ended. Let's take a look to the last one: the administration interface.

3.4.4 Administrator's login and administration interface.

Here we are, this is the ending topic of this long tutorial. What is left to do is to login the Administrator and make him/her apply changes to or deleting existing messages. The administrator can login essentially for two reasons: looking at user's IP address and edit/cancel their messages. The login interface is the same used in the admin.fla file. So the code below is not new to you, it looks like the one I showed you during the explaining of that section. It differs only for one thing: the showIP variable. This value is sent went the user (the administrator in this case) clicks on the 'showIPBtn' button in the record movieclip instances in the "main" frame. In case the login is successful the _global variable IWantIP is set to true and evalued in the "main" frame as the playhead is addressed there again. With this variable set the program will show the IP address in its textbox.

Code:

  1. backBtn.onPress = goBack;
  2. function loginSuccessfull(){
  3. if(showIP){
  4. _global.IWantIP = true;
  5. _root.gotoAndPlay("main");
  6. }else{
  7. _root.administrator = logClip.logAdmin.administrator;
  8. _root.password = logClip.logAdmin.password;
  9. _root.gotoAndPlay("admin");
  10. }
  11. clearInterval(intervalID);
  12. }
  13. #include "include/set_password.as"
  14. #include "include/read_cookie.as"
  15. returned = true;
  16. stop();

If the [b]editb/i] button was pressed rather than the showIPBtn one the script moves the playhead to the admin frame that looks again like the insertEntry one, with some little differences. Look to picture 3.10

Picture 3.10

An instance of the insert_edit_message movieclip named 'messageEditor' is again on the Stage. A CheckBox instance named 'deleteBox' has been inserted and the left PushButton instance is now labeled "Submit changes". The top label has been properly changed into "ADMINISTRATION PANEL - ...". This form simply shows the current record's entry information and let the administrator modify them or delete the record in case the deleteBox checkbox is checked. Select the CheckBox and assign it deleteBoxHandler as its Change Handler.

Well, look to the code for the last time ::happy:: :

as just said above, the next fucntion is the deleteBox change handler: it shows a message to warn the user that the message will be erased.

Code:

  1. function deleteBoxHandler(){
  2. if(deleteBox.getValue() == true) setError("Caution: this message will be erased!");
  3. else setError("");
  4. }

The following function receives the current row ID parameter and shows the current record's data in the form's textfields.

Code:

  1. function setTextFields(rowID){
  2. for(var i = 1; i <= 5; i++){
  3. var textBox = messageEditor["text"+i];
  4. var currentData = entryData[rowID];
  5. if(i < 5 && i <> 4){
  6. if(currentData[i] == undefined) var value = "";
  7. else var value = currentData[i];
  8. textBox.myText.text = value;
  9. setFadeOut(textBox, value);
  10. }else if(i == 4){
  11. textBox.myText.text = checkURL(currentData[i]);
  12. setFadeOut(textBox, textBox.myText.text);
  13. }else textBox.text = currentData[i];
  14. }
  15. clearInterval(intervalID);
  16. }

And this one is the "key" of the administration panel: it checks for the deleteBox value and evaluates if deleting the record or simply update the changed data setting up the server-side script to use and sending the required parameters to it. The onLoad event handler is managed in the same way I showed in all the other scripts I used before.

Code:

  1. function submit(){
  2. var deleteRow = deleteBox.getValue();
  3. if(deleteRow == true) var manageUrl = "delete_entry.php";
  4. else var manageUrl = "update_entry.php";
  5. var submitLoader = new LoadVars();
  6. submitLoader.administrator = _root.administrator;
  7. submitLoader.password = _root.password;
  8. submitLoader.id = entryData[_currentRowID][0];
  9. if(!deleteRow){
  10. submitLoader.name = checkText(messageEditor.text1.myText.text);
  11. submitLoader.email = checkText(messageEditor.text2.myText.text);
  12. submitLoader.homepage = checkText(messageEditor.text3.myText.text);
  13. submitLoader.location = checkText(messageEditor.text4.myText.text);
  14. submitLoader.entry = checkText(messageEditor.text5.text);
  15. }
  16. submitLoader.sendAndLoad(manageUrl, submitLoader, 'POST');
  17. submitLoader.onLoad = function(success){
  18. if(success){
  19. if(this.action != undefined){
  20. setError(this.action);
  21. }else if(this.error != undefined){
  22. setError(this.error);
  23. }
  24. }else{
  25. setError("There was a problem while sending or receiving data");
  26. }
  27. };
  28. }

The following lines simply initialize this section, assigning handlers and calling the setTextFields() function that can so activate itself and show all data in the apposite textfields. As you can notice, an interval is used again to allow the FadingCoverTextbox instances initialize correctly and execute their own fade outs. The return variable has the same meaning that I showed before talking about the "login" frame.

Code:

  1. setError("Welcome, " + _root.administrator + "!");
  2. backBtn.onPress = goBack;
  3. submitBtn.setClickHandler("submit");
  4. cancelBtn.setClickHandler("resetMessage");
  5. intervalID = setInterval(setTextFields, 500, _currentRowID);
  6. returned = true;
  7. stop();

Before ending, let me add one more thing: the last frame, labeled "install", contains a getURL action: this frame is called from the second one in case the install_check.as file detects that the guestbook needs to be installed.

4. CONCLUSION AND USEFUL LINKS

Note: Before proceeding to install my IWE Guestbook, please read carefully the README_FIRST file, as it contains precious information about the information process and troubleshooting tips and, naturally, the license agreement.

You can also find the last version of this application in my site Media section(the downloads requires your subscription to my website. note: the subscription is free).

Well, that's all, I hope that this tutorial helped you to grow a little and will also make you contribute to help the community grow better.

Back to top

0 comments

No comments available: add yours!

Login or register to add a comment (registered users only)

Back to top

Site search
Are you a member?

Lost your password?

Register

ubuntu kernel panic not syncing vfs unable to mount root fs on unknown-block

Posted by Alessandro on Feb 23, 2012 11:57:47 AM
Filed under Networking | Comments (0)

ubuntu kernel panic not syncing vfs unable to mount root fs on unknown-block!! Help! :-) The last time I tried to reboot one of my Ubuntu Server (10.04 LTS) machines I received this message and the boot process simply did not continue. I hate rebo[…]

Read more

Pacchetto di traduzioni in Italiano di Magento

Posted by Alessandro on Feb 22, 2012 1:03:16 PM
Filed under Magento | Comments (0)

Chi di voi è abituato a passare molto tempo installando/configurando/personalizzando Magento saprà che si tratta di un'ottima piattaforma per realizzare siti e-commerce di elevata qualità. Però, per chi è un po' all[…]

Read more

GnuTLS error -12: A TLS fatal alert has been received

Posted by Alessandro on Feb 22, 2012 12:32:04 PM
Filed under Security | Comments (0)

Last week I was trying to connect to my FTP server after I rebooted the server machine and after a lot of time I didn't connect to it. Normally, I use Filezilla FTP client to connect to FTP servers, and I'm running the version 3.5.3 under Windows. […]

Read more

Web applications security: just a matter of quality

Posted by Alessandro on Oct 5, 2011 2:03:08 PM
Filed under Security | Comments (0)

I'm writing this article to warn all those people that think to pay less for developing their own websites. I'm referring, as usual, to my own experience, but I think that the problem could be encountered in a quite high percentage of web applicat[…]

Read more

Ma alla fine che cosa ha un senso?

Posted by Alessandro on Sep 28, 2011 4:58:29 PM
Filed under generic | Comments (0)

Torno a scrivere sul mio blog dopo un periodo di assenza un po' forzata (causa vacanze estive ed impegni vari intercorsi nel frattempo e successivamente) e un po' causata dalla poca voglia (lo ammetto!), dato che in questo periodo sono molto stanco. […]

Read more

Tags per questo articolo

flash guestbook languages php programming
Paypal Donate Button