summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2016-06-26 15:17:38 -0400
committerFrederic Guillot <fred@kanboard.net>2016-06-26 15:17:38 -0400
commitf62112983635a281108575bb69bb90df6bed68b7 (patch)
tree6e5cf4713e0ba1f5301376f568f0229ddd27a520
parent3d34681610854474cb9dbdd93886dbcf0e208a99 (diff)
Added new API calls for project attachements
-rw-r--r--ChangeLog2
-rw-r--r--app/Api/Procedure/ProjectFileProcedure.php68
-rw-r--r--app/Api/Procedure/SubtaskTimeTrackingProcedure.php6
-rw-r--r--app/Api/Procedure/TaskFileProcedure.php4
-rw-r--r--app/ServiceProvider/ApiProvider.php2
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php1
-rw-r--r--doc/api-json-rpc.markdown3
-rw-r--r--doc/api-project-file-procedures.markdown221
-rw-r--r--doc/api-task-file-procedures.markdown (renamed from doc/api-file-procedures.markdown)4
-rwxr-xr-xtests/docker/entrypoint.sh2
-rw-r--r--tests/integration/ProjectFileProcedureTest.php66
-rw-r--r--tests/integration/TaskFileProcedureTest.php2
12 files changed, 370 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 85966ed8..7a56139c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@ New features:
* Added application and project roles validation for API procedure calls
* Added new API call: "getProjectByIdentifier"
-* Added new API calls for external task links
+* Added new API calls for external task links, project attachments
Improvements:
diff --git a/app/Api/Procedure/ProjectFileProcedure.php b/app/Api/Procedure/ProjectFileProcedure.php
new file mode 100644
index 00000000..48466ce3
--- /dev/null
+++ b/app/Api/Procedure/ProjectFileProcedure.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Core\ObjectStorage\ObjectStorageException;
+
+/**
+ * Project File API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class ProjectFileProcedure extends BaseProcedure
+{
+ public function getProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectFile', $project_id);
+ return $this->projectFileModel->getById($file_id);
+ }
+
+ public function getAllProjectFiles($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllProjectFiles', $project_id);
+ return $this->projectFileModel->getAll($project_id);
+ }
+
+ public function downloadProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadProjectFile', $project_id);
+
+ try {
+ $file = $this->projectFileModel->getById($file_id);
+
+ if (! empty($file)) {
+ return base64_encode($this->objectStorage->get($file['path']));
+ }
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+
+ return '';
+ }
+
+ public function createProjectFile($project_id, $filename, $blob)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createProjectFile', $project_id);
+
+ try {
+ return $this->projectFileModel->uploadContent($project_id, $filename, $blob);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
+ return false;
+ }
+ }
+
+ public function removeProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectFile', $project_id);
+ return $this->projectFileModel->remove($file_id);
+ }
+
+ public function removeAllProjectFiles($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllProjectFiles', $project_id);
+ return $this->projectFileModel->removeAll($project_id);
+ }
+}
diff --git a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
index 5d1988d6..b6d1102a 100644
--- a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
+++ b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
@@ -5,7 +5,7 @@ namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\SubtaskAuthorization;
/**
- * Subtask Time Tracking API controller
+ * Subtask Time Tracking API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
@@ -25,13 +25,13 @@ class SubtaskTimeTrackingProcedure extends BaseProcedure
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
}
- public function logSubtaskEndTime($subtask_id,$user_id)
+ public function logSubtaskEndTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskEndTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
}
- public function getSubtaskTimeSpent($subtask_id,$user_id)
+ public function getSubtaskTimeSpent($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
diff --git a/app/Api/Procedure/TaskFileProcedure.php b/app/Api/Procedure/TaskFileProcedure.php
index 5aa7ea0b..bd006578 100644
--- a/app/Api/Procedure/TaskFileProcedure.php
+++ b/app/Api/Procedure/TaskFileProcedure.php
@@ -30,7 +30,7 @@ class TaskFileProcedure extends BaseProcedure
public function downloadTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
-
+
try {
$file = $this->taskFileModel->getById($file_id);
@@ -51,7 +51,7 @@ class TaskFileProcedure extends BaseProcedure
try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php
index 194bee5b..5cf6231c 100644
--- a/app/ServiceProvider/ApiProvider.php
+++ b/app/ServiceProvider/ApiProvider.php
@@ -9,6 +9,7 @@ use Kanboard\Api\Procedure\BoardProcedure;
use Kanboard\Api\Procedure\CategoryProcedure;
use Kanboard\Api\Procedure\ColumnProcedure;
use Kanboard\Api\Procedure\CommentProcedure;
+use Kanboard\Api\Procedure\ProjectFileProcedure;
use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
use Kanboard\Api\Procedure\TaskFileProcedure;
use Kanboard\Api\Procedure\GroupProcedure;
@@ -58,6 +59,7 @@ class ApiProvider implements ServiceProviderInterface
->withObject(new CategoryProcedure($container))
->withObject(new CommentProcedure($container))
->withObject(new TaskFileProcedure($container))
+ ->withObject(new ProjectFileProcedure($container))
->withObject(new LinkProcedure($container))
->withObject(new ProjectProcedure($container))
->withObject(new ProjectPermissionProcedure($container))
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index 34b81b9d..978bc05b 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -202,6 +202,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('ProjectFileProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown
index 8e783e71..6498b0cc 100644
--- a/doc/api-json-rpc.markdown
+++ b/doc/api-json-rpc.markdown
@@ -58,7 +58,8 @@ Usage
- [Automatic Actions](api-action-procedures.markdown)
- [Tasks](api-task-procedures.markdown)
- [Subtasks](api-subtask-procedures.markdown)
-- [Files](api-file-procedures.markdown)
+- [Task Files](api-task-file-procedures.markdown)
+- [Project Files](api-project-file-procedures.markdown)
- [Links](api-link-procedures.markdown)
- [Internal Task Links](api-internal-task-link-procedures.markdown)
- [External Task Links](api-external-task-link-procedures.markdown)
diff --git a/doc/api-project-file-procedures.markdown b/doc/api-project-file-procedures.markdown
new file mode 100644
index 00000000..fdc5da1a
--- /dev/null
+++ b/doc/api-project-file-procedures.markdown
@@ -0,0 +1,221 @@
+Project File API Procedures
+===========================
+
+## createProjectFile
+
+- Purpose: **Create and upload a new project attachment**
+- Parameters:
+ - **project_id** (integer, required)
+ - **filename** (integer, required)
+ - **blob** File content encoded in base64 (string, required)
+- Result on success: **file_id**
+- Result on failure: **false**
+- Note: **The maximum file size depends of your PHP configuration, this method should not be used to upload large files**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createProjectFile",
+ "id": 94500810,
+ "params": [
+ 1,
+ "My file",
+ "cGxhaW4gdGV4dCBmaWxl"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 94500810,
+ "result": 1
+}
+```
+
+## getAllProjectFiles
+
+- Purpose: **Get all files attached to a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **list of files**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllProjectFiles",
+ "id": 1880662820,
+ "params": {
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1880662820,
+ "result": [
+ {
+ "id": "1",
+ "name": "My file",
+ "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
+ "is_image": "0",
+ "project_id": "1",
+ "date": "1432509941",
+ "user_id": "0",
+ "size": "15",
+ "username": null,
+ "user_name": null
+ }
+ ]
+}
+```
+
+## getProjectFile
+
+- Purpose: **Get file information**
+- Parameters:
+ - **project_id** (integer, required)
+ - **file_id** (integer, required)
+- Result on success: **file properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectFile",
+ "id": 318676852,
+ "params": [
+ "42",
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 318676852,
+ "result": {
+ "id": "1",
+ "name": "My file",
+ "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
+ "is_image": "0",
+ "project_id": "1",
+ "date": "1432509941",
+ "user_id": "0",
+ "size": "15"
+ }
+}
+```
+
+## downloadProjectFile
+
+- Purpose: **Download project file contents (encoded in base64)**
+- Parameters:
+ - **project_id** (integer, required)
+ - **file_id** (integer, required)
+- Result on success: **base64 encoded string**
+- Result on failure: **empty string**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "downloadProjectFile",
+ "id": 235943344,
+ "params": [
+ "1",
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 235943344,
+ "result": "cGxhaW4gdGV4dCBmaWxl"
+}
+```
+
+## removeProjectFile
+
+- Purpose: **Remove a file associated to a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **file_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProjectFile",
+ "id": 447036524,
+ "params": [
+ "1",
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 447036524,
+ "result": true
+}
+```
+
+## removeAllProjectFiles
+
+- Purpose: **Remove all files associated to a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeAllProjectFiles",
+ "id": 593312993,
+ "params": {
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 593312993,
+ "result": true
+}
+```
diff --git a/doc/api-file-procedures.markdown b/doc/api-task-file-procedures.markdown
index 930be733..51840bea 100644
--- a/doc/api-file-procedures.markdown
+++ b/doc/api-task-file-procedures.markdown
@@ -1,5 +1,5 @@
-API File Procedures
-===================
+Task File API Procedures
+========================
## createTaskFile
diff --git a/tests/docker/entrypoint.sh b/tests/docker/entrypoint.sh
index a88c7ed8..5a37ae4e 100755
--- a/tests/docker/entrypoint.sh
+++ b/tests/docker/entrypoint.sh
@@ -23,7 +23,7 @@ case "$1" in
/var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.sqlite.xml
;;
"integration-test-postgres")
- wait_schema_creation 5
+ wait_schema_creation 10
/var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.postgres.xml
;;
"integration-test-mysql")
diff --git a/tests/integration/ProjectFileProcedureTest.php b/tests/integration/ProjectFileProcedureTest.php
new file mode 100644
index 00000000..8ac70d87
--- /dev/null
+++ b/tests/integration/ProjectFileProcedureTest.php
@@ -0,0 +1,66 @@
+<?php
+
+require_once __DIR__.'/BaseProcedureTest.php';
+
+class ProjectFileProcedureTest extends BaseProcedureTest
+{
+ protected $projectName = 'My project to test project files';
+ protected $fileId;
+
+ public function testAll()
+ {
+ $this->assertCreateTeamProject();
+ $this->assertCreateProjectFile();
+ $this->assertGetProjectFile();
+ $this->assertDownloadProjectFile();
+ $this->assertGetAllFiles();
+ $this->assertRemoveProjectFile();
+ $this->assertRemoveAllProjectFiles();
+ }
+
+ public function assertCreateProjectFile()
+ {
+ $this->fileId = $this->app->createProjectFile($this->projectId, 'My file.txt', base64_encode('plain text file'));
+ $this->assertNotFalse($this->fileId);
+ }
+
+ public function assertGetProjectFile()
+ {
+ $file = $this->app->getProjectFile($this->projectId, $this->fileId);
+ $this->assertNotEmpty($file);
+ $this->assertEquals('My file.txt', $file['name']);
+ }
+
+ public function assertDownloadProjectFile()
+ {
+ $content = $this->app->downloadProjectFile($this->projectId, $this->fileId);
+ $this->assertNotEmpty($content);
+ $this->assertEquals('plain text file', base64_decode($content));
+ }
+
+ public function assertGetAllFiles()
+ {
+ $files = $this->app->getAllProjectFiles($this->projectId);
+ $this->assertCount(1, $files);
+ $this->assertEquals('My file.txt', $files[0]['name']);
+ }
+
+ public function assertRemoveProjectFile()
+ {
+ $this->assertTrue($this->app->removeProjectFile($this->projectId, $this->fileId));
+
+ $files = $this->app->getAllProjectFiles($this->projectId);
+ $this->assertEmpty($files);
+ }
+
+ public function assertRemoveAllProjectFiles()
+ {
+ $this->assertCreateProjectFile();
+ $this->assertCreateProjectFile();
+
+ $this->assertTrue($this->app->removeAllProjectFiles($this->projectId));
+
+ $files = $this->app->getAllProjectFiles($this->projectId);
+ $this->assertEmpty($files);
+ }
+}
diff --git a/tests/integration/TaskFileProcedureTest.php b/tests/integration/TaskFileProcedureTest.php
index 61155555..60909ecd 100644
--- a/tests/integration/TaskFileProcedureTest.php
+++ b/tests/integration/TaskFileProcedureTest.php
@@ -21,7 +21,7 @@ class TaskFileProcedureTest extends BaseProcedureTest
public function assertCreateTaskFile()
{
- $this->fileId = $this->app->createTaskFile(1, $this->taskId, 'My file', base64_encode('plain text file'));
+ $this->fileId = $this->app->createTaskFile($this->projectId, $this->taskId, 'My file', base64_encode('plain text file'));
$this->assertNotFalse($this->fileId);
}