summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/File.php38
-rw-r--r--app/Core/ObjectStorage/FileStorage.php150
-rw-r--r--app/Core/ObjectStorage/ObjectStorageException.php9
-rw-r--r--app/Core/ObjectStorage/ObjectStorageInterface.php68
-rw-r--r--app/Core/Tool.php78
-rw-r--r--app/Model/File.php244
-rw-r--r--app/ServiceProvider/ClassProvider.php5
-rw-r--r--app/Template/file/show.php2
8 files changed, 446 insertions, 148 deletions
diff --git a/app/Controller/File.php b/app/Controller/File.php
index f73a9de9..7b7c75ee 100644
--- a/app/Controller/File.php
+++ b/app/Controller/File.php
@@ -60,7 +60,7 @@ class File extends Base
{
$task = $this->getTask();
- if (! $this->file->upload($task['project_id'], $task['id'], 'files')) {
+ if (! $this->file->uploadFiles($task['project_id'], $task['id'], 'files')) {
$this->session->flashError(t('Unable to upload the file.'));
}
@@ -76,14 +76,13 @@ class File extends Base
{
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
- $filename = FILES_DIR.$file['path'];
- if ($file['task_id'] == $task['id'] && file_exists($filename)) {
- $this->response->forceDownload($file['name']);
- $this->response->binary(file_get_contents($filename));
+ if ($file['task_id'] != $task['id']) {
+ $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
+ $this->response->forceDownload($file['name']);
+ $this->objectStorage->passthru($file['path']);
}
/**
@@ -113,16 +112,13 @@ class File extends Base
{
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
- $filename = FILES_DIR.$file['path'];
-
- if ($file['task_id'] == $task['id'] && file_exists($filename)) {
- $metadata = getimagesize($filename);
- if (isset($metadata['mime'])) {
- $this->response->contentType($metadata['mime']);
- readfile($filename);
- }
+ if ($file['task_id'] != $task['id']) {
+ $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
+
+ $this->response->contentType($this->file->getImageMimeType($file['name']));
+ $this->objectStorage->passthru($file['path']);
}
/**
@@ -134,17 +130,13 @@ class File extends Base
{
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
- $filename = FILES_DIR.$file['path'];
-
- if ($file['task_id'] == $task['id'] && file_exists($filename)) {
- $this->response->contentType('image/jpeg');
- $this->file->generateThumbnail(
- $filename,
- $this->request->getIntegerParam('width'),
- $this->request->getIntegerParam('height')
- );
+ if ($file['task_id'] != $task['id']) {
+ $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
+
+ $this->response->contentType('image/jpeg');
+ $this->objectStorage->passthru($this->file->getThumbnailPath($file['path']));
}
/**
diff --git a/app/Core/ObjectStorage/FileStorage.php b/app/Core/ObjectStorage/FileStorage.php
new file mode 100644
index 00000000..66c62334
--- /dev/null
+++ b/app/Core/ObjectStorage/FileStorage.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace Core\ObjectStorage;
+
+/**
+ * Local File Storage
+ *
+ * @package ObjectStorage
+ * @author Frederic Guillot
+ */
+class FileStorage implements ObjectStorageInterface
+{
+ /**
+ * Base path
+ *
+ * @access private
+ * @var string
+ */
+ private $path = '';
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $path
+ */
+ public function __construct($path)
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * Fetch object contents
+ *
+ * @access public
+ * @param string $key
+ * @return string
+ */
+ public function get($key)
+ {
+ $filename = $this->path.DIRECTORY_SEPARATOR.$key;
+
+ if (! file_exists($filename)) {
+ throw new ObjectStorageException('File not found: '.$filename);
+ }
+
+ return file_get_contents($filename);
+ }
+
+ /**
+ * Save object
+ *
+ * @access public
+ * @param string $key
+ * @param string $blob
+ * @return string
+ */
+ public function put($key, &$blob)
+ {
+ $this->createFolder($key);
+
+ if (file_put_contents($this->path.DIRECTORY_SEPARATOR.$key, $blob) === false) {
+ throw new ObjectStorageException('Unable to write the file: '.$this->path.DIRECTORY_SEPARATOR.$key);
+ }
+ }
+
+ /**
+ * Output directly object content
+ *
+ * @access public
+ * @param string $key
+ */
+ public function passthru($key)
+ {
+ $filename = $this->path.DIRECTORY_SEPARATOR.$key;
+
+ if (! file_exists($filename)) {
+ throw new ObjectStorageException('File not found: '.$filename);
+ }
+
+ return readfile($filename);
+ }
+
+ /**
+ * Move local file to object storage
+ *
+ * @access public
+ * @param string $filename
+ * @param string $key
+ * @return boolean
+ */
+ public function moveFile($src_filename, $key)
+ {
+ $this->createFolder($key);
+ $dst_filename = $this->path.DIRECTORY_SEPARATOR.$key;
+
+ if (! rename($src_filename, $dst_filename)) {
+ throw new ObjectStorageException('Unable to move the file: '.$src_filename.' to '.$dst_filename);
+ }
+
+ return true;
+ }
+
+ /**
+ * Move uploaded file to object storage
+ *
+ * @access public
+ * @param string $filename
+ * @param string $key
+ * @return boolean
+ */
+ public function moveUploadedFile($filename, $key)
+ {
+ $this->createFolder($key);
+ return move_uploaded_file($filename, $this->path.DIRECTORY_SEPARATOR.$key);
+ }
+
+ /**
+ * Remove object
+ *
+ * @access public
+ * @param string $key
+ * @return boolean
+ */
+ public function remove($key)
+ {
+ $filename = $this->path.DIRECTORY_SEPARATOR.$key;
+
+ if (file_exists($filename)) {
+ return unlink($filename);
+ }
+
+ return false;
+ }
+
+ /**
+ * Create object folder
+ *
+ * @access private
+ * @param string $key
+ */
+ private function createFolder($key)
+ {
+ $folder = $this->path.DIRECTORY_SEPARATOR.dirname($key);
+
+ if (! is_dir($folder) && ! mkdir($folder, 0755, true)) {
+ throw new ObjectStorageException('Unable to create folder: '.$folder);
+ }
+ }
+}
diff --git a/app/Core/ObjectStorage/ObjectStorageException.php b/app/Core/ObjectStorage/ObjectStorageException.php
new file mode 100644
index 00000000..e89aeb25
--- /dev/null
+++ b/app/Core/ObjectStorage/ObjectStorageException.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace Core\ObjectStorage;
+
+use Exception;
+
+class ObjectStorageException extends Exception
+{
+}
diff --git a/app/Core/ObjectStorage/ObjectStorageInterface.php b/app/Core/ObjectStorage/ObjectStorageInterface.php
new file mode 100644
index 00000000..5440cf2b
--- /dev/null
+++ b/app/Core/ObjectStorage/ObjectStorageInterface.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Core\ObjectStorage;
+
+/**
+ * Object Storage Interface
+ *
+ * @package ObjectStorage
+ * @author Frederic Guillot
+ */
+interface ObjectStorageInterface
+{
+ /**
+ * Fetch object contents
+ *
+ * @access public
+ * @param string $key
+ * @return string
+ */
+ public function get($key);
+
+ /**
+ * Save object
+ *
+ * @access public
+ * @param string $key
+ * @param string $blob
+ * @return string
+ */
+ public function put($key, &$blob);
+
+ /**
+ * Output directly object content
+ *
+ * @access public
+ * @param string $key
+ */
+ public function passthru($key);
+
+ /**
+ * Move local file to object storage
+ *
+ * @access public
+ * @param string $filename
+ * @param string $key
+ * @return boolean
+ */
+ public function moveFile($filename, $key);
+
+ /**
+ * Move uploaded file to object storage
+ *
+ * @access public
+ * @param string $filename
+ * @param string $key
+ * @return boolean
+ */
+ public function moveUploadedFile($filename, $key);
+
+ /**
+ * Remove object
+ *
+ * @access public
+ * @param string $key
+ * @return boolean
+ */
+ public function remove($key);
+}
diff --git a/app/Core/Tool.php b/app/Core/Tool.php
index 7939a80e..887c8fb3 100644
--- a/app/Core/Tool.php
+++ b/app/Core/Tool.php
@@ -72,4 +72,82 @@ class Tool
}
}
}
+
+ /**
+ * Generate a jpeg thumbnail from an image
+ *
+ * @static
+ * @access public
+ * @param string $src_file Source file image
+ * @param string $dst_file Destination file image
+ * @param integer $resize_width Desired image width
+ * @param integer $resize_height Desired image height
+ */
+ public static function generateThumbnail($src_file, $dst_file, $resize_width = 250, $resize_height = 100)
+ {
+ $metadata = getimagesize($src_file);
+ $src_width = $metadata[0];
+ $src_height = $metadata[1];
+ $dst_y = 0;
+ $dst_x = 0;
+
+ if (empty($metadata['mime'])) {
+ return;
+ }
+
+ if ($resize_width == 0 && $resize_height == 0) {
+ $resize_width = 100;
+ $resize_height = 100;
+ }
+
+ if ($resize_width > 0 && $resize_height == 0) {
+ $dst_width = $resize_width;
+ $dst_height = floor($src_height * ($resize_width / $src_width));
+ $dst_image = imagecreatetruecolor($dst_width, $dst_height);
+ }
+ elseif ($resize_width == 0 && $resize_height > 0) {
+ $dst_width = floor($src_width * ($resize_height / $src_height));
+ $dst_height = $resize_height;
+ $dst_image = imagecreatetruecolor($dst_width, $dst_height);
+ }
+ else {
+
+ $src_ratio = $src_width / $src_height;
+ $resize_ratio = $resize_width / $resize_height;
+
+ if ($src_ratio <= $resize_ratio) {
+ $dst_width = $resize_width;
+ $dst_height = floor($src_height * ($resize_width / $src_width));
+
+ $dst_y = ($dst_height - $resize_height) / 2 * (-1);
+ }
+ else {
+ $dst_width = floor($src_width * ($resize_height / $src_height));
+ $dst_height = $resize_height;
+
+ $dst_x = ($dst_width - $resize_width) / 2 * (-1);
+ }
+
+ $dst_image = imagecreatetruecolor($resize_width, $resize_height);
+ }
+
+ switch ($metadata['mime']) {
+ case 'image/jpeg':
+ case 'image/jpg':
+ $src_image = imagecreatefromjpeg($src_file);
+ break;
+ case 'image/png':
+ $src_image = imagecreatefrompng($src_file);
+ break;
+ case 'image/gif':
+ $src_image = imagecreatefromgif($src_file);
+ break;
+ default:
+ return;
+ }
+
+ imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
+ imagejpeg($dst_image, $dst_file);
+ imagedestroy($dst_image);
+ }
}
diff --git a/app/Model/File.php b/app/Model/File.php
index f884e460..7adab42b 100644
--- a/app/Model/File.php
+++ b/app/Model/File.php
@@ -3,6 +3,8 @@
namespace Model;
use Event\FileEvent;
+use Core\Tool;
+use Core\ObjectStorage\ObjectStorageException;
/**
* File model
@@ -47,14 +49,17 @@ class File extends Base
*/
public function remove($file_id)
{
- $file = $this->getbyId($file_id);
+ try {
- if (! empty($file)) {
- @unlink(FILES_DIR.$file['path']);
- return $this->db->table(self::TABLE)->eq('id', $file_id)->remove();
- }
+ $file = $this->getbyId($file_id);
+ $this->objectStorage->remove($file['path']);
- return false;
+ return $this->db->table(self::TABLE)->eq('id', $file['id'])->remove();
+ }
+ catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ return false;
+ }
}
/**
@@ -66,11 +71,11 @@ class File extends Base
*/
public function removeAll($task_id)
{
- $files = $this->getAll($task_id);
+ $file_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->asc('id')->findAllByColumn('id');
$results = array();
- foreach ($files as $file) {
- $results[] = $this->remove($file['id']);
+ foreach ($file_ids as $file_id) {
+ $results[] = $this->remove($file_id);
}
return ! in_array(false, $results, true);
@@ -196,6 +201,30 @@ class File extends Base
}
/**
+ * Return the image mimetype based on the file extension
+ *
+ * @access public
+ * @param $filename
+ * @return string
+ */
+ public function getImageMimeType($filename)
+ {
+ $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+
+ switch ($extension) {
+ case 'jpeg':
+ case 'jpg':
+ return 'image/jpeg';
+ case 'png':
+ return 'image/png';
+ case 'gif':
+ return 'image/gif';
+ default:
+ return 'image/jpeg';
+ }
+ }
+
+ /**
* Generate the path for a new filename
*
* @access public
@@ -210,6 +239,18 @@ class File extends Base
}
/**
+ * Generate the path for a thumbnails
+ *
+ * @access public
+ * @param string $key Storage key
+ * @return string
+ */
+ public function getThumbnailPath($key)
+ {
+ return 'thumbnails'.DIRECTORY_SEPARATOR.$key;
+ }
+
+ /**
* Handle file upload
*
* @access public
@@ -218,11 +259,13 @@ class File extends Base
* @param string $form_name File form name
* @return bool
*/
- public function upload($project_id, $task_id, $form_name)
+ public function uploadFiles($project_id, $task_id, $form_name)
{
- $results = array();
+ try {
- if (! empty($_FILES[$form_name])) {
+ if (empty($_FILES[$form_name])) {
+ return false;
+ }
foreach ($_FILES[$form_name]['error'] as $key => $error) {
@@ -232,22 +275,27 @@ class File extends Base
$uploaded_filename = $_FILES[$form_name]['tmp_name'][$key];
$destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
- @mkdir(FILES_DIR.dirname($destination_filename), 0755, true);
+ if ($this->isImage($original_filename)) {
+ $this->generateThumbnailFromFile($uploaded_filename, $destination_filename);
+ }
- if (@move_uploaded_file($uploaded_filename, FILES_DIR.$destination_filename)) {
+ $this->objectStorage->moveUploadedFile($uploaded_filename, $destination_filename);
- $results[] = $this->create(
- $task_id,
- $original_filename,
- $destination_filename,
- $_FILES[$form_name]['size'][$key]
- );
- }
+ $this->create(
+ $task_id,
+ $original_filename,
+ $destination_filename,
+ $_FILES[$form_name]['size'][$key]
+ );
}
}
- }
- return ! in_array(false, $results, true);
+ return true;
+ }
+ catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ return false;
+ }
}
/**
@@ -261,129 +309,77 @@ class File extends Base
*/
public function uploadScreenshot($project_id, $task_id, $blob)
{
- $data = base64_decode($blob);
-
- if (empty($data)) {
- return false;
- }
-
$original_filename = e('Screenshot taken %s', dt('%B %e, %Y at %k:%M %p', time())).'.png';
- $destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
-
- @mkdir(FILES_DIR.dirname($destination_filename), 0755, true);
- @file_put_contents(FILES_DIR.$destination_filename, $data);
-
- return $this->create(
- $task_id,
- $original_filename,
- $destination_filename,
- strlen($data)
- );
+ return $this->uploadContent($project_id, $task_id, $original_filename, $blob);
}
/**
* Handle file upload (base64 encoded content)
*
* @access public
- * @param integer $project_id Project id
- * @param integer $task_id Task id
- * @param string $filename Filename
- * @param string $blob Base64 encoded image
+ * @param integer $project_id Project id
+ * @param integer $task_id Task id
+ * @param string $original_filename Filename
+ * @param string $blob Base64 encoded file
* @return bool|integer
*/
- public function uploadContent($project_id, $task_id, $filename, $blob)
+ public function uploadContent($project_id, $task_id, $original_filename, $blob)
{
- $data = base64_decode($blob);
+ try {
- if (empty($data)) {
- return false;
- }
+ $data = base64_decode($blob);
- $destination_filename = $this->generatePath($project_id, $task_id, $filename);
+ if (empty($data)) {
+ return false;
+ }
+
+ $destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
+ $this->objectStorage->put($destination_filename, $data);
- @mkdir(FILES_DIR.dirname($destination_filename), 0755, true);
- @file_put_contents(FILES_DIR.$destination_filename, $data);
+ if ($this->isImage($original_filename)) {
+ $this->generateThumbnailFromData($destination_filename, $data);
+ }
- return $this->create(
- $task_id,
- $filename,
- $destination_filename,
- strlen($data)
- );
+ return $this->create(
+ $task_id,
+ $original_filename,
+ $destination_filename,
+ strlen($data)
+ );
+ }
+ catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ return false;
+ }
}
/**
- * Generate a jpeg thumbnail from an image (output directly the image)
+ * Generate thumbnail from a blob
*
* @access public
- * @param string $filename Source image
- * @param integer $resize_width Desired image width
- * @param integer $resize_height Desired image height
+ * @param string $destination_filename
+ * @param string $data
*/
- public function generateThumbnail($filename, $resize_width, $resize_height)
+ public function generateThumbnailFromData($destination_filename, &$data)
{
- $metadata = getimagesize($filename);
- $src_width = $metadata[0];
- $src_height = $metadata[1];
- $dst_y = 0;
- $dst_x = 0;
-
- if (empty($metadata['mime'])) {
- return;
- }
-
- if ($resize_width == 0 && $resize_height == 0) {
- $resize_width = 100;
- $resize_height = 100;
- }
+ $temp_filename = tempnam(sys_get_temp_dir(), 'datafile');
- if ($resize_width > 0 && $resize_height == 0) {
- $dst_width = $resize_width;
- $dst_height = floor($src_height * ($resize_width / $src_width));
- $dst_image = imagecreatetruecolor($dst_width, $dst_height);
- }
- elseif ($resize_width == 0 && $resize_height > 0) {
- $dst_width = floor($src_width * ($resize_height / $src_height));
- $dst_height = $resize_height;
- $dst_image = imagecreatetruecolor($dst_width, $dst_height);
- }
- else {
-
- $src_ratio = $src_width / $src_height;
- $resize_ratio = $resize_width / $resize_height;
-
- if ($src_ratio <= $resize_ratio) {
- $dst_width = $resize_width;
- $dst_height = floor($src_height * ($resize_width / $src_width));
-
- $dst_y = ($dst_height - $resize_height) / 2 * (-1);
- }
- else {
- $dst_width = floor($src_width * ($resize_height / $src_height));
- $dst_height = $resize_height;
-
- $dst_x = ($dst_width - $resize_width) / 2 * (-1);
- }
-
- $dst_image = imagecreatetruecolor($resize_width, $resize_height);
- }
-
- switch ($metadata['mime']) {
- case 'image/jpeg':
- case 'image/jpg':
- $src_image = imagecreatefromjpeg($filename);
- break;
- case 'image/png':
- $src_image = imagecreatefrompng($filename);
- break;
- case 'image/gif':
- $src_image = imagecreatefromgif($filename);
- break;
- default:
- return;
- }
+ file_put_contents($temp_filename, $data);
+ $this->generateThumbnailFromFile($temp_filename, $destination_filename);
+ unlink($temp_filename);
+ }
- imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
- imagejpeg($dst_image);
+ /**
+ * Generate thumbnail from a blob
+ *
+ * @access public
+ * @param string $uploaded_filename
+ * @param string $destination_filename
+ */
+ public function generateThumbnailFromFile($uploaded_filename, $destination_filename)
+ {
+ $thumbnail_filename = tempnam(sys_get_temp_dir(), 'thumbnail');
+ Tool::generateThumbnail($uploaded_filename, $thumbnail_filename);
+ $this->objectStorage->moveFile($thumbnail_filename, $this->getThumbnailPath($destination_filename));
}
}
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 630ab9f3..a5677948 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -2,6 +2,7 @@
namespace ServiceProvider;
+use Core\ObjectStorage\FileStorage;
use Core\Paginator;
use Core\OAuth2;
use Core\Tool;
@@ -106,5 +107,9 @@ class ClassProvider implements ServiceProviderInterface
$container['htmlConverter'] = function($c) {
return new HtmlConverter(array('strip_tags' => true));
};
+
+ $container['objectStorage'] = function($c) {
+ return new FileStorage(FILES_DIR);
+ };
}
}
diff --git a/app/Template/file/show.php b/app/Template/file/show.php
index b1a0a813..a390c9fb 100644
--- a/app/Template/file/show.php
+++ b/app/Template/file/show.php
@@ -11,7 +11,7 @@
<li>
<?php if (function_exists('imagecreatetruecolor')): ?>
<div class="img_container">
- <img src="<?= $this->url->href('file', 'thumbnail', array('width' => 250, 'height' => 100, 'file_id' => $file['id'], 'project_id' => $task['project_id'], 'task_id' => $file['task_id'])) ?>" alt="<?= $this->e($file['name']) ?>"/>
+ <img src="<?= $this->url->href('file', 'thumbnail', array('file_id' => $file['id'], 'project_id' => $task['project_id'], 'task_id' => $file['task_id'])) ?>" alt="<?= $this->e($file['name']) ?>"/>
</div>
<?php endif ?>
<p>