project page
mailing list

Getting the data in there: the student-side frontend

Sorry this page is such a mess. I'm trying to get as much out as possible as quickly as possible.
As I work, I'll be cleaning it up-- Clark.

Section A: using includes for templating
Section B: General rundown of student-side pages
Section C: the form object from PHPLIB
  • Section C.1: optional v. required, and form validation in general
  • Section C.2: a full explanation of the doSelect widget
  • Section C.3: other useful db-based forms
Section D: UpdateOrInsert
Section E: the navigation bar

Section A: using includes for templating

The way that the pages are set up on the student side is with four standard include files that are on every page: cat_header_code.inc, cat_header_layout.inc, cat_leftbar.inc, and cat_footer.inc. cat_leftbar.inc is included by cat_header_layout.inc.

cat_header_code.inc and cat_header_layout.inc are the more interesting of these files: the code component, predictably, calls include files and sets up global variables and such. One important file that it includes is called widgets.inc (/html/include/widgets.inc). Yes, that's a terrible name for the file, but we're stuck with it (for now). This file is basically a library of useful functions that are used all over the student-side. If you see a function used somewhere and you don't know where it's defined, it's probably defined in widgets.inc. Also, many of the functions in widgets.inc are no longer used by anything, they are just leftovers from the way we used to do things. One project is to clean out all that crap.

cat_header_layout.inc sets up the colors and tables for the page. It also includes all our JavaScript include files, common.js, widgets.js, and popUpHelp.js, which we'll talk about later.

cat_leftbar.inc is the category links on the left side; it does a DB lookup to do those checkboxes and the suggestion. Pretty straightforward. One annoying thing about this page is that the ordering of the categories is not done right: if you want to change the order, you have to do it in multiple places. So that needs to be fixed. Also, one thing to be aware of is that the category names don't always correspond to the file names of the pages: Personal is personal.php3, but Education is scholastic.php3, Experience is job.php3, Portfolio is samples.php3.

So, as you can see, the basic idea here is to take advantage of PHP's include function so as not to duplicate code and HTML in every page. Pretty straightforward.

Section B: a general sketch of the how the pages work

Before we dive headlong into the nuts and bolts, let's go over what the student-side pages do, in general. Each page consists of an Edit bar, the Status bar (with Skip and Help buttons), the main body (ie, the forms), and the Save bar. (The Status bar and the Save bar together we call "the Navigation bars," and we'll talk about them more later.) A good way to think of it is in terms of states--for each page there are a handful of possible states it can be in, and the page has to behave appropriately for each state.
1.) So the first possible scenario is that the user just arrived at the page, say the Activities page. In that case, the Edit bar loads with the existing activities, or if there are no activities in the profile, it doesn't display. The Status bar says "Please enter or edit your information below." And the form is of course blank.

2.) Second, the user posts correct data with the "Stay Here" button. In that case, the data must be added to the person's profile (using the UpdateOrInsert() function--see below), and appear in the Edit bar when the page is redisplayed. Also, the form is empty and the Status bar says "Your information has been successfully recorded." Alternately, if the user clicks the "Next Category" button, the data will be saved, and then the user forwarded to the next category on the list.

3.) User posts the page, with either the "Stay Here" or "Next Category" button, but there is an error--some required element is missing, or validation fails. In this case, the Status bar says "An error occurred. Please see below." The form loads with all the data in it (using the load_defaults() function in the form class), with a red error message above each offending form element.

4.) User chooses an existing item from the Edit bar menu. In this case, the primary form element, namely the activity name, or the school name, or the company name, freezes, and the rest of the form loads up the existing data, using the loadValues() function (found in widgets.inc) and the load_defaults() function.

So, as you will see, the beginning of each category page consists of identifying and handling these cases. Foolishly, this is done separately and in large part differently on each page. One good project would be standardizing and code-sharing this process.

Section B: the form object from PHPLIB

