questlab/controllers/QrcodesController.inc

388 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Questlab
*
* @author Oliver Hanraths <oliver.hanraths@uni-duesseldorf.de>
* @copyright 2014 2016 Heinrich-Heine-Universität Düsseldorf
* @license http://www.gnu.org/licenses/gpl.html
* @link https://github.com/coderkun/questlab
*/
namespace hhu\z\controllers;
/**
* Controller of the QrcodeAgent to generate and show QR-codes.
*
* @author Oliver Hanraths <oliver.hanraths@uni-duesseldorf.de>
*/
class QrcodesController extends \hhu\z\controllers\SeminaryController
{
/**
* Required models
*
* @var array
*/
public $models = array('seminaries', 'achievements', 'charactertitles', 'charactergroups', 'charactergroupsquests', 'charactergroupsqueststations', 'charactergroupsachievements');
/**
* Prefilter.
*
* @param \nre\core\Request $request Current request
* @param \nre\core\Response $response Current response
*/
public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
{
parent::preFilter($request, $response);
// Set headers for caching control
$response->addHeader("Pragma: public");
$response->addHeader("Cache-control: must-revalidate");
$response->addHeader("Date: ".gmdate(\DateTime::RFC822));
}
/**
* Action: achievement
*
* Display a QR-code for an Achievement.
*
* @throws \nre\exceptions\IdNotFoundException
* @param string $seminaryUrl URL-Title of a Seminary
* @param string $achievementUrl URL of Achievement
* @param int $size QR-code size
*/
public function achievement($seminaryUrl, $achievementUrl, $size=1)
{
// Get seminary
$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
// Get Achievement
$achievement = $this->Achievements->getAchievementByUrl(
$seminary['id'],
$achievementUrl
);
// Get condition
$conditions = $this->Achievements->getAchievementConditionsQrcode(
$achievement['id']
);
if(empty($conditions)) {
throw new IdNotFoundException($achievementUrl);
}
// Generate QR-code
$url = $this->linker->link(array('qr', 'a', $conditions[0]['hash']), 0, true, null, true, null, true);
$file = $this->generateQRcode($url, $size);
if(is_null($file)) {
return;
}
// Pass data to view
$this->set('file', $file);
}
/**
* Action: charactertitles
*
* Display a QR-code for a Character title.
*
* @throws \nre\exceptions\IdNotFoundException
* @param string $seminaryUrl URL-Title of a Seminary
* @param string $titleHash Hash of Character title
* @param int $size Size of QR-code (default: 1)
*/
public function charactertitle($seminaryUrl, $titleHash, $size=1)
{
// Get seminary
$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
// Get Character title
$title = $this->Charactertitles->getTitleByHash($titleHash);
// Generate QR-code
$url = $this->linker->link(array('qr', 'ct', $title['hash']), 0, true, null, true, null, true);
$file = $this->generateQRcode($url, $size);
if(is_null($file)) {
return;
}
// Pass data to view
$this->set('file', $file);
}
/**
* Action: charactergroupsqueststation
*
* Display a QR-code for a Character groups Quest station.
*
* @throws \nre\exceptions\IdNotFoundException
* @param string $seminaryUrl URL-Title of a Seminary
* @param string $groupsgroupUrl URL-Title of a Character groups-group
* @param string $questUrl URL-Title of a Character groups Quest
* @param string $stationUrl URL of Character groups Quest station
* @param int $size QR-code size
*/
public function charactergroupsqueststation($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl, $size=1)
{
// Get seminary
$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
// Get Character groups-group
$groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
// Get Character groups Quests
$quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
// Get station
$station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
// Generate QR-code
$url = $this->linker->link(array('qr', 'cgqs', $station['url']), 0, true, null, true, null, true);
$file = $this->generateQRcode($url, $size);
if(is_null($file)) {
return;
}
// Pass data to view
$this->set('file', $file);
}
/**
* Action: charactergroupsachievements
*
* Display a QR-code for a Character groups Achievement.
*
* @throws \nre\exceptions\IdNotFoundException
* @param string $seminaryUrl URL-Title of a Seminary
* @param string $groupsgroupUrl URL-Title of a Character groups-group
* @param string $achievementUrl URL of Achievement
* @param int $size QR-code size
*/
public function charactergroupsachievements($seminaryUrl, $groupsgroupUrl, $achievementUrl, $size=1)
{
// Get seminary
$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
// Get Character groups-group
$groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
// Get Achievement
$achievement = $this->Charactergroupsachievements->getAchievementByUrl(
$groupsgroup['id'],
$achievementUrl
);
// Generate QR-code
$url = $this->linker->link(array('qr', 'cga', $achievement['hash']), 0, true, null, true, null, true);
$file = $this->generateQRcode($url, $size);
if(is_null($file)) {
return;
}
// Pass data to view
$this->set('file', $file);
}
/**
* Determine file information and set the HTTP-header for
* caching accordingly.
*
* @param string $fileName Filename
* @return boolean HTTP-status 304 was set (in cache)
*/
private function setCacheHeaders($fileName)
{
// Determine last change of file
$fileLastModified = gmdate('r', filemtime($fileName));
// Generate E-Tag
$fileEtag = hash('sha256', $fileLastModified.$fileName);
// Set header
$this->response->addHeader("Last-Modified: ".$fileLastModified);
$this->response->addHeader("Etag: ".$fileEtag);
// HTTP-status
$headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE');
$headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH');
if(
!is_null($headerModifiedSince) && strtotime($fileLastModified) <= strtotime($headerModifiedSince) &&
!is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag
) {
$this->response->setExit(true);
$this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304));
return true;
}
return false;
}
/**
* Generate QR-code with given text and in given size.
*
* @param string $text QR-code content
* @param int $size QR-code size
* @return mixed Null
*/
private function generateQRcode($text, $size)
{
\hhu\z\lib\Phpqrcode::load();
\QRcode::png($text, false, QR_ECLEVEL_L, intval($size), 1);
return null;
}
/**
* Determine the file for a medium and process it if necessary.
*
* @throws \nre\exceptions\IdNotFoundException
* @throws \nre\exceptions\ParamsNotValidException
* @param array $media Medium to get file for
* @param string $action Action for processing the media
* @return object File for the medium (or null if medium is cached)
*/
private function getMediaFile($media, $action=null)
{
// Get format
$format = explode('/', $media['mimetype']);
$format = $format[1];
// Set content-type
$this->response->addHeader("Content-type: ".$media['mimetype']."");
// Set filename
$media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id'];
if(!file_exists($media['filename'])) {
throw new \nre\exceptions\IdNotFoundException($media['id'].': '.$media['url']);
}
// Cache
if($this->setCacheHeaders($media['filename'])) {
return null;
}
// Load and process file
$file = null;
if(is_null($action) || !in_array(strtoupper($format), self::getImageTypes()))
{
// Do not process the file
$file = file_get_contents($media['filename']);
}
else
{
// Process file
switch($action)
{
case 'questgroup':
case 'quest':
case 'avatar':
case 'charactergroup':
case 'charactergroupsquest':
$file = self::resizeImage(
$media['filename'],
$format,
\nre\configs\AppConfig::$media[$action]['width'],
\nre\configs\AppConfig::$media[$action]['height']
);
break;
default:
throw new ParamsNotValidException($action);
break;
}
}
// Return file
return $file;
}
/**
* Get supported image types.
*
* @return array List of supported image types
*/
private static function getImageTypes()
{
$im = new \Imagick();
return $im->queryFormats();
}
/**
* Resize an image.
*
* @param string $fileName Absolute pathname of image to resize
* @param string $mimeType Mimetype of target image
* @param int $width Max. width to resize to
* @param int $height Max. height to resize to
* @return mixed Resized image
*/
private static function resizeImage($fileName, $mimeType, $width, $height)
{
// Read image from cache
$tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$width.'x'.$height;
if(file_exists($tempFileName))
{
// Check age of file
if(filemtime($fileName) > filemtime($tempFileName)) {
// Too old, delete
unlink($tempFileName);
}
else {
// Valid, read and return
return file_get_contents($tempFileName);
}
}
// ImageMagick
$im = new \Imagick($fileName);
// Calculate new size
$geometry = $im->getImageGeometry();
if($geometry['width'] < $width) {
$width = $geometry['width'];
}
if($geometry['height'] < $height) {
$height = $geometry['width'];
}
// Process
$im->thumbnailImage($width, $height, true);
$im->contrastImage(1);
$im->setImageFormat($mimeType);
// Save temporary file
$im->writeImage($tempFileName);
// Return resized image
return $im;
}
}
?>