Added new API calls for project attachements
This commit is contained in:
@@ -5,7 +5,7 @@ New features:
|
|||||||
|
|
||||||
* Added application and project roles validation for API procedure calls
|
* Added application and project roles validation for API procedure calls
|
||||||
* Added new API call: "getProjectByIdentifier"
|
* Added new API call: "getProjectByIdentifier"
|
||||||
* Added new API calls for external task links
|
* Added new API calls for external task links, project attachments
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
|
|||||||
68
app/Api/Procedure/ProjectFileProcedure.php
Normal file
68
app/Api/Procedure/ProjectFileProcedure.php
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ namespace Kanboard\Api\Procedure;
|
|||||||
use Kanboard\Api\Authorization\SubtaskAuthorization;
|
use Kanboard\Api\Authorization\SubtaskAuthorization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtask Time Tracking API controller
|
* Subtask Time Tracking API controller
|
||||||
*
|
*
|
||||||
* @package Kanboard\Api\Procedure
|
* @package Kanboard\Api\Procedure
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
@@ -25,13 +25,13 @@ class SubtaskTimeTrackingProcedure extends BaseProcedure
|
|||||||
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
|
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);
|
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskEndTime', $subtask_id);
|
||||||
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_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);
|
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
|
||||||
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
|
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class TaskFileProcedure extends BaseProcedure
|
|||||||
try {
|
try {
|
||||||
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
|
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
|
||||||
} catch (ObjectStorageException $e) {
|
} catch (ObjectStorageException $e) {
|
||||||
$this->logger->error($e->getMessage());
|
$this->logger->error(__METHOD__.': '.$e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use Kanboard\Api\Procedure\BoardProcedure;
|
|||||||
use Kanboard\Api\Procedure\CategoryProcedure;
|
use Kanboard\Api\Procedure\CategoryProcedure;
|
||||||
use Kanboard\Api\Procedure\ColumnProcedure;
|
use Kanboard\Api\Procedure\ColumnProcedure;
|
||||||
use Kanboard\Api\Procedure\CommentProcedure;
|
use Kanboard\Api\Procedure\CommentProcedure;
|
||||||
|
use Kanboard\Api\Procedure\ProjectFileProcedure;
|
||||||
use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
|
use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
|
||||||
use Kanboard\Api\Procedure\TaskFileProcedure;
|
use Kanboard\Api\Procedure\TaskFileProcedure;
|
||||||
use Kanboard\Api\Procedure\GroupProcedure;
|
use Kanboard\Api\Procedure\GroupProcedure;
|
||||||
@@ -58,6 +59,7 @@ class ApiProvider implements ServiceProviderInterface
|
|||||||
->withObject(new CategoryProcedure($container))
|
->withObject(new CategoryProcedure($container))
|
||||||
->withObject(new CommentProcedure($container))
|
->withObject(new CommentProcedure($container))
|
||||||
->withObject(new TaskFileProcedure($container))
|
->withObject(new TaskFileProcedure($container))
|
||||||
|
->withObject(new ProjectFileProcedure($container))
|
||||||
->withObject(new LinkProcedure($container))
|
->withObject(new LinkProcedure($container))
|
||||||
->withObject(new ProjectProcedure($container))
|
->withObject(new ProjectProcedure($container))
|
||||||
->withObject(new ProjectPermissionProcedure($container))
|
->withObject(new ProjectPermissionProcedure($container))
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ class AuthenticationProvider implements ServiceProviderInterface
|
|||||||
$acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER);
|
$acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER);
|
||||||
$acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER);
|
$acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER);
|
||||||
$acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER);
|
$acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER);
|
||||||
|
$acl->add('ProjectFileProcedure', '*', Role::PROJECT_MEMBER);
|
||||||
$acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER);
|
$acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER);
|
||||||
$acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
|
$acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
|
||||||
$acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
|
$acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ Usage
|
|||||||
- [Automatic Actions](api-action-procedures.markdown)
|
- [Automatic Actions](api-action-procedures.markdown)
|
||||||
- [Tasks](api-task-procedures.markdown)
|
- [Tasks](api-task-procedures.markdown)
|
||||||
- [Subtasks](api-subtask-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)
|
- [Links](api-link-procedures.markdown)
|
||||||
- [Internal Task Links](api-internal-task-link-procedures.markdown)
|
- [Internal Task Links](api-internal-task-link-procedures.markdown)
|
||||||
- [External Task Links](api-external-task-link-procedures.markdown)
|
- [External Task Links](api-external-task-link-procedures.markdown)
|
||||||
|
|||||||
221
doc/api-project-file-procedures.markdown
Normal file
221
doc/api-project-file-procedures.markdown
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
API File Procedures
|
Task File API Procedures
|
||||||
===================
|
========================
|
||||||
|
|
||||||
## createTaskFile
|
## createTaskFile
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ case "$1" in
|
|||||||
/var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.sqlite.xml
|
/var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.sqlite.xml
|
||||||
;;
|
;;
|
||||||
"integration-test-postgres")
|
"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
|
/var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.postgres.xml
|
||||||
;;
|
;;
|
||||||
"integration-test-mysql")
|
"integration-test-mysql")
|
||||||
|
|||||||
66
tests/integration/ProjectFileProcedureTest.php
Normal file
66
tests/integration/ProjectFileProcedureTest.php
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ class TaskFileProcedureTest extends BaseProcedureTest
|
|||||||
|
|
||||||
public function assertCreateTaskFile()
|
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);
|
$this->assertNotFalse($this->fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user