Files
infopump/itemrecord.php

612 lines
26 KiB
PHP

<?php
// Enable error reporting for development (remove in production)
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Include settings
include_once "settings.php";
/**
* Safely escape output for HTML
*/
function esc_html($text) {
return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
}
/**
* Safely escape output for HTML attributes
*/
function esc_attr($text) {
return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
}
/**
* Generate Chicago citation
*/
function generate_chicago_citation($type, $author_sort, $title, $location, $publisher, $pubdate) {
if ($type == 'Text') {
return "{$author_sort}. <em>{$title}.</em> {$location}: {$publisher}, {$pubdate}.";
} elseif ($type == 'Music') {
return "{$author_sort}, <em>{$title}.</em> {$publisher}, {$pubdate}.";
} else {
return "{$author_sort}, director. <em>{$title}.</em> {$publisher}, {$pubdate}.";
}
}
/**
* Generate APA citation
*/
function generate_apa_citation($type, $author_sort, $title, $publisher, $pubdate, $creator) {
$name_parts = explode(', ', $author_sort);
$last_name = $name_parts[0] ?? '';
$first_initial = isset($name_parts[1]) ? mb_substr($name_parts[1], 0, 1) : '';
if ($type == 'Text') {
return "{$last_name}, {$first_initial}. ({$pubdate}). {$title}. {$publisher}.";
} elseif ($type == 'Music') {
return "{$creator}. ({$pubdate}). <em>{$title}</em> [Album]. {$publisher}.";
} else {
return "{$last_name}, {$first_initial}. (Director). ({$pubdate}). <em>{$title}</em> [Film]. {$publisher}.";
}
}
/**
* Generate MLA citation
*/
function generate_mla_citation($type, $author_sort, $title, $publisher, $pubdate) {
if ($type == 'Text' || $type == 'Music') {
return "{$author_sort}. <em>{$title}.</em> {$publisher}, {$pubdate}.";
} else {
return "{$author_sort}, director. <em>{$title}.</em> {$publisher}, {$pubdate}.";
}
}
// Initialize variables
$ItemID = null;
$db = null;
$error_message = null;
// Data arrays to store results
$book_data = [];
$identifiers = [];
$languages = [];
$tags = [];
$similar_items = [];
try {
// Validate and sanitize ItemID - CRITICAL for SQL injection prevention
if (!isset($_GET["itemid"]) || !is_numeric($_GET["itemid"])) {
throw new Exception('Invalid item ID');
}
$ItemID = (int)$_GET["itemid"];
if ($ItemID <= 0) {
throw new Exception('Invalid item ID');
}
// Check if database exists
if (!file_exists('metadata.sqlite')) {
throw new Exception('Database file not found');
}
// Establish database connection
$db = new SQLite3('metadata.sqlite');
if (!$db) {
throw new Exception('Unable to open database');
}
$db->busyTimeout(5000);
// Pull data from books table - Using parameterized query for safety
$stmt = $db->prepare("
SELECT
id,
title,
date(timestamp) as created,
author_sort,
strftime('%Y',pubdate) AS pubyear,
date(last_modified) as modified
FROM books
WHERE id = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data = $row;
} else {
throw new Exception('Item not found');
}
// Pull author data
$stmt = $db->prepare("
SELECT name
FROM authors
INNER JOIN books_authors_link ON books_authors_link.author = authors.id
WHERE books_authors_link.book = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['creator'] = $row['name'];
}
// Pull summary/comments
$stmt = $db->prepare("SELECT text FROM comments WHERE book = :itemid");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['summary'] = $row['text'];
}
// Pull publisher
$stmt = $db->prepare("
SELECT name
FROM publishers
INNER JOIN books_publishers_link ON books_publishers_link.publisher = publishers.id
WHERE books_publishers_link.book = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['publisher'] = $row['name'];
}
// Pull tags (excluding infopump internal tags)
$stmt = $db->prepare("
SELECT name
FROM tags
INNER JOIN books_tags_link ON books_tags_link.tag = tags.id
WHERE tags.name NOT LIKE 'infopump%'
AND books_tags_link.book = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$tags[] = $row['name'];
}
// Pull identifiers
$stmt = $db->prepare("SELECT type, val FROM identifiers WHERE book = :itemid");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$identifiers[] = $row;
}
// Pull languages
$stmt = $db->prepare("
SELECT languages.lang_code AS lang_code
FROM languages
INNER JOIN books_languages_link ON books_languages_link.lang_code = languages.id
WHERE book = :itemid
ORDER BY books_languages_link.item_order
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$languages[] = $row['lang_code'];
}
// Pull type
$stmt = $db->prepare("
SELECT custom_column_1.value AS itemtype
FROM books_custom_column_1_link
INNER JOIN custom_column_1 ON custom_column_1.id = books_custom_column_1_link.value
WHERE books_custom_column_1_link.book = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['type'] = mb_convert_case($row['itemtype'], MB_CASE_TITLE, "UTF-8");
}
// Pull series information
$stmt = $db->prepare("
SELECT series.name AS series, books.series_index AS seriesindex
FROM series
INNER JOIN books_series_link ON books_series_link.series = series.id
INNER JOIN books ON books.id = books_series_link.book
WHERE books_series_link.book = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['series'] = $row['series'];
$book_data['series_index'] = $row['seriesindex'];
}
// Pull publisher location
$stmt = $db->prepare("
SELECT custom_column_2.value AS publoc
FROM books
INNER JOIN books_custom_column_2_link ON books_custom_column_2_link.book = books.id
INNER JOIN custom_column_2 ON custom_column_2.id = books_custom_column_2_link.value
WHERE books.id = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['publisher_location'] = $row['publoc'];
}
// Pull subtype
$stmt = $db->prepare("
SELECT custom_column_3.value AS subtype
FROM books
INNER JOIN books_custom_column_3_link ON books_custom_column_3_link.book = books.id
INNER JOIN custom_column_3 ON custom_column_3.id = books_custom_column_3_link.value
WHERE books.id = :itemid
");
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$book_data['subtype'] = $row['subtype'];
}
// Get similar items based on tags
if (count($tags) >= 2) {
// Get two random tags for similarity
$random_tags = array_rand(array_flip($tags), min(2, count($tags)));
if (!is_array($random_tags)) {
$random_tags = [$random_tags];
}
$tag_placeholders = implode(',', array_fill(0, count($random_tags), '?'));
$stmt = $db->prepare("
SELECT DISTINCT books.id, title, author_sort
FROM books
INNER JOIN books_tags_link ON books_tags_link.book = books.id
INNER JOIN tags ON tags.id = books_tags_link.tag
WHERE tags.name IN ($tag_placeholders)
AND books.id != :itemid
LIMIT 4
");
foreach ($random_tags as $index => $tag) {
$stmt->bindValue($index + 1, $tag, SQLITE3_TEXT);
}
$stmt->bindValue(':itemid', $ItemID, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($result && $row = $result->fetchArray(SQLITE3_ASSOC)) {
$similar_items[] = $row;
}
}
} catch (Exception $e) {
error_log('Error in itemrecord.php: ' . $e->getMessage());
$error_message = 'Unable to load item details. Please try again later.';
}
// Helper function to get identifier URL
function get_identifier_url($type) {
$urls = [
'google' => 'https://books.google.com/books?id=',
'isbn' => 'https://www.librarything.com/isbn/',
'oclc' => 'https://worldcat.org/title/',
'tmdb' => 'https://www.themoviedb.org/movie/'
];
return $urls[$type] ?? '#';
}
// Check if image exists
$image_path = "images/{$ItemID}.jpg";
if (!file_exists($image_path)) {
$image_path = "images/placeholder.jpg";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="<?php echo esc_attr('Infopump - Item record - ' . ($book_data['title'] ?? 'Unknown')); ?>">
<meta name="keywords" content="infopump, bibliography, media, catalog">
<meta name="author" content="Daniel Messer">
<!-- Social Media Integration -->
<meta property="og:title" content="<?php echo esc_attr($SiteName . ' - Item Record'); ?>">
<meta property="og:image" content="<?php echo esc_attr($SiteURL . '/' . $image_path); ?>">
<meta property="og:url" content="<?php echo esc_attr($SiteURL . '/itemrecord.php?itemid=' . $ItemID); ?>">
<meta property="og:site_name" content="<?php echo esc_attr($SiteName); ?>">
<meta property="og:description" content="<?php echo esc_attr(($book_data['title'] ?? 'Unknown') . ' - ' . ($book_data['creator'] ?? 'Unknown')); ?>">
<meta name="twitter:title" content="<?php echo esc_attr(($book_data['title'] ?? 'Unknown') . ' - ' . ($book_data['creator'] ?? 'Unknown')); ?>">
<meta name="twitter:image" content="<?php echo esc_attr($SiteURL . '/' . $image_path); ?>">
<meta name="twitter:url" content="<?php echo esc_attr($SiteURL . '/itemrecord.php?itemid=' . $ItemID); ?>">
<meta name="twitter:card" content="summary">
<!-- Zotero and RDF Related Metadata -->
<meta name="citation_title" content="<?php echo esc_attr($book_data['title'] ?? ''); ?>">
<meta name="citation_authors" content="<?php echo esc_attr($book_data['author_sort'] ?? ''); ?>">
<meta name="citation_publisher" content="<?php echo esc_attr($book_data['publisher'] ?? ''); ?>">
<?php foreach ($identifiers as $identifier): ?>
<?php if ($identifier['type'] == 'isbn'): ?>
<meta name="citation_isbn" content="<?php echo esc_attr($identifier['val']); ?>">
<?php endif; ?>
<?php endforeach; ?>
<title><?php echo esc_html('Item Record: ' . ($book_data['title'] ?? 'Unknown')); ?></title>
<!-- Favicon-->
<link rel="icon" type="image/x-icon" href="item/assets/favicon.ico">
<!-- Bootstrap icons-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<!-- Core theme CSS (includes Bootstrap)-->
<link href="item/css/styles.css" rel="stylesheet">
</head>
<body>
<?php if ($error_message): ?>
<div class="alert alert-danger" role="alert">
<?php echo esc_html($error_message); ?>
</div>
<?php endif; ?>
<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container px-4 px-lg-5">
<a class="navbar-brand" href="index.php">
<strong><?php echo esc_html($book_data['title'] ?? 'Unknown'); ?></strong> | Item Record <?php echo (int)$ItemID; ?>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 ms-lg-4">
<li class="nav-item"><a class="nav-link active" href="index.php">Home</a></li>
</ul>
</div>
</div>
</nav>
<!-- Product section-->
<section class="py-5">
<div class="container px-4 px-lg-5 my-5">
<div class="row gx-4 gx-lg-5 align-items-top">
<div class="col-md-6">
<img class="card-img-top mb-5 mb-md-0"
src="<?php echo esc_attr($image_path); ?>"
alt="<?php echo esc_attr($book_data['title'] ?? 'Unknown'); ?>"
title="<?php echo esc_attr($book_data['title'] ?? 'Unknown'); ?>">
</div>
<div class="col-md-6">
<div class="small mb-1">Item Record <?php echo (int)$ItemID; ?></div>
<h1 class="display-5 fw-bolder"><?php echo esc_html($book_data['title'] ?? 'Unknown'); ?></h1>
<?php if (isset($book_data['series'])): ?>
<div class="medium">
<hr>
<strong>
Number <?php echo esc_html($book_data['series_index']); ?> in the
<a href="results.php?se=<?php echo urlencode($book_data['series']); ?>">
<?php echo esc_html($book_data['series']); ?>
</a> series
</strong>
<hr>
</div>
<?php else: ?>
<hr>
<?php endif; ?>
<p class="lead"><?php echo $book_data['summary'] ?? ''; ?></p>
</div>
</div>
<div class="row gx-4 gx-lg-5 align-items-top mt-5">
<!-- CITATIONS -->
<div class="col-md-6">
<a name="citations"></a>
<h3 class="fw-bolder"><u>Bibliographic Citations</u></h3><br>
<p class="small"><em>Note: You may need to slightly edit citations for final use. Citations are based upon available data. Refer to your format guides for proper bibliographic citations.</em></p>
<?php if (isset($book_data['type'])): ?>
<!-- Chicago Citation -->
<p class="metadata"><strong>Chicago</strong></p>
<?php
$chicago = generate_chicago_citation(
$book_data['type'],
$book_data['author_sort'] ?? '',
$book_data['title'] ?? '',
$book_data['publisher_location'] ?? '',
$book_data['publisher'] ?? '',
$book_data['pubyear'] ?? ''
);
?>
<p class="metadata"><?php echo $chicago; ?><br>
<div id="chicago" style="display: none;"><?php echo $chicago; ?></div>
<a href="#citations" onclick="copyToClip(document.getElementById('chicago').innerHTML)">Copy Chicago citation</a></p>
<hr>
<!-- APA Citation -->
<p class="metadata"><strong>APA</strong></p>
<?php
$apa = generate_apa_citation(
$book_data['type'],
$book_data['author_sort'] ?? '',
$book_data['title'] ?? '',
$book_data['publisher'] ?? '',
$book_data['pubyear'] ?? '',
$book_data['creator'] ?? ''
);
?>
<p class="metadata"><?php echo $apa; ?><br>
<div id="apa" style="display: none;"><?php echo $apa; ?></div>
<a href="#citations" onclick="copyToClip(document.getElementById('apa').innerHTML)">Copy APA citation</a></p>
<hr>
<!-- MLA Citation -->
<p class="metadata"><strong>MLA</strong></p>
<?php
$mla = generate_mla_citation(
$book_data['type'],
$book_data['author_sort'] ?? '',
$book_data['title'] ?? '',
$book_data['publisher'] ?? '',
$book_data['pubyear'] ?? ''
);
?>
<p class="metadata"><?php echo $mla; ?><br>
<div id="mla" style="display: none;"><?php echo $mla; ?></div>
<a href="#citations" onclick="copyToClip(document.getElementById('mla').innerHTML)">Copy MLA citation</a></p>
<?php endif; ?>
</div>
<!-- METADATA -->
<div class="col-md-6">
<h3 class="fw-bolder"><u>Metadata</u></h3>
<p class="metadata"><br>
<strong>Item Control Number:</strong> <?php echo (int)$ItemID; ?><br>
<strong>Title:</strong> <?php echo esc_html($book_data['title'] ?? 'Unknown'); ?><br>
<strong>Creator:</strong>
<a href="results.php?au=<?php echo urlencode($book_data['author_sort'] ?? ''); ?>">
<?php echo esc_html($book_data['author_sort'] ?? 'Unknown'); ?>
</a><br>
<?php if (isset($book_data['series'])): ?>
<strong>Series:</strong>
<a href="results.php?se=<?php echo urlencode($book_data['series']); ?>">
<?php echo esc_html($book_data['series']); ?>
</a><br>
<strong>Series Index:</strong> <?php echo esc_html($book_data['series_index']); ?><br>
<?php endif; ?>
<strong>Publisher:</strong> <?php echo esc_html($book_data['publisher'] ?? 'Unknown'); ?><br>
<strong>Location:</strong> <?php echo esc_html($book_data['publisher_location'] ?? 'Unknown'); ?><br>
<strong>Date:</strong> <?php echo esc_html($book_data['pubyear'] ?? 'Unknown'); ?><br>
<strong>Type:</strong>
<a href="results.php?ty=<?php echo urlencode($book_data['type'] ?? ''); ?>">
<?php echo esc_html($book_data['type'] ?? 'Unknown'); ?>
</a><br>
<?php if (isset($book_data['subtype'])): ?>
<strong>Subtype:</strong>
<a href="results.php?st=<?php echo urlencode($book_data['subtype']); ?>">
<?php echo esc_html($book_data['subtype']); ?>
</a><br>
<?php endif; ?>
<strong>Subjects:</strong>
<?php foreach ($tags as $tag): ?>
[<a href="results.php?su=<?php echo urlencode($tag); ?>"><?php echo esc_html($tag); ?></a>]
<?php endforeach; ?>
<br><strong>Identifiers:</strong><br>
<?php foreach ($identifiers as $identifier): ?>
<?php $url = get_identifier_url($identifier['type']); ?>
<span style="margin-left: 10px;">
<?php echo esc_html($identifier['type']); ?>:
<a href="<?php echo esc_attr($url . $identifier['val']); ?>" target="_blank">
<?php echo esc_html($identifier['val']); ?>
</a>
</span><br>
<?php endforeach; ?>
<strong>Language(s):</strong>
<?php foreach ($languages as $lang): ?>
[ <?php echo esc_html($lang); ?> ]
<?php endforeach; ?>
<br><strong>Created:</strong> <?php echo esc_html($book_data['created'] ?? 'Unknown'); ?><br>
<strong>Last Modified:</strong> <?php echo esc_html($book_data['modified'] ?? 'Unknown'); ?>
</p>
</div>
</div>
</div>
</section>
<!-- Related items section-->
<?php if (count($similar_items) > 0): ?>
<section class="py-5 bg-light">
<div class="container px-4 px-lg-5 mt-5">
<h2 class="fw-bolder mb-4">Similar items</h2>
<div class="row gx-4 gx-lg-5 row-cols-2 row-cols-md-3 row-cols-xl-4 justify-content-center">
<?php foreach ($similar_items as $item): ?>
<?php
$item_id = (int)$item['id'];
$item_image = "images/{$item_id}.jpg";
if (!file_exists($item_image)) {
$item_image = "images/placeholder.jpg";
}
?>
<div class="col mb-5">
<div class="card h-100">
<a href="itemrecord.php?itemid=<?php echo $item_id; ?>">
<img class="card-img-top"
src="<?php echo esc_attr($item_image); ?>"
alt="<?php echo esc_attr($item['title']); ?>">
</a>
<div class="card-body p-4">
<div class="text-center">
<h5 class="fw-bolder"><?php echo esc_html($item['title']); ?></h5>
<?php echo esc_html($item['author_sort']); ?>
</div>
</div>
<div class="card-footer p-4 pt-0 border-top-0 bg-transparent">
<div class="text-center">
<a class="btn btn-outline-dark mt-auto"
href="itemrecord.php?itemid=<?php echo $item_id; ?>">
View Item
</a>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<!-- Footer-->
<footer class="py-5 bg-dark">
<div class="container">
<p class="m-0 text-center text-white">Copyright - Creative Commons By-NC-SA - Infopump</p>
</div>
</footer>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Citation copy script -->
<script>
function copyToClip(str) {
function listener(e) {
e.clipboardData.setData("text/html", str);
e.clipboardData.setData("text/plain", str);
e.preventDefault();
}
document.addEventListener("copy", listener);
document.execCommand("copy");
document.removeEventListener("copy", listener);
}
</script>
<!-- Core theme JS-->
<script src="item/js/scripts.js"></script>
</body>
</html>
<?php
// Clean up database connection
if ($db) {
$db->close();
}
?>