Merge pull request 'Rework meta data display on photo page' (#45) from photo-page into master

Reviewed-on: Public/pics#45
Reviewed-by: Bart Schuurmans <bart@minnozz.com>
This commit is contained in:
Bart Schuurmans 2024-01-13 17:23:05 +01:00
commit 0da1558bd3
4 changed files with 105 additions and 121 deletions

View File

@ -8,6 +8,8 @@
class ViewPhoto extends HTMLController class ViewPhoto extends HTMLController
{ {
private Image $photo;
public function __construct() public function __construct()
{ {
// Ensure we're logged in at this point. // Ensure we're logged in at this point.
@ -19,44 +21,36 @@ class ViewPhoto extends HTMLController
if (empty($photo)) if (empty($photo))
throw new NotFoundException(); throw new NotFoundException();
$this->photo = $photo->getImage();
Session::resetSessionToken(); Session::resetSessionToken();
parent::__construct($photo->getTitle() . ' - ' . SITE_TITLE); parent::__construct($this->photo->getTitle() . ' - ' . SITE_TITLE);
if (!empty($_POST)) if (!empty($_POST))
$this->handleTagging($photo->getImage()); $this->handleTagging();
else else
$this->handleViewPhoto($photo); $this->handleViewPhoto();
} }
private function handleViewPhoto(Asset $photo) private function handleViewPhoto()
{ {
$page = new PhotoPage($photo->getImage()); $page = new PhotoPage($this->photo);
// Exif data? // Any (EXIF) meta data?
$exif = EXIF::fromFile($photo->getFullPath()); $metaData = $this->prepareMetaData();
if ($exif) $page->setMetaData($metaData);
$page->setExif($exif);
// What tag are we browsing? // What tag are we browsing?
$tag = isset($_GET['in']) ? Tag::fromId($_GET['in']) : null; $tag = isset($_GET['in']) ? Tag::fromId($_GET['in']) : null;
$id_tag = isset($tag) ? $tag->id_tag : null; if (isset($tag))
$page->setTag($tag);
// Find previous photo in set.
$previous_url = $photo->getUrlForPreviousInSet($id_tag);
if ($previous_url)
$page->setPreviousPhotoUrl($previous_url);
// ... and the next photo, too.
$next_url = $photo->getUrlForNextInSet($id_tag);
if ($next_url)
$page->setNextPhotoUrl($next_url);
$this->page->adopt($page); $this->page->adopt($page);
$this->page->setCanonicalUrl($photo->getPageUrl()); $this->page->setCanonicalUrl($this->photo->getPageUrl());
} }
private function handleTagging(Image $photo) private function handleTagging()
{ {
header('Content-Type: text/json; charset=utf-8'); header('Content-Type: text/json; charset=utf-8');
@ -70,7 +64,7 @@ class ViewPhoto extends HTMLController
// We are! // We are!
if (!isset($_POST['delete'])) if (!isset($_POST['delete']))
{ {
$photo->linkTags([(int) $_POST['id_tag']]); $this->photo->linkTags([(int) $_POST['id_tag']]);
echo json_encode(['success' => true]); echo json_encode(['success' => true]);
exit; exit;
} }
@ -78,9 +72,43 @@ class ViewPhoto extends HTMLController
// ... deleting, that is. // ... deleting, that is.
else else
{ {
$photo->unlinkTags([(int) $_POST['id_tag']]); $this->photo->unlinkTags([(int) $_POST['id_tag']]);
echo json_encode(['success' => true]); echo json_encode(['success' => true]);
exit; exit;
} }
} }
private function prepareMetaData()
{
if (!($exif = EXIF::fromFile($this->photo->getFullPath())))
throw new UnexpectedValueException('Photo file not found!');
$metaData = [];
if (!empty($exif->created_timestamp))
$metaData['Date Taken'] = date("j M Y, H:i:s", $exif->created_timestamp);
if ($author = $this->photo->getAuthor())
$metaData['Uploaded by'] = $author->getfullName();
if (!empty($exif->camera))
$metaData['Camera Model'] = $exif->camera;
if (!empty($exif->shutter_speed))
$metaData['Shutter Speed'] = $exif->shutterSpeedFraction();
if (!empty($exif->aperture))
$metaData['Aperture'] = 'f/' . number_format($exif->aperture, 1);
if (!empty($exif->focal_length))
$metaData['Focal Length'] = $exif->focal_length . ' mm';
if (!empty($exif->iso))
$metaData['ISO Speed'] = $exif->iso;
if (!empty($exif->software))
$metaData['Software'] = $exif->software;
return $metaData;
}
} }

View File

@ -697,11 +697,11 @@ class Asset
$params); $params);
} }
public function getUrlForPreviousInSet($id_tag = null) public function getUrlForPreviousInSet(?Tag $tag)
{ {
$row = Registry::get('db')->queryAssoc(' $row = Registry::get('db')->queryAssoc('
SELECT a.* SELECT a.*
' . (isset($id_tag) ? ' ' . (isset($tag) ? '
FROM assets_tags AS t FROM assets_tags AS t
INNER JOIN assets AS a ON a.id_asset = t.id_asset INNER JOIN assets AS a ON a.id_asset = t.id_asset
WHERE t.id_tag = {int:id_tag} AND WHERE t.id_tag = {int:id_tag} AND
@ -715,24 +715,24 @@ class Asset
LIMIT 1', LIMIT 1',
[ [
'id_asset' => $this->id_asset, 'id_asset' => $this->id_asset,
'id_tag' => $id_tag, 'id_tag' => $tag->id_tag,
'date_captured' => $this->date_captured, 'date_captured' => $this->date_captured,
]); ]);
if ($row) if ($row)
{ {
$obj = self::byRow($row, 'object'); $obj = self::byRow($row, 'object');
return $obj->getPageUrl() . ($id_tag ? '?in=' . $id_tag : ''); return $obj->getPageUrl() . ($tag ? '?in=' . $tag->id_tag : '');
} }
else else
return false; return false;
} }
public function getUrlForNextInSet($id_tag = null) public function getUrlForNextInSet(?Tag $tag)
{ {
$row = Registry::get('db')->queryAssoc(' $row = Registry::get('db')->queryAssoc('
SELECT a.* SELECT a.*
' . (isset($id_tag) ? ' ' . (isset($tag) ? '
FROM assets_tags AS t FROM assets_tags AS t
INNER JOIN assets AS a ON a.id_asset = t.id_asset INNER JOIN assets AS a ON a.id_asset = t.id_asset
WHERE t.id_tag = {int:id_tag} AND WHERE t.id_tag = {int:id_tag} AND
@ -746,14 +746,14 @@ class Asset
LIMIT 1', LIMIT 1',
[ [
'id_asset' => $this->id_asset, 'id_asset' => $this->id_asset,
'id_tag' => $id_tag, 'id_tag' => $tag->id_tag,
'date_captured' => $this->date_captured, 'date_captured' => $this->date_captured,
]); ]);
if ($row) if ($row)
{ {
$obj = self::byRow($row, 'object'); $obj = self::byRow($row, 'object');
return $obj->getPageUrl() . ($id_tag ? '?in=' . $id_tag : ''); return $obj->getPageUrl() . ($tag ? '?in=' . $tag->id_tag : '');
} }
else else
return false; return false;

View File

@ -523,7 +523,7 @@ a#previous_photo:hover, a#next_photo:hover {
right: 0; right: 0;
} }
#sub_photo h2, #sub_photo h3, #photo_exif_box h3, #user_actions_box h3 { #sub_photo h2, #sub_photo h3 {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
#sub_photo #tag_list { #sub_photo #tag_list {
@ -542,18 +542,17 @@ a#previous_photo:hover, a#next_photo:hover {
opacity: 1.0; opacity: 1.0;
} }
.photo_meta {
#photo_exif_box dt { background-color: var(--bs-body-bg);
box-shadow: 0 0.25rem 0.5rem rgba(0,0,0,0.1);
}
.photo_meta li {
padding: 0.6rem 1rem;
}
.photo_meta h4 {
font-family: 'Coda', sans-serif;
font-size: inherit;
font-weight: bold; font-weight: bold;
float: left;
clear: left;
width: 120px;
}
#photo_exif_box dt:after {
content: ':';
}
#photo_exif_box dd {
float: left;
margin: 0; margin: 0;
} }
@ -583,13 +582,6 @@ a#previous_photo:hover, a#next_photo:hover {
#previous_photo, #next_photo { #previous_photo, #next_photo {
display: none; display: none;
} }
#sub_photo, #photo_exif_box {
float: none;
margin: 30px 0;
padding: 10px;
width: auto;
}
} }