For a given category page, say scholastic.php3, the setup_scholastic.inc file is where the form object is defined. The setup_x.inc file contains a single function which returns a form object (the form class is defined in html/php/oohforms.inc). On the page itself, this form object is always called $input_form, because of the way the navigation bars are set up. But we'll get into that in Section D. The way the object works (and this is documented in the PHPLIB documentation) is you add form elements using the add_element() function, passing it an array with various parameters, such as the type, size, etc. And then once you have built up the form object, you call show_element() at the moment when you want the actual HTML for the form to display. It's a little more complicated than that, but that's the basic idea.

So, to the standard array of form elements--select, radio, checkbox, etc--we have added our own special types, basically extensions to the select. The most complicated of these is the doSelect type or "widget" as we sometimes call it. Here is an example of how it is invoked, taken from html/edit/setup_scholastic.inc, line 18.

$schform->add_element( array( "type" => "doSelect",
	           	      "name" => "student_school_schoolid",
			      "table" => "school",
			      "valid_e" => "Please specify the school you attended.",
			      "value" => -2,
			      "min_students" => 1 ) );
What this generates (when $input_form->show_element( "student_school_schoolid" ) is called in scholastic.php3) is the first group of form elements you see on the page: the selectbox with all our known schools in it, as entered by our users (see the DB schema section), a text input field, and a button that says "Search our Database". What this does, and you can see for yourself, is allow the student to either select an existing school, or pop up a window for entering a new school. So let's take a look at the code line by line.

"type" => "doSelect" says that we are dealing with a doSelect widget. What that means is that we're going to look up in the table specified by the "table" => "school" line, and from that table generate a selectbox. The values in the 'name' column are the labels, while the values in the 'schoolid' column are the values for the select. So you can see how useful this is: we generate a selectbox on the fly from the database. The text field and the 'Search the Database' button are part of the process for adding new schools (or what have you) to the database. It's complicated, and requires some hairy JavaScript, so we'll leave the full explanation for later.

"name" => "student_school_schoolid" specifies the name of the form element. This is important for entering the data into the database. The way it works is, the first portion of the name specifies the table we will be modifying. The last part of the name specifies which column in that table we will be modifying. So in this case, it's the 'student_school' table, 'schoolid column'. See Section C, the UpdateOrInsert() function for further explanation.

"table" => "school" we already talked about with regard to the doSelect widget.

"valid_e" => "Please specify..." Since this is a required element, the form will display an error message directly above the element if the user doesn't enter anything. This valid_e simply specifies the text of the message. Form validation is also a whole other issue which we'll get into more later.

"value" => -2 just specifies the default position of the selectbox on load. In our case, it's the dummy item "Other X (Please specify)". This is to let the user know that she can specify another school if she doesn't see hers in the select. More about that when we talk about entering new items into the DB.

