Add ProjecFile and TaskFile models

This commit is contained in:
Frederic Guillot
2016-02-14 15:25:16 -05:00
parent fbb58e08d3
commit 8e25c875f2
30 changed files with 1223 additions and 563 deletions

View File

@@ -2,31 +2,44 @@
namespace Kanboard\Model;
use Exception;
use Kanboard\Event\FileEvent;
use Kanboard\Core\Tool;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* File model
* Base File Model
*
* @package model
* @author Frederic Guillot
*/
class File extends Base
abstract class File extends Base
{
/**
* SQL table name
* Get PicoDb query to get all files
*
* @var string
* @access protected
* @return \PicoDb\Table
*/
const TABLE = 'files';
/**
* Events
*
* @var string
*/
const EVENT_CREATE = 'file.create';
protected function getQuery()
{
return $this->db
->table(static::TABLE)
->columns(
static::TABLE.'.id',
static::TABLE.'.name',
static::TABLE.'.path',
static::TABLE.'.is_image',
static::TABLE.'.'.static::FOREIGN_KEY,
static::TABLE.'.date',
static::TABLE.'.user_id',
static::TABLE.'.size',
User::TABLE.'.username',
User::TABLE.'.name as user_name'
)
->join(User::TABLE, 'id', 'user_id')
->asc(static::TABLE.'.name');
}
/**
* Get a file by the id
@@ -37,7 +50,96 @@ class File extends Base
*/
public function getById($file_id)
{
return $this->db->table(self::TABLE)->eq('id', $file_id)->findOne();
return $this->db->table(static::TABLE)->eq('id', $file_id)->findOne();
}
/**
* Get all files
*
* @access public
* @param integer $id
* @return array
*/
public function getAll($id)
{
return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->findAll();
}
/**
* Get all images
*
* @access public
* @param integer $id
* @return array
*/
public function getAllImages($id)
{
return $this->getQuery()->eq(static::FOREIGN_KEY, $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(static::FOREIGN_KEY, $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(
static::FOREIGN_KEY => $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(static::TABLE)->insert($values);
if ($result) {
$file_id = (int) $this->db->getLastId();
$event = new FileEvent($values + array('file_id' => $file_id));
$this->dispatcher->dispatch(static::EVENT_CREATE, $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(static::TABLE)->eq(static::FOREIGN_KEY, $id)->asc('id')->findAllByColumn('id');
$results = array();
foreach ($file_ids as $file_id) {
$results[] = $this->remove($file_id);
}
return ! in_array(false, $results, true);
}
/**
@@ -50,135 +152,20 @@ class File extends Base
public function remove($file_id)
{
try {
$file = $this->getbyId($file_id);
$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(self::TABLE)->eq('id', $file['id'])->remove();
return $this->db->table(static::TABLE)->eq('id', $file['id'])->remove();
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
return false;
}
}
/**
* Remove all files for a given task
*
* @access public
* @param integer $task_id Task id
* @return bool
*/
public function removeAll($task_id)
{
$file_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->asc('id')->findAllByColumn('id');
$results = array();
foreach ($file_ids as $file_id) {
$results[] = $this->remove($file_id);
}
return ! in_array(false, $results, true);
}
/**
* Create a file entry in the database
*
* @access public
* @param integer $task_id Task id
* @param string $name Filename
* @param string $path Path on the disk
* @param integer $size File size
* @return bool|integer
*/
public function create($task_id, $name, $path, $size)
{
$result = $this->db->table(self::TABLE)->save(array(
'task_id' => $task_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(),
));
if ($result) {
$this->container['dispatcher']->dispatch(
self::EVENT_CREATE,
new FileEvent(array('task_id' => $task_id, 'name' => $name))
);
return (int) $this->db->getLastId();
}
return false;
}
/**
* Get PicoDb query to get all files
*
* @access public
* @return \PicoDb\Table
*/
public function getQuery()
{
return $this->db
->table(self::TABLE)
->columns(
self::TABLE.'.id',
self::TABLE.'.name',
self::TABLE.'.path',
self::TABLE.'.is_image',
self::TABLE.'.task_id',
self::TABLE.'.date',
self::TABLE.'.user_id',
self::TABLE.'.size',
User::TABLE.'.username',
User::TABLE.'.name as user_name'
)
->join(User::TABLE, 'id', 'user_id')
->asc(self::TABLE.'.name');
}
/**
* Get all files for a given task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAll($task_id)
{
return $this->getQuery()->eq('task_id', $task_id)->findAll();
}
/**
* Get all images for a given task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAllImages($task_id)
{
return $this->getQuery()->eq('task_id', $task_id)->eq('is_image', 1)->findAll();
}
/**
* Get all files without images for a given task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAllDocuments($task_id)
{
return $this->getQuery()->eq('task_id', $task_id)->eq('is_image', 0)->findAll();
}
/**
* Check if a filename is an image (file types that can be shown as thumbnail)
*
@@ -225,20 +212,6 @@ class File extends Base
}
}
/**
* Generate the path for a new filename
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $filename Filename
* @return string
*/
public function generatePath($project_id, $task_id, $filename)
{
return $project_id.DIRECTORY_SEPARATOR.$task_id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time());
}
/**
* Generate the path for a thumbnails
*
@@ -252,77 +225,84 @@ class File extends Base
}
/**
* Handle file upload
* Generate the path for a new filename
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $form_name File form name
* @param integer $id Foreign key
* @param string $filename Filename
* @return string
*/
public function generatePath($id, $filename)
{
return static::PATH_PREFIX.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($project_id, $task_id, $form_name)
public function uploadFiles($id, array $files)
{
try {
$file = $this->request->getFileInfo($form_name);
if (empty($file)) {
if (empty($files)) {
return false;
}
foreach ($file['error'] as $key => $error) {
if ($error == UPLOAD_ERR_OK && $file['size'][$key] > 0) {
$original_filename = $file['name'][$key];
$uploaded_filename = $file['tmp_name'][$key];
$destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
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],
);
if ($this->isImage($original_filename)) {
$this->generateThumbnailFromFile($uploaded_filename, $destination_filename);
}
$this->objectStorage->moveUploadedFile($uploaded_filename, $destination_filename);
$this->create(
$task_id,
$original_filename,
$destination_filename,
$file['size'][$key]
);
}
$this->uploadFile($id, $file);
}
return true;
} catch (ObjectStorageException $e) {
} catch (Exception $e) {
$this->logger->error($e->getMessage());
return false;
}
}
/**
* Handle screenshot upload
* Upload a file
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $blob Base64 encoded image
* @return bool|integer
* @param integer $id
* @param array $file
*/
public function uploadScreenshot($project_id, $task_id, $blob)
public function uploadFile($id, array $file)
{
$original_filename = e('Screenshot taken %s', $this->helper->dt->datetime(time())).'.png';
return $this->uploadContent($project_id, $task_id, $original_filename, $blob);
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 $project_id Project id
* @param integer $task_id Task id
* @param string $original_filename Filename
* @param string $blob Base64 encoded file
* @param integer $id
* @param string $original_filename
* @param string $blob
* @return bool|integer
*/
public function uploadContent($project_id, $task_id, $original_filename, $blob)
public function uploadContent($id, $original_filename, $blob)
{
try {
$data = base64_decode($blob);
@@ -331,7 +311,7 @@ class File extends Base
return false;
}
$destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
$destination_filename = $this->generatePath($id, $original_filename);
$this->objectStorage->put($destination_filename, $data);
if ($this->isImage($original_filename)) {
@@ -339,7 +319,7 @@ class File extends Base
}
return $this->create(
$task_id,
$id,
$original_filename,
$destination_filename,
strlen($data)

View File

@@ -72,7 +72,7 @@ class Notification extends Base
return e('%s updated a comment on the task #%d', $event_author, $event_data['task']['id']);
case Comment::EVENT_CREATE:
return e('%s commented on the task #%d', $event_author, $event_data['task']['id']);
case File::EVENT_CREATE:
case TaskFile::EVENT_CREATE:
return e('%s attached a file to the task #%d', $event_author, $event_data['task']['id']);
case Task::EVENT_USER_MENTION:
return e('%s mentioned you in the task #%d', $event_author, $event_data['task']['id']);
@@ -94,7 +94,7 @@ class Notification extends Base
public function getTitleWithoutAuthor($event_name, array $event_data)
{
switch ($event_name) {
case File::EVENT_CREATE:
case TaskFile::EVENT_CREATE:
return e('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
case Comment::EVENT_CREATE:
return e('New comment on task #%d', $event_data['comment']['task_id']);

40
app/Model/ProjectFile.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace Kanboard\Model;
/**
* Project File Model
*
* @package model
* @author Frederic Guillot
*/
class ProjectFile extends File
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'project_has_files';
/**
* SQL foreign key
*
* @var string
*/
const FOREIGN_KEY = 'project_id';
/**
* Path prefix
*
* @var string
*/
const PATH_PREFIX = 'projects';
/**
* Events
*
* @var string
*/
const EVENT_CREATE = 'project.file.create';
}

View File

@@ -92,7 +92,7 @@ class Task extends Base
return false;
}
$this->file->removeAll($task_id);
$this->taskFile->removeAll($task_id);
return $this->db->table(self::TABLE)->eq('id', $task_id)->remove();
}

54
app/Model/TaskFile.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
namespace Kanboard\Model;
/**
* Task File Model
*
* @package model
* @author Frederic Guillot
*/
class TaskFile extends File
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'task_has_files';
/**
* SQL foreign key
*
* @var string
*/
const FOREIGN_KEY = 'task_id';
/**
* Path prefix
*
* @var string
*/
const PATH_PREFIX = 'tasks';
/**
* Events
*
* @var string
*/
const EVENT_CREATE = 'task.file.create';
/**
* Handle screenshot upload
*
* @access public
* @param integer $task_id Task id
* @param string $blob Base64 encoded image
* @return bool|integer
*/
public function uploadScreenshot($task_id, $blob)
{
$original_filename = e('Screenshot taken %s', $this->helper->dt->datetime(time())).'.png';
return $this->uploadContent($task_id, $original_filename, $blob);
}
}

View File

@@ -89,7 +89,7 @@ class TaskFinder extends Base
->table(Task::TABLE)
->columns(
'(SELECT COUNT(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments',
'(SELECT COUNT(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files',
'(SELECT COUNT(*) FROM '.TaskFile::TABLE.' WHERE task_id=tasks.id) AS nb_files',
'(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks',
'(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks',
'(SELECT COUNT(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links',