Aaron van Geffen
e28fcd8b03
Removes the intermediate confirmation page, instead using JavaScript for confirmation. Fixes an XSS issue, in that the previous method was not passing or checking the session (!)
276 lines
7.1 KiB
PHP
276 lines
7.1 KiB
PHP
<?php
|
|
/*****************************************************************************
|
|
* PhotoPage.php
|
|
* Contains the photo page template.
|
|
*
|
|
* Kabuki CMS (C) 2013-2016, Aaron van Geffen
|
|
*****************************************************************************/
|
|
|
|
class PhotoPage extends Template
|
|
{
|
|
protected $photo;
|
|
private $exif;
|
|
private $previous_photo_url = '';
|
|
private $next_photo_url = '';
|
|
private $is_asset_owner = false;
|
|
|
|
public function __construct(Image $photo)
|
|
{
|
|
$this->photo = $photo;
|
|
}
|
|
|
|
public function setPreviousPhotoUrl($url)
|
|
{
|
|
$this->previous_photo_url = $url;
|
|
}
|
|
|
|
public function setNextPhotoUrl($url)
|
|
{
|
|
$this->next_photo_url = $url;
|
|
}
|
|
|
|
public function setIsAssetOwner($flag)
|
|
{
|
|
$this->is_asset_owner = $flag;
|
|
}
|
|
|
|
public function html_main()
|
|
{
|
|
$this->photoNav();
|
|
$this->photo();
|
|
|
|
echo '
|
|
<div class="row mt-5">
|
|
<div class="col-lg-8">
|
|
<div id="sub_photo" class="content-box">
|
|
<h2 class="entry-title">', $this->photo->getTitle(), '</h2>';
|
|
|
|
$this->taggedPeople();
|
|
$this->linkNewTags();
|
|
|
|
echo '
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-4">';
|
|
|
|
$this->photoMeta();
|
|
$this->userActions();
|
|
|
|
echo '
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript" src="', BASEURL, '/js/photonav.js"></script>';
|
|
}
|
|
|
|
protected function photo()
|
|
{
|
|
echo '
|
|
<a href="', $this->photo->getUrl(), '">
|
|
<div id="photo_frame">';
|
|
|
|
if ($this->photo->isPortrait())
|
|
{
|
|
echo '
|
|
<figure id="photo-figure" class="portrait-figure">',
|
|
$this->photo->getInlineImage(null, 960, 'normal-photo'),
|
|
$this->photo->getInlineImage(null, 960, 'blur-photo'), '
|
|
</figure>';
|
|
}
|
|
else
|
|
{
|
|
$className = $this->photo->isPanorama() ? 'panorama-figure' : 'landscape-figure';
|
|
echo '
|
|
<figure id="photo-figure" class="', $className, '">',
|
|
$this->photo->getInlineImage(1280, null, 'normal-photo'),
|
|
$this->photo->getInlineImage(1280, null, 'blur-photo'), '
|
|
</figure>';
|
|
}
|
|
|
|
echo '
|
|
</figure>
|
|
</div>
|
|
</a>';
|
|
}
|
|
|
|
private function photoNav()
|
|
{
|
|
if ($this->previous_photo_url)
|
|
echo '
|
|
<a href="', $this->previous_photo_url, '#photo_frame" id="previous_photo"><i class="bi bi-arrow-left"></i></a>';
|
|
else
|
|
echo '
|
|
<span id="previous_photo"><i class="bi bi-arrow-left"></i></span>';
|
|
|
|
if ($this->next_photo_url)
|
|
echo '
|
|
<a href="', $this->next_photo_url, '#photo_frame" id="next_photo"><i class="bi bi-arrow-right"></i></a>';
|
|
else
|
|
echo '
|
|
<span id="next_photo"><i class="bi bi-arrow-right"></i></span>';
|
|
}
|
|
|
|
private function photoMeta()
|
|
{
|
|
echo '
|
|
<div id="photo_exif_box" class="content-box clearfix">
|
|
<h3>EXIF</h3>
|
|
<dl class="photo_meta">';
|
|
|
|
if (!empty($this->exif->created_timestamp))
|
|
echo '
|
|
<dt>Date Taken</dt>
|
|
<dd>', date("j M Y, H:i:s", $this->exif->created_timestamp), '</dd>';
|
|
|
|
echo '
|
|
<dt>Uploaded by</dt>
|
|
<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()
|
|
{
|
|
echo '
|
|
<h3>Tags</h3>
|
|
<ul id="tag_list">';
|
|
|
|
foreach ($this->photo->getTags() as $tag)
|
|
{
|
|
echo '
|
|
<li id="tag-', $tag->id_tag, '">
|
|
<a rel="tag" title="View all posts tagged ', $tag->tag, '" href="', $tag->getUrl(), '" class="entry-tag">', $tag->tag, '</a>';
|
|
|
|
if ($tag->kind === 'Person')
|
|
echo '
|
|
<a class="delete-tag" title="Unlink this tag from this photo" href="#" data-id="', $tag->id_tag, '">❌</a>';
|
|
|
|
echo '
|
|
</li>';
|
|
}
|
|
|
|
echo '
|
|
</ul>';
|
|
}
|
|
|
|
private function linkNewTags()
|
|
{
|
|
echo '
|
|
<div>
|
|
<h3>Link tags</h3>
|
|
<p style="position: relative">
|
|
<input class="form-control w-auto" type="text" id="new_tag" placeholder="Type to link a new tag">
|
|
</p>
|
|
</div>
|
|
<script type="text/javascript" src="', BASEURL, '/js/ajax.js"></script>
|
|
<script type="text/javascript" src="', BASEURL, '/js/autosuggest.js"></script>
|
|
<script type="text/javascript">
|
|
setTimeout(function() {
|
|
var removeTag = function(event) {
|
|
event.preventDefault();
|
|
var that = this;
|
|
var request = new HttpRequest("post", "', $this->photo->getPageUrl(), '",
|
|
"id_tag=" + this.dataset["id"] + "&delete", function(response) {
|
|
if (!response.success) {
|
|
return;
|
|
}
|
|
|
|
var tagNode = document.getElementById("tag-" + that.dataset["id"]);
|
|
tagNode.parentNode.removeChild(tagNode);
|
|
});
|
|
};
|
|
|
|
var tagRemovalTargets = document.getElementsByClassName("delete-tag");
|
|
for (var i = 0; i < tagRemovalTargets.length; i++) {
|
|
tagRemovalTargets[i].addEventListener("click", removeTag);
|
|
}
|
|
|
|
var tag_autosuggest = new TagAutoSuggest({
|
|
inputElement: "new_tag",
|
|
listElement: "tag_list",
|
|
baseUrl: "', BASEURL, '",
|
|
appendCallback: function(item) {
|
|
var request = new HttpRequest("post", "', $this->photo->getPageUrl(), '",
|
|
"id_tag=" + item.id_tag, function(response) {
|
|
var newLink = document.createElement("a");
|
|
newLink.href = item.url;
|
|
|
|
var newLabel = document.createTextNode(item.label);
|
|
newLink.appendChild(newLabel);
|
|
|
|
var removeLink = document.createElement("a");
|
|
removeLink.className = "delete-tag";
|
|
removeLink.dataset["id"] = item.id_tag;
|
|
removeLink.href = "#";
|
|
removeLink.addEventListener("click", removeTag);
|
|
|
|
var crossmark = document.createTextNode("❌");
|
|
removeLink.appendChild(crossmark);
|
|
|
|
var newNode = document.createElement("li");
|
|
newNode.id = "tag-" + item.id_tag;
|
|
newNode.appendChild(newLink);
|
|
newNode.appendChild(removeLink);
|
|
|
|
var list = document.getElementById("tag_list");
|
|
list.appendChild(newNode);
|
|
}, this);
|
|
}
|
|
});
|
|
}, 100);
|
|
</script>';
|
|
}
|
|
|
|
public function setExif(EXIF $exif)
|
|
{
|
|
$this->exif = $exif;
|
|
}
|
|
|
|
public function userActions()
|
|
{
|
|
if (!$this->photo->isOwnedBy(Registry::get('user')))
|
|
return;
|
|
|
|
echo '
|
|
<div id="user_actions_box" class="content-box">
|
|
<h3>Actions</h3>
|
|
<a class="btn btn-primary" href="', $this->photo->getEditUrl(), '">Edit photo</a>
|
|
<a class="btn btn-danger" href="', $this->photo->getDeleteUrl(), '&',
|
|
Session::getSessionTokenKey(), '=', Session::getSessionToken(),
|
|
'" onclick="return confirm(\'Are you sure you want to delete this photo?\');"',
|
|
'">Delete photo</a>
|
|
</div>';
|
|
}
|
|
}
|