Merge branch 'master' of https://github.com/ukko/kanboard into 1245_bug_with_subtask_timer
This commit is contained in:
@@ -126,6 +126,17 @@ abstract class Base extends \Core\Base
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getProjectId()
|
||||
{
|
||||
return $this->project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an user defined parameter
|
||||
*
|
||||
|
||||
@@ -54,6 +54,8 @@ abstract class Base extends \Core\Base
|
||||
else if (! $is_user && ! $is_both_procedure && $is_user_procedure) {
|
||||
throw new AccessDeniedException('Permission denied');
|
||||
}
|
||||
|
||||
$this->logger->debug('API call: '.$procedure);
|
||||
}
|
||||
|
||||
public function checkProjectPermission($project_id)
|
||||
@@ -70,7 +72,7 @@ abstract class Base extends \Core\Base
|
||||
}
|
||||
}
|
||||
|
||||
protected function formatTask(array $task)
|
||||
protected function formatTask($task)
|
||||
{
|
||||
if (! empty($task)) {
|
||||
$task['url'] = $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), '', true);
|
||||
@@ -80,7 +82,7 @@ abstract class Base extends \Core\Base
|
||||
return $task;
|
||||
}
|
||||
|
||||
protected function formatTasks(array $tasks)
|
||||
protected function formatTasks($tasks)
|
||||
{
|
||||
if (! empty($tasks)) {
|
||||
foreach ($tasks as &$task) {
|
||||
@@ -91,7 +93,7 @@ abstract class Base extends \Core\Base
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
protected function formatProject(array $project)
|
||||
protected function formatProject($project)
|
||||
{
|
||||
if (! empty($project)) {
|
||||
$project['url'] = array(
|
||||
@@ -104,7 +106,7 @@ abstract class Base extends \Core\Base
|
||||
return $project;
|
||||
}
|
||||
|
||||
protected function formatProjects(array $projects)
|
||||
protected function formatProjects($projects)
|
||||
{
|
||||
if (! empty($projects)) {
|
||||
foreach ($projects as &$project) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Api;
|
||||
|
||||
use Core\ObjectStorage\ObjectStorageException;
|
||||
|
||||
/**
|
||||
* File API controller
|
||||
*
|
||||
@@ -22,16 +24,17 @@ class File extends \Core\Base
|
||||
|
||||
public function downloadFile($file_id)
|
||||
{
|
||||
$file = $this->file->getById($file_id);
|
||||
try {
|
||||
|
||||
if (! empty($file)) {
|
||||
$file = $this->file->getById($file_id);
|
||||
|
||||
$filename = FILES_DIR.$file['path'];
|
||||
|
||||
if (file_exists($filename)) {
|
||||
return base64_encode(file_get_contents($filename));
|
||||
if (! empty($file)) {
|
||||
return base64_encode($this->objectStorage->get($file['path']));
|
||||
}
|
||||
}
|
||||
catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Core\ObjectStorage\ObjectStorageException;
|
||||
|
||||
/**
|
||||
* File controller
|
||||
*
|
||||
@@ -74,15 +76,21 @@ class File extends Base
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
try {
|
||||
|
||||
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'])));
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
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->forceDownload($file['name']);
|
||||
$this->objectStorage->output($file['path']);
|
||||
}
|
||||
catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->response->forceDownload($file['name']);
|
||||
$this->objectStorage->passthru($file['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,15 +118,21 @@ class File extends Base
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
try {
|
||||
|
||||
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'])));
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
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->output($file['path']);
|
||||
}
|
||||
catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->response->contentType($this->file->getImageMimeType($file['name']));
|
||||
$this->objectStorage->passthru($file['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,15 +142,21 @@ class File extends Base
|
||||
*/
|
||||
public function thumbnail()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
try {
|
||||
|
||||
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'])));
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
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->output($this->file->getThumbnailPath($file['path']));
|
||||
}
|
||||
catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->response->contentType('image/jpeg');
|
||||
$this->objectStorage->passthru($this->file->getThumbnailPath($file['path']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ class FileStorage implements ObjectStorageInterface
|
||||
* @access public
|
||||
* @param string $key
|
||||
*/
|
||||
public function passthru($key)
|
||||
public function output($key)
|
||||
{
|
||||
$filename = $this->path.DIRECTORY_SEPARATOR.$key;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ interface ObjectStorageInterface
|
||||
* @access public
|
||||
* @param string $key
|
||||
*/
|
||||
public function passthru($key);
|
||||
public function output($key);
|
||||
|
||||
/**
|
||||
* Move local file to object storage
|
||||
|
||||
@@ -28,8 +28,8 @@ class Loader extends \Core\Base
|
||||
*/
|
||||
public function scan()
|
||||
{
|
||||
if (file_exists(__DIR__.'/../../../plugins')) {
|
||||
$dir = new DirectoryIterator(__DIR__.'/../../../plugins');
|
||||
if (file_exists(PLUGINS_DIR)) {
|
||||
$dir = new DirectoryIterator(PLUGINS_DIR);
|
||||
|
||||
foreach ($dir as $fileinfo) {
|
||||
if (! $fileinfo->isDot() && $fileinfo->isDir()) {
|
||||
@@ -65,7 +65,7 @@ class Loader extends \Core\Base
|
||||
*/
|
||||
public function loadSchema($plugin)
|
||||
{
|
||||
$filename = __DIR__.'/../../../plugins/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php';
|
||||
$filename = PLUGINS_DIR.'/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require_once($filename);
|
||||
|
||||
@@ -10,6 +10,26 @@ namespace Helper;
|
||||
*/
|
||||
class Hook extends \Core\Base
|
||||
{
|
||||
/**
|
||||
* Add assets JS or CSS
|
||||
*
|
||||
* @access public
|
||||
* @param string $type
|
||||
* @param string $hook
|
||||
* @param array $variables
|
||||
* @return string
|
||||
*/
|
||||
public function asset($type, $hook)
|
||||
{
|
||||
$buffer = '';
|
||||
|
||||
foreach ($this->hook->getListeners($hook) as $file) {
|
||||
$buffer .= $this->helper->asset->$type($file);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all attached hooks
|
||||
*
|
||||
|
||||
@@ -36,7 +36,7 @@ class SlackWebhook extends \Core\Base
|
||||
}
|
||||
|
||||
$options = $this->projectIntegration->getParameters($project_id);
|
||||
return $options['slack_webhook_url'];
|
||||
return isset($options['slack_webhook_url']) ? $options['slack_webhook_url'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,14 +52,14 @@ class SlackWebhook extends \Core\Base
|
||||
|
||||
if (! empty($channel)) {
|
||||
return $channel;
|
||||
}
|
||||
}
|
||||
|
||||
$options = $this->projectIntegration->getParameters($project_id);
|
||||
return $options['slack_webhook_channel'];
|
||||
$options = $this->projectIntegration->getParameters($project_id);
|
||||
return isset($options['slack_webhook_channel']) ? $options['slack_webhook_channel'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to the incoming Slack webhook
|
||||
* Send notification to Slack
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
@@ -76,23 +76,52 @@ class SlackWebhook extends \Core\Base
|
||||
$event['event_name'] = $event_name;
|
||||
$event['author'] = $this->user->getFullname($this->session['user']);
|
||||
|
||||
$payload = array(
|
||||
'text' => '*['.$project['name'].']* '.str_replace('"', '"', $this->projectActivity->getTitle($event)).(isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : ''),
|
||||
'username' => 'Kanboard',
|
||||
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
|
||||
);
|
||||
$message = '*['.$project['name'].']* ';
|
||||
$message .= str_replace('"', '"', $this->projectActivity->getTitle($event));
|
||||
$message .= isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : '';
|
||||
|
||||
if ($this->config->get('application_url')) {
|
||||
$payload['text'] .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true);
|
||||
$payload['text'] .= '|'.t('view the task on Kanboard').'>';
|
||||
$message .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true);
|
||||
$message .= '|'.t('view the task on Kanboard').'>';
|
||||
}
|
||||
|
||||
$channel = $this->getChannel($project_id);
|
||||
if (! empty($channel)) {
|
||||
$payload['channel'] = $channel;
|
||||
}
|
||||
|
||||
$this->httpClient->postJson($this->getWebhookUrl($project_id), $payload);
|
||||
$this->sendMessage($project_id, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to Slack
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param string $message
|
||||
*/
|
||||
public function sendMessage($project_id, $message)
|
||||
{
|
||||
$payload = array(
|
||||
'text' => $message,
|
||||
'username' => 'Kanboard',
|
||||
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
|
||||
);
|
||||
|
||||
$this->sendPayload($project_id, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send payload to Slack
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param array $payload
|
||||
*/
|
||||
public function sendPayload($project_id, array $payload)
|
||||
{
|
||||
$channel = $this->getChannel($project_id);
|
||||
|
||||
if (! empty($channel)) {
|
||||
$payload['channel'] = $channel;
|
||||
}
|
||||
|
||||
$this->httpClient->postJson($this->getWebhookUrl($project_id), $payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,28 @@ class Action extends Base
|
||||
*/
|
||||
const TABLE_PARAMS = 'action_has_params';
|
||||
|
||||
/**
|
||||
* Extended actions
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $actions = array();
|
||||
|
||||
/**
|
||||
* Extend the list of default actions
|
||||
*
|
||||
* @access public
|
||||
* @param string $className
|
||||
* @param string $description
|
||||
* @return Action
|
||||
*/
|
||||
public function extendActions($className, $description)
|
||||
{
|
||||
$this->actions[$className] = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name and description of available actions
|
||||
*
|
||||
@@ -62,6 +84,8 @@ class Action extends Base
|
||||
'TaskAssignColorLink' => t('Change task color when using a specific task link'),
|
||||
);
|
||||
|
||||
$values = array_merge($values, $this->actions);
|
||||
|
||||
asort($values);
|
||||
|
||||
return $values;
|
||||
@@ -296,7 +320,7 @@ class Action extends Base
|
||||
*/
|
||||
public function load($name, $project_id, $event)
|
||||
{
|
||||
$className = '\Action\\'.$name;
|
||||
$className = $name{0} !== '\\' ? '\Action\\'.$name : $name;
|
||||
return new $className($this->container, $project_id, $event);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ class File extends Base
|
||||
$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();
|
||||
}
|
||||
catch (ObjectStorageException $e) {
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<?= $this->asset->css('assets/css/print.css', true, 'print') ?>
|
||||
<?= $this->asset->customCss() ?>
|
||||
|
||||
<?= $this->hook->asset('css', 'template:layout:css') ?>
|
||||
<?= $this->hook->asset('js', 'template:layout:js') ?>
|
||||
|
||||
<link rel="icon" type="image/png" href="<?= $this->url->dir() ?>assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" href="<?= $this->url->dir() ?>assets/img/touch-icon-iphone.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="<?= $this->url->dir() ?>assets/img/touch-icon-ipad.png">
|
||||
@@ -44,7 +47,7 @@
|
||||
<?= $this->render('header', array(
|
||||
'title' => $title,
|
||||
'description' => isset($description) ? $description : '',
|
||||
'board_selector' => $board_selector,
|
||||
'board_selector' => isset($board_selector) ? $board_selector : array(),
|
||||
)) ?>
|
||||
<section class="page">
|
||||
<?= $this->app->flashMessage() ?>
|
||||
|
||||
@@ -29,24 +29,7 @@ if (! extension_loaded('mbstring')) {
|
||||
die('PHP extension required: mbstring');
|
||||
}
|
||||
|
||||
// Check if /data is writeable
|
||||
if (! is_writable('data')) {
|
||||
die('The directory "data" must be writeable by your web server user');
|
||||
}
|
||||
|
||||
// Fix wrong value for arg_separator.output, used by the function http_build_query()
|
||||
if (ini_get('arg_separator.output') === '&') {
|
||||
ini_set('arg_separator.output', '&');
|
||||
}
|
||||
|
||||
// Prepare folder for uploaded files
|
||||
if (! is_dir(FILES_DIR)) {
|
||||
if (! mkdir(FILES_DIR, 0755, true)) {
|
||||
die('Unable to create the upload directory: "'.FILES_DIR.'"');
|
||||
}
|
||||
}
|
||||
|
||||
// Check permissions for files folder
|
||||
if (! is_writable(FILES_DIR)) {
|
||||
die('The directory "'.FILES_DIR.'" must be writeable by your webserver user');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
defined('DEBUG') or define('DEBUG', false);
|
||||
defined('DEBUG_FILE') or define('DEBUG_FILE', __DIR__.'/../data/debug.log');
|
||||
|
||||
// Plugin directory
|
||||
defined('PLUGINS_DIR') or define('PLUGINS_DIR', __DIR__.'/../plugins');
|
||||
|
||||
// Application version
|
||||
defined('APP_VERSION') or define('APP_VERSION', 'master');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user