"min_students" => 1 This option is actually fairly tricky. It's there to prevent massive selectboxes full of options which are unlikely to be relevant to the user. As we mentioned in the section on the DB schema, users get to populate the database themselves. In some cases they may put in bogus or very self-specific data. "min_students" specifies the minimum number of students who must have the item in their profile for it to be included in the selectbox. In this case, it's 1, so as long as a school appears in at least one student's profile, then the school will be displayed in the selectbox. This is just yet another hack, so if you don't get it now, don't worry.

  • Section B.1: optional v. required, and form validation in general

    By default, all form elements are required, unless the "optional" => true option is set. For some for types, you can also set a "min_length", a minimum length requirement. If a "valid_e" is specified, then that text will be displayed if the submitted data somehow fails validation. If a "length_e" is specified, then that text will be displayed if the data is too short. Optional elements only go through validation if there is something entered. So you may see a valid_e specified even for an optional form element.

    OK, so you're probably wondering how validation actually works. We don't do JavaScript validation, which you may find on other sites, because JavaScript is generally a headache, and it doesn't have the flexibility we need. Validation is based on the type of the form element. There are many different special types that have their own validation. For instance, you can have a form element like so

    $my_form->add_element( array( "type" => "phone",
                                  etc... ) );
    What this will do is generate a form that looks like any old text input box, but when it gets submitted, the validation code, part of the form object, will check to see if it is a valid phone number in the form of (nnn) nnn-nnnn. Other fancy form types like this include: "email"--will make sure it's a valid email address; "url"--will make sure it's a valid URL; "zip"--zip code, and so on. validate() is a member function of the form object, and we run it before the form is displayed; that way, all the elements that don't pass validation get flagged with a red valid_e error message.

  • Section B.2: a full explanation of the doSelect widget and adding new items to the "editable-shared" tables

    The doSelect widget is the beast of the student side. It isn't pretty, but when it works, it's pretty cool. It consists of three components, which are generated in the class of_doSelect, found in php/of_select.inc, around line 252. The doQuickText() function and the doSearchButton() function are responsible for the text input box and the "Search our Database" button respectively. So, when you type in the text box, it automatically searches the select for the item--that way, if it is a lengthy selectbox, as is common, you need not hunt through all the hundreds of options. If the item is not found in the select, what the user is supposed to do is click "Search our Database." (If they don't, then when the page is submitted, a red error message tells them what to do. That's handled in validation.) So, when the person clicks the button, a new window pops up, and one of two things happens. If the item actually is in our database, then they will see a "frozen" form with all of our existing information in it. If the item is not in our database, then a bunch of complicated, but not really very effective code will look up in the database for possible "near" matches for whatever the person typed. It presents these in a select; otherwise, if this is legitimately a new item the person can enter it in the form. When they submit this form, we check yet one more time to make sure that the item is not already in the database. If it is, then the form is loaded up with the existing data, and that's the end of that. Otherwise, the person is asked to check over their entry, and given the opportunity to edit it again. If they confirm the submission, the item at last goes into the database, with the PostToInsert() function in insert_item.php3. There is some hairy JavaScript involved in this process because we have to make sure that the selectbox and the textbox on the original page are set correctly to make sure the right thing goes into the student's profile. Check out html/session/set_doSelect.js. Also take a look at widgets.js for some of the JS functions on the main page.

    So that's the doSelect form element in a nutshell. It's one of the more complex, obfuscated components of the student side. We have tried to make it as intuitive as possible for the user, but it is still confusing and could use some UI thinking.

  • Section B.3: other useful db-based dynamically generated forms

    Basically the only other dynamically generated form you'll see is is doSelectNoText. What that means is a doSelect with no text box or button. This is actually pretty straightforward: it takes a table, and generates a select from that table using the 'name' column as the labels and the 'xid' column for the values, where x is the name of the table. For instance, a doSelectNoText is used for the degree select on the Education page. The table is 'degree'--the doSelectNoText widget is used primarily for locked tables.

    Another one to know about, but which is used primarily as an intermediary object, and rarely in the form itself, is SelectFromQuery. What this does is generate the select form element using an SQL query. In that case, you need AS LABEL and AS VALUE clauses in the query to tell it what you want to be the labels and values for the select. Predictably, doSelect and doSelectNoText rely on SelectFromQuery.

Section C: UpdateOrInsert

UpdateOrInsert() is a function which is so key, it's defined in its own include file, /html/include/UpdateOrInsert.inc. This function is responsible for inserting posted form data into a student's profile. The reason it's called UpdateOrInsert is that for a given profile component, let's say a school or an activity or what have you, the user could be either editing the attributes of an existing record, or else inserting the item for the first time. Clearly, this is a very important function, and there is a lot of hacking--ahem, fine tuning in this function. Basically the way it works is by going through the POST data from the posted form, and, because the variables are all named carefully, based on what table they belong in, the function knows where to insert what. Namely, all the form elements (and therefore variables, that's one of the features of PHP) are named with the table, then an underscore, then the column of that table into which the item will be inserted. For example, on the Experience page, the names of the form elements are student_company_companyid, student_company_start, student_company_end, etc. The UpdateOrInsert function is passed the name of the table, eg, student_company; it then goes to the database to get the names of all the columns in that table, and then goes through the POST data looking for variables in the form described--table name, underscore, column name. Finally, once it does the UPDATE or the INSERT query on the table, it does a SELECT query to select out the exact item it just UPDATEd or INSERTed, and then returns the id of that item, eg, the student_companyid field from that record in the student_company table.

Section D: the Navigation Bars
[Coming soon]

hits since May 16, 2001.