Tasks Module Stage 3: Attachments

- Add migration for task_attachments table
- Create TaskAttachmentModel with file handling methods
- Add TaskService methods for upload/delete attachments
- Add API routes for attachments (upload, delete, download)
- Add controller methods for attachment operations
- Add UI section in task view with file upload form
- Add JavaScript handlers for AJAX file operations
This commit is contained in:
2026-02-08 19:43:39 +00:00
parent 7ebca3c762
commit a9976a5d85
6 changed files with 463 additions and 0 deletions
@@ -614,4 +614,110 @@ class TasksController extends BaseController
'success' => $result,
]);
}
// ========== Вложения (Attachments) ==========
/**
* API: загрузить вложение
*/
public function uploadAttachment(int $taskId)
{
if (!$this->access->canEdit('tasks')) {
return $this->response->setJSON([
'success' => false,
'error' => 'Нет прав для изменения задач'
]);
}
$organizationId = $this->requireActiveOrg();
$userId = $this->getCurrentUserId();
// Проверяем что задача принадлежит организации
$task = $this->taskService->getTask($taskId, $organizationId);
if (!$task) {
return $this->response->setJSON([
'success' => false,
'error' => 'Задача не найдена'
]);
}
$file = $this->request->getFile('file');
if (!$file || !$file->isValid()) {
return $this->response->setJSON([
'success' => false,
'error' => 'Файл не загружен'
]);
}
$attachmentId = $this->taskService->uploadAttachment($taskId, [
'name' => $file->getName(),
'type' => $file->getClientMimeType(),
'tmp_name' => $file->getTempName(),
'size' => $file->getSize(),
], $userId);
return $this->response->setJSON([
'success' => (bool) $attachmentId,
'attachment_id' => $attachmentId,
]);
}
/**
* API: удалить вложение
*/
public function deleteAttachment(int $taskId, int $attachmentId)
{
if (!$this->access->canEdit('tasks')) {
return $this->response->setJSON([
'success' => false,
'error' => 'Нет прав для изменения задач'
]);
}
$organizationId = $this->requireActiveOrg();
$userId = $this->getCurrentUserId();
// Проверяем что задача принадлежит организации
$task = $this->taskService->getTask($taskId, $organizationId);
if (!$task) {
return $this->response->setJSON([
'success' => false,
'error' => 'Задача не найдена'
]);
}
$result = $this->taskService->deleteAttachment($taskId, $attachmentId, $userId);
return $this->response->setJSON([
'success' => $result,
]);
}
/**
* API: скачать вложение
*/
public function downloadAttachment(int $taskId, int $attachmentId)
{
$organizationId = $this->requireActiveOrg();
// Проверяем что задача принадлежит организации
$task = $this->taskService->getTask($taskId, $organizationId);
if (!$task) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Задача не найдена');
}
$attachmentModel = new \App\Modules\Tasks\Models\TaskAttachmentModel();
$attachment = $attachmentModel->find($attachmentId);
if (!$attachment || $attachment['task_id'] != $taskId) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Вложение не найдено');
}
if (!file_exists($attachment['file_path'])) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Файл не найден');
}
return $this->response->download($attachment['file_path'], null)
->setFileName($attachment['file_name']);
}
}