diff options
Diffstat (limited to 'app/Model/FileModel.php')
-rw-r--r-- | app/Model/FileModel.php | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/app/Model/FileModel.php b/app/Model/FileModel.php new file mode 100644 index 00000000..8cdea9a0 --- /dev/null +++ b/app/Model/FileModel.php @@ -0,0 +1,378 @@ +<?php + +namespace Kanboard\Model; + +use Exception; +use Kanboard\Core\Base; +use Kanboard\Core\Thumbnail; +use Kanboard\Event\FileEvent; +use Kanboard\Core\ObjectStorage\ObjectStorageException; + +/** + * Base File Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +abstract class FileModel extends Base +{ + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getTable(); + + /** + * Define the foreign key + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getForeignKey(); + + /** + * Get the path prefix + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getPathPrefix(); + + /** + * Get event name + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getEventName(); + + /** + * Get PicoDb query to get all files + * + * @access protected + * @return \PicoDb\Table + */ + protected function getQuery() + { + return $this->db + ->table($this->getTable()) + ->columns( + $this->getTable().'.id', + $this->getTable().'.name', + $this->getTable().'.path', + $this->getTable().'.is_image', + $this->getTable().'.'.$this->getForeignKey(), + $this->getTable().'.date', + $this->getTable().'.user_id', + $this->getTable().'.size', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name as user_name' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc($this->getTable().'.name'); + } + + /** + * Get a file by the id + * + * @access public + * @param integer $file_id File id + * @return array + */ + public function getById($file_id) + { + return $this->db->table($this->getTable())->eq('id', $file_id)->findOne(); + } + + /** + * Get all files + * + * @access public + * @param integer $id + * @return array + */ + public function getAll($id) + { + return $this->getQuery()->eq($this->getForeignKey(), $id)->findAll(); + } + + /** + * Get all images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllImages($id) + { + return $this->getQuery()->eq($this->getForeignKey(), $id)->eq('is_image', 1)->findAll(); + } + + /** + * Get all files without images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllDocuments($id) + { + return $this->getQuery()->eq($this->getForeignKey(), $id)->eq('is_image', 0)->findAll(); + } + + /** + * Create a file entry in the database + * + * @access public + * @param integer $id Foreign key + * @param string $name Filename + * @param string $path Path on the disk + * @param integer $size File size + * @return bool|integer + */ + public function create($id, $name, $path, $size) + { + $values = array( + $this->getForeignKey() => $id, + 'name' => substr($name, 0, 255), + 'path' => $path, + 'is_image' => $this->isImage($name) ? 1 : 0, + 'size' => $size, + 'user_id' => $this->userSession->getId() ?: 0, + 'date' => time(), + ); + + $result = $this->db->table($this->getTable())->insert($values); + + if ($result) { + $file_id = (int) $this->db->getLastId(); + $event = new FileEvent($values + array('file_id' => $file_id)); + $this->dispatcher->dispatch($this->getEventName(), $event); + return $file_id; + } + + return false; + } + + /** + * Remove all files + * + * @access public + * @param integer $id + * @return bool + */ + public function removeAll($id) + { + $file_ids = $this->db->table($this->getTable())->eq($this->getForeignKey(), $id)->asc('id')->findAllByColumn('id'); + $results = array(); + + foreach ($file_ids as $file_id) { + $results[] = $this->remove($file_id); + } + + return ! in_array(false, $results, true); + } + + /** + * Remove a file + * + * @access public + * @param integer $file_id File id + * @return bool + */ + public function remove($file_id) + { + try { + $file = $this->getById($file_id); + $this->objectStorage->remove($file['path']); + + if ($file['is_image'] == 1) { + $this->objectStorage->remove($this->getThumbnailPath($file['path'])); + } + + return $this->db->table($this->getTable())->eq('id', $file['id'])->remove(); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Check if a filename is an image (file types that can be shown as thumbnail) + * + * @access public + * @param string $filename Filename + * @return bool + */ + public function isImage($filename) + { + $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + + switch ($extension) { + case 'jpeg': + case 'jpg': + case 'png': + case 'gif': + return true; + } + + return false; + } + + /** + * Generate the path for a thumbnails + * + * @access public + * @param string $key Storage key + * @return string + */ + public function getThumbnailPath($key) + { + return 'thumbnails'.DIRECTORY_SEPARATOR.$key; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $id Foreign key + * @param string $filename Filename + * @return string + */ + public function generatePath($id, $filename) + { + return $this->getPathPrefix().DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time()); + } + + /** + * Upload multiple files + * + * @access public + * @param integer $id + * @param array $files + * @return bool + */ + public function uploadFiles($id, array $files) + { + try { + if (empty($files)) { + return false; + } + + foreach (array_keys($files['error']) as $key) { + $file = array( + 'name' => $files['name'][$key], + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + ); + + $this->uploadFile($id, $file); + } + + return true; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Upload a file + * + * @access public + * @param integer $id + * @param array $file + * @throws Exception + */ + public function uploadFile($id, array $file) + { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destination_filename = $this->generatePath($id, $file['name']); + + if ($this->isImage($file['name'])) { + $this->generateThumbnailFromFile($file['tmp_name'], $destination_filename); + } + + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename); + $this->create($id, $file['name'], $destination_filename, $file['size']); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + } + + /** + * Handle file upload (base64 encoded content) + * + * @access public + * @param integer $id + * @param string $original_filename + * @param string $blob + * @return bool|integer + */ + public function uploadContent($id, $original_filename, $blob) + { + try { + $data = base64_decode($blob); + + if (empty($data)) { + return false; + } + + $destination_filename = $this->generatePath($id, $original_filename); + $this->objectStorage->put($destination_filename, $data); + + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromData($destination_filename, $data); + } + + return $this->create( + $id, + $original_filename, + $destination_filename, + strlen($data) + ); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Generate thumbnail from a blob + * + * @access public + * @param string $destination_filename + * @param string $data + */ + public function generateThumbnailFromData($destination_filename, &$data) + { + $blob = Thumbnail::createFromString($data) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } + + /** + * Generate thumbnail from a local file + * + * @access public + * @param string $uploaded_filename + * @param string $destination_filename + */ + public function generateThumbnailFromFile($uploaded_filename, $destination_filename) + { + $blob = Thumbnail::createFromFile($uploaded_filename) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } +} |