<?php
/**
* @file reference.php
*
*/
// TO DO: Make sure field names are consistent between XML, XSL, web form, and MySQL
// schema.
// e.g., we have citetype and reference_type
// If not consistent we will have major problems.
// Need to handle keywords (separate table for these, like authors
// Need to store id of user who added record, and timestamp it
// Need to support notes (which could be edited by others?
require_once('config.inc.php');
require_once($config['adodb_dir']);
require_once('XML/Tree.php');
require_once('xslt.php');
/**
* @brief Reference object
*
* We store the reference internally as an array of strings, with
* authors stored as arrays nested within the larger array.
*
* The data structure is modelled on Endnote. In particular I use
* secondary authors and secondary titles to handle book chapters.
*/
class Reference {
var $elements;
var $db;
var $new_author;
function Reference ()
{
unset ($this->elements);
$this->new_author = false;
}
function GetElement ($key)
{
return $this->elements[$key];
}
//----------------------------------------------------------------------------
/**
* @brief Populate Reference object from an array of values.
*
* This is a convenient way to initialise a Reference object from,
* for example, a web form. By passing the global array <pre>_POST</pre> to
* this constructor we can extract the form variables corresponding
* to each element of a reference.
*
* @param data The array of reference elements
*/
function Populate ($data)
{
foreach ($data as $key => $value)
{
if (($value != '') && ($value != 'NULL'))
{
switch ($key)
{
// Ignore items specific to web form
case "operation":
case "Submit":
break;
// Authors and secondary authors are stored
// as arrays.
case "authors":
case "secondary_authors":
$list = explode ("\n", trim(stripslashes($value)));
$this->elements[$key] = $list;
break;
default:
$this->elements[$key] = stripslashes($value);
break;
}
}
}
}
//----------------------------------------------------------------------------
/**
* @brief Return Empty Reference object in XML format.
*
*/
function EmptyXML ()
{
$tree = new XML_Tree();
$root = & $tree->addRoot("references");
$reference = & $root->addChild(
"reference",
NULL,
array(
"type" => "article"
)
);
$authors = & $reference->addChild("authors");
$xml = $tree->get();
$xml = str_replace (
'<?xml version="1.0"?>',
'<?xml version="1.0" encoding="iso-8859-1"?>',
$xml);
return $xml;
}
//----------------------------------------------------------------------------
/**
* @brief Return Reference object in XML format.
*
*/
function ToXML ()
{
$tree = new XML_Tree();
$root = & $tree->addRoot("references");
$reference = & $root->addChild(
"reference",
NULL,
array(
"id" => $this->elements['reference_id'],
"type" => $this->elements['reference_type']
)
);
// Authors
$authors = & $reference->addChild("authors");
foreach ($this->elements['authors'] as $key => $value)
{
if ($value != '')
{
$tmp = explode(",", $value);
$author = & $authors->addChild("author");
$author->addChild("firstname", $tmp[1]);
$author->addChild("lastname", $tmp[0]);
}
}
$reference->addChild("title", $this->elements['title']);
$reference->addChild("year", $this->elements['year']);
// Abstract
if (array_key_exists('abstract', $this->elements))
{
$reference->addChild("abstract", $this->elements['abstract']);
}
//---------------article----------------------------
if ($this->elements['reference_type'] == "article")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "journal":
case "volume":
case "issue":
case "pages":
case "doi":
case "pmid":
case "url":
case "google_scholar":
$reference->addChild($key, $value);
break;
default:
break;
}
}
}
}
//---------------book-------------------------------
if ($this->elements['reference_type'] == "book")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "isbn":
$reference->addChild($key, $value);
break;
default:
break;
}
}
}
}
//---------------book chapter-----------------------
if ($this->elements['reference_type'] == "inbook")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "secondary_title":
$reference->addChild($key, $value);
break;
default:
break;
}
}
}
// Secondary authors
if (array_key_exists('secondary_authors', $this->elements))
{
$authors = & $reference->addChild("secondary_authors");
foreach ($this->elements['secondary_authors'] as $key => $value)
{
if ($value != '')
{
$tmp = explode(",", $value);
$author = & $authors->addChild("author");
$author->addChild("firstname", $tmp[1]);
$author->addChild("lastname", $tmp[0]);
}
}
}
}
// Notes
global $config;
global $ADODB_FETCH_MODE;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
// Ensure fields are (only) indexed by column name
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
// Add list of notes users may have added
$sql = "SELECT member.name, member.email, UNIX_TIMESTAMP(notes.time_stamp) as time, notes.note_text ";
$sql .= "FROM notes INNER JOIN reference ON notes.reference_id = reference.reference_id ";
$sql .= "INNER JOIN member ON notes.user_id = member.id ";
$sql .= "WHERE notes.reference_id = " . $this->elements['reference_id'] . " ";
$sql .= "ORDER BY notes.time_stamp";
$result = $this->db->Execute($sql);
if ($result === false) die("failed");
$notes = & $reference->addChild("notes");
while (!$result->EOF)
{
$values = array();
$values = $result->fields;
$note = & $notes->addChild("note");
$note->addChild("creator", $values['name']);
$note->addChild("email", $values['email']);
$note->addChild("date", date("F j, Y, g:i a", $values['time']));
// $note->addChild("date", $result->UnixDate ($values['time_stamp']));
// $note->addChild("date", $values['time_stamp']);
$note->addChild("text", stripslashes($values['note_text']));
$result->MoveNext();
}
$xml = $tree->get();
$xml = str_replace (
'<?xml version="1.0"?>',
'<?xml version="1.0" encoding="iso-8859-1"?>',
$xml);
// Debug XML
//echo $xml;
return $xml;
}
//----------------------------------------------------------------------------
/**
* @brief Display Reference object contents for debugging.
*
*/
function Dump ()
{
echo "<h2>elements</h2>";
echo "<pre>";
print_r ($this->elements);
echo "</pre>";
}
//----------------------------------------------------------------------------
/**
* @brief Retrieve Reference object from SQL database.
*
*/
function Retrieve ($ref_id)
{
global $config;
global $ADODB_FETCH_MODE;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
// Ensure fields are (only) indexed by column name
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
// Get reference and then output in desired format
$sql = "SELECT * FROM reference WHERE (reference.reference_id=$ref_id)";
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
$numrows = $result->RecordCount();
while (!$result->EOF)
{
$values = array();
$values = $result->fields;
$values['authors'] = $this->RetrieveAuthors ($ref_id);
if ($values['reference_type'] == "inbook")
{
$values['secondary_authors'] = $this->RetrieveAuthors ($ref_id, false);
}
$this->Populate ($values);
// Debug
//echo "<p>result->fields<br/><pre>" , print_r ($result->fields) , "</pre></p>";
$result->MoveNext();
}
}
//----------------------------------------------------------------------------
/**
* @brief Retrieve authors from SQL database.
*
* Used internally by Retrieve method.
*
* @param ref_id Unique id of reference
* @param primary True if we want authors of the reference, false if we want
* secondary authors (e.g., book editor).
*
* @return List of authors in form last_name,first_name\n
*/
function RetrieveAuthors ($ref_id, $primary=true)
{
$authors = "";
// Get authors for this reference
$author_sql = "SELECT first_name, last_name ";
$author_sql .= "FROM author ";
if ($primary)
{
$author_sql .= " INNER JOIN author_reference_joiner ";
$author_sql .= " ON author.author_id = author_reference_joiner.author_id ";
$author_sql .= "WHERE (reference_id LIKE \"$ref_id\") ";
$author_sql .= "ORDER BY author_order";
}
else
{
$author_sql .= " INNER JOIN secondary_author_reference_joiner ";
$author_sql .= " ON author.author_id = secondary_author_reference_joiner.author_id ";
$author_sql .= "WHERE (reference_id LIKE \"$ref_id\") ";
$author_sql .= "ORDER BY secondary_author_reference_joiner.author_order";
}
$author_result = $this->db->Execute($author_sql);
if ($author_result === false) die("failed");
while (!$author_result->EOF)
{
$authors .= $author_result->fields['last_name']
. "," . $author_result->fields['first_name'] . "\n";
$author_result->MoveNext();
}
return $authors;
}
//----------------------------------------------------------------------------
/**
* @brief Get database ids for authors.
*
* Looks up each author in the SQL database. If the author is not found it is
* is added to the database and the new_author field set to true. Returns an
* array with author ids. This is useful for populating the author_reference
* table.
*
* @param primary True if we want authors of the reference, false if we want
* secondary authors (e.g., book editor).
*
* @return List of author ids
*/
function GetAuthorIDs ($primary=true)
{
$id_list = array();
$field = ($primary ? 'authors' : 'secondary_authors');
foreach ($this->elements[$field] as $key => $value)
{
// split name into last name and first name using comma as separator
$parts = explode(",", $value);
$last_name = trim($parts[0]);
$first_name = trim($parts[1]);
if (strlen($last_name) != 0)
{
// Do we already have this author in our database?
$find_sql = "SELECT author_id ";
$find_sql .= " FROM author ";
$find_sql .= "WHERE (last_name LIKE \"$last_name\") AND (first_name LIKE \"$first_name\");";
$result = $this->db->Execute($find_sql);
if ($result === false) die("failed");
$num = $result->RecordCount();
if ($num == 0)
{
// This author not found, so add author to the database
$insert_sql = "INSERT INTO author (last_name, first_name) ";
$insert_sql .= " VALUES (\"$last_name\", \"$first_name\");";
$result = $this->db->Execute($insert_sql);
if ($result == false) die("failed");
array_push ($id_list, $this->db->Insert_ID());
// Set this flag so that we know this author is new
$this->new_author = true;
}
else
{
array_push ($id_list, $result->fields['author_id']);
}
}
}
// Debug
//echo "<h2>id_list</h2><pre>";
//print_r($id_list);
//echo "</pre>";
return $id_list;
}
//----------------------------------------------------------------------------
/**
* @brief Test whether reference is already in SQL database
*
* If reference is found we ensure the <pre>reference_id</pre> field
* is set.
*
* @return Database id if reference exists, 0 otherwise
*/
function InDatabase ()
{
global $config;
global $ADODB_FETCH_MODE;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
// Ensure fields are (only) indexed by column name
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$sql = "SELECT reference_id ";
$sql .= "FROM reference ";
$sql .= "WHERE (year LIKE \"" . $this->elements['year'] . "\") ";
$sql .= "AND (title LIKE " . $this->db->qstr($this->elements['title']) . ") ";
$sql .= "AND (reference_type LIKE \"" . $this->elements['reference_type'] . "\") ";
$sql .= "AND (pages LIKE \"" . $this->elements['pages'] . "\");";
$result = $this->db->Execute($sql);
if ($result == false) die("failed: " . $sql);
$found = 0;
if ($result->RecordCount() > 0)
{
$this->elements['reference_id'] = $result->fields['reference_id'];
$found = $this->elements['reference_id'];
}
$this->db->Close();
return $found;
}
//----------------------------------------------------------------------------
/**
* @brief Delete this reference from SQL database
*
* Just the reference is deleted (the authors remain in the database)
*
*/
function Delete ()
{
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
$sql = "DELETE FROM reference WHERE reference_id = " . $this->elements['reference_id'];
$result = $this->db->Execute($sql);
if ($result === false) die("failed");
$this->db->Close();
}
//----------------------------------------------------------------------------
/**
* @brief Store this reference in the SQL database
*
* Note that we need to store the reference, then populate the author joiner
* tables.
*
* @return The id of the new reference, or 0 if it failed.
*
*/
function Store ()
{
$new_id =0;
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
$insert ="(";
$values ="(";
$insert .= "time_stamp";
$values .= $this->db->DBTimeStamp(time());
$insert .= ",year";
$values .= "," . $this->db->qstr($this->elements['year']);
$insert .= ",reference_type";
$values .= "," . $this->db->qstr($this->elements['reference_type']);
$insert .= ",title";
$values .= "," . $this->db->qstr($this->elements['title']);
if (array_key_exists('abstract', $this->elements))
{
$insert .= ",abstract";
$values .= "," . $this->db->qstr($this->elements['abstract']);
}
//---------------article----------------------------
if ($this->elements['reference_type'] == "article")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "journal":
case "volume":
case "issue":
case "pages":
case "doi":
case "pmid":
case "url":
$insert .= ",$key";
$values .= "," . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
//---------------book-------------------------------
if ($this->elements['reference_type'] == "book")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "isbn":
$insert .= ",$key";
$values .= "," . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
//---------------book chapter-----------------------
if ($this->elements['reference_type'] == "inbook")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "secondary_title":
$insert .= ",$key";
$values .= "," . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
$insert .=")";
$values .=")";
$sql = "INSERT INTO reference $insert VALUES $values;";
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
// We now have an id for this reference
$this->elements['reference_id'] = $this->db->Insert_ID();
// Link to authors
$this->AuthorJoin();
if ($this->elements['reference_type'] == "inbook")
{
$this->AuthorJoin(false);
}
$this->db->Close();
return $this->elements['reference_id'];
}
//----------------------------------------------------------------------------
/**
* @brief Join author(s) to reference in the SQL database
*
*/
function AuthorJoin ()
{
// Authors
$id_list = $this->GetAuthorIDs (true);
foreach ($id_list as $key => $value)
{
$join_sql = "INSERT INTO author_reference_joiner ";
$join_sql .= "(author_id, reference_id, author_order) ";
$join_sql .= "VALUES ($value," . $this->elements['reference_id'] . "," . ($key + 1) . ")";
$result = $this->db->Execute($join_sql);
if ($result === false) die("failed");
}
// Secondary authors
if ($this->elements['reference_type'] == "inbook")
{
$id_list = $this->GetAuthorIDs (false);
foreach ($id_list as $key => $value)
{
$join_sql = "INSERT INTO secondary_author_reference_joiner ";
$join_sql .= "(author_id, reference_id, author_order) ";
$join_sql .= "VALUES ($value," . $this->elements['reference_id'] . "," . ($key + 1) . ")";
$result = $this->db->Execute($join_sql);
if ($result === false) die("failed");
}
}
}
//----------------------------------------------------------------------------
/**
* @brief Remove links between author(s) and this reference in the SQL database
*
* This function is used prior to updating a reference in the database
*/
function AuthorUnJoin ()
{
$join_sql = "DELETE ";
$join_sql .= "FROM author_reference_joiner ";
$join_sql .= "WHERE (reference_id = " . $this->elements['reference_id'] . ")";
$result = $this->db->Execute($join_sql);
if ($result === false) die("failed");
$join_sql = "DELETE ";
$join_sql .= "FROM secondary_author_reference_joiner ";
$join_sql .= "WHERE (reference_id = " . $this->elements['reference_id'] . ")";
$result = $this->db->Execute($join_sql);
if ($result === false) die("failed");
}
//----------------------------------------------------------------------------
/**
* @brief Update this reference in the SQL database
*
* An elegant way to update would be to check what fields had changed
* and simply update those. However, in practice it is simpler to update
* everything (a lot could change, especially if the reference type is
* changed).
*
* Eventually, some sort of version control would be useful.
*/
function Update ()
{
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
// Remove links to authors, and the user may have edited the authors
$this->AuthorUnJoin();
$update = "time_stamp = " . $this->db->DBTimeStamp(time());
$update .= ",year = " . $this->db->qstr($this->elements['year']);
$update .= ",title = " . $this->db->qstr($this->elements['title']);
if (array_key_exists('abstract', $this->elements))
{
$update .= ",abstract = " . $this->db->qstr($this->elements['abstract']);
}
//---------------article----------------------------
if ($this->elements['reference_type'] == "article")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "journal":
case "volume":
case "issue":
case "pages":
case "doi":
case "pmid":
case "url":
$update .= ",$key = " . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
//---------------book-------------------------------
if ($this->elements['reference_type'] == "book")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "isbn":
$update .= ",$key = " . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
//---------------book chapter-----------------------
if ($this->elements['reference_type'] == "inbook")
{
foreach ($this->elements as $key => $value)
{
if ($value != '')
{
switch ($key)
{
case "publisher":
case "pubaddress":
case "pages":
case "url":
case "secondary_title":
$update .= ",$key = " . $this->db->qstr($value);
break;
default:
break;
}
}
}
}
$sql = "UPDATE reference ";
$sql .= " SET " . $update;
$sql .= " WHERE (reference_id = " . $this->elements['reference_id'] . ")";
//echo $sql;
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
// Link to authors
$this->AuthorJoin();
if ($this->elements['reference_type'] == "inbook")
{
$this->AuthorJoin(false);
}
$this->db->Close();
}
function GetHeadline ($ref_id)
{
$this->Retrieve ($ref_id);
$rowstr = "";
if (isset($this->elements['title']))
{
$author = $this->elements['authors'][0];
$tmp = explode(",", $author);
$author = substr ($tmp[0], 0, 10);
$len = strlen ($author);
while ($len < 10)
{
$author .= " "; // we pad it with so that the Web browser renders the string correctly
$len++;
}
$rowstr .= $author;
$rowstr .= " ";
$year = $this->elements['year'];
$len = strlen ($year);
while ($len < 4)
{
$year .= " "; // we pad it with so that the Web browser renders the string correctly
$len++;
}
$rowstr .= $year;
$rowstr .= " " . substr ($this->elements['title'], 0, 80);
}
return $rowstr;
}
function GetFormattedReference ($id, $format = 'html')
{
$this->Retrieve ($id);
$xml = $this->ToXML();
//echo $xml;
// Display format requested by user
$xslt_file = 'phpbib/xsl/';
switch ($format)
{
case 'simple':
$xslt_file .= "simple.xslt";
break;
case 'html':
$xslt_file .= "html.xslt";
break;
case 'report':
$xslt_file .= "report_html.xslt";
break;
case 'refer':
$xslt_file .= "refer.xslt";
break;
case 'ris':
$xslt_file .= "ris.xslt";
break;
case 'bibtex':
$xslt_file .= "bibtex.xslt";
break;
case 'prism':
$xslt_file .= "prism.xslt";
break;
}
$xpresult = XSLT_Buffer ($xml, $xslt_file);
return $xpresult;
}
function AddNote ($ref_id, $user_id, $note_text)
{
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
$insert ="(";
$values ="(";
$insert .= "reference_id";
$values .= $ref_id;
$insert .= ",time_stamp";
$values .= "," . $this->db->DBTimeStamp(time());
$insert .= ",user_id";
$values .= "," . $user_id;
$insert .= ",note_text";
$values .= "," . $this->db->qstr($note_text);
$insert .=")";
$values .=")";
$sql = "INSERT INTO notes $insert VALUES $values;";
// echo $sql;
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
$this->db->Close();
}
//----------------------------------------------------------------------------
/**
* @brief Create a note from the current URL
*
* This function is used when a PDF (or similar) file is uploaded. The link
* to that file is stored in the url field in the database, so we store the
* old url in the notes field so we don't loose it entirely.
*
*/
function MoveURLToNotes ($ref_id, $user_id)
{
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
// Is there a URL for this reference?
$sql = "SELECT url FROM reference WHERE (reference_id = \"" . $ref_id . "\") AND (url IS NOT NULL)";
//echo $sql;
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
// Get array
while (!$result->EOF)
{
$this->AddNote ($ref_id, $user_id, "Uploaded a new file and replaced this old url: " . $result->fields['url']);
$result->MoveNext();
}
$this->db->Close();
}
function SetURL ($ref_id, $url)
{
global $config;
$this->db = NewADOConnection('mysql');
$this->db->Connect("localhost",
$config['db_user'], $config['db_passwd'], $config['db_name']);
$sql = "UPDATE reference ";
$sql .= " SET url = \"" . $url . "\"";
$sql .= " WHERE (reference_id = " . $ref_id . ")";
//echo $sql;
$result = $this->db->Execute($sql);
if ($result == false) die("failed");
$this->db->Close();
}
}
?>