Add files upload

This commit is contained in:
Frédéric Guillot 2014-05-22 20:58:21 -04:00
parent 2230dd4e6b
commit 40917992e7
25 changed files with 549 additions and 53 deletions

1
.gitignore vendored
View File

@ -52,3 +52,4 @@ Thumbs.db
# App specific # # App specific #
################ ################
config.php config.php
data/files

View File

@ -216,6 +216,7 @@ abstract class Base
'task' => $task, 'task' => $task,
'columns_list' => $this->board->getColumnsList($task['project_id']), 'columns_list' => $this->board->getColumnsList($task['project_id']),
'colors_list' => $this->task->getColors(), 'colors_list' => $this->task->getColors(),
'files' => $this->file->getAll($task['id']),
'menu' => 'tasks', 'menu' => 'tasks',
'title' => $task['title'], 'title' => $task['title'],
))); )));

View File

@ -3,6 +3,7 @@
namespace Controller; namespace Controller;
use Model\Project; use Model\Project;
use Model\File;
/** /**
* Task controller * Task controller
@ -12,6 +13,19 @@ use Model\Project;
*/ */
class Task extends Base 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) * Webhook to create a task (useful for external software)
* *
@ -57,12 +71,7 @@ class Task extends Base
*/ */
public function show() public function show()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true); $this->showTask($this->getTask());
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
$this->showTask($task);
} }
/** /**
@ -247,10 +256,7 @@ class Task extends Base
*/ */
public function close() public function close()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id')); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
if ($this->task->close($task['id'])) { if ($this->task->close($task['id'])) {
$this->session->flash(t('Task closed successfully.')); $this->session->flash(t('Task closed successfully.'));
@ -268,10 +274,7 @@ class Task extends Base
*/ */
public function confirmClose() public function confirmClose()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
$this->response->html($this->taskLayout('task_close', array( $this->response->html($this->taskLayout('task_close', array(
'task' => $task, 'task' => $task,
@ -287,10 +290,7 @@ class Task extends Base
*/ */
public function open() public function open()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id')); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
if ($this->task->open($task['id'])) { if ($this->task->open($task['id'])) {
$this->session->flash(t('Task opened successfully.')); $this->session->flash(t('Task opened successfully.'));
@ -308,10 +308,7 @@ class Task extends Base
*/ */
public function confirmOpen() public function confirmOpen()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
$this->response->html($this->taskLayout('task_open', array( $this->response->html($this->taskLayout('task_open', array(
'task' => $task, 'task' => $task,
@ -327,10 +324,7 @@ class Task extends Base
*/ */
public function remove() public function remove()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id')); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
if ($this->task->remove($task['id'])) { if ($this->task->remove($task['id'])) {
$this->session->flash(t('Task removed successfully.')); $this->session->flash(t('Task removed successfully.'));
@ -348,10 +342,7 @@ class Task extends Base
*/ */
public function confirmRemove() public function confirmRemove()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
$this->response->html($this->taskLayout('task_remove', array( $this->response->html($this->taskLayout('task_remove', array(
'task' => $task, 'task' => $task,
@ -367,10 +358,7 @@ class Task extends Base
*/ */
public function duplicate() public function duplicate()
{ {
$task = $this->task->getById($this->request->getIntegerParam('task_id')); $task = $this->getTask();
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
if (! empty($task['date_due'])) { if (! empty($task['date_due'])) {
$task['date_due'] = date(t('m/d/Y'), $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') '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')
)));
}
} }

View File

@ -4,6 +4,11 @@ namespace Core;
class Response class Response
{ {
public function contentType($mimetype)
{
header('Content-Type: '.$mimetype);
}
public function forceDownload($filename) public function forceDownload($filename)
{ {
header('Content-Disposition: attachment; filename="'.$filename.'"'); header('Content-Disposition: attachment; filename="'.$filename.'"');

View File

@ -331,4 +331,11 @@ return array(
// 'All categories' => '', // 'All categories' => '',
// 'No category' => '', // 'No category' => '',
// 'The name is required' => '', // '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' => '',
); );

View File

@ -331,4 +331,11 @@ return array(
'All categories' => 'Toutes les catégories', 'All categories' => 'Toutes les catégories',
'No category' => 'Aucune catégorie', 'No category' => 'Aucune catégorie',
'The name is required' => 'Le nom est requis', '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',
); );

View File

@ -336,4 +336,11 @@ return array(
// 'All categories' => '', // 'All categories' => '',
// 'No category' => '', // 'No category' => '',
// 'The name is required' => '', // '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' => '',
); );

View File

@ -332,4 +332,11 @@ return array(
// 'All categories' => '', // 'All categories' => '',
// 'No category' => '', // 'No category' => '',
// 'The name is required' => '', // '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' => '',
); );

View File

@ -32,10 +32,31 @@ class Acl extends Base
'app' => array('index'), 'app' => array('index'),
'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'), 'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'),
'project' => array('tasks', 'index', 'forbidden', 'search'), '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'), 'comment' => array('save', 'confirm', 'remove', 'update', 'edit'),
'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle'), 'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle'),
'config' => array('index', 'removeremembermetoken'), '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',
),
); );
/** /**

176
app/Model/File.php Normal file
View File

@ -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)
);
}
}
}
}
}
}

View File

@ -139,6 +139,7 @@ class Task extends Base
->table(self::TABLE) ->table(self::TABLE)
->columns( ->columns(
'(SELECT count(*) FROM comments WHERE task_id=tasks.id) AS nb_comments', '(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.id',
'tasks.title', 'tasks.title',
'tasks.description', 'tasks.description',

View File

@ -2,7 +2,22 @@
namespace Schema; 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) function version_16($pdo)
{ {

View File

@ -2,7 +2,21 @@
namespace Schema; 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) function version_16($pdo)
{ {

View File

@ -59,7 +59,7 @@
</div> </div>
<?php endif ?> <?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"> <div class="task-footer">
<?php if (! empty($task['date_due'])): ?> <?php if (! empty($task['date_due'])): ?>
@ -69,6 +69,10 @@
<?php endif ?> <?php endif ?>
<div class="task-icons"> <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'])): ?> <?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> <?= $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 ?> <?php endif ?>

View File

@ -1,3 +1,7 @@
<div class="page-header">
<h2><?= t('Close a task') ?></h2>
</div>
<div class="confirm"> <div class="confirm">
<p class="alert alert-info"> <p class="alert alert-info">
<?= t('Do you really want to close this task: "%s"?', Helper\escape($task['title'])) ?> <?= t('Do you really want to close this task: "%s"?', Helper\escape($task['title'])) ?>

View File

@ -13,4 +13,5 @@
<?= $task_content_for_layout ?> <?= $task_content_for_layout ?>
</div> </div>
</section> </section>
</section> </section>
<script type="text/javascript" src="assets/js/task.js"></script>

View File

@ -1,16 +1,14 @@
<section id="main"> <div class="page-header">
<div class="page-header"> <h2><?= t('Open a task') ?></h2>
<h2><?= t('Open a task') ?></h2> </div>
</div>
<div class="confirm"> <div class="confirm">
<p class="alert alert-info"> <p class="alert alert-info">
<?= t('Do you really want to open this task: "%s"?', Helper\escape($task['title'])) ?> <?= t('Do you really want to open this task: "%s"?', Helper\escape($task['title'])) ?>
</p> </p>
<div class="form-actions"> <div class="form-actions">
<a href="?controller=task&amp;action=open&amp;task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> <a href="?controller=task&amp;action=open&amp;task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
<?= t('or') ?> <a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a> <?= t('or') ?> <a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
</div>
</div> </div>
</section> </div>

View File

@ -0,0 +1,6 @@
<div class="page-header">
<h2><?= Helper\escape($file['name']) ?></h2>
<div class="task-file-viewer">
<img src="?controller=task&amp;action=image&amp;file_id=<?= $file['id'] ?>&amp;task_id=<?= $file['task_id'] ?>" alt="<?= Helper\escape($file['name']) ?>"/>
</div>
</div>

View File

@ -1,3 +1,7 @@
<div class="page-header">
<h2><?= t('Remove a task') ?></h2>
</div>
<div class="confirm"> <div class="confirm">
<p class="alert alert-info"> <p class="alert alert-info">
<?= t('Do you really want to remove this task: "%s"?', Helper\escape($task['title'])) ?> <?= t('Do you really want to remove this task: "%s"?', Helper\escape($task['title'])) ?>

View File

@ -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&amp;action=removeFile&amp;task_id=<?= $task['id'] ?>&amp;file_id=<?= $file['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
<?= t('or') ?> <a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
</div>
</div>

View File

@ -64,6 +64,23 @@
</form> </form>
<?php endif ?> <?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&amp;action=download&amp;file_id=<?= $file['id'] ?>&amp;task_id=<?= $task['id'] ?>"><?= Helper\escape($file['name']) ?></a>
<span class="task-show-file-actions">
<?php if ($file['is_image']): ?>
<a href="?controller=task&amp;action=openFile&amp;file_id=<?= $file['id'] ?>&amp;task_id=<?= $task['id'] ?>" class="popover"><?= t('open') ?></a>,
<?php endif ?>
<a href="?controller=task&amp;action=confirmRemoveFile&amp;file_id=<?= $file['id'] ?>&amp;task_id=<?= $task['id'] ?>"><?= t('remove') ?></a>
</span>
</li>
<?php endforeach ?>
</ul>
<?php endif ?>
<h2><?= t('Comments') ?></h2> <h2><?= t('Comments') ?></h2>
<?php if ($comments): ?> <?php if ($comments): ?>
<ul id="comments"> <ul id="comments">

View File

@ -2,8 +2,10 @@
<h2><?= t('Actions') ?></h2> <h2><?= t('Actions') ?></h2>
<div class="task-show-actions"> <div class="task-show-actions">
<ul> <ul>
<li><a href="?controller=task&amp;action=duplicate&amp;project_id=<?= $task['project_id'] ?>&amp;task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li> <li><a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>"><?= t('Description') ?></a></li>
<li><a href="?controller=task&amp;action=edit&amp;task_id=<?= $task['id'] ?>"><?= t('Edit') ?></a></li> <li><a href="?controller=task&amp;action=edit&amp;task_id=<?= $task['id'] ?>"><?= t('Edit') ?></a></li>
<li><a href="?controller=task&amp;action=file&amp;task_id=<?= $task['id'] ?>"><?= t('Attach a document') ?></a></li>
<li><a href="?controller=task&amp;action=duplicate&amp;project_id=<?= $task['project_id'] ?>&amp;task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li>
<li> <li>
<?php if ($task['is_active'] == 1): ?> <?php if ($task['is_active'] == 1): ?>
<a href="?controller=task&amp;action=confirmClose&amp;task_id=<?= $task['id'] ?>"><?= t('Close this task') ?></a> <a href="?controller=task&amp;action=confirmClose&amp;task_id=<?= $task['id'] ?>"><?= t('Close this task') ?></a>

View File

@ -0,0 +1,10 @@
<div class="page-header">
<h2><?= t('Attach a document') ?></h2>
</div>
<form action="?controller=task&amp;action=upload&amp;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>

View File

@ -702,6 +702,43 @@ div.task .task-score {
padding: 10px; 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 content */
.markdown { .markdown {
line-height: 1.4em; line-height: 1.4em;

27
assets/js/task.js Normal file
View File

@ -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);
});
});
}());