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--
Section A: using includes for templating
Section B: General rundown of student-side pages
Section C: the form object from PHPLIB
Section D: UpdateOrInsert
- 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 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
cat_header_layout.inc sets up the colors and tables for the page. It also
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
Section B: a general sketch of the how the pages
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
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
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()
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
the full explanation for later.
"name" => "student_school_schoolid" specifies the name of the
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
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.
you're probably wondering how validation actually works. We don't do
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
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
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
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