<<

. 67
( 132 .)



>>


require(˜authenticate.php™);

Because we have placed the administrative code in a subdirectory of the main con-
tent directory, this one statement will include either the basic authenticate.php file
(for normal users) or the content/admin/authenticate.php file (for administrators).
Here are the contents of the basic authenticate.php file.

<?php



check_session();
if (!isset($_SESSION))
{
global $_SESSION;
}

$realm = ˜Netsloth Content Management™;
$message = ˜You must enter a valid name & password to access this
function™;

$submit = array_key_value($_REQUEST, ˜submit™, NULL);
if ($submit == ˜logout™)
{

logout();
}

$username = session_auth(array(
˜realm™ => $realm
, ˜message™ => $message
, ˜validate_function™ => ˜connect_validate_login™
));
$result = db_connect()->getRow(
˜select u.*, if(a.username is null, 0, 1) as is_admin
from users u left join admin a on u.username = a.username
where u.username = ? ˜
, array($username)
, DB_FETCHMODE_ASSOC
);
foreach ($result as $k => $v)
{
Chapter 11: Content-Management System 389

$_SESSION[$k] = $v;
}
extract($result);
?>

The logout() function is one of our standard functions to handle removing a
logged-in user. When using HTTP authentication, this can be somewhat tricky.
Otherwise, we can just unset the PHP session values $_SESSION[[˜PHP_AUTH_USER™]
and $_SESSION[˜PHP_AUTH_PW™].

content/admin/user.php
This page, like many you have seen before, has many purposes. The exact portion
of the script that will run will depend on the variables sent to the page. It can do the
following:

— Enable an administrator to create new users

— Display the information specific to a single user_id, including the stages
associated with that user
— Grant additional stages to an existing user

— Revoke the rights to a stage from a user

If the page is accessed without any variable information in the querystring or
from POST, the form elements for user information will be blank. This information
must be filled in before the form is submitted. When the form is submitted the
admin_user.php page will be called again, this time holding the entered form data
and with the $submit variable equal to Save Changes.
When submitted, the condition in the if statement at the top of the page will
test true:

if ($submit == “Save Changes”)

The page will then call the write_user() function, defined in
content/admin/functions/write_user.php, to update or create the user™s record in the
database. If the user™s information must be updated, the form passes a user_id
from a hidden form element; otherwise the $user_id variable will be empty. The
result of this statement decides whether the script is to perform an update or
insert query.
The PEAR DB class provides a simulation of a “sequence” in MySQL. This works
like an auto_increment key in a table ” in fact, that™s exactly what it is ” but
rather than doing the insert and then discovering what the new key value is, we
first get the new key value and then use it in the insert query.
390 Part IV: Not So Simple Applications


A caution about the DB::nextId() method: if the table containing the ID
values doesn™t exist when this method is called, the method will try to create
it. Since it™s common for a Web application™s user account to not have the
privilege to create tables, this is likely to result in a runtime error.You should
be sure to have created these tables ahead of time. The table names are
sequencename_seq, where sequencename is the name you pass in to
DB::nextId().



if (empty($user_id))
{
// if we don™t have an ID value, there is no record
// for this user - create one
$user_id = db_connect()->nextId(˜user™);
$query = ˜insert into users
(username, password, name, email, user_id)

values (?, password(?), ?, ?, ?) ˜
;
}
else
{
// if we have an ID value, a record for this user
// currently exists in the users table - update it
$query = ˜update users set username=?
, password=password(?), name=? , email=?
where user_id = ? ˜
;
}
$stmt = db_connect()->prepare($query);
$bind = array($username,$password,$name,$email,$user_id);
if (!db_connect()->execute($stmt,$bind))
{
$private_error = db_connect()->last_query;
user_error(˜could not update user record™, E_USER_WARNING);
return FALSE;
}

Note that when this section of the script is completed, the user_id is known:
Either it was passed from the form or it was created by the call to DB::nextId().
Next comes a series of function calls that set up normal permissions on the com-
mon tables of the application, such as the stories and authors tables, and the specific
permissions on the workflow stage tables that correspond to the stages we chose to
give this user access to.
Chapter 11: Content-Management System 391

if (empty($user_id))
{
// if we don™t have an ID value, there is no record
// for this user - create one
$user_id = db_connect()->nextId(˜user™);
$query = ˜insert into users
(username, password, name, email, user_id)
values
(?,password(?),?,?,?) ˜
;
}
else
{
// if we have an ID value, a record for this user
// currently exists in the users table - update it
$query = ˜update users set username=?
, password=password(?), name=?
, email=? where user_id = ? ˜
;
}
$stmt = db_connect()->prepare($query);
$bind = array($username,$password,$name,$email,$user_id);
if (!db_connect()->execute($stmt,$bind))
{
$private_error = db_connect()->last_query;
user_error(˜could not update user record™, E_USER_WARNING);
return FALSE;
}

Returning to the user.php file, the code next prints out the appropriate user
information (if existing user information exists) and the stages as a series of check-
boxes. The checkboxes are checked if the user has rights for that stage.
The following query is intended to work with the checkbox_field() function
created earlier. That function takes three arguments (form name, value, and match
value). If the value and matchvalue arguments match, the checkbox will be
checked.

$query = ˜select distinct m.stage_id as matchvalue
, s.stage_id, s.stage, s.stage_dsc
from stages s
left join users u on u.user_id = ?
left join user_stage_map m on s.stage_id = m.stage_id
and m.user_id = u.user_id
˜;
392 Part IV: Not So Simple Applications

This query gathers all the stages and does an outer join on the users table. If the
user has been granted access to a stage, that stage name appears in the returned
record set, in the matchvalue field. If not, a hyphen appears in the field. When the
checkbox_field() function is run later in the loop, the third argument will either
be a hyphen or have the same value as the stage field. The results of this query
might look like this:

+------------+----------+------------+-----------------+
| matchvalue | stage_id | stage | stage_dsc |
+------------+----------+------------+-----------------+
| NULL | 1 | Writing | the words |
| NULL | 2 | Editing | fixing mistakes |
| 3| 3 | Publishing | making HTML |
| 4| 4 | Live | On the web |
| 5| 5 | Killed | dead |
+------------+----------+------------+-----------------+

This knowledge should enable you to read the rest of this script. And, of course,
further comments are included with the application on the CD-ROM.

content/story.php
At 340 lines or so, this script is long, but it isn™t especially complicated. Given the
data structure we discussed earlier, it needs to create new stories and update existing
stories after they have been through an editorial pass. Along the way the script will
need to check if the user has the rights to do the work on the story, and clean up text
that users put into the forms.
The file should be made readable by the comments within the page, which are
supplied on the accompanying CD-ROM. You must make quite a few decisions in
order to get this page to work correctly, and that adds to the length. But decisions to
be made within the file are pretty straightforward. Additionally, the page contains
quite a few insert and update statements. If you refer to Figure 11-8 while you™re
reading through the code, it shouldn™t be too tough to get through.
This chapter has spent a fair amount of space discussing how to assign rights to
a user using MySQL™s grant statements. Hopefully at this point you see how those
rights are assigned. The short piece of script that follows tests whether the current
user has the rights to work on a story, based on the rights in the grants tables. It
first gets the stage_name, based on a stage_id, and then creates the string of the
table name by appending _table to the stage name. Then a select statement runs
that includes the table name we have just created. If that query is not allowed, the
query will fail and return false. Within the query we are also involving the
user_stage_map table. That table provides our primary security, and the user must
have rights for the current stage in the user_stage_map table. If the user does not
have rights defined in that table, the query will return no rows. If the query fails or
returns nothing, an error will print and the script will exit.
Chapter 11: Content-Management System 393

// if we have an ID value, this is an existing story -
// get information about it from the database

$result = NULL;
if (empty($modify_dt))
{
// if no timestamp value is passed in, get the
// current version of the story from the stories
// table.

$query = <<<EOQ
select m.user_id as is_ok
, s.*
, date_format(s.publish_dt, ˜%Y™) as publish_yr
, date_format(s.publish_dt, ˜%m™) as publish_mn
, date_format(s.publish_dt, ˜%d™) as publish_dy
from stories s
left join user_stage_map m on s.stage_id = m.stage_id
and m.user_id = ?
where s.story_id = ?
EOQ;
$bind = array($user_id,$story_id);
}
else
{
// if a timestamp is passed in, get the version
// of the story it identifies from the story_versions
// table.
$query = <<<EOQ
select m.user_id as is_ok
, s.*
, date_format(s.publish_dt, ˜%Y™) as publish_yr
, date_format(s.publish_dt, ˜%m™) as publish_mn
, date_format(s.publish_dt, ˜%d™) as publish_dy
from story_versions s
left join user_stage_map m on s.stage_id = m.stage_id
and m.user_id = ?
where s.story_id = ? and s.modify_dt = ?
EOQ;
$bind = array($user_id,$story_id,$modify_dt);

<<

. 67
( 132 .)



>>