<<

. 66
( 132 .)



>>

E_USER_WARNING);
return FALSE;
}
return TRUE;
}

write_story()
The write_story() function creates or updates a record in the stories table in the
database. It also moves a story from one stage to another. Because a user may
attempt to modify a story that is in a stage to which the user does not have access,
or send a story forward or backward in the workflow to a restricted stage, we may
end up getting MySQL permission errors from a query. We don™t want the applica-
tion to simply roll back the transaction and stop when this happens, so we use the
DB class pushErrorHandling() and popErrorHandling() methods (actually,
these are methods inherited from the general PEAR Error class) to temporarily
change the way database errors are handled.

<?php
function start_dbhandler()
{
db_connect()->pushErrorHandling(PEAR_ERROR_TRIGGER,
E_USER_NOTICE);
}
function end_dbhandler($result=TRUE)
{
db_connect()->popErrorHandling();
return $result;
}

function write_story($args=array())
{
$stage_id = NULL;
$publish_yr = NULL;
$publish_mn = NULL;
Chapter 11: Content-Management System 383

$publish_dy = NULL;
$publish_dt = NULL;
$headline = NULL;
$subtitle = NULL;
$byline_prefix = NULL;
$summary = NULL;
$body = NULL;
$story_id = NULL;
$submit = NULL;
$author = NULL;
$author_id = NULL;

if (is_assoc($args))
{
extract($args, EXTR_IF_EXISTS);
}
else
{
$private_error = ˜write_story: error: bad arguments: ˜
. var_export($args, TRUE)
;
user_error(˜Could not update story™, E_USER_WARNING);
return FALSE;
}

start_dbhandler();

// begin transaction
db_connect()->query(˜begin™);

if (empty($story_id))
{
// if we have no ID value, this is a new story.
// get the ID value of a new record from story sequence
$story_id = db_connect()->nextId(˜story™);
$result = db_connect()->query(
˜insert into stories (story_id,headline) values (?,?)™
, array($story_id,™Not Yet Updated™)
);
if (!$result)
{
db_connect()->query(˜rollback™);
user_error(
˜Could not insert new record into stories table™
384 Part IV: Not So Simple Applications

, E_USER_ERROR
);
return end_dbhandler(FALSE);
}
}
else
{
// if we have an ID value, this is an existing story.
// get the name of its current stage table.
// (see admin/stage.php for notes about the purpose and
// use of the stage access tables.)
$oldstage_table = db_connect()->getOne(
˜select s.stage_table from stages s, stories t
where t.story_id = ? and t.stage_id = s.stage_id™
, array($story_id)
);
if (!$oldstage_table)
{
db_connect()->query(˜rollback™);
user_error(
˜Could not access current stage table for story
˜.$story_id
, E_USER_WARNING
);
return end_dbhandler(FALSE);
}

// remove the story from the old stage access table
$result = db_connect()->query(
˜delete from ! where story_id = ?™
, array($oldstage_table,$story_id)
);
if (!$result or DB::isError($result))
{
db_connect()->query(˜rollback™);
user_error(
˜Could not delete from current stage table for story
˜.$story_id
, E_USER_WARNING
);
return end_dbhandler(FALSE);
}
}

// get the assigned stage, or the first stage by default
Chapter 11: Content-Management System 385

$query = ˜select stage_id, stage, stage_table from stages
where stage_id = ?
union
select stage_id, stage, stage_table from stages
having stage_id = min(stage_id)
˜;
$row = db_connect()->getRow(
$query
, array($stage_id)
, DB_FETCHMODE_ORDERED
);
if ($row)
{
list($stage_id,$stage,$stage_table) = $row;
}
else
{
user_error(˜Unable to get current stage for this story™
, E_USER_ERROR
);
}

// create or update a record for this story in the stage access
// table for the new stage.
$result = db_connect()->query(
˜replace into ! (story_id) values (?)™
, array($stage_table,$story_id)
);
if (!$result)
{
if (preg_match(˜/denied to user/™, db_connect()-
>error_message))
{
$error = “Access denied to $stage for $username”;
}
else
{
$error = ˜Database error - could not update stage™;
}
user_error($error, E_USER_ERROR);
}

if (!empty($publish_yr) && !empty($publish_mn) &&
!empty($publish_dy))
{
386 Part IV: Not So Simple Applications

// build a publish date from the three related select
// fields in the form, if all three were set to a value.
$publish_dt = $publish_yr.™-™.$publish_mn.™-™.$publish_dy;
}
elseif ($stage == ˜Live™)
{
// if no publish date was set and the story is being
// set to the ˜Live™ stage, use a default publish date
// of now (i.e., the story will go live immediately).
$publish_dt = date(˜Y-m-d™);
}
else
{
// if no publish_dt was set and the story is not Live,
// set $publish_dt to ˜null™ for use in the query.
$publish_dt = NULL;
}

// update the story record in the database
$stmt = db_connect()->autoPrepare(
˜stories™
, array(˜stage_id™,™publish_dt™,™headline™,™subtitle™
,™byline_prefix™,™summary™,™body™
)
, DB_AUTOQUERY_UPDATE
, ˜story_id = ?™
);
db_connect()->execute(
$stmt
, array($stage_id, $publish_dt, $headline, $subtitle
, $byline_prefix, $summary, $body, $story_id
)
);

// now save a copy of the updated record in the story_versions
// table. this keeps the history of the story complete up to
// the present moment.
$query =
˜insert into story_versions
(modify_by, story_id, stage_id, publish_dt, headline
, subtitle, byline_prefix, summary, body)
select user() as modify_by, story_id, stage_id, publish_dt,
headline
, subtitle, byline_prefix, summary, body
from stories where story_id = ?™
Chapter 11: Content-Management System 387

;
db_connect()->query($query,array($story_id));

if (!empty($author_id))
{
// if an author was selected for the story, remove any
// current link between the story and an author, and
// add a link for the selected author.
db_connect()->query(
˜delete from story_author_map where story_id = ?™
, array($story_id)
);
db_connect()->query(
˜insert into story_author_map (story_id, author_id)
values (?,?)™
, array($story_id, $author_id)
);
}

// end the transaction
db_connect()->query(˜commit™);

return end_dbhandler(TRUE);
}
?>




Interesting Code Flow
Since most of the more complicated aspects of our application have to do with
maintaining users and stages, we will start the breakdown of code with the pages
that take care of these stages. Later we will move on to the other features performed
by this application.

content/authenticate.php
As we already mentioned, this application differs from the previous ones in that
each user will be logging in to the database with his or her own username and pass-
word. The script that performs this login will need to be just a touch more flexible
than the one we used in the other applications.
This application is going to use the same authentication methods seen in the
previous examples, but here the values for $PHP_AUTH_USER and $PHP_AUTH_PW
will also be the values used to log in to the database.
388 Part IV: Not So Simple Applications

The content/header.php file, which is included in every page in the content-
management system, contains the following code:

<<

. 66
( 132 .)



>>