* @copyright 2014 Heinrich-Heine-Universität Düsseldorf * @license http://www.gnu.org/licenses/gpl.html * @link https://bitbucket.org/coderkun/the-legend-of-z */ namespace hhu\z\controllers; /** * Controller of the UploadsAgent to process and show user uploads. * * @author Oliver Hanraths */ class UploadsController extends \hhu\z\controllers\SeminaryController { /** * Required models * * @var array */ public $models = array('uploads', 'users', 'userroles', 'characterroles', 'seminaries', 'charactergroups'); /** * User permissions * * @var array */ public $permissions = array( 'seminary' => array('admin', 'moderator', 'user', 'guest'), 'charactergroup' => array('admin', 'moderator', 'user', 'guest') ); /** * User seminary permissions * * @var array */ public $seminaryPermissions = array( 'seminary' => array('admin', 'moderator', 'user', 'guest'), 'charactergroup' => array('admin', 'moderator', 'user') ); /** * 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: public, max-age=".(60*60*24)); $response->addHeader("Expires: ".gmdate('r', time()+(60*60*24))); $response->addHeader("Date: ".gmdate(\DateTime::RFC822)); } /** * Action: seminary. * * Display a Seminary upload. * * @throws \nre\exceptions\AccessDeniedException * @throws \nre\exceptions\IdNotFoundException * @param string $seminaryUrl URL-title of Seminary * @param string $uploadUrl URL-name of the upload * @param string $action Current action (optional) */ public function seminary($seminaryUrl, $uploadUrl, $action=null) { // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Upload $upload = $this->Uploads->getSeminaryuploadByUrl($seminary['id'], $uploadUrl); // Check permissions if(!$upload['public']) { $user = $this->Users->getUserById($this->Auth->getUserId()); $user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById($user['id'])); // System roles if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) { // Owner of file if($upload['created_user_id'] != $user['id']) { // Seminary permissions $characterRoles = array_map(function($r) { return $r['name']; }, $this->Characterroles->getCharacterrolesForCharacterById($character['id'])); if(count(array_intersect(array('admin', 'moderator'), $characterRoles)) == 0) { throw new \nre\exceptions\AccessDeniedException(); } } } } // Get file switch($action) { case null: $file = $this->getUploadFile($upload); break; case 'thumbnail': $file = $this->createThumbnail($upload); break; default: throw new \nre\exceptions\ParamsNotValidException($action); break; } if(is_null($file)) { return; } // Pass data to view $this->set('upload', $upload); $this->set('file', $file); } /** * Action: charactergroup. * * Display the icon of a Character group. * * @throws \nre\exceptions\IdNotFoundException * @param string $seminaryUrl URL-Title of a Seminary * @param string $groupsgroupUrl URL-Title of a Character groups-group * @param string $groupUrl URL-Title of a Character group */ public function charactergroup($seminaryUrl, $groupsgroupUrl, $groupUrl) { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); // Get Character group $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl); // Get Upload $upload = $this->Uploads->getSeminaryuploadById($group['seminaryupload_id']); // Get file $file = $this->getUploadFile($upload); if(is_null($file)) { return; } // Pass data to view $this->set('upload', $upload); $this->set('file', $file); } /** * Determine the file for an upload. * * @throws \nre\exceptions\IdNotFoundException * @param array $upload Upload to get file for * @return object File for the upload (or null if upload is cached) */ private function getUploadFile($upload) { // Set content-type $this->response->addHeader("Content-type: ".$upload['mimetype'].""); // Set filename $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$upload['url']; if(!file_exists($upload['filename'])) { throw new \nre\exceptions\IdNotFoundException($uploadUrl); } // Cache if($this->setCacheHeaders($upload['filename'])) { return null; } return file_get_contents($upload['filename']); } /** * Create a thumbnail from an upload. * * @param array $upload Upload to create thumbnail for * @return object Thumbnail for the upload (or null if thumbnail is cached) */ private function createThumbnail($upload) { // Set filename $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$upload['url']; // Set content-type $this->response->addHeader("Content-type: image/jpeg"); // Check file if(!file_exists($upload['filename'])) { throw new \nre\exceptions\IdNotFoundException($upload['url']); } // Cache if($this->setCacheHeaders($upload['filename'])) { return null; } // Set geometry $width = 100; $height = 100; switch($upload['mimetype']) { case 'image/jpeg': case 'image/png': // Read image from cache $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.$upload['url'].'-'.$width.'x'.$height; if(file_exists($tempFileName)) { // Check age of file if(date('r', filemtime($tempFileName)+(60*60*24)) > date('r', time())) { // Too old, delete unlink($tempFileName); } else { // Valid, read and return return file_get_contents($tempFileName); } } // ImageMagick $im = new \Imagick($upload['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('jpeg'); // Save temporary file $im->writeImage($tempFileName); // Return resized image return $im; break; default: throw new \nre\exceptions\ParamsNotValidException('thumbnail'); break; } return $this->getUploadFile($upload); } /** * 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) && $fileLastModified < strtotime($headerModifiedSince) && !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag ) { $this->response->setExit(true); $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304)); return true; } return false; } } ?>