Add files upload
This commit is contained in:
parent
2230dd4e6b
commit
40917992e7
|
|
@ -52,3 +52,4 @@ Thumbs.db
|
|||
# App specific #
|
||||
################
|
||||
config.php
|
||||
data/files
|
||||
|
|
@ -216,6 +216,7 @@ abstract class Base
|
|||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'files' => $this->file->getAll($task['id']),
|
||||
'menu' => 'tasks',
|
||||
'title' => $task['title'],
|
||||
)));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Controller;
|
||||
|
||||
use Model\Project;
|
||||
use Model\File;
|
||||
|
||||
/**
|
||||
* Task controller
|
||||
|
|
@ -12,6 +13,19 @@ use Model\Project;
|
|||
*/
|
||||
class Task extends Base
|
||||
{
|
||||
private function getTask()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webhook to create a task (useful for external software)
|
||||
*
|
||||
|
|
@ -57,12 +71,7 @@ class Task extends Base
|
|||
*/
|
||||
public function show()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
$this->showTask($task);
|
||||
$this->showTask($this->getTask());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,10 +256,7 @@ class Task extends Base
|
|||
*/
|
||||
public function close()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->close($task['id'])) {
|
||||
$this->session->flash(t('Task closed successfully.'));
|
||||
|
|
@ -268,10 +274,7 @@ class Task extends Base
|
|||
*/
|
||||
public function confirmClose()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_close', array(
|
||||
'task' => $task,
|
||||
|
|
@ -287,10 +290,7 @@ class Task extends Base
|
|||
*/
|
||||
public function open()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->open($task['id'])) {
|
||||
$this->session->flash(t('Task opened successfully.'));
|
||||
|
|
@ -308,10 +308,7 @@ class Task extends Base
|
|||
*/
|
||||
public function confirmOpen()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_open', array(
|
||||
'task' => $task,
|
||||
|
|
@ -327,10 +324,7 @@ class Task extends Base
|
|||
*/
|
||||
public function remove()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->remove($task['id'])) {
|
||||
$this->session->flash(t('Task removed successfully.'));
|
||||
|
|
@ -348,10 +342,7 @@ class Task extends Base
|
|||
*/
|
||||
public function confirmRemove()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_remove', array(
|
||||
'task' => $task,
|
||||
|
|
@ -367,10 +358,7 @@ class Task extends Base
|
|||
*/
|
||||
public function duplicate()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
$task = $this->getTask();
|
||||
|
||||
if (! empty($task['date_due'])) {
|
||||
$task['date_due'] = date(t('m/d/Y'), $task['date_due']);
|
||||
|
|
@ -394,4 +382,126 @@ class Task extends Base
|
|||
'title' => t('New task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_upload', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Attach a document')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload (save files)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function upload()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->file->upload($task['project_id'], $task['id'], 'files');
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#attachments');
|
||||
}
|
||||
|
||||
/**
|
||||
* File download
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
$filename = File::BASE_PATH.$file['path'];
|
||||
|
||||
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
|
||||
$this->response->forceDownload($file['name']);
|
||||
$this->response->binary(file_get_contents($filename));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a file (show the content in a popover)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function openFile()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id']) {
|
||||
$this->response->html($this->template->load('task_open_file', array(
|
||||
'file' => $file
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file content (work only for images)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
$filename = File::BASE_PATH.$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function removeFile()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id'] && $this->file->remove($file['id'])) {
|
||||
$this->session->flash(t('File removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmRemoveFile()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
$this->response->html($this->taskLayout('task_remove_file', array(
|
||||
'task' => $task,
|
||||
'file' => $file,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a file')
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ namespace Core;
|
|||
|
||||
class Response
|
||||
{
|
||||
public function contentType($mimetype)
|
||||
{
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
|
||||
public function forceDownload($filename)
|
||||
{
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
|
|
|
|||
|
|
@ -331,4 +331,11 @@ return array(
|
|||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
// 'Remove a file' => '',
|
||||
// 'Unable to remove this file.' => '',
|
||||
// 'File removed successfully.' => '',
|
||||
// 'Attach a document' => '',
|
||||
// 'Do you really want to remove this file: "%s"?' => '',
|
||||
// 'open' => '',
|
||||
// 'Attachments' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -331,4 +331,11 @@ return array(
|
|||
'All categories' => 'Toutes les catégories',
|
||||
'No category' => 'Aucune catégorie',
|
||||
'The name is required' => 'Le nom est requis',
|
||||
'Remove a file' => 'Supprimer un fichier',
|
||||
'Unable to remove this file.' => 'Impossible de supprimer ce fichier.',
|
||||
'File removed successfully.' => 'Fichier supprimé avec succès.',
|
||||
'Attach a document' => 'Joindre un document',
|
||||
'Do you really want to remove this file: "%s"?' => 'Voulez-vous vraiment supprimer ce fichier « %s » ?',
|
||||
'open' => 'ouvrir',
|
||||
'Attachments' => 'Pièces-jointes',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -336,4 +336,11 @@ return array(
|
|||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
// 'Remove a file' => '',
|
||||
// 'Unable to remove this file.' => '',
|
||||
// 'File removed successfully.' => '',
|
||||
// 'Attach a document' => '',
|
||||
// 'Do you really want to remove this file: "%s"?' => '',
|
||||
// 'open' => '',
|
||||
// 'Attachments' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -332,4 +332,11 @@ return array(
|
|||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
// 'Remove a file' => '',
|
||||
// 'Unable to remove this file.' => '',
|
||||
// 'File removed successfully.' => '',
|
||||
// 'Attach a document' => '',
|
||||
// 'Do you really want to remove this file: "%s"?' => '',
|
||||
// 'open' => '',
|
||||
// 'Attachments' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,10 +32,31 @@ class Acl extends Base
|
|||
'app' => array('index'),
|
||||
'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'),
|
||||
'project' => array('tasks', 'index', 'forbidden', 'search'),
|
||||
'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'confirmclose', 'open', 'confirmopen', 'description', 'duplicate', 'remove', 'confirmremove'),
|
||||
'comment' => array('save', 'confirm', 'remove', 'update', 'edit'),
|
||||
'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle'),
|
||||
'config' => array('index', 'removeremembermetoken'),
|
||||
'task' => array(
|
||||
'show',
|
||||
'create',
|
||||
'save',
|
||||
'edit',
|
||||
'update',
|
||||
'close',
|
||||
'confirmclose',
|
||||
'open',
|
||||
'confirmopen',
|
||||
'description',
|
||||
'duplicate',
|
||||
'remove',
|
||||
'confirmremove',
|
||||
'file',
|
||||
'upload',
|
||||
'download',
|
||||
'openfile',
|
||||
'image',
|
||||
'removefile',
|
||||
'confirmremovefile',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* File model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class File extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'task_has_files';
|
||||
|
||||
/**
|
||||
* Directory where are stored files
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const BASE_PATH = 'data/files/';
|
||||
|
||||
/**
|
||||
* 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(self::TABLE)->eq('id', $file_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
*
|
||||
* @access public
|
||||
* @param integer $file_id File id
|
||||
* @return bool
|
||||
*/
|
||||
public function remove($file_id)
|
||||
{
|
||||
$file = $this->getbyId($file_id);
|
||||
|
||||
if (! empty($file) && @unlink(self::BASE_PATH.$file['path'])) {
|
||||
return $this->db->table(self::TABLE)->eq('id', $file_id)->remove();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 bool $is_image Image or not
|
||||
* @return bool
|
||||
*/
|
||||
public function create($task_id, $name, $path, $is_image)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->save(array(
|
||||
'task_id' => $task_id,
|
||||
'name' => $name,
|
||||
'path' => $path,
|
||||
'is_image' => $is_image ? '1' : '0',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all files for a given task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($task_id)
|
||||
{
|
||||
return $listing = $this->db->table(self::TABLE)
|
||||
->eq('task_id', $task_id)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a filename is an image
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename Filename
|
||||
* @return bool
|
||||
*/
|
||||
public function isImage($filename)
|
||||
{
|
||||
return getimagesize($filename) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 bool
|
||||
*/
|
||||
public function generatePath($project_id, $task_id, $filename)
|
||||
{
|
||||
return $project_id.DIRECTORY_SEPARATOR.$task_id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the base directory is created correctly
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function setup()
|
||||
{
|
||||
if (! is_dir(self::BASE_PATH)) {
|
||||
if (! mkdir(self::BASE_PATH, 0755, true)) {
|
||||
die('Unable to create the upload directory: "'.self::BASE_PATH.'"');
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_writable(self::BASE_PATH)) {
|
||||
die('The directory "'.self::BASE_PATH.'" must be writeable by your webserver user');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle file upload
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param string $form_name File form name
|
||||
*/
|
||||
public function upload($project_id, $task_id, $form_name)
|
||||
{
|
||||
$this->setup();
|
||||
|
||||
if (! empty($_FILES[$form_name])) {
|
||||
|
||||
foreach ($_FILES[$form_name]['error'] as $key => $error) {
|
||||
|
||||
if ($error == UPLOAD_ERR_OK && $_FILES[$form_name]['size'][$key] > 0) {
|
||||
|
||||
$original_filename = basename($_FILES[$form_name]['name'][$key]);
|
||||
$uploaded_filename = $_FILES[$form_name]['tmp_name'][$key];
|
||||
$destination_filename = $this->generatePath($project_id, $task_id, $original_filename);
|
||||
|
||||
@mkdir(self::BASE_PATH.dirname($destination_filename), 0755, true);
|
||||
|
||||
if (@move_uploaded_file($uploaded_filename, self::BASE_PATH.$destination_filename)) {
|
||||
|
||||
$this->create(
|
||||
$task_id,
|
||||
$original_filename,
|
||||
$destination_filename,
|
||||
$this->isImage(self::BASE_PATH.$destination_filename)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -139,6 +139,7 @@ class Task extends Base
|
|||
->table(self::TABLE)
|
||||
->columns(
|
||||
'(SELECT count(*) FROM comments WHERE task_id=tasks.id) AS nb_comments',
|
||||
'(SELECT count(*) FROM task_has_files WHERE task_id=tasks.id) AS nb_files',
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.description',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,22 @@
|
|||
|
||||
namespace Schema;
|
||||
|
||||
const VERSION = 16;
|
||||
const VERSION = 17;
|
||||
|
||||
function version_17($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE task_has_files (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(50),
|
||||
path VARCHAR(255),
|
||||
is_image TINYINT(1) DEFAULT 0,
|
||||
task_id INT,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8"
|
||||
);
|
||||
}
|
||||
|
||||
function version_16($pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,21 @@
|
|||
|
||||
namespace Schema;
|
||||
|
||||
const VERSION = 16;
|
||||
const VERSION = 17;
|
||||
|
||||
function version_17($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE task_has_files (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT COLLATE NOCASE,
|
||||
path TEXT,
|
||||
is_image INTEGER DEFAULT 0,
|
||||
task_id INTEGER,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)"
|
||||
);
|
||||
}
|
||||
|
||||
function version_16($pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_files']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
|
||||
<div class="task-footer">
|
||||
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
|
|
@ -69,6 +69,10 @@
|
|||
<?php endif ?>
|
||||
|
||||
<div class="task-icons">
|
||||
<?php if (! empty($task['nb_files'])): ?>
|
||||
<?= $task['nb_files'] ?> <i class="fa fa-paperclip"></i>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<?php endif ?>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Close a task') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to close this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
|
|
|
|||
|
|
@ -13,4 +13,5 @@
|
|||
<?= $task_content_for_layout ?>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<script type="text/javascript" src="assets/js/task.js"></script>
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Open a task') ?></h2>
|
||||
</div>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Open a task') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to open this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
</p>
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to open this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=open&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=open&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<div class="page-header">
|
||||
<h2><?= Helper\escape($file['name']) ?></h2>
|
||||
<div class="task-file-viewer">
|
||||
<img src="?controller=task&action=image&file_id=<?= $file['id'] ?>&task_id=<?= $file['task_id'] ?>" alt="<?= Helper\escape($file['name']) ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove a task') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove a file') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this file: "%s"?', Helper\escape($file['name'])) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=removeFile&task_id=<?= $task['id'] ?>&file_id=<?= $file['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -64,6 +64,23 @@
|
|||
</form>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($files)): ?>
|
||||
<h2 id="attachments"><?= t('Attachments') ?></h2>
|
||||
<ul class="task-show-files">
|
||||
<?php foreach ($files as $file): ?>
|
||||
<li>
|
||||
<a href="?controller=task&action=download&file_id=<?= $file['id'] ?>&task_id=<?= $task['id'] ?>"><?= Helper\escape($file['name']) ?></a>
|
||||
<span class="task-show-file-actions">
|
||||
<?php if ($file['is_image']): ?>
|
||||
<a href="?controller=task&action=openFile&file_id=<?= $file['id'] ?>&task_id=<?= $task['id'] ?>" class="popover"><?= t('open') ?></a>,
|
||||
<?php endif ?>
|
||||
<a href="?controller=task&action=confirmRemoveFile&file_id=<?= $file['id'] ?>&task_id=<?= $task['id'] ?>"><?= t('remove') ?></a>
|
||||
</span>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
|
||||
<h2><?= t('Comments') ?></h2>
|
||||
<?php if ($comments): ?>
|
||||
<ul id="comments">
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
<h2><?= t('Actions') ?></h2>
|
||||
<div class="task-show-actions">
|
||||
<ul>
|
||||
<li><a href="?controller=task&action=duplicate&project_id=<?= $task['project_id'] ?>&task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li>
|
||||
<li><a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('Description') ?></a></li>
|
||||
<li><a href="?controller=task&action=edit&task_id=<?= $task['id'] ?>"><?= t('Edit') ?></a></li>
|
||||
<li><a href="?controller=task&action=file&task_id=<?= $task['id'] ?>"><?= t('Attach a document') ?></a></li>
|
||||
<li><a href="?controller=task&action=duplicate&project_id=<?= $task['project_id'] ?>&task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li>
|
||||
<li>
|
||||
<?php if ($task['is_active'] == 1): ?>
|
||||
<a href="?controller=task&action=confirmClose&task_id=<?= $task['id'] ?>"><?= t('Close this task') ?></a>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Attach a document') ?></h2>
|
||||
</div>
|
||||
|
||||
<form action="?controller=task&action=upload&task_id=<?= $task['id'] ?>" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="files[]" multiple />
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -702,6 +702,43 @@ div.task .task-score {
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
.task-show-files a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.task-show-files li {
|
||||
margin-left: 25px;
|
||||
list-style-type: square;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.task-show-file-actions {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.task-show-file-actions:before {
|
||||
content: " [";
|
||||
}
|
||||
|
||||
.task-show-file-actions:after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
.task-show-file-actions a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.task-file-viewer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.task-file-viewer img {
|
||||
max-width: 95%;
|
||||
max-height: 85%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* markdown content */
|
||||
.markdown {
|
||||
line-height: 1.4em;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
(function () {
|
||||
|
||||
// Show popup
|
||||
function popover_show(content)
|
||||
{
|
||||
$("body").append('<div id="popover-container"><div id="popover-content">' + content + '</div></div>');
|
||||
|
||||
$("#popover-container").click(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
$("#popover-content").click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
$(".popover").click(function(e) {
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$.get($(this).attr("href"), function(data) {
|
||||
popover_show(data);
|
||||
});
|
||||
});
|
||||
|
||||
}());
|
||||
Loading…
Reference in New Issue