summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorMax Kamashev <kamashev@gollard.ru>2015-09-24 12:07:40 +0300
committerMax Kamashev <kamashev@gollard.ru>2015-09-24 12:07:40 +0300
commit3f5b636c998171837fc2265f760359b421d67b61 (patch)
treea505f4f1767efff50224b649e647a02ae3638e12 /app
parent5b2e49d2945ce2c1daaf4dd78746a910eab9c9c8 (diff)
parent1b0b69a43f7528fd5188d4b48216c6fa7e32eac3 (diff)
Merge branch 'master' of https://github.com/ukko/kanboard into 1245_bug_with_subtask_timer
Diffstat (limited to 'app')
-rw-r--r--app/Action/Base.php11
-rw-r--r--app/Api/Base.php10
-rw-r--r--app/Api/File.php15
-rw-r--r--app/Controller/File.php62
-rw-r--r--app/Core/ObjectStorage/FileStorage.php2
-rw-r--r--app/Core/ObjectStorage/ObjectStorageInterface.php2
-rw-r--r--app/Core/Plugin/Loader.php6
-rw-r--r--app/Helper/Hook.php20
-rw-r--r--app/Integration/SlackWebhook.php63
-rw-r--r--app/Model/Action.php26
-rw-r--r--app/Model/File.php4
-rw-r--r--app/Template/layout.php5
-rw-r--r--app/check_setup.php17
-rw-r--r--app/constants.php3
14 files changed, 174 insertions, 72 deletions
diff --git a/app/Action/Base.php b/app/Action/Base.php
index d0c81d89..c8ff02a4 100644
--- a/app/Action/Base.php
+++ b/app/Action/Base.php
@@ -127,6 +127,17 @@ abstract class Base extends \Core\Base
}
/**
+ * Get project id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getProjectId()
+ {
+ return $this->project_id;
+ }
+
+ /**
* Set an user defined parameter
*
* @access public
diff --git a/app/Api/Base.php b/app/Api/Base.php
index fef36e0c..0287e0ec 100644
--- a/app/Api/Base.php
+++ b/app/Api/Base.php
@@ -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) {
diff --git a/app/Api/File.php b/app/Api/File.php
index 97aa9d82..ad736ad4 100644
--- a/app/Api/File.php
+++ b/app/Api/File.php
@@ -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);
-
- if (! empty($file)) {
+ try {
- $filename = FILES_DIR.$file['path'];
+ $file = $this->file->getById($file_id);
- 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 '';
}
diff --git a/app/Controller/File.php b/app/Controller/File.php
index 7b7c75ee..ef90c55a 100644
--- a/app/Controller/File.php
+++ b/app/Controller/File.php
@@ -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->passthru($file['path']);
+ $this->response->forceDownload($file['name']);
+ $this->objectStorage->output($file['path']);
+ }
+ catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
}
/**
@@ -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'));
- $this->response->contentType($this->file->getImageMimeType($file['name']));
- $this->objectStorage->passthru($file['path']);
+ 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());
+ }
}
/**
@@ -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->passthru($this->file->getThumbnailPath($file['path']));
+ $this->response->contentType('image/jpeg');
+ $this->objectStorage->output($this->file->getThumbnailPath($file['path']));
+ }
+ catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
}
/**
diff --git a/app/Core/ObjectStorage/FileStorage.php b/app/Core/ObjectStorage/FileStorage.php
index 96478c3a..fa1efe21 100644
--- a/app/Core/ObjectStorage/FileStorage.php
+++ b/app/Core/ObjectStorage/FileStorage.php
@@ -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;
diff --git a/app/Core/ObjectStorage/ObjectStorageInterface.php b/app/Core/ObjectStorage/ObjectStorageInterface.php
index 5440cf2b..180bdf86 100644
--- a/app/Core/ObjectStorage/ObjectStorageInterface.php
+++ b/app/Core/ObjectStorage/ObjectStorageInterface.php
@@ -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
diff --git a/app/Core/Plugin/Loader.php b/app/Core/Plugin/Loader.php
index 2758f37e..04b2bfff 100644
--- a/app/Core/Plugin/Loader.php
+++ b/app/Core/Plugin/Loader.php
@@ -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);
diff --git a/app/Helper/Hook.php b/app/Helper/Hook.php
index d7fe3d34..bf879878 100644
--- a/app/Helper/Hook.php
+++ b/app/Helper/Hook.php
@@ -11,6 +11,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
*
* @access public
diff --git a/app/Integration/SlackWebhook.php b/app/Integration/SlackWebhook.php
index d238652f..71244739 100644
--- a/app/Integration/SlackWebhook.php
+++ b/app/Integration/SlackWebhook.php
@@ -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('&quot;', '"', $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('&quot;', '"', $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->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->httpClient->postJson($this->getWebhookUrl($project_id), $payload);
+ $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);
}
}
diff --git a/app/Model/Action.php b/app/Model/Action.php
index 87058cce..57bd5b0d 100644
--- a/app/Model/Action.php
+++ b/app/Model/Action.php
@@ -31,6 +31,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
*
* @access public
@@ -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);
}
diff --git a/app/Model/File.php b/app/Model/File.php
index 7adab42b..1d44a415 100644
--- a/app/Model/File.php
+++ b/app/Model/File.php
@@ -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) {
diff --git a/app/Template/layout.php b/app/Template/layout.php
index 49ac2a08..20582952 100644
--- a/app/Template/layout.php
+++ b/app/Template/layout.php
@@ -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() ?>
diff --git a/app/check_setup.php b/app/check_setup.php
index 624b6b34..65f291e5 100644
--- a/app/check_setup.php
+++ b/app/check_setup.php
@@ -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') === '&amp;') {
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');
-}
diff --git a/app/constants.php b/app/constants.php
index f25bd903..47e14c9e 100644
--- a/app/constants.php
+++ b/app/constants.php
@@ -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');