View File

@ -8,26 +8,15 @@
class PhotoPage extends Template class PhotoPage extends Template
{ {
protected $photo; private $photo;
private $exif; private $metaData;
private $previous_photo_url = ''; private $tag;
private $next_photo_url = '';
public function __construct(Image $photo) public function __construct(Image $photo)
{ {
$this->photo = $photo; $this->photo = $photo;
} }
public function setPreviousPhotoUrl($url)
{
$this->previous_photo_url = $url;
}
public function setNextPhotoUrl($url)
{
$this->next_photo_url = $url;
}
public function html_main() public function html_main()
{ {
$this->photoNav(); $this->photoNav();
@ -35,8 +24,12 @@ class PhotoPage extends Template
echo ' echo '
<div class="row mt-5"> <div class="row mt-5">
<div class="col-lg-8"> <div class="col-lg-9">
<div id="sub_photo" class="content-box"> <div id="sub_photo" class="content-box">';
$this->userActions();
echo '
<h2 class="entry-title">', $this->photo->getTitle(), '</h2>'; <h2 class="entry-title">', $this->photo->getTitle(), '</h2>';
$this->taggedPeople(); $this->taggedPeople();
@ -45,10 +38,9 @@ class PhotoPage extends Template
echo ' echo '
</div> </div>
</div> </div>
<div class="col-lg-4">'; <div class="col-lg-3">';
$this->photoMeta(); $this->photoMeta();
$this->userActions();
echo ' echo '
</div> </div>
@ -86,18 +78,23 @@ class PhotoPage extends Template
</a>'; </a>';
} }
public function setTag(Tag $tag)
{
$this->tag = $tag;
}
private function photoNav() private function photoNav()
{ {
if ($this->previous_photo_url) if ($previousUrl = $this->photo->getUrlForPreviousInSet($this->tag))
echo ' echo '
<a href="', $this->previous_photo_url, '#photo_frame" id="previous_photo"><i class="bi bi-arrow-left"></i></a>'; <a href="', $previousUrl, '#photo_frame" id="previous_photo"><i class="bi bi-arrow-left"></i></a>';
else else
echo ' echo '
<span id="previous_photo"><i class="bi bi-arrow-left"></i></span>'; <span id="previous_photo"><i class="bi bi-arrow-left"></i></span>';
if ($this->next_photo_url) if ($nextUrl = $this->photo->getUrlForNextInSet($this->tag))
echo ' echo '
<a href="', $this->next_photo_url, '#photo_frame" id="next_photo"><i class="bi bi-arrow-right"></i></a>'; <a href="', $nextUrl, '#photo_frame" id="next_photo"><i class="bi bi-arrow-right"></i></a>';
else else
echo ' echo '
<span id="next_photo"><i class="bi bi-arrow-right"></i></span>'; <span id="next_photo"><i class="bi bi-arrow-right"></i></span>';
@ -106,52 +103,19 @@ class PhotoPage extends Template
private function photoMeta() private function photoMeta()
{ {
echo ' echo '
<div id="photo_exif_box" class="content-box clearfix"> <ul class="list-group photo_meta">';
<h3>EXIF</h3>
<dl class="photo_meta">';
if (!empty($this->exif->created_timestamp)) foreach ($this->metaData as $header => $body)
{
echo ' echo '
<dt>Date Taken</dt> <li class="list-group-item">
<dd>', date("j M Y, H:i:s", $this->exif->created_timestamp), '</dd>'; <h4>', $header, '</h4>
', $body, '
</li>';
}
echo ' echo '
<dt>Uploaded by</dt> </ul>';
<dd>', $this->photo->getAuthor()->getfullName(), '</dd>';
if (!empty($this->exif->camera))
echo '
<dt>Camera Model</dt>
<dd>', $this->exif->camera, '</dd>';
if (!empty($this->exif->shutter_speed))
echo '
<dt>Shutter Speed</dt>
<dd>', $this->exif->shutterSpeedFraction(), '</dd>';
if (!empty($this->exif->aperture))
echo '
<dt>Aperture</dt>
<dd>f/', number_format($this->exif->aperture, 1), '</dd>';
if (!empty($this->exif->focal_length))
echo '
<dt>Focal Length</dt>
<dd>', $this->exif->focal_length, ' mm</dd>';
if (!empty($this->exif->iso))
echo '
<dt>ISO Speed</dt>
<dd>', $this->exif->iso, '</dd>';
if (!empty($this->exif->software))
echo '
<dt>Software</dt>
<dd>', $this->exif->software, '</dd>';
echo '
</dl>
</div>';
} }
private function taggedPeople() private function taggedPeople()
@ -246,9 +210,9 @@ class PhotoPage extends Template
</script>'; </script>';
} }
public function setExif(EXIF $exif) public function setMetaData(array $metaData)
{ {
$this->exif = $exif; $this->metaData = $metaData;
} }
public function userActions() public function userActions()
@ -257,13 +221,13 @@ class PhotoPage extends Template
return; return;
echo ' echo '
<div id="user_actions_box" class="content-box"> <div class="float-end">
<h3>Actions</h3> <a class="btn btn-primary" href="', $this->photo->getEditUrl(), '">
<a class="btn btn-primary" href="', $this->photo->getEditUrl(), '">Edit photo</a> <i class="bi bi-pencil"></i> Edit</a>
<a class="btn btn-danger" href="', $this->photo->getDeleteUrl(), '&', <a class="btn btn-danger" href="', $this->photo->getDeleteUrl(), '&',
Session::getSessionTokenKey(), '=', Session::getSessionToken(), Session::getSessionTokenKey(), '=', Session::getSessionToken(),
'" onclick="return confirm(\'Are you sure you want to delete this photo?\');"', '" onclick="return confirm(\'Are you sure you want to delete this photo?\');"',
'">Delete photo</a> '"><i class="bi bi-pencil"></i> Delete</a></a>
</div>'; </div>';
} }
